Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
ndp_misc.c
Go to the documentation of this file.
00001 /** 00002 * @file ndp_misc.c 00003 * @brief Helper functions for 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 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL NDP_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include "core/net.h" 00034 #include "ipv6/ipv6.h" 00035 #include "ipv6/ipv6_misc.h" 00036 #include "ipv6/ndp.h" 00037 #include "ipv6/ndp_cache.h" 00038 #include "ipv6/ndp_misc.h" 00039 #include "mdns/mdns_responder.h" 00040 #include "debug.h" 00041 00042 //Check TCP/IP stack configuration 00043 #if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED) 00044 00045 00046 /** 00047 * @brief Parse Prefix Information Option 00048 * @param[in] interface Underlying network interface 00049 * @param[in] option Pointer to the Prefix Information option 00050 **/ 00051 00052 void ndpParsePrefixInfoOption(NetInterface *interface, NdpPrefixInfoOption *option) 00053 { 00054 //Make sure the Prefix Information option is valid 00055 if(option == NULL || option->length != 4) 00056 return; 00057 00058 //A prefix Information option that have the on-link flag set indicates a 00059 //prefix identifying a range of addresses that should be considered on-link 00060 if(!option->l) 00061 return; 00062 00063 //If the prefix is the link-local prefix, silently ignore the 00064 //Prefix Information option 00065 if(ipv6CompPrefix(&option->prefix, &IPV6_LINK_LOCAL_ADDR_PREFIX, 10)) 00066 return; 00067 00068 //If the preferred lifetime is greater than the valid lifetime, 00069 //silently ignore the Prefix Information option 00070 if(ntohl(option->preferredLifetime) > ntohl(option->validLifetime)) 00071 return; 00072 00073 //Check whether the Valid Lifetime field is non-zero 00074 if(ntohl(option->validLifetime) != 0) 00075 { 00076 //If the prefix is not already present in the Prefix List, create a new 00077 //entry for the prefix. If the prefix is already present in the list, 00078 //reset its invalidation timer 00079 ipv6AddPrefix(interface, &option->prefix, option->prefixLength, 00080 ntohl(option->validLifetime), ntohl(option->preferredLifetime)); 00081 } 00082 else 00083 { 00084 //If the new Lifetime value is zero, time-out the prefix immediately 00085 ipv6RemovePrefix(interface, &option->prefix, option->prefixLength); 00086 } 00087 } 00088 00089 00090 /** 00091 * @brief Manage the lifetime of IPv6 addresses 00092 * @param[in] interface Underlying network interface 00093 **/ 00094 00095 void ndpUpdateAddrList(NetInterface *interface) 00096 { 00097 uint_t i; 00098 systime_t time; 00099 Ipv6AddrEntry *entry; 00100 NdpContext *context; 00101 00102 //Point to the NDP context 00103 context = &interface->ndpContext; 00104 00105 //Get current time 00106 time = osGetSystemTime(); 00107 00108 //Go through the list of IPv6 addresses 00109 for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) 00110 { 00111 //Point to the current entry 00112 entry = &interface->ipv6Context.addrList[i]; 00113 00114 //Tentative address? 00115 if(entry->state == IPV6_ADDR_STATE_TENTATIVE) 00116 { 00117 //Check whether the link is up 00118 if(interface->linkState) 00119 { 00120 //To check an address, a node should send Neighbor Solicitation messages 00121 if(entry->dadRetransmitCount == 0) 00122 { 00123 //Set time stamp 00124 entry->timestamp = time; 00125 00126 //Check whether Duplicate Address Detection should be performed 00127 if(context->dupAddrDetectTransmits > 0) 00128 { 00129 //Link-local address? 00130 if(i == 0) 00131 { 00132 //Delay before transmitting the first solicitation 00133 entry->dadTimeout = netGetRandRange(0, NDP_MAX_RTR_SOLICITATION_DELAY); 00134 //Prepare to send the first Neighbor Solicitation message 00135 entry->dadRetransmitCount = 1; 00136 } 00137 else 00138 { 00139 //Valid link-local address? 00140 if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) 00141 { 00142 //Prepare to send the first Neighbor Solicitation message 00143 entry->dadTimeout = 0; 00144 entry->dadRetransmitCount = 1; 00145 } 00146 } 00147 } 00148 else 00149 { 00150 //Do not perform Duplicate Address Detection 00151 entry->state = IPV6_ADDR_STATE_PREFERRED; 00152 } 00153 } 00154 else 00155 { 00156 //Check current time 00157 if(timeCompare(time, entry->timestamp + entry->dadTimeout) >= 0) 00158 { 00159 //Duplicate Address Detection failed? 00160 if(entry->duplicate) 00161 { 00162 //A tentative address that is determined to be a duplicate 00163 //must not be assigned to an interface 00164 if(entry->permanent) 00165 { 00166 //The IPv6 address should be preserved if it has been 00167 //manually assigned 00168 ipv6SetAddr(interface, i, &entry->addr, 00169 IPV6_ADDR_STATE_INVALID, 0, 0, TRUE); 00170 } 00171 else 00172 { 00173 //The IPv6 address is no more valid and should be 00174 //removed from the list 00175 ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR, 00176 IPV6_ADDR_STATE_INVALID, 0, 0, FALSE); 00177 } 00178 } 00179 //Duplicate Address Detection is on-going? 00180 else if(entry->dadRetransmitCount <= context->dupAddrDetectTransmits) 00181 { 00182 //Send a multicast Neighbor Solicitation message 00183 ndpSendNeighborSol(interface, &entry->addr, TRUE); 00184 00185 //Set timeout value 00186 entry->dadTimeout += context->retransTimer; 00187 //Increment retransmission counter 00188 entry->dadRetransmitCount++; 00189 } 00190 //Duplicate Address Detection is complete? 00191 else 00192 { 00193 //The use of the IPv6 address is now unrestricted 00194 entry->state = IPV6_ADDR_STATE_PREFERRED; 00195 00196 #if (MDNS_RESPONDER_SUPPORT == ENABLED) 00197 //Link-local address? 00198 if(i == 0) 00199 { 00200 //Restart mDNS probing process 00201 mdnsResponderStartProbing(interface->mdnsResponderContext); 00202 } 00203 #endif 00204 } 00205 } 00206 } 00207 } 00208 } 00209 //Preferred address? 00210 else if(entry->state == IPV6_ADDR_STATE_PREFERRED) 00211 { 00212 //An IPv6 address with an infinite preferred lifetime is never timed out 00213 if(entry->preferredLifetime != NDP_INFINITE_LIFETIME) 00214 { 00215 //When the preferred lifetime expires, the address becomes deprecated 00216 if(timeCompare(time, entry->timestamp + entry->preferredLifetime) >= 0) 00217 { 00218 //A deprecated address should continue to be used as a source 00219 //address in existing communications, but should not be used 00220 //to initiate new communications 00221 entry->state = IPV6_ADDR_STATE_DEPRECATED; 00222 } 00223 } 00224 } 00225 //Deprecated address? 00226 else if(entry->state == IPV6_ADDR_STATE_DEPRECATED) 00227 { 00228 //An IPv6 address with an infinite valid lifetime is never timed out 00229 if(entry->validLifetime != NDP_INFINITE_LIFETIME) 00230 { 00231 //When the valid lifetime expires, the address becomes invalid 00232 if(timeCompare(time, entry->timestamp + entry->validLifetime) >= 0) 00233 { 00234 //The IPv6 address is no more valid and should be removed from the list 00235 ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR, 00236 IPV6_ADDR_STATE_INVALID, 0, 0, FALSE); 00237 } 00238 } 00239 } 00240 } 00241 } 00242 00243 00244 /** 00245 * @brief Periodically update Prefix List 00246 * @param[in] interface Underlying network interface 00247 **/ 00248 00249 void ndpUpdatePrefixList(NetInterface *interface) 00250 { 00251 uint_t i; 00252 systime_t time; 00253 Ipv6PrefixEntry *entry; 00254 00255 //Get current time 00256 time = osGetSystemTime(); 00257 00258 //Go through the Prefix List 00259 for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) 00260 { 00261 //Point to the current entry 00262 entry = &interface->ipv6Context.prefixList[i]; 00263 00264 //Check the lifetime value 00265 if(entry->validLifetime > 0 && entry->validLifetime < INFINITE_DELAY) 00266 { 00267 //A node should retain entries in the Prefix List until their 00268 //lifetimes expire 00269 if(timeCompare(time, entry->timestamp + entry->validLifetime) >= 0) 00270 { 00271 //When removing an entry from the Prefix List, there is no need 00272 //to purge any entries from the Destination or Neighbor Caches 00273 ipv6RemovePrefix(interface, &entry->prefix, entry->prefixLength); 00274 } 00275 } 00276 } 00277 } 00278 00279 00280 /** 00281 * @brief Periodically update Default Router List 00282 * @param[in] interface Underlying network interface 00283 **/ 00284 00285 void ndpUpdateDefaultRouterList(NetInterface *interface) 00286 { 00287 uint_t i; 00288 bool_t flag; 00289 systime_t time; 00290 Ipv6RouterEntry *entry; 00291 00292 //This flag will be set if any entry has been removed from 00293 //the Default Router List 00294 flag = FALSE; 00295 00296 //Get current time 00297 time = osGetSystemTime(); 00298 00299 //Go through the Default Router List 00300 for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++) 00301 { 00302 //Point to the current entry 00303 entry = &interface->ipv6Context.routerList[i]; 00304 00305 //Check the lifetime value 00306 if(entry->lifetime > 0 && entry->lifetime < INFINITE_DELAY) 00307 { 00308 //A node should retain entries in the Default Router List until 00309 //their lifetimes expire 00310 if(timeCompare(time, entry->timestamp + entry->lifetime) >= 0) 00311 { 00312 //Immediately time-out the entry 00313 entry->addr = IPV6_UNSPECIFIED_ADDR; 00314 entry->lifetime = 0; 00315 00316 //Set flag 00317 flag = TRUE; 00318 } 00319 } 00320 } 00321 00322 //Check whether an entry has been removed from the list 00323 if(flag) 00324 { 00325 //When removing an entry from the Default Router List, any entries 00326 //in the Destination Cache that go through that router must perform 00327 //next-hop determination again to select a new default router 00328 ndpFlushDestCache(interface); 00329 } 00330 } 00331 00332 00333 /** 00334 * @brief Default Router Selection 00335 * @param[in] interface Underlying network interface 00336 * @param[in] unreachableAddr IPv6 address of the unreachable router (optional parameter) 00337 * @param[out] addr IPv6 address of the default router to be used 00338 * @return Error code 00339 **/ 00340 00341 error_t ndpSelectDefaultRouter(NetInterface *interface, 00342 const Ipv6Addr *unreachableAddr, Ipv6Addr *addr) 00343 { 00344 uint_t i; 00345 uint_t j; 00346 uint_t k; 00347 Ipv6RouterEntry *routerEntry; 00348 NdpNeighborCacheEntry *neighborCacheEntry; 00349 00350 //Initialize index 00351 i = 0; 00352 00353 //This parameter is optional... 00354 if(unreachableAddr != NULL) 00355 { 00356 //Search the Default Router List for the router whose reachability is suspect 00357 for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++) 00358 { 00359 //Point to the current entry 00360 routerEntry = &interface->ipv6Context.routerList[j]; 00361 00362 //Check the lifetime associated with the default router 00363 if(routerEntry->lifetime) 00364 { 00365 //Check the router address against the address whose reachability is suspect 00366 if(ipv6CompAddr(&routerEntry->addr, unreachableAddr)) 00367 { 00368 //Routers should be selected in a round-robin fashion 00369 i = j + 1; 00370 //We are done 00371 break; 00372 } 00373 } 00374 } 00375 } 00376 00377 //Routers that are reachable or probably reachable should be preferred 00378 //over routers whose reachability is unknown or suspect 00379 for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++) 00380 { 00381 //Get current index 00382 k = (i + j) % IPV6_ROUTER_LIST_SIZE; 00383 00384 //Point to the corresponding entry 00385 routerEntry = &interface->ipv6Context.routerList[k]; 00386 00387 //Check the lifetime associated with the default router 00388 if(routerEntry->lifetime) 00389 { 00390 //Search the Neighbor Cache for the router address 00391 neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &routerEntry->addr); 00392 00393 //Check whether the router is reachable or probably reachable 00394 if(neighborCacheEntry != NULL) 00395 { 00396 //Any state other than INCOMPLETE? 00397 if(neighborCacheEntry->state != NDP_STATE_INCOMPLETE) 00398 { 00399 //Return the IPv6 address of the default router 00400 *addr = routerEntry->addr; 00401 //Successful default router selection 00402 return NO_ERROR; 00403 } 00404 } 00405 } 00406 } 00407 00408 //When no routers on the list are known to be reachable or probably 00409 //reachable, routers should be selected in a round-robin fashion, so 00410 //that subsequent requests for a default router do not return the 00411 //same router until all other routers have been selected 00412 for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++) 00413 { 00414 //Get current index 00415 k = (i + j) % IPV6_ROUTER_LIST_SIZE; 00416 00417 //Point to the corresponding entry 00418 routerEntry = &interface->ipv6Context.routerList[k]; 00419 00420 //Check the lifetime associated with the default router 00421 if(routerEntry->lifetime) 00422 { 00423 //Return the IPv6 address of the default router 00424 *addr = routerEntry->addr; 00425 //Successful default router selection 00426 return NO_ERROR; 00427 } 00428 } 00429 00430 //No default router found... 00431 return ERROR_NO_ROUTE; 00432 } 00433 00434 00435 /** 00436 * @brief Check whether an address is the first-hop router for the specified destination 00437 * @param[in] interface Underlying network interface 00438 * @param[in] destAddr Destination address 00439 * @param[in] nextHop First-hop address to be checked 00440 * @return TRUE if the address is the first-hop router, else FALSE 00441 **/ 00442 00443 bool_t ndpIsFirstHopRouter(NetInterface *interface, 00444 const Ipv6Addr *destAddr, const Ipv6Addr *nextHop) 00445 { 00446 uint_t i; 00447 bool_t isFirstHopRouter; 00448 Ipv6RouterEntry *routerEntry; 00449 NdpDestCacheEntry *destCacheEntry; 00450 00451 //Clear flag 00452 isFirstHopRouter = FALSE; 00453 00454 //Search the cache for the specified destination address 00455 destCacheEntry = ndpFindDestCacheEntry(interface, destAddr); 00456 00457 //Any matching entry? 00458 if(destCacheEntry != NULL) 00459 { 00460 //Check if the address is the same as the current first-hop 00461 //router for the specified destination 00462 if(ipv6CompAddr(&destCacheEntry->nextHop, nextHop)) 00463 isFirstHopRouter = TRUE; 00464 } 00465 else 00466 { 00467 //Loop through the Default Router List 00468 for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++) 00469 { 00470 //Point to the current entry 00471 routerEntry = &interface->ipv6Context.routerList[i]; 00472 00473 //Check the lifetime associated with the default router 00474 if(routerEntry->lifetime) 00475 { 00476 //Check whether the current entry matches the specified address 00477 if(ipv6CompAddr(&routerEntry->addr, nextHop)) 00478 { 00479 //The specified address is a valid first-hop router 00480 isFirstHopRouter = TRUE; 00481 //We are done 00482 break; 00483 } 00484 } 00485 } 00486 } 00487 00488 //Return TRUE if the address is the same as the current first-hop 00489 //router for the specified destination 00490 return isFirstHopRouter; 00491 } 00492 00493 00494 /** 00495 * @brief Next-hop determination 00496 * @param[in] interface Underlying network interface 00497 * @param[in] destAddr Destination address 00498 * @param[in] unreachableNextHop Address of the unreachable next-hop (optional parameter) 00499 * @param[out] nextHop Next-hop address to be used 00500 * @return Error code 00501 **/ 00502 00503 error_t ndpSelectNextHop(NetInterface *interface, const Ipv6Addr *destAddr, 00504 const Ipv6Addr *unreachableNextHop, Ipv6Addr *nextHop) 00505 { 00506 error_t error; 00507 00508 //Destination IPv6 address is a multicast address? 00509 if(ipv6IsMulticastAddr(destAddr)) 00510 { 00511 //For multicast packets, the next-hop is always the (multicast) 00512 //destination address and is considered to be on-link 00513 *nextHop = *destAddr; 00514 //Successful next-hop determination 00515 error = NO_ERROR; 00516 } 00517 else 00518 { 00519 //The sender performs a longest prefix match against the Prefix 00520 //List to determine whether the packet's destination is on-link 00521 //or off-link 00522 if(ipv6IsOnLink(interface, destAddr)) 00523 { 00524 //If the destination is on-link, the next-hop address is the 00525 //same as the packet's destination address 00526 *nextHop = *destAddr; 00527 //Successful next-hop determination 00528 error = NO_ERROR; 00529 } 00530 else 00531 { 00532 //If the destination is off-link, the sender selects a router 00533 //from the Default Router List 00534 error = ndpSelectDefaultRouter(interface, unreachableNextHop, nextHop); 00535 } 00536 } 00537 00538 //Return status code 00539 return error; 00540 } 00541 00542 00543 /** 00544 * @brief Update next-hop field of Destination Cache entries 00545 * @param[in] interface Underlying network interface 00546 * @param[in] unreachableNextHop Address of the unreachable next-hop 00547 **/ 00548 00549 void ndpUpdateNextHop(NetInterface *interface, const Ipv6Addr *unreachableNextHop) 00550 { 00551 error_t error; 00552 uint_t i; 00553 NdpDestCacheEntry *entry; 00554 00555 //Go through Destination Cache 00556 for(i = 0; i < NDP_DEST_CACHE_SIZE; i++) 00557 { 00558 //Point to the current entry 00559 entry = &interface->ndpContext.destCache[i]; 00560 00561 //Check whether the unreachable IPv6 address is used a first-hop router 00562 if(ipv6CompAddr(&entry->nextHop, unreachableNextHop)) 00563 { 00564 //Perform next-hop determination 00565 error = ndpSelectNextHop(interface, &entry->destAddr, 00566 &entry->nextHop, &entry->nextHop); 00567 00568 //Next-hop determination failed? 00569 if(error) 00570 { 00571 //Remove the current entry from the Destination Cache 00572 entry->destAddr = IPV6_UNSPECIFIED_ADDR; 00573 } 00574 } 00575 } 00576 } 00577 00578 00579 /** 00580 * @brief Append an option to a NDP message 00581 * @param[in] message Pointer to the NDP message 00582 * @param[in,out] messageLength Length of the entire message 00583 * @param[in] type Option type 00584 * @param[in] value Option value 00585 * @param[in] length Length of the option value 00586 **/ 00587 00588 void ndpAddOption(void *message, size_t *messageLength, 00589 uint8_t type, const void *value, size_t length) 00590 { 00591 size_t optionLength; 00592 size_t paddingLength; 00593 NdpOption *option; 00594 00595 //Length of the option in units of 8 bytes including the type and length fields 00596 optionLength = (length + sizeof(NdpOption) + 7) / 8; 00597 00598 //Sanity check 00599 if(optionLength <= UINT8_MAX) 00600 { 00601 //Point to the buffer where the option is to be written 00602 option = (NdpOption *) ((uint8_t *) message + *messageLength); 00603 00604 //Option type 00605 option->type = type; 00606 //Option length 00607 option->length = (uint8_t) optionLength; 00608 //Option value 00609 memcpy(option->value, value, length); 00610 00611 //Options should be padded when necessary to ensure that they end on 00612 //their natural 64-bit boundaries 00613 if((length + sizeof(NdpOption)) < (optionLength * 8)) 00614 { 00615 //Determine the amount of padding data to append 00616 paddingLength = (optionLength * 8) - length - sizeof(NdpOption); 00617 //Write padding data 00618 memset(option->value + length, 0, paddingLength); 00619 } 00620 00621 //Adjust the length of the NDP message 00622 *messageLength += optionLength * 8; 00623 } 00624 } 00625 00626 00627 /** 00628 * @brief Find a specified option in a NDP message 00629 * @param[in] options Pointer to the Options field 00630 * @param[in] length Length of the Options field 00631 * @param[in] type Type of the option to find 00632 * @return If the specified option is found, a pointer to the corresponding 00633 * option is returned. Otherwise NULL pointer is returned 00634 **/ 00635 00636 void *ndpGetOption(uint8_t *options, size_t length, uint8_t type) 00637 { 00638 size_t i; 00639 NdpOption *option; 00640 00641 //Point to the very first option of the NDP message 00642 i = 0; 00643 00644 //Parse options 00645 while((i + sizeof(NdpOption)) <= length) 00646 { 00647 //Point to the current option 00648 option = (NdpOption *) (options + i); 00649 00650 //Nodes must silently discard an NDP message that contains 00651 //an option with length zero 00652 if(option->length == 0) 00653 break; 00654 //Check option length 00655 if((i + option->length * 8) > length) 00656 break; 00657 00658 //Current option type matches the specified one? 00659 if(option->type == type || type == NDP_OPT_ANY) 00660 return option; 00661 00662 //Jump to next the next option 00663 i += option->length * 8; 00664 } 00665 00666 //Specified option type not found 00667 return NULL; 00668 } 00669 00670 00671 /** 00672 * @brief Check NDP message options 00673 * @param[in] options Pointer to the Options field 00674 * @param[in] length Length of the Options field 00675 * @return Error code 00676 **/ 00677 00678 error_t ndpCheckOptions(const uint8_t *options, size_t length) 00679 { 00680 size_t i; 00681 NdpOption *option; 00682 00683 //Point to the very first option of the NDP message 00684 i = 0; 00685 00686 //Parse options 00687 while((i + sizeof(NdpOption)) <= length) 00688 { 00689 //Point to the current option 00690 option = (NdpOption *) (options + i); 00691 00692 //Nodes must silently discard an NDP message that contains 00693 //an option with length zero 00694 if(option->length == 0) 00695 return ERROR_INVALID_OPTION; 00696 00697 //Jump to next the next option 00698 i += option->length * 8; 00699 } 00700 00701 //The Options field is valid 00702 return NO_ERROR; 00703 } 00704 00705 #endif 00706
Generated on Tue Jul 12 2022 17:10:15 by
