Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
ndp.c
Go to the documentation of this file.
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
