Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ndp.c
00001 /** 00002 * @file ndp.c 00003 * @brief NDP (Neighbor Discovery Protocol) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @section Description 00026 * 00027 * The Neighbor Discovery Protocol is responsible for address autoconfiguration 00028 * of nodes, discovery of the link-layer addresses of other nodes, duplicate 00029 * address detection, finding available routers and address prefix discovery. 00030 * Refer to RFC 4861 for more details 00031 * 00032 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00033 * @version 1.7.6 00034 **/ 00035 00036 //Switch to the appropriate trace level 00037 #define TRACE_LEVEL NDP_TRACE_LEVEL 00038 00039 //Dependencies 00040 #include <limits.h> 00041 #include <string.h> 00042 #include "core/net.h" 00043 #include "ipv6/ipv6.h" 00044 #include "ipv6/ipv6_misc.h" 00045 #include "ipv6/icmpv6.h" 00046 #include "ipv6/ndp.h" 00047 #include "ipv6/ndp_cache.h" 00048 #include "ipv6/ndp_misc.h" 00049 #include "ipv6/slaac.h" 00050 #include "debug.h" 00051 00052 //Check TCP/IP stack configuration 00053 #if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED) 00054 00055 //Tick counter to handle periodic operations 00056 systime_t ndpTickCounter; 00057 00058 00059 /** 00060 * @brief Neighbor cache initialization 00061 * @param[in] interface Underlying network interface 00062 * @return Error code 00063 **/ 00064 00065 error_t ndpInit(NetInterface *interface) 00066 { 00067 NdpContext *context; 00068 00069 //Point to the NDP context 00070 context = &interface->ndpContext; 00071 00072 //Clear the NDP context 00073 memset(context, 0, sizeof(NdpContext)); 00074 00075 //Initialize interface specific variables 00076 context->reachableTime = NDP_REACHABLE_TIME; 00077 context->retransTimer = NDP_RETRANS_TIMER; 00078 context->dupAddrDetectTransmits = NDP_DUP_ADDR_DETECT_TRANSMITS; 00079 context->minRtrSolicitationDelay = NDP_MIN_RTR_SOLICITATION_DELAY; 00080 context->maxRtrSolicitationDelay = NDP_MAX_RTR_SOLICITATION_DELAY; 00081 context->rtrSolicitationInterval = NDP_RTR_SOLICITATION_INTERVAL; 00082 context->maxRtrSolicitations = NDP_MAX_RTR_SOLICITATIONS; 00083 00084 //Successful initialization 00085 return NO_ERROR; 00086 } 00087 00088 00089 /** 00090 * @brief Address resolution using Neighbor Discovery protocol 00091 * @param[in] interface Underlying network interface 00092 * @param[in] ipAddr IPv6 address 00093 * @param[in] macAddr Physical address matching the specified IPv6 address 00094 * @return Error code 00095 **/ 00096 00097 error_t ndpResolve(NetInterface *interface, const Ipv6Addr *ipAddr, MacAddr *macAddr) 00098 { 00099 error_t error; 00100 NdpNeighborCacheEntry *entry; 00101 00102 //Search the ndpCacheMutex cache for the specified IPv6 address 00103 entry = ndpFindNeighborCacheEntry(interface, ipAddr); 00104 00105 //Check whether a matching entry has been found 00106 if(entry != NULL) 00107 { 00108 //Check the state of the Neighbor cache entry 00109 if(entry->state == NDP_STATE_INCOMPLETE) 00110 { 00111 //The address resolution is already in progress 00112 error = ERROR_IN_PROGRESS; 00113 } 00114 else if(entry->state == NDP_STATE_STALE) 00115 { 00116 //Copy the MAC address associated with the specified IPv6 address 00117 *macAddr = entry->macAddr; 00118 00119 //Start delay timer 00120 entry->timestamp = osGetSystemTime(); 00121 //Delay before sending the first probe 00122 entry->timeout = NDP_DELAY_FIRST_PROBE_TIME; 00123 //Switch to the DELAY state 00124 entry->state = NDP_STATE_DELAY; 00125 00126 //Successful address resolution 00127 error = NO_ERROR; 00128 } 00129 else 00130 { 00131 //Copy the MAC address associated with the specified IPv6 address 00132 *macAddr = entry->macAddr; 00133 00134 //Successful address resolution 00135 error = NO_ERROR; 00136 } 00137 } 00138 else 00139 { 00140 //If no entry exists, then create a new one 00141 entry = ndpCreateNeighborCacheEntry(interface); 00142 00143 //Neighbor Cache entry successfully created? 00144 if(entry != NULL) 00145 { 00146 //Record the IPv6 address whose MAC address is unknown 00147 entry->ipAddr = *ipAddr; 00148 00149 //Reset retransmission counter 00150 entry->retransmitCount = 0; 00151 //No packet are pending in the transmit queue 00152 entry->queueSize = 0; 00153 00154 //Send a multicast Neighbor Solicitation message 00155 ndpSendNeighborSol(interface, ipAddr, TRUE); 00156 00157 //Save the time at which the message was sent 00158 entry->timestamp = osGetSystemTime(); 00159 //Set timeout value 00160 entry->timeout = interface->ndpContext.retransTimer; 00161 //Enter INCOMPLETE state 00162 entry->state = NDP_STATE_INCOMPLETE; 00163 00164 //The address resolution is in progress 00165 error = ERROR_IN_PROGRESS; 00166 } 00167 else 00168 { 00169 //Failed to create Neighbor Cache entry... 00170 error = ERROR_OUT_OF_RESOURCES; 00171 } 00172 } 00173 00174 //Return status code 00175 return error; 00176 } 00177 00178 00179 /** 00180 * @brief Enqueue an IPv6 packet waiting for address resolution 00181 * @param[in] srcInterface Interface from which the packet has been received 00182 * @param[in] destInterface Interface on which the packet should be sent 00183 * @param[in] ipAddr IPv6 address of the destination host 00184 * @param[in] buffer Multi-part buffer containing the packet to be enqueued 00185 * @param[in] offset Offset to the first byte of the packet 00186 * @return Error code 00187 **/ 00188 00189 error_t ndpEnqueuePacket(NetInterface *srcInterface, NetInterface *destInterface, 00190 const Ipv6Addr *ipAddr, NetBuffer *buffer, size_t offset) 00191 { 00192 error_t error; 00193 uint_t i; 00194 size_t length; 00195 NdpNeighborCacheEntry *entry; 00196 00197 //Retrieve the length of the multi-part buffer 00198 length = netBufferGetLength(buffer); 00199 00200 //Search the Neighbor cache for the specified IPv6 address 00201 entry = ndpFindNeighborCacheEntry(destInterface, ipAddr); 00202 00203 //Check whether a matching entry exists 00204 if(entry != NULL) 00205 { 00206 //Check current state 00207 if(entry->state == NDP_STATE_INCOMPLETE) 00208 { 00209 //Check whether the packet queue is full 00210 if(entry->queueSize >= NDP_MAX_PENDING_PACKETS) 00211 { 00212 //When the queue overflows, the new arrival should replace the oldest entry 00213 netBufferFree(entry->queue[0].buffer); 00214 00215 //Make room for the new packet 00216 for(i = 1; i < NDP_MAX_PENDING_PACKETS; i++) 00217 entry->queue[i - 1] = entry->queue[i]; 00218 00219 //Adjust the number of pending packets 00220 entry->queueSize--; 00221 } 00222 00223 //Index of the entry to be filled in 00224 i = entry->queueSize; 00225 //Allocate a memory buffer to store the packet 00226 entry->queue[i].buffer = netBufferAlloc(length); 00227 00228 //Successful memory allocation? 00229 if(entry->queue[i].buffer != NULL) 00230 { 00231 //If the IPv6 packet has been forwarded, record the network 00232 //interface from which the packet has been received 00233 entry->queue[i].srcInterface = srcInterface; 00234 00235 //Copy the contents of the IPv6 packet 00236 netBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length); 00237 //Offset to the first byte of the IPv6 header 00238 entry->queue[i].offset = offset; 00239 00240 //Increment the number of queued packets 00241 entry->queueSize++; 00242 //The packet was successfully enqueued 00243 error = NO_ERROR; 00244 } 00245 else 00246 { 00247 //Failed to allocate memory 00248 error = ERROR_OUT_OF_MEMORY; 00249 } 00250 } 00251 else 00252 { 00253 //The address is already resolved 00254 error = ERROR_UNEXPECTED_STATE; 00255 } 00256 } 00257 else 00258 { 00259 //No matching entry in Neighbor Cache 00260 error = ERROR_NOT_FOUND; 00261 } 00262 00263 //Return status code 00264 return error; 00265 } 00266 00267 00268 /** 00269 * @brief NDP timer handler 00270 * @param[in] interface Underlying network interface 00271 **/ 00272 00273 void ndpTick(NetInterface *interface) 00274 { 00275 systime_t time; 00276 NdpContext *context; 00277 00278 //Point to the NDP context 00279 context = &interface->ndpContext; 00280 00281 //Get current time 00282 time = osGetSystemTime(); 00283 00284 //When an interface becomes enabled, a host may send some Router 00285 //Solicitation messages to obtain Router Advertisements quickly 00286 if(interface->linkState && !interface->ipv6Context.isRouter) 00287 { 00288 //Make sure that a valid link-local address has been assigned to the interface 00289 if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) 00290 { 00291 //The host should transmit up to MAX_RTR_SOLICITATIONS Router 00292 //Solicitation messages 00293 if(context->rtrSolicitationCount == 0) 00294 { 00295 //Set time stamp 00296 context->timestamp = time; 00297 00298 //Check whether the host has already performed Duplicate Address 00299 //Detection for the link-local address 00300 if(context->dupAddrDetectTransmits > 0) 00301 { 00302 //If a host has already performed a random delay since the interface 00303 //became enabled, there is no need to delay again before sending the 00304 //first Router Solicitation message 00305 context->timeout = 0; 00306 } 00307 else 00308 { 00309 //Before a host sends an initial solicitation, it should delay the 00310 //transmission for a random amount of time in order to alleviate 00311 //congestion when many hosts start up on a link at the same time 00312 context->timeout = netGetRandRange(context->minRtrSolicitationDelay, 00313 context->maxRtrSolicitationDelay); 00314 } 00315 00316 //Prepare to send the first Router Solicitation message 00317 context->rtrSolicitationCount = 1; 00318 } 00319 else if(context->rtrSolicitationCount <= context->maxRtrSolicitations) 00320 { 00321 //Once the host sends a Router Solicitation, and receives a valid 00322 //Router Advertisement with a non-zero Router Lifetime, the host must 00323 //desist from sending additional solicitations on that interface 00324 if(!context->rtrAdvReceived) 00325 { 00326 //Check current time 00327 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 00328 { 00329 //Send Router Solicitation message 00330 ndpSendRouterSol(interface); 00331 00332 //Save the time at which the message was sent 00333 context->timestamp = time; 00334 //Set timeout value 00335 context->timeout = context->rtrSolicitationInterval; 00336 //Increment retransmission counter 00337 context->rtrSolicitationCount++; 00338 } 00339 } 00340 } 00341 } 00342 } 00343 00344 //Periodically update the Neighbor Cache 00345 ndpUpdateNeighborCache(interface); 00346 00347 //Manage the lifetime of IPv6 addresses 00348 ndpUpdateAddrList(interface); 00349 00350 //Periodically update the Prefix List 00351 ndpUpdatePrefixList(interface); 00352 00353 //Periodically update the Default Router List 00354 ndpUpdateDefaultRouterList(interface); 00355 } 00356 00357 00358 /** 00359 * @brief Callback function for link change event 00360 * @param[in] interface Underlying network interface 00361 **/ 00362 00363 void ndpLinkChangeEvent(NetInterface *interface) 00364 { 00365 NdpContext *context; 00366 00367 //Point to the NDP context 00368 context = &interface->ndpContext; 00369 00370 //Restore default parameters 00371 context->reachableTime = NDP_REACHABLE_TIME; 00372 context->retransTimer = NDP_RETRANS_TIMER; 00373 context->dupAddrDetectTransmits = NDP_DUP_ADDR_DETECT_TRANSMITS; 00374 context->minRtrSolicitationDelay = NDP_MIN_RTR_SOLICITATION_DELAY; 00375 context->maxRtrSolicitationDelay = NDP_MAX_RTR_SOLICITATION_DELAY; 00376 context->rtrSolicitationInterval = NDP_RTR_SOLICITATION_INTERVAL; 00377 context->maxRtrSolicitations = NDP_MAX_RTR_SOLICITATIONS; 00378 00379 //Reset retransmission counter for RS messages 00380 context->rtrSolicitationCount = 0; 00381 //Valid RA message not yet received 00382 context->rtrAdvReceived = FALSE; 00383 00384 //Flush the Neighbor Cache 00385 ndpFlushNeighborCache(interface); 00386 //Flush the Destination Cache 00387 ndpFlushDestCache(interface); 00388 } 00389 00390 00391 /** 00392 * @brief Router Advertisement message processing 00393 * @param[in] interface Underlying network interface 00394 * @param[in] pseudoHeader IPv6 pseudo header 00395 * @param[in] buffer Multi-part buffer containing the Router Advertisement message 00396 * @param[in] offset Offset to the first byte of the message 00397 * @param[in] hopLimit Hop Limit field from IPv6 header 00398 **/ 00399 00400 void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, 00401 const NetBuffer *buffer, size_t offset, uint8_t hopLimit) 00402 { 00403 error_t error; 00404 uint32_t n; 00405 size_t length; 00406 NdpRouterAdvMessage *message; 00407 NdpMtuOption *mtuOption; 00408 NdpPrefixInfoOption *prefixInfoOption; 00409 #if (ETH_SUPPORT == ENABLED) 00410 NdpLinkLayerAddrOption *linkLayerAddrOption; 00411 NdpNeighborCacheEntry *entry; 00412 #endif 00413 00414 //Retrieve the length of the message 00415 length = netBufferGetLength(buffer) - offset; 00416 00417 //Check the length of the Router Advertisement message 00418 if(length < sizeof(NdpRouterAdvMessage)) 00419 return; 00420 00421 //Point to the beginning of the message 00422 message = netBufferAt(buffer, offset); 00423 //Sanity check 00424 if(message == NULL) 00425 return; 00426 00427 //Debug message 00428 TRACE_INFO("Router Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); 00429 //Dump message contents for debugging purpose 00430 ndpDumpRouterAdvMessage(message); 00431 00432 //Routers must use their link-local address as the source for the 00433 //Router Advertisement so that hosts can uniquely identify routers 00434 if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) 00435 return; 00436 00437 //The IPv6 Hop Limit field must have a value of 255 to ensure 00438 //that the packet has not been forwarded by a router 00439 if(hopLimit != NDP_HOP_LIMIT) 00440 return; 00441 00442 //ICMPv6 Code must be 0. An advertisement that passes the validity 00443 //checks is called a valid advertisement 00444 if(message->code) 00445 return; 00446 00447 //Calculate the length of the Options field 00448 length -= sizeof(NdpRouterAdvMessage); 00449 00450 //Parse Options field 00451 error = ndpCheckOptions(message->options, length); 00452 //All included options must have a length that is greater than zero 00453 if(error) 00454 return; 00455 00456 //Check the Router Lifetime value 00457 if(ntohs(message->routerLifetime) != 0) 00458 { 00459 //Add a new entry in the Default Router List 00460 ipv6AddDefaultRouter(interface, &pseudoHeader->srcAddr, 00461 ntohs(message->routerLifetime)); 00462 00463 //The host should send at least one solicitation in the case where 00464 //an advertisement is received prior to having sent a solicitation 00465 if(interface->ndpContext.rtrSolicitationCount > 1) 00466 { 00467 //Once the host sends a Router Solicitation, and receives a valid 00468 //Router Advertisement with a non-zero Router Lifetime, the host must 00469 //desist from sending additional solicitations on that interface 00470 interface->ndpContext.rtrAdvReceived = TRUE; 00471 } 00472 } 00473 else 00474 { 00475 //Immediately time-out the entry 00476 ipv6RemoveDefaultRouter(interface, &pseudoHeader->srcAddr); 00477 } 00478 00479 //6LoWPAN interface? 00480 if(interface->nicDriver->type == NIC_TYPE_6LOWPAN) 00481 { 00482 //In all cases, the Router Solicitation retransmissions are terminated 00483 //when a Router Advertisement is received (refer to RFC 6675 5.3) 00484 interface->ndpContext.rtrAdvReceived = TRUE; 00485 } 00486 00487 //A Router Advertisement field (Cur Hop Limit, Reachable Time, and 00488 //Retrans Timer) may contain a value denoting that it is unspecified. 00489 //In such cases, the parameter should be ignored and the host should 00490 //continue using whatever value it is already using 00491 if(message->curHopLimit != 0) 00492 { 00493 //Get the default value that should be placed in the Hop Count 00494 //field of the IP header for outgoing IP packets 00495 interface->ipv6Context.curHopLimit = message->curHopLimit; 00496 } 00497 00498 //A value of zero means unspecified... 00499 if(message->reachableTime != 0) 00500 { 00501 //The Reachable Time field holds the time, in milliseconds, that 00502 //a node assumes a neighbor is reachable after having received a 00503 //reachability confirmation 00504 interface->ndpContext.reachableTime = ntohl(message->reachableTime); 00505 } 00506 00507 //A value of zero means unspecified... 00508 if(message->retransTimer != 0) 00509 { 00510 //The Retrans Timer field holds the time, in milliseconds, 00511 //between retransmitted Neighbor Solicitation messages 00512 interface->ndpContext.retransTimer = ntohl(message->retransTimer); 00513 } 00514 00515 #if (ETH_SUPPORT == ENABLED) 00516 //Search for the Source Link-Layer Address option 00517 linkLayerAddrOption = ndpGetOption(message->options, 00518 length, NDP_OPT_SOURCE_LINK_LAYER_ADDR); 00519 00520 //Source Link-Layer Address option found? 00521 if(linkLayerAddrOption != NULL && linkLayerAddrOption->length == 1) 00522 { 00523 //Debug message 00524 TRACE_DEBUG(" Source Link-Layer Address = %s\r\n", 00525 macAddrToString(&linkLayerAddrOption->linkLayerAddr, NULL)); 00526 } 00527 else 00528 { 00529 //No valid Source Link-Layer Address option... 00530 linkLayerAddrOption = NULL; 00531 } 00532 00533 //Search the Neighbor cache for the router 00534 entry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr); 00535 00536 //No matching entry has been found? 00537 if(!entry) 00538 { 00539 //If the advertisement contains a Source Link-Layer Address option, 00540 //the link-layer address should be recorded in the Neighbor cache 00541 if(linkLayerAddrOption) 00542 { 00543 //Create an entry for the router 00544 entry = ndpCreateNeighborCacheEntry(interface); 00545 00546 //Neighbor cache entry successfully created? 00547 if(entry) 00548 { 00549 //Record the IPv6 address and the corresponding MAC address 00550 entry->ipAddr = pseudoHeader->srcAddr; 00551 entry->macAddr = linkLayerAddrOption->linkLayerAddr; 00552 //The IsRouter flag must be set to TRUE 00553 entry->isRouter = TRUE; 00554 //Save current time 00555 entry->timestamp = osGetSystemTime(); 00556 //The reachability state must be set to STALE 00557 entry->state = NDP_STATE_STALE; 00558 } 00559 } 00560 } 00561 else 00562 { 00563 //The sender of a Router Advertisement is implicitly assumed to be a router 00564 entry->isRouter = TRUE; 00565 00566 //Check if the advertisement contains a Source Link-Layer Address option 00567 if(linkLayerAddrOption) 00568 { 00569 //INCOMPLETE state? 00570 if(entry->state == NDP_STATE_INCOMPLETE) 00571 { 00572 //Record link-layer address 00573 entry->macAddr = linkLayerAddrOption->linkLayerAddr; 00574 //Send all the packets that are pending for transmission 00575 n = ndpSendQueuedPackets(interface, entry); 00576 //Save current time 00577 entry->timestamp = osGetSystemTime(); 00578 00579 //Check whether any packets have been sent 00580 if(n > 0) 00581 { 00582 //Start delay timer 00583 entry->timeout = NDP_DELAY_FIRST_PROBE_TIME; 00584 //Switch to the DELAY state 00585 entry->state = NDP_STATE_DELAY; 00586 } 00587 else 00588 { 00589 //Enter the STALE state 00590 entry->state = NDP_STATE_STALE; 00591 } 00592 } 00593 //REACHABLE, STALE, DELAY or PROBE state? 00594 else 00595 { 00596 //Different link-layer address than cached? 00597 if(!macCompAddr(&entry->macAddr, &linkLayerAddrOption->linkLayerAddr)) 00598 { 00599 //Update link-layer address 00600 entry->macAddr = linkLayerAddrOption->linkLayerAddr; 00601 //Save current time 00602 entry->timestamp = osGetSystemTime(); 00603 //The reachability state must be set to STALE 00604 entry->state = NDP_STATE_STALE; 00605 } 00606 } 00607 } 00608 } 00609 #endif 00610 00611 //Search for the MTU option 00612 mtuOption = ndpGetOption(message->options, length, NDP_OPT_MTU); 00613 00614 //MTU option found? 00615 if(mtuOption != NULL && mtuOption->length == 1) 00616 { 00617 //This option specifies the recommended MTU for the link 00618 n = ntohl(mtuOption->mtu); 00619 00620 //The host should copy the option's value so long as the value is greater 00621 //than or equal to the minimum IPv6 MTU and does not exceed the maximum 00622 //MTU of the interface 00623 if(n >= IPV6_DEFAULT_MTU && n <= interface->nicDriver->mtu) 00624 { 00625 //Save the MTU value 00626 interface->ipv6Context.linkMtu = n; 00627 } 00628 } 00629 00630 //Point to the beginning of the Options field 00631 n = 0; 00632 00633 //Parse Options field 00634 while(1) 00635 { 00636 //Search the Options field for any Prefix Information options 00637 prefixInfoOption = ndpGetOption(message->options + n, 00638 length - n, NDP_OPT_PREFIX_INFORMATION); 00639 00640 //No more option of the specified type? 00641 if(prefixInfoOption == NULL) 00642 break; 00643 00644 //Hosts use the advertised on-link prefixes to build and maintain 00645 //a list that is used in deciding when a packet's destination is 00646 //on-link or beyond a router 00647 ndpParsePrefixInfoOption(interface, prefixInfoOption); 00648 00649 //Retrieve the offset to the current position 00650 n = (uint8_t *) prefixInfoOption - message->options; 00651 //Jump to the next option 00652 n += prefixInfoOption->length * 8; 00653 } 00654 00655 #if (SLAAC_SUPPORT == ENABLED) 00656 //Stateless Address Autoconfiguration is currently used? 00657 if(interface->slaacContext != NULL) 00658 { 00659 //Process the valid advertisement 00660 slaacParseRouterAdv(interface->slaacContext, message, 00661 length + sizeof(NdpRouterAdvMessage)); 00662 } 00663 #endif 00664 } 00665 00666 00667 /** 00668 * @brief Neighbor Solicitation message processing 00669 * @param[in] interface Underlying network interface 00670 * @param[in] pseudoHeader IPv6 pseudo header 00671 * @param[in] buffer Multi-part buffer containing the Neighbor Solicitation message 00672 * @param[in] offset Offset to the first byte of the message 00673 * @param[in] hopLimit Hop Limit field from IPv6 header 00674 **/ 00675 00676 void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, 00677 const NetBuffer *buffer, size_t offset, uint8_t hopLimit) 00678 { 00679 #if (ETH_SUPPORT == ENABLED) 00680 error_t error; 00681 uint_t i; 00682 uint_t n; 00683 size_t length; 00684 bool_t validTarget; 00685 NdpNeighborSolMessage *message; 00686 NdpLinkLayerAddrOption *option; 00687 NdpNeighborCacheEntry *neighborCacheEntry; 00688 Ipv6AddrEntry *addrEntry; 00689 00690 //Retrieve the length of the message 00691 length = netBufferGetLength(buffer) - offset; 00692 00693 //Check the length of the Neighbor Solicitation message 00694 if(length < sizeof(NdpNeighborSolMessage)) 00695 return; 00696 00697 //Point to the beginning of the message 00698 message = netBufferAt(buffer, offset); 00699 //Sanity check 00700 if(message == NULL) 00701 return; 00702 00703 //Debug message 00704 TRACE_INFO("Neighbor Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length); 00705 //Dump message contents for debugging purpose 00706 ndpDumpNeighborSolMessage(message); 00707 00708 //The IPv6 Hop Limit field must have a value of 255 to ensure 00709 //that the packet has not been forwarded by a router 00710 if(hopLimit != NDP_HOP_LIMIT) 00711 return; 00712 00713 //ICMPv6 Code must be 0 00714 if(message->code) 00715 return; 00716 00717 //If the IP source address is the unspecified address, the IP 00718 //destination address must be a solicited-node multicast address 00719 if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) && 00720 !ipv6IsSolicitedNodeAddr(&pseudoHeader->destAddr)) 00721 { 00722 //Debug message 00723 TRACE_WARNING("Destination address must be a solicited-node address!\r\n"); 00724 //Exit immediately 00725 return; 00726 } 00727 00728 //Calculate the length of the Options field 00729 length -= sizeof(NdpNeighborSolMessage); 00730 00731 //Parse Options field 00732 error = ndpCheckOptions(message->options, length); 00733 //All included options must have a length that is greater than zero 00734 if(error) 00735 return; 00736 00737 //Search for the Source Link-Layer Address option 00738 option = ndpGetOption(message->options, 00739 length, NDP_OPT_SOURCE_LINK_LAYER_ADDR); 00740 00741 //The target address must a valid unicast or anycast address assigned to 00742 //the interface or a tentative address on which DAD is being performed 00743 validTarget = FALSE; 00744 00745 //Loop through the IPv6 addresses assigned to the interface 00746 for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) 00747 { 00748 //Point to the current entry 00749 addrEntry = &interface->ipv6Context.addrList[i]; 00750 00751 //Compare target address 00752 if(ipv6CompAddr(&addrEntry->addr, &message->targetAddr)) 00753 { 00754 //Check address state 00755 if(addrEntry->state == IPV6_ADDR_STATE_TENTATIVE) 00756 { 00757 //If the source address of the Neighbor Solicitation is the 00758 //unspecified address, the solicitation is from a node 00759 //performing Duplicate Address Detection 00760 if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) 00761 { 00762 //The source link-layer address must not be included when the 00763 //source IP address is the unspecified address... 00764 if(option == NULL) 00765 { 00766 //Debug message 00767 TRACE_WARNING("The tentative address %s is a duplicate!\r\n", 00768 ipv6AddrToString(&addrEntry->addr, NULL)); 00769 00770 //The tentative address is a duplicate and should not be used 00771 addrEntry->duplicate = TRUE; 00772 } 00773 } 00774 00775 //In all cases, a node must not respond to a Neighbor Solicitation 00776 //for a tentative address 00777 return; 00778 } 00779 else if(addrEntry->state != IPV6_ADDR_STATE_INVALID) 00780 { 00781 //The target address is a valid address assigned to the interface 00782 validTarget = TRUE; 00783 //We are done 00784 break; 00785 } 00786 } 00787 } 00788 00789 //Invalid target address? 00790 if(!validTarget) 00791 { 00792 //The Neighbor Solicitation must be discarded if the target address 00793 //is not a valid anycast address assigned to the interface 00794 if(!ipv6IsAnycastAddr(interface, &message->targetAddr)) 00795 { 00796 //Debug message 00797 TRACE_WARNING("Wrong target address!\r\n"); 00798 //Exit immediately 00799 return; 00800 } 00801 } 00802 00803 //Source Link-Layer Address option found? 00804 if(option != NULL && option->length == 1) 00805 { 00806 //Debug message 00807 TRACE_DEBUG(" Source Link-Layer Address = %s\r\n", 00808 macAddrToString(&option->linkLayerAddr, NULL)); 00809 00810 //The Source Link-Layer Address option must not be included when the 00811 //source IP address is the unspecified address 00812 if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) 00813 return; 00814 00815 //Search the Neighbor Cache for the source address of the solicitation 00816 neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr); 00817 00818 //No matching entry has been found? 00819 if(!neighborCacheEntry) 00820 { 00821 //Create an entry 00822 neighborCacheEntry = ndpCreateNeighborCacheEntry(interface); 00823 00824 //Neighbor Cache entry successfully created? 00825 if(neighborCacheEntry) 00826 { 00827 //Record the IPv6 and the corresponding MAC address 00828 neighborCacheEntry->ipAddr = pseudoHeader->srcAddr; 00829 neighborCacheEntry->macAddr = option->linkLayerAddr; 00830 //Save current time 00831 neighborCacheEntry->timestamp = osGetSystemTime(); 00832 //Enter the STALE state 00833 neighborCacheEntry->state = NDP_STATE_STALE; 00834 } 00835 } 00836 else 00837 { 00838 //INCOMPLETE state? 00839 if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE) 00840 { 00841 //Record link-layer address 00842 neighborCacheEntry->macAddr = option->linkLayerAddr; 00843 //Send all the packets that are pending for transmission 00844 n = ndpSendQueuedPackets(interface, neighborCacheEntry); 00845 //Save current time 00846 neighborCacheEntry->timestamp = osGetSystemTime(); 00847 00848 //Check whether any packets have been sent 00849 if(n > 0) 00850 { 00851 //Start delay timer 00852 neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME; 00853 //Switch to the DELAY state 00854 neighborCacheEntry->state = NDP_STATE_DELAY; 00855 } 00856 else 00857 { 00858 //Enter the STALE state 00859 neighborCacheEntry->state = NDP_STATE_STALE; 00860 } 00861 } 00862 //REACHABLE, STALE, DELAY or PROBE state? 00863 else 00864 { 00865 //Different link-layer address than cached? 00866 if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr)) 00867 { 00868 //Update link-layer address 00869 neighborCacheEntry->macAddr = option->linkLayerAddr; 00870 //Save current time 00871 neighborCacheEntry->timestamp = osGetSystemTime(); 00872 //Enter the STALE state 00873 neighborCacheEntry->state = NDP_STATE_STALE; 00874 } 00875 } 00876 } 00877 } 00878 //Source Link-Layer Address option not found? 00879 else 00880 { 00881 //The Source Link-Layer Address option must not be included when the 00882 //source IP address is the unspecified address. Otherwise, this option 00883 //must be included in multicast solicitations 00884 if(!ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) && 00885 ipv6IsMulticastAddr(&pseudoHeader->destAddr)) 00886 { 00887 //Debug message 00888 TRACE_WARNING("The Source Link-Layer Address must be included!\r\n"); 00889 //Exit immediately 00890 return; 00891 } 00892 } 00893 00894 //After any updates to the Neighbor cache, the node sends a Neighbor 00895 //Advertisement response as described in RFC 4861 7.2.4 00896 ndpSendNeighborAdv(interface, &message->targetAddr, &pseudoHeader->srcAddr); 00897 #endif 00898 } 00899 00900 00901 /** 00902 * @brief Neighbor Advertisement message processing 00903 * @param[in] interface Underlying network interface 00904 * @param[in] pseudoHeader IPv6 pseudo header 00905 * @param[in] buffer Multi-part buffer containing the Neighbor Advertisement message 00906 * @param[in] offset Offset to the first byte of the message 00907 * @param[in] hopLimit Hop Limit field from IPv6 header 00908 **/ 00909 00910 void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, 00911 const NetBuffer *buffer, size_t offset, uint8_t hopLimit) 00912 { 00913 #if (ETH_SUPPORT == ENABLED) 00914 error_t error; 00915 uint_t i; 00916 uint_t n; 00917 size_t length; 00918 bool_t differentLinkLayerAddr; 00919 NdpNeighborAdvMessage *message; 00920 NdpLinkLayerAddrOption *option; 00921 NdpNeighborCacheEntry *neighborCacheEntry; 00922 Ipv6AddrEntry *addrEntry; 00923 00924 //Retrieve the length of the message 00925 length = netBufferGetLength(buffer) - offset; 00926 00927 //Check the length of the Neighbor Advertisement message 00928 if(length < sizeof(NdpNeighborAdvMessage)) 00929 return; 00930 00931 //Point to the beginning of the message 00932 message = netBufferAt(buffer, offset); 00933 //Sanity check 00934 if(message == NULL) 00935 return; 00936 00937 //Debug message 00938 TRACE_INFO("Neighbor Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); 00939 //Dump message contents for debugging purpose 00940 ndpDumpNeighborAdvMessage(message); 00941 00942 //The IPv6 Hop Limit field must have a value of 255 to ensure 00943 //that the packet has not been forwarded by a router 00944 if(hopLimit != NDP_HOP_LIMIT) 00945 return; 00946 00947 //ICMPv6 Code must be 0 00948 if(message->code) 00949 return; 00950 00951 //The target address must not be a multicast address 00952 if(ipv6IsMulticastAddr(&message->targetAddr)) 00953 { 00954 //Debug message 00955 TRACE_WARNING("Target address must not be a multicast address!\r\n"); 00956 //Exit immediately 00957 return; 00958 } 00959 00960 //If the destination address is a multicast address 00961 //then the Solicited flag must be zero 00962 if(ipv6IsMulticastAddr(&pseudoHeader->destAddr) && message->s) 00963 { 00964 //Debug message 00965 TRACE_WARNING("Solicited flag must be zero!\r\n"); 00966 //Exit immediately 00967 return; 00968 } 00969 00970 //Calculate the length of the Options field 00971 length -= sizeof(NdpNeighborAdvMessage); 00972 00973 //Parse Options field 00974 error = ndpCheckOptions(message->options, length); 00975 //All included options must have a length that is greater than zero 00976 if(error) 00977 return; 00978 00979 //Duplicate address detection 00980 for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) 00981 { 00982 //Point to the current entry 00983 addrEntry = &interface->ipv6Context.addrList[i]; 00984 00985 //Valid entry? 00986 if(addrEntry->state != IPV6_ADDR_STATE_INVALID) 00987 { 00988 //Check whether the target address is tentative or matches 00989 //a unicast address assigned to the interface 00990 if(ipv6CompAddr(&addrEntry->addr, &message->targetAddr)) 00991 { 00992 //Debug message 00993 TRACE_WARNING("The address %s is a duplicate!\r\n", 00994 ipv6AddrToString(&addrEntry->addr, NULL)); 00995 00996 //The address is a duplicate and should not be used 00997 addrEntry->duplicate = TRUE; 00998 //Exit immediately 00999 return; 01000 } 01001 } 01002 } 01003 01004 //Search the Neighbor cache for the specified target address 01005 neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &message->targetAddr); 01006 01007 //If no entry exists, the advertisement should be silently discarded 01008 if(neighborCacheEntry) 01009 { 01010 //This flag tells whether the supplied link-layer 01011 //address differs from that in the cache 01012 differentLinkLayerAddr = FALSE; 01013 01014 //Search for the Target Link-Layer Address option 01015 option = ndpGetOption(message->options, 01016 length, NDP_OPT_TARGET_LINK_LAYER_ADDR); 01017 01018 //Target Link-Layer Address option found? 01019 if(option != NULL && option->length == 1) 01020 { 01021 //Debug message 01022 TRACE_DEBUG(" Target Link-Layer Address = %s\r\n", 01023 macAddrToString(&option->linkLayerAddr, NULL)); 01024 01025 //Different link-layer address than cached? 01026 if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr)) 01027 differentLinkLayerAddr = TRUE; 01028 } 01029 01030 //INCOMPLETE state? 01031 if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE) 01032 { 01033 //If no Target Link-Layer Address option is included, the receiving 01034 //node should silently discard the received advertisement 01035 if(option != NULL && option->length == 1) 01036 { 01037 //Record the link-layer address 01038 neighborCacheEntry->macAddr = option->linkLayerAddr; 01039 //Send all the packets that are pending for transmission 01040 n = ndpSendQueuedPackets(interface, neighborCacheEntry); 01041 //Save current time 01042 neighborCacheEntry->timestamp = osGetSystemTime(); 01043 01044 //Solicited flag is set? 01045 if(message->s) 01046 { 01047 //Computing the random ReachableTime value 01048 neighborCacheEntry->timeout = interface->ndpContext.reachableTime; 01049 //Switch to the REACHABLE state 01050 neighborCacheEntry->state = NDP_STATE_REACHABLE; 01051 } 01052 else 01053 { 01054 //Check whether any packets have been sent 01055 if(n > 0) 01056 { 01057 //Start delay timer 01058 neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME; 01059 //Switch to the DELAY state 01060 neighborCacheEntry->state = NDP_STATE_DELAY; 01061 } 01062 else 01063 { 01064 //Enter the STALE state 01065 neighborCacheEntry->state = NDP_STATE_STALE; 01066 } 01067 } 01068 } 01069 } 01070 //REACHABLE, STALE, DELAY or PROBE state? 01071 else 01072 { 01073 //Check whether the Override flag is clear and the supplied 01074 //link-layer address differs from that in the cache 01075 if(!message->o && differentLinkLayerAddr) 01076 { 01077 //REACHABLE state? 01078 if(neighborCacheEntry->state == NDP_STATE_REACHABLE) 01079 { 01080 //Save current time 01081 neighborCacheEntry->timestamp = osGetSystemTime(); 01082 //Enter the STALE state 01083 neighborCacheEntry->state = NDP_STATE_STALE; 01084 } 01085 } 01086 else 01087 { 01088 //Solicited flag is set? 01089 if(message->s) 01090 { 01091 //Different link-layer address than cached? 01092 if(differentLinkLayerAddr) 01093 { 01094 //The link-layer address must be inserted in the cache 01095 neighborCacheEntry->macAddr = option->linkLayerAddr; 01096 } 01097 01098 //Save current time 01099 neighborCacheEntry->timestamp = osGetSystemTime(); 01100 //Computing the random ReachableTime value 01101 neighborCacheEntry->timeout = interface->ndpContext.reachableTime; 01102 //Switch to the REACHABLE state 01103 neighborCacheEntry->state = NDP_STATE_REACHABLE; 01104 } 01105 else 01106 { 01107 //Different link-layer address than cached? 01108 if(differentLinkLayerAddr) 01109 { 01110 //The link-layer address must be inserted in the cache 01111 neighborCacheEntry->macAddr = option->linkLayerAddr; 01112 //Save current time 01113 neighborCacheEntry->timestamp = osGetSystemTime(); 01114 //The state must be set to STALE 01115 neighborCacheEntry->state = NDP_STATE_STALE; 01116 } 01117 } 01118 } 01119 } 01120 01121 //The IsRouter flag in the cache entry must be set based 01122 //on the Router flag in the received advertisement 01123 if(message->r) 01124 { 01125 //The neighbor is a router 01126 neighborCacheEntry->isRouter = TRUE; 01127 } 01128 else 01129 { 01130 //Check whether the IsRouter flag changes from TRUE to FALSE 01131 //as a result of this update 01132 if(neighborCacheEntry->isRouter) 01133 { 01134 //The node must remove that router from the Default Router list 01135 //and update the Destination cache entries for all destinations 01136 //using that neighbor as a router 01137 ipv6RemoveDefaultRouter(interface, &neighborCacheEntry->ipAddr); 01138 } 01139 01140 //The neighbor is a host 01141 neighborCacheEntry->isRouter = FALSE; 01142 } 01143 } 01144 #endif 01145 } 01146 01147 01148 /** 01149 * @brief Redirect message processing 01150 * @param[in] interface Underlying network interface 01151 * @param[in] pseudoHeader IPv6 pseudo header 01152 * @param[in] buffer Multi-part buffer containing the Redirect message 01153 * @param[in] offset Offset to the first byte of the message 01154 * @param[in] hopLimit Hop Limit field from IPv6 header 01155 **/ 01156 01157 void ndpProcessRedirect(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, 01158 const NetBuffer *buffer, size_t offset, uint8_t hopLimit) 01159 { 01160 #if (ETH_SUPPORT == ENABLED) 01161 error_t error; 01162 uint_t n; 01163 size_t length; 01164 NdpRedirectMessage *message; 01165 NdpLinkLayerAddrOption *option; 01166 NdpNeighborCacheEntry *neighborCacheEntry; 01167 NdpDestCacheEntry *destCacheEntry; 01168 01169 //Retrieve the length of the message 01170 length = netBufferGetLength(buffer) - offset; 01171 01172 //Check the length of the Redirect message 01173 if(length < sizeof(NdpRedirectMessage)) 01174 return; 01175 01176 //Point to the beginning of the message 01177 message = netBufferAt(buffer, offset); 01178 //Sanity check 01179 if(message == NULL) 01180 return; 01181 01182 //Debug message 01183 TRACE_INFO("Redirect message received (%" PRIuSIZE " bytes)...\r\n", length); 01184 //Dump message contents for debugging purpose 01185 ndpDumpRedirectMessage(message); 01186 01187 //The IPv6 Hop Limit field must have a value of 255 to ensure 01188 //that the packet has not been forwarded by a router 01189 if(hopLimit != NDP_HOP_LIMIT) 01190 return; 01191 01192 //ICMPv6 Code must be 0 01193 if(message->code) 01194 return; 01195 01196 //Routers must use their link-local address as the source for Redirect 01197 //messages so that hosts can uniquely identify routers 01198 if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) 01199 return; 01200 01201 //The IP source address of the Redirect must be the same as the current 01202 //first-hop router for the specified Destination address 01203 if(!ndpIsFirstHopRouter(interface, &message->destAddr, &pseudoHeader->srcAddr)) 01204 return; 01205 01206 //The Destination Address field in the Redirect message must not 01207 //contain a multicast address 01208 if(ipv6IsMulticastAddr(&message->destAddr)) 01209 return; 01210 01211 //The Target Address must be either a link-local address (when redirected 01212 //to a router) or the same as the Destination Address (when redirected to 01213 //the on-link destination) 01214 if(!ipv6IsLinkLocalUnicastAddr(&message->targetAddr) && 01215 !ipv6CompAddr(&message->targetAddr, &message->destAddr)) 01216 { 01217 //Silently discard the received Redirect message 01218 return; 01219 } 01220 01221 //Calculate the length of the Options field 01222 length -= sizeof(NdpNeighborAdvMessage); 01223 01224 //Parse Options field 01225 error = ndpCheckOptions(message->options, length); 01226 //All included options must have a length that is greater than zero 01227 if(error) 01228 return; 01229 01230 //Search the Destination cache for the specified address 01231 destCacheEntry = ndpFindDestCacheEntry(interface, &message->destAddr); 01232 01233 //Check whether a corresponding Destination cache entry exists 01234 if(destCacheEntry) 01235 { 01236 //The entry is updated with information learned from Redirect messages 01237 destCacheEntry->nextHop = message->targetAddr; 01238 //Save current time 01239 destCacheEntry->timestamp = osGetSystemTime(); 01240 } 01241 else 01242 { 01243 //If no Destination Cache entry exists for the destination, an 01244 //implementation should create such an entry 01245 destCacheEntry = ndpCreateDestCacheEntry(interface); 01246 01247 //Destination cache entry successfully created? 01248 if(destCacheEntry) 01249 { 01250 //Destination address 01251 destCacheEntry->destAddr = message->destAddr; 01252 //Address of the next hop 01253 destCacheEntry->nextHop = message->targetAddr; 01254 01255 //Initially, the PMTU value for a path is assumed to be 01256 //the MTU of the first-hop link 01257 destCacheEntry->pathMtu = interface->ipv6Context.linkMtu; 01258 01259 //Save current time 01260 destCacheEntry->timestamp = osGetSystemTime(); 01261 } 01262 } 01263 01264 //Search for the Target Link-Layer Address option 01265 option = ndpGetOption(message->options, 01266 length, NDP_OPT_TARGET_LINK_LAYER_ADDR); 01267 01268 //If the Redirect contains a Target Link-Layer Address option, the host 01269 //either creates or updates the Neighbor Cache entry for the target 01270 if(option != NULL && option->length == 1) 01271 { 01272 //Debug message 01273 TRACE_DEBUG(" Target Link-Layer Address = %s\r\n", 01274 macAddrToString(&option->linkLayerAddr, NULL)); 01275 01276 //Search the Neighbor cache for the specified target address 01277 neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &message->targetAddr); 01278 01279 //No matching entry has been found? 01280 if(!neighborCacheEntry) 01281 { 01282 //Create an entry for the target 01283 neighborCacheEntry = ndpCreateNeighborCacheEntry(interface); 01284 01285 //Neighbor cache entry successfully created? 01286 if(neighborCacheEntry) 01287 { 01288 //Record the Target address 01289 neighborCacheEntry->ipAddr = message->targetAddr; 01290 //The cached link-layer address is copied from the option 01291 neighborCacheEntry->macAddr = option->linkLayerAddr; 01292 //Newly created Neighbor Cache entries should set the IsRouter flag to FALSE 01293 neighborCacheEntry->isRouter = FALSE; 01294 //Save current time 01295 neighborCacheEntry->timestamp = osGetSystemTime(); 01296 //The reachability state must be set to STALE 01297 neighborCacheEntry->state = NDP_STATE_STALE; 01298 } 01299 } 01300 else 01301 { 01302 //If the Target Address is not the same as the Destination Address, 01303 //the host must set IsRouter to TRUE for the target 01304 if(!ipv6CompAddr(&message->targetAddr, &message->destAddr)) 01305 neighborCacheEntry->isRouter = TRUE; 01306 01307 //INCOMPLETE state? 01308 if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE) 01309 { 01310 //Record link-layer address 01311 neighborCacheEntry->macAddr = option->linkLayerAddr; 01312 //Send all the packets that are pending for transmission 01313 n = ndpSendQueuedPackets(interface, neighborCacheEntry); 01314 //Save current time 01315 neighborCacheEntry->timestamp = osGetSystemTime(); 01316 01317 //Check whether any packets have been sent 01318 if(n > 0) 01319 { 01320 //Start delay timer 01321 neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME; 01322 //Switch to the DELAY state 01323 neighborCacheEntry->state = NDP_STATE_DELAY; 01324 } 01325 else 01326 { 01327 //Enter the STALE state 01328 neighborCacheEntry->state = NDP_STATE_STALE; 01329 } 01330 } 01331 //REACHABLE, STALE, DELAY or PROBE state? 01332 else 01333 { 01334 //Different link-layer address than cached? 01335 if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr)) 01336 { 01337 //Update link-layer address 01338 neighborCacheEntry->macAddr = option->linkLayerAddr; 01339 //Save current time 01340 neighborCacheEntry->timestamp = osGetSystemTime(); 01341 //The reachability state must be set to STALE 01342 neighborCacheEntry->state = NDP_STATE_STALE; 01343 } 01344 } 01345 } 01346 } 01347 #endif 01348 } 01349 01350 01351 /** 01352 * @brief Send a Router Solicitation message 01353 * @param[in] interface Underlying network interface 01354 * @return Error code 01355 **/ 01356 01357 error_t ndpSendRouterSol(NetInterface *interface) 01358 { 01359 error_t error; 01360 size_t offset; 01361 size_t length; 01362 NetBuffer *buffer; 01363 NdpRouterSolMessage *message; 01364 Ipv6PseudoHeader pseudoHeader; 01365 01366 //The destination address is typically the all-routers multicast address 01367 pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR; 01368 01369 //Select the most appropriate source address to be used when 01370 //sending the Router Solicitation message 01371 error = ipv6SelectSourceAddr(&interface, 01372 &pseudoHeader.destAddr, &pseudoHeader.srcAddr); 01373 01374 //No address assigned to the interface? 01375 if(error) 01376 { 01377 //Use the unspecified address if no address is assigned 01378 //to the sending interface 01379 pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR; 01380 } 01381 01382 //The only defined option that may appear in a Router Solicitation 01383 //message is the Source Link-Layer Address option 01384 length = sizeof(NdpRouterSolMessage) + sizeof(NdpLinkLayerAddrOption); 01385 01386 //Allocate a memory buffer to hold the Router Solicitation message 01387 buffer = ipAllocBuffer(length, &offset); 01388 //Failed to allocate memory? 01389 if(buffer == NULL) 01390 return ERROR_OUT_OF_MEMORY; 01391 01392 //Point to the beginning of the message 01393 message = netBufferAt(buffer, offset); 01394 01395 //Format Router Solicitation message 01396 message->type = ICMPV6_TYPE_ROUTER_SOL; 01397 message->code = 0; 01398 message->checksum = 0; 01399 message->reserved = 0; 01400 01401 //Length of the message, excluding any option 01402 length = sizeof(NdpRouterSolMessage); 01403 01404 //The Source Link-Layer Address option must not be included 01405 //when the source IPv6 address is the unspecified address 01406 if(!ipv6CompAddr(&pseudoHeader.srcAddr, &IPV6_UNSPECIFIED_ADDR)) 01407 { 01408 #if (ETH_SUPPORT == ENABLED) 01409 //Check whether a MAC address has been assigned to the interface 01410 if(!macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR)) 01411 { 01412 //Add Source Link-Layer Address option 01413 ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR, 01414 &interface->macAddr, sizeof(MacAddr)); 01415 } 01416 #endif 01417 } 01418 01419 //Adjust the length of the multi-part buffer 01420 netBufferSetLength(buffer, offset + length); 01421 01422 //Format IPv6 pseudo header 01423 pseudoHeader.length = htonl(length); 01424 pseudoHeader.reserved = 0; 01425 pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; 01426 01427 //Calculate ICMPv6 header checksum 01428 message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, 01429 sizeof(Ipv6PseudoHeader), buffer, offset, length); 01430 01431 //Debug message 01432 TRACE_INFO("Sending Router Solicitation message (%" PRIuSIZE " bytes)...\r\n", length); 01433 //Dump message contents for debugging purpose 01434 ndpDumpRouterSolMessage(message); 01435 01436 //Send Router Solicitation message 01437 error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); 01438 01439 //Free previously allocated memory 01440 netBufferFree(buffer); 01441 //Return status code 01442 return error; 01443 } 01444 01445 01446 /** 01447 * @brief Send a Neighbor Solicitation message 01448 * @param[in] interface Underlying network interface 01449 * @param[in] targetIpAddr Target IPv6 address 01450 * @param[in] multicast Unicast or unicast Neighbor Solicitation message 01451 * @return Error code 01452 **/ 01453 01454 error_t ndpSendNeighborSol(NetInterface *interface, 01455 const Ipv6Addr *targetIpAddr, bool_t multicast) 01456 { 01457 error_t error; 01458 size_t offset; 01459 size_t length; 01460 NetBuffer *buffer; 01461 NdpNeighborSolMessage *message; 01462 Ipv6PseudoHeader pseudoHeader; 01463 01464 //Multicast Neighbor Solicitation message? 01465 if(multicast) 01466 { 01467 //Compute the solicited-node multicast address that 01468 //corresponds to the target IPv6 address 01469 ipv6ComputeSolicitedNodeAddr(targetIpAddr, &pseudoHeader.destAddr); 01470 } 01471 else 01472 { 01473 //Unicast Neighbor Solicitation message 01474 pseudoHeader.destAddr = *targetIpAddr; 01475 } 01476 01477 //Check whether the target address is a tentative address 01478 if(ipv6IsTentativeAddr(interface, targetIpAddr)) 01479 { 01480 //The IPv6 source is set to the unspecified address 01481 pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR; 01482 } 01483 else 01484 { 01485 //Select the most appropriate source address to be used 01486 //when sending the Neighbor Solicitation message 01487 error = ipv6SelectSourceAddr(&interface, 01488 targetIpAddr, &pseudoHeader.srcAddr); 01489 01490 //No address assigned to the interface? 01491 if(error) 01492 return error; 01493 } 01494 01495 //The only defined option that may appear in a Neighbor Solicitation 01496 //message is the Source Link-Layer Address option 01497 length = sizeof(NdpNeighborSolMessage) + sizeof(NdpLinkLayerAddrOption); 01498 01499 //Allocate a memory buffer to hold the Neighbor Solicitation message 01500 buffer = ipAllocBuffer(length, &offset); 01501 //Failed to allocate memory? 01502 if(buffer == NULL) 01503 return ERROR_OUT_OF_MEMORY; 01504 01505 //Point to the beginning of the message 01506 message = netBufferAt(buffer, offset); 01507 01508 //Format Neighbor Solicitation message 01509 message->type = ICMPV6_TYPE_NEIGHBOR_SOL; 01510 message->code = 0; 01511 message->checksum = 0; 01512 message->reserved = 0; 01513 message->targetAddr = *targetIpAddr; 01514 01515 //Length of the message, excluding any option 01516 length = sizeof(NdpNeighborSolMessage); 01517 01518 //The Source Link-Layer Address option must not be included 01519 //when the source IPv6 address is the unspecified address 01520 if(!ipv6CompAddr(&pseudoHeader.srcAddr, &IPV6_UNSPECIFIED_ADDR)) 01521 { 01522 #if (ETH_SUPPORT == ENABLED) 01523 //Add Source Link-Layer Address option 01524 ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR, 01525 &interface->macAddr, sizeof(MacAddr)); 01526 #endif 01527 } 01528 01529 //Adjust the length of the multi-part buffer 01530 netBufferSetLength(buffer, offset + length); 01531 01532 //Format IPv6 pseudo header 01533 pseudoHeader.length = htonl(length); 01534 pseudoHeader.reserved = 0; 01535 pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; 01536 01537 //Calculate ICMPv6 header checksum 01538 message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, 01539 sizeof(Ipv6PseudoHeader), buffer, offset, length); 01540 01541 //Debug message 01542 TRACE_INFO("Sending Neighbor Solicitation message (%" PRIuSIZE " bytes)...\r\n", length); 01543 //Dump message contents for debugging purpose 01544 ndpDumpNeighborSolMessage(message); 01545 01546 //Send Neighbor Solicitation message 01547 error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); 01548 01549 //Free previously allocated memory 01550 netBufferFree(buffer); 01551 //Return status code 01552 return error; 01553 } 01554 01555 01556 /** 01557 * @brief Send a Neighbor Advertisement message 01558 * @param[in] interface Underlying network interface 01559 * @param[in] targetIpAddr Target IPv6 address 01560 * @param[in] destIpAddr Destination IPv6 address 01561 * @return Error code 01562 **/ 01563 01564 error_t ndpSendNeighborAdv(NetInterface *interface, 01565 const Ipv6Addr *targetIpAddr, const Ipv6Addr *destIpAddr) 01566 { 01567 error_t error; 01568 size_t offset; 01569 size_t length; 01570 NetBuffer *buffer; 01571 NdpNeighborAdvMessage *message; 01572 Ipv6PseudoHeader pseudoHeader; 01573 01574 //Destination IP address is the unspecified address? 01575 if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR)) 01576 { 01577 //If the destination is the unspecified address, the node must 01578 //multicast the advertisement to the all-nodes address 01579 pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR; 01580 } 01581 else 01582 { 01583 //Otherwise, the node must unicast the advertisement to 01584 //the destination IP address 01585 pseudoHeader.destAddr = *destIpAddr; 01586 } 01587 01588 //Check whether the target address is a valid anycast address 01589 //assigned to the interface 01590 if(ipv6IsAnycastAddr(interface, targetIpAddr)) 01591 { 01592 //Select the most appropriate source address to be used 01593 //when sending the Neighbor Advertisement message 01594 error = ipv6SelectSourceAddr(&interface, 01595 targetIpAddr, &pseudoHeader.srcAddr); 01596 01597 //No address assigned to the interface? 01598 if(error) 01599 return error; 01600 } 01601 else 01602 { 01603 //Set the source IP address 01604 pseudoHeader.srcAddr = *targetIpAddr; 01605 } 01606 01607 //The only defined option that may appear in a Neighbor Advertisement 01608 //message is the Target Link-Layer Address option 01609 length = sizeof(NdpNeighborAdvMessage) + sizeof(NdpLinkLayerAddrOption); 01610 01611 //Allocate a memory buffer to hold the Neighbor Advertisement message 01612 buffer = ipAllocBuffer(length, &offset); 01613 //Failed to allocate memory? 01614 if(buffer == NULL) 01615 return ERROR_OUT_OF_MEMORY; 01616 01617 //Point to the beginning of the message 01618 message = netBufferAt(buffer, offset); 01619 01620 //Format Neighbor Advertisement message 01621 message->type = ICMPV6_TYPE_NEIGHBOR_ADV; 01622 message->code = 0; 01623 message->checksum = 0; 01624 message->reserved1 = 0; 01625 message->reserved2 = 0; 01626 message->targetAddr = *targetIpAddr; 01627 01628 //The Router flag indicates that the sender is a router 01629 if(interface->ipv6Context.isRouter) 01630 message->r = TRUE; 01631 else 01632 message->r = FALSE; 01633 01634 //If the destination is the unspecified address, the node must set 01635 //the Solicited flag to zero 01636 if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR)) 01637 message->s = FALSE; 01638 else 01639 message->s = TRUE; 01640 01641 //The Override flag should not be set in solicited advertisements 01642 //for anycast addresses 01643 if(ipv6IsAnycastAddr(interface, targetIpAddr)) 01644 message->o = FALSE; 01645 else 01646 message->o = TRUE; 01647 01648 //Length of the message, excluding any option 01649 length = sizeof(NdpNeighborAdvMessage); 01650 01651 #if (ETH_SUPPORT == ENABLED) 01652 //Add Target Link-Layer Address option 01653 ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR, 01654 &interface->macAddr, sizeof(MacAddr)); 01655 #endif 01656 01657 //Adjust the length of the multi-part buffer 01658 netBufferSetLength(buffer, offset + length); 01659 01660 //Format IPv6 pseudo header 01661 pseudoHeader.length = htonl(length); 01662 pseudoHeader.reserved = 0; 01663 pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; 01664 01665 //Calculate ICMPv6 header checksum 01666 message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, 01667 sizeof(Ipv6PseudoHeader), buffer, offset, length); 01668 01669 //Debug message 01670 TRACE_INFO("Sending Neighbor Advertisement message (%" PRIuSIZE " bytes)...\r\n", length); 01671 //Dump message contents for debugging purpose 01672 ndpDumpNeighborAdvMessage(message); 01673 01674 //Send Neighbor Advertisement message 01675 error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); 01676 01677 //Free previously allocated memory 01678 netBufferFree(buffer); 01679 //Return status code 01680 return error; 01681 } 01682 01683 01684 /** 01685 * @brief Send a Redirect message 01686 * @param[in] interface Underlying network interface 01687 * @param[in] targetAddr IPv6 address that is a better first hop to use 01688 * for the destination address 01689 * @param[in] ipPacket Multi-part buffer that holds the IPv6 packet that 01690 * triggered the sending of the Redirect 01691 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet 01692 * @return Error code 01693 **/ 01694 01695 error_t ndpSendRedirect(NetInterface *interface, const Ipv6Addr *targetAddr, 01696 const NetBuffer *ipPacket, size_t ipPacketOffset) 01697 { 01698 error_t error; 01699 size_t offset; 01700 size_t length; 01701 size_t ipPacketLength; 01702 size_t optionLength; 01703 size_t paddingLength; 01704 NetBuffer *buffer; 01705 NdpRedirectMessage *message; 01706 NdpRedirectedHeaderOption *option; 01707 NdpNeighborCacheEntry *entry; 01708 Ipv6Header *ipHeader; 01709 Ipv6PseudoHeader pseudoHeader; 01710 uint8_t padding[8]; 01711 01712 //Retrieve the length of the forwarded IPv6 packet 01713 ipPacketLength = netBufferGetLength(ipPacket) - ipPacketOffset; 01714 01715 //Check the length of the IPv6 packet 01716 if(ipPacketLength < sizeof(Ipv6Header)) 01717 return ERROR_INVALID_LENGTH; 01718 01719 //Point to the header of the invoking packet 01720 ipHeader = netBufferAt(ipPacket, ipPacketOffset); 01721 //Sanity check 01722 if(ipHeader == NULL) 01723 return ERROR_FAILURE; 01724 01725 //The only defined options that may appear in a Redirect message are the 01726 //Target Link-Layer Address option and the Redirected Header option 01727 length = sizeof(NdpRedirectMessage) + sizeof(NdpLinkLayerAddrOption) + 01728 sizeof(NdpRedirectedHeaderOption); 01729 01730 //Allocate a memory buffer to hold the Redirect message 01731 buffer = ipAllocBuffer(length, &offset); 01732 //Failed to allocate memory? 01733 if(buffer == NULL) 01734 return ERROR_OUT_OF_MEMORY; 01735 01736 //Point to the beginning of the message 01737 message = netBufferAt(buffer, offset); 01738 01739 //Format Redirect message 01740 message->type = ICMPV6_TYPE_REDIRECT; 01741 message->code = 0; 01742 message->checksum = 0; 01743 message->reserved = 0; 01744 message->targetAddr = *targetAddr; 01745 message->destAddr = ipHeader->destAddr; 01746 01747 //Length of the message, excluding any option 01748 length = sizeof(NdpRedirectMessage); 01749 01750 //Search the Neighbor cache for the specified target address 01751 entry = ndpFindNeighborCacheEntry(interface, targetAddr); 01752 01753 //Include the link-layer address of the target, if known 01754 if(entry != NULL) 01755 { 01756 //Add Target Link-Layer Address option 01757 ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR, 01758 &entry->macAddr, sizeof(MacAddr)); 01759 } 01760 01761 //Retrieve the length of the IPv6 packet that triggered the sending 01762 //of the Redirect 01763 ipPacketLength = netBufferGetLength(ipPacket) - ipPacketOffset; 01764 01765 //Return as much of the forwarded IPv6 packet as can fit without 01766 //the redirect packet exceeding the minimum IPv6 MTU 01767 ipPacketLength = MIN(ipPacketLength, IPV6_DEFAULT_MTU - 01768 sizeof(NdpRedirectedHeaderOption) - length); 01769 01770 //Length of the Redirected Header option in units of 8 bytes including 01771 //the type and length fields 01772 optionLength = (ipPacketLength + sizeof(NdpOption) + 7) / 8; 01773 01774 //Add Redirected Header option 01775 option = (NdpRedirectedHeaderOption *) ((uint8_t *) message + length); 01776 01777 //Format Redirected Header option 01778 option->type = NDP_OPT_REDIRECTED_HEADER; 01779 option->length = (uint8_t) optionLength; 01780 option->reserved1 = 0; 01781 option->reserved2 = 0; 01782 01783 //Update the length of Redirect message 01784 length += sizeof(NdpRedirectedHeaderOption); 01785 01786 //Adjust the length of the multi-part buffer 01787 netBufferSetLength(buffer, offset + length); 01788 01789 //Copy the contents of the forwarded IPv6 packet 01790 error = netBufferConcat(buffer, ipPacket, ipPacketOffset, ipPacketLength); 01791 01792 //Check status code 01793 if(!error) 01794 { 01795 //Options should be padded when necessary to ensure that they end on 01796 //their natural 64-bit boundaries 01797 if((ipPacketLength + sizeof(NdpRedirectedHeaderOption)) < (optionLength * 8)) 01798 { 01799 //Determine the amount of padding data to append 01800 paddingLength = (optionLength * 8) - ipPacketLength - 01801 sizeof(NdpRedirectedHeaderOption); 01802 01803 //Prepare padding data 01804 memset(padding, 0, paddingLength); 01805 //Append padding bytes 01806 error = netBufferAppend(buffer, padding, paddingLength); 01807 } 01808 } 01809 01810 //Check status code 01811 if(!error) 01812 { 01813 //Get the length of the resulting message 01814 length = netBufferGetLength(buffer) - offset; 01815 01816 //Format IPv6 pseudo header 01817 pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr; 01818 pseudoHeader.destAddr = ipHeader->srcAddr; 01819 pseudoHeader.length = htonl(length); 01820 pseudoHeader.reserved = 0; 01821 pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; 01822 01823 //Message checksum calculation 01824 message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, 01825 sizeof(Ipv6PseudoHeader), buffer, offset, length); 01826 01827 //Debug message 01828 TRACE_INFO("Sending Redirect message (%" PRIuSIZE " bytes)...\r\n", length); 01829 //Dump message contents for debugging purpose 01830 ndpDumpRedirectMessage(message); 01831 01832 //Send Redirect message 01833 error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); 01834 } 01835 01836 //Free previously allocated memory 01837 netBufferFree(buffer); 01838 01839 //Return status code 01840 return error; 01841 } 01842 01843 01844 /** 01845 * @brief Dump Router Solicitation message for debugging purpose 01846 * @param[in] message Router Solicitation message 01847 **/ 01848 01849 void ndpDumpRouterSolMessage(const NdpRouterSolMessage *message) 01850 { 01851 //Dump Router Solicitation message 01852 TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); 01853 TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); 01854 TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); 01855 } 01856 01857 01858 /** 01859 * @brief Dump Router Advertisement message for debugging purpose 01860 * @param[in] message Router Advertisement message 01861 **/ 01862 01863 void ndpDumpRouterAdvMessage(const NdpRouterAdvMessage *message) 01864 { 01865 //Dump Router Advertisement message 01866 TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); 01867 TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); 01868 TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); 01869 TRACE_DEBUG(" Cur Hop Limit = %" PRIu8 "\r\n", message->curHopLimit); 01870 TRACE_DEBUG(" M = %" PRIu8 "\r\n", message->m); 01871 TRACE_DEBUG(" O = %" PRIu8 "\r\n", message->o); 01872 TRACE_DEBUG(" Router Lifetime = %" PRIu16 "\r\n", ntohs(message->routerLifetime)); 01873 TRACE_DEBUG(" Reachable Time = %" PRIu32 "\r\n", ntohl(message->reachableTime)); 01874 TRACE_DEBUG(" Retrans Timer = %" PRIu32 "\r\n", ntohl(message->retransTimer)); 01875 } 01876 01877 01878 /** 01879 * @brief Dump Neighbor Solicitation message for debugging purpose 01880 * @param[in] message Neighbor Solicitation message 01881 **/ 01882 01883 void ndpDumpNeighborSolMessage(const NdpNeighborSolMessage *message) 01884 { 01885 //Dump Neighbor Solicitation message 01886 TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); 01887 TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); 01888 TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); 01889 TRACE_DEBUG(" Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL)); 01890 } 01891 01892 01893 /** 01894 * @brief Dump Neighbor Advertisement message for debugging purpose 01895 * @param[in] message Neighbor Advertisement message 01896 **/ 01897 01898 void ndpDumpNeighborAdvMessage(const NdpNeighborAdvMessage *message) 01899 { 01900 //Dump Neighbor Advertisement message 01901 TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); 01902 TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); 01903 TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); 01904 TRACE_DEBUG(" R = %" PRIu8 "\r\n", message->r); 01905 TRACE_DEBUG(" S = %" PRIu8 "\r\n", message->s); 01906 TRACE_DEBUG(" O = %" PRIu8 "\r\n", message->o); 01907 TRACE_DEBUG(" Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL)); 01908 } 01909 01910 01911 /** 01912 * @brief Dump Redirect message for debugging purpose 01913 * @param[in] message Redirect message 01914 **/ 01915 01916 void ndpDumpRedirectMessage(const NdpRedirectMessage *message) 01917 { 01918 //Dump Neighbor Advertisement message 01919 TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); 01920 TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); 01921 TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); 01922 TRACE_DEBUG(" Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL)); 01923 TRACE_DEBUG(" Destination Address = %s\r\n", ipv6AddrToString(&message->destAddr, NULL)); 01924 } 01925 01926 #endif 01927
Generated on Tue Jul 12 2022 17:10:15 by
1.7.2