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.
slaac.c
00001 /** 00002 * @file slaac.c 00003 * @brief IPv6 Stateless Address Autoconfiguration 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 * Stateless Address Autoconfiguration is a facility to allow devices to 00028 * configure themselves independently. Refer to the following RFCs for 00029 * complete details: 00030 * - RFC 4862: IPv6 Stateless Address Autoconfiguration 00031 * - RFC 6106: IPv6 Router Advertisement Options for DNS Configuration 00032 * 00033 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00034 * @version 1.7.6 00035 **/ 00036 00037 //Switch to the appropriate trace level 00038 #define TRACE_LEVEL SLAAC_TRACE_LEVEL 00039 00040 //Dependencies 00041 #include "core/net.h" 00042 #include "core/ethernet.h" 00043 #include "ipv6/ipv6.h" 00044 #include "ipv6/ipv6_misc.h" 00045 #include "ipv6/slaac.h" 00046 #include "ipv6/ndp.h" 00047 #include "ipv6/ndp_misc.h" 00048 #include "debug.h" 00049 00050 //Check TCP/IP stack configuration 00051 #if (IPV6_SUPPORT == ENABLED && SLAAC_SUPPORT == ENABLED) 00052 00053 00054 /** 00055 * @brief Initialize settings with default values 00056 * @param[out] settings Structure that contains SLAAC settings 00057 **/ 00058 00059 void slaacGetDefaultSettings(SlaacSettings *settings) 00060 { 00061 //Use default interface 00062 settings->interface = netGetDefaultInterface(); 00063 00064 //Use the DNS servers specified by the RDNSS option 00065 settings->manualDnsConfig = FALSE; 00066 //Link state change event 00067 settings->linkChangeEvent = NULL; 00068 //Router Advertisement parsing callback 00069 settings->parseRouterAdvCallback = NULL; 00070 } 00071 00072 00073 /** 00074 * @brief SLAAC initialization 00075 * @param[in] context Pointer to the SLAAC context 00076 * @param[in] settings SLAAC specific settings 00077 * @return Error code 00078 **/ 00079 00080 error_t slaacInit(SlaacContext *context, const SlaacSettings *settings) 00081 { 00082 NetInterface *interface; 00083 00084 //Debug message 00085 TRACE_INFO("Initializing SLAAC...\r\n"); 00086 00087 //Ensure the parameters are valid 00088 if(context == NULL || settings == NULL) 00089 return ERROR_INVALID_PARAMETER; 00090 00091 //A valid pointer to the interface being configured is required 00092 if(settings->interface == NULL) 00093 return ERROR_INVALID_PARAMETER; 00094 00095 //Point to the underlying network interface 00096 interface = settings->interface; 00097 00098 //Clear the SLAAC context 00099 memset(context, 0, sizeof(SlaacContext)); 00100 //Save user settings 00101 context->settings = *settings; 00102 00103 //SLAAC operation is currently suspended 00104 context->running = FALSE; 00105 00106 //Attach the SLAAC context to the network interface 00107 interface->slaacContext = context; 00108 00109 //Successful initialization 00110 return NO_ERROR; 00111 } 00112 00113 00114 /** 00115 * @brief Start SLAAC process 00116 * @param[in] context Pointer to the SLAAC context 00117 * @return Error code 00118 **/ 00119 00120 error_t slaacStart(SlaacContext *context) 00121 { 00122 NetInterface *interface; 00123 00124 //Check parameter 00125 if(context == NULL) 00126 return ERROR_INVALID_PARAMETER; 00127 00128 //Debug message 00129 TRACE_INFO("Starting SLAAC...\r\n"); 00130 00131 //Get exclusive access 00132 osAcquireMutex(&netMutex); 00133 00134 //Point to the underlying network interface 00135 interface = context->settings.interface; 00136 00137 //Clear the list of IPv6 addresses 00138 ipv6FlushAddrList(interface); 00139 00140 //Automatic DNS server configuration? 00141 if(!context->settings.manualDnsConfig) 00142 { 00143 //Clear the list of DNS servers 00144 ipv6FlushDnsServerList(interface); 00145 } 00146 00147 //Check if the link is up? 00148 if(interface->linkState) 00149 { 00150 //A link-local address is formed by combining the well-known 00151 //link-local prefix fe80::/10 with the interface identifier 00152 slaacGenerateLinkLocalAddr(context); 00153 } 00154 00155 //Start SLAAC operation 00156 context->running = TRUE; 00157 00158 //Release exclusive access 00159 osReleaseMutex(&netMutex); 00160 00161 //Successful processing 00162 return NO_ERROR; 00163 } 00164 00165 00166 /** 00167 * @brief Stop SLAAC process 00168 * @param[in] context Pointer to the SLAAC context 00169 * @return Error code 00170 **/ 00171 00172 error_t slaacStop(SlaacContext *context) 00173 { 00174 //Check parameter 00175 if(context == NULL) 00176 return ERROR_INVALID_PARAMETER; 00177 00178 //Debug message 00179 TRACE_INFO("Stopping SLAAC...\r\n"); 00180 00181 //Get exclusive access 00182 osAcquireMutex(&netMutex); 00183 00184 //Suspend SLAAC operation 00185 context->running = FALSE; 00186 00187 //Release exclusive access 00188 osReleaseMutex(&netMutex); 00189 00190 //Successful processing 00191 return NO_ERROR; 00192 } 00193 00194 00195 /** 00196 * @brief Callback function for link change event 00197 * @param[in] context Pointer to the SLAAC context 00198 **/ 00199 00200 void slaacLinkChangeEvent(SlaacContext *context) 00201 { 00202 NetInterface *interface; 00203 00204 //Make sure the SLAAC service has been properly instantiated 00205 if(context == NULL) 00206 return; 00207 00208 //Point to the underlying network interface 00209 interface = context->settings.interface; 00210 00211 //Check whether SLAAC is enabled 00212 if(context->running) 00213 { 00214 //Automatic DNS server configuration? 00215 if(!context->settings.manualDnsConfig) 00216 { 00217 //Clear the list of DNS servers 00218 ipv6FlushDnsServerList(interface); 00219 } 00220 00221 //Link-up event? 00222 if(interface->linkState) 00223 { 00224 //A link-local address is formed by combining the well-known 00225 //link-local prefix fe80::/10 with the interface identifier 00226 slaacGenerateLinkLocalAddr(context); 00227 } 00228 } 00229 00230 //Any registered callback? 00231 if(context->settings.linkChangeEvent != NULL) 00232 { 00233 //Release exclusive access 00234 osReleaseMutex(&netMutex); 00235 //Invoke user callback function 00236 context->settings.linkChangeEvent(context, interface, interface->linkState); 00237 //Get exclusive access 00238 osAcquireMutex(&netMutex); 00239 } 00240 } 00241 00242 00243 /** 00244 * @brief Parse Router Advertisement message 00245 * @param[in] context Pointer to the SLAAC context 00246 * @param[in] message Pointer to the Router Advertisement message 00247 * @param[in] length Length of the message, in bytes 00248 **/ 00249 00250 void slaacParseRouterAdv(SlaacContext *context, 00251 NdpRouterAdvMessage *message, size_t length) 00252 { 00253 uint_t i; 00254 uint_t n; 00255 NetInterface *interface; 00256 NdpPrefixInfoOption *prefixInfoOption; 00257 NdpRdnssOption *rdnssOption; 00258 00259 //Point to the underlying network interface 00260 interface = context->settings.interface; 00261 00262 //Check whether SLAAC is enabled 00263 if(!context->running) 00264 return; 00265 00266 //Make sure that a valid link-local address has been assigned to the interface 00267 if(ipv6GetLinkLocalAddrState(interface) != IPV6_ADDR_STATE_PREFERRED) 00268 return; 00269 00270 //Calculate the length of the Options field 00271 length -= sizeof(NdpRouterAdvMessage); 00272 00273 //This flag tracks changes in IPv6 configuration 00274 context->configUpdated = FALSE; 00275 00276 //Point to the beginning of the Options field 00277 n = 0; 00278 00279 //Parse Options field 00280 while(1) 00281 { 00282 //Search the Options field for any Prefix Information options 00283 prefixInfoOption = ndpGetOption(message->options + n, 00284 length - n, NDP_OPT_PREFIX_INFORMATION); 00285 00286 //No more option of the specified type? 00287 if(prefixInfoOption == NULL) 00288 break; 00289 00290 //Parse the Prefix Information Option 00291 slaacParsePrefixInfoOption(context, prefixInfoOption); 00292 00293 //Retrieve the offset to the current position 00294 n = (uint8_t *) prefixInfoOption - message->options; 00295 //Jump to the next option 00296 n += prefixInfoOption->length * 8; 00297 } 00298 00299 //Automatic DNS server configuration? 00300 if(!context->settings.manualDnsConfig) 00301 { 00302 //Search for the Recursive DNS Server (RDNSS) option 00303 rdnssOption = ndpGetOption(message->options, length, 00304 NDP_OPT_RECURSIVE_DNS_SERVER); 00305 00306 //RDNSS option found? 00307 if(rdnssOption != NULL && rdnssOption->length >= 1) 00308 { 00309 //Retrieve the number of addresses 00310 n = (rdnssOption->length - 1) / 2; 00311 00312 //Loop through the list of DNS servers 00313 for(i = 0; i < n && i < IPV6_DNS_SERVER_LIST_SIZE; i++) 00314 { 00315 //Record DNS server address 00316 interface->ipv6Context.dnsServerList[i] = rdnssOption->address[i]; 00317 } 00318 } 00319 } 00320 00321 //Any registered callback? 00322 if(context->settings.parseRouterAdvCallback != NULL) 00323 { 00324 //Invoke user callback function 00325 context->settings.parseRouterAdvCallback(context, message, length); 00326 } 00327 00328 //Check whether a new IPv6 address has been assigned to the interface 00329 if(context->configUpdated) 00330 { 00331 //Dump current IPv6 configuration for debugging purpose 00332 slaacDumpConfig(context); 00333 } 00334 } 00335 00336 00337 /** 00338 * @brief Parse Prefix Information Option 00339 * @param[in] context Pointer to the SLAAC context 00340 * @param[in] option Pointer to the Prefix Information option 00341 **/ 00342 00343 void slaacParsePrefixInfoOption(SlaacContext *context, 00344 NdpPrefixInfoOption *option) 00345 { 00346 uint_t i; 00347 bool_t found; 00348 systime_t time; 00349 systime_t validLifetime; 00350 systime_t preferredLifetime; 00351 systime_t remainingLifetime; 00352 NetInterface *interface; 00353 Ipv6AddrEntry *entry; 00354 Ipv6Addr addr; 00355 00356 //Make sure the Prefix Information option is valid 00357 if(option == NULL || option->length != 4) 00358 return; 00359 00360 //If the Autonomous flag is not set, silently ignore the Prefix 00361 //Information option 00362 if(!option->a) 00363 return; 00364 00365 //If the prefix is the link-local prefix, silently ignore the 00366 //Prefix Information option 00367 if(ipv6CompPrefix(&option->prefix, &IPV6_LINK_LOCAL_ADDR_PREFIX, 10)) 00368 return; 00369 00370 //Check whether the valid lifetime is zero 00371 if(ntohl(option->validLifetime) == 0) 00372 return; 00373 00374 //If the preferred lifetime is greater than the valid lifetime, 00375 //silently ignore the Prefix Information option 00376 if(ntohl(option->preferredLifetime) > ntohl(option->validLifetime)) 00377 return; 00378 00379 //If the sum of the prefix length and interface identifier length does 00380 //not equal 128 bits, the Prefix Information option must be ignored 00381 if(option->prefixLength != 64) 00382 return; 00383 00384 //Get current time 00385 time = osGetSystemTime(); 00386 00387 //Point to the underlying network interface 00388 interface = context->settings.interface; 00389 00390 //Form an address by combining the advertised prefix 00391 //with the interface identifier 00392 addr.w[0] = option->prefix.w[0]; 00393 addr.w[1] = option->prefix.w[1]; 00394 addr.w[2] = option->prefix.w[2]; 00395 addr.w[3] = option->prefix.w[3]; 00396 addr.w[4] = interface->eui64.w[0]; 00397 addr.w[5] = interface->eui64.w[1]; 00398 addr.w[6] = interface->eui64.w[2]; 00399 addr.w[7] = interface->eui64.w[3]; 00400 00401 //Convert Valid Lifetime to host byte order 00402 validLifetime = ntohl(option->validLifetime); 00403 00404 //Check the valid lifetime 00405 if(validLifetime != NDP_INFINITE_LIFETIME) 00406 { 00407 //The length of time in seconds that the prefix is valid 00408 //for the purpose of on-link determination 00409 if(validLifetime < (MAX_DELAY / 1000)) 00410 validLifetime *= 1000; 00411 else 00412 validLifetime = MAX_DELAY; 00413 } 00414 else 00415 { 00416 //A value of all one bits (0xffffffff) represents infinity 00417 validLifetime = INFINITE_DELAY; 00418 } 00419 00420 //Convert Preferred Lifetime to host byte order 00421 preferredLifetime = ntohl(option->preferredLifetime); 00422 00423 //Check the preferred lifetime 00424 if(preferredLifetime != NDP_INFINITE_LIFETIME) 00425 { 00426 //The length of time in seconds that addresses generated from the 00427 //prefix via stateless address autoconfiguration remain preferred 00428 if(preferredLifetime < (MAX_DELAY / 1000)) 00429 preferredLifetime *= 1000; 00430 else 00431 preferredLifetime = MAX_DELAY; 00432 } 00433 else 00434 { 00435 //A value of all one bits (0xffffffff) represents infinity 00436 preferredLifetime = INFINITE_DELAY; 00437 } 00438 00439 //This flag will be set if the advertised prefix matches an address 00440 //assigned to the interface 00441 found = FALSE; 00442 00443 //Loop through the list of IPv6 addresses 00444 for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++) 00445 { 00446 //Point to the current entry 00447 entry = &interface->ipv6Context.addrList[i]; 00448 00449 //Check whether the advertised prefix is equal to the prefix of an 00450 //address configured by stateless autoconfiguration in the list 00451 if(ipv6CompPrefix(&entry->addr, &option->prefix, option->prefixLength)) 00452 { 00453 //Valid address? 00454 if(entry->state == IPV6_ADDR_STATE_PREFERRED || 00455 entry->state == IPV6_ADDR_STATE_DEPRECATED) 00456 { 00457 //Set flag 00458 found = TRUE; 00459 00460 //The preferred lifetime of the address is reset to the Preferred 00461 //Lifetime in the received advertisement 00462 entry->preferredLifetime = preferredLifetime; 00463 00464 //Compute the remaining time to the valid lifetime expiration 00465 //of the previously autoconfigured address 00466 if(timeCompare(time, entry->timestamp + entry->validLifetime) < 0) 00467 remainingLifetime = entry->timestamp + entry->validLifetime - time; 00468 else 00469 remainingLifetime = 0; 00470 00471 //The specific action to perform for the valid lifetime of the 00472 //address depends on the Valid Lifetime in the received Router 00473 //Advertisement and the remaining time 00474 if(validLifetime > SLAAC_LIFETIME_2_HOURS || 00475 validLifetime > remainingLifetime) 00476 { 00477 //If the received Valid Lifetime is greater than 2 hours or 00478 //greater than remaining lifetime, set the valid lifetime of 00479 //the corresponding address to the advertised Valid Lifetime 00480 entry->validLifetime = validLifetime; 00481 00482 //Save current time 00483 entry->timestamp = time; 00484 //Update the state of the IPv6 address 00485 entry->state = IPV6_ADDR_STATE_PREFERRED; 00486 } 00487 else if(remainingLifetime <= SLAAC_LIFETIME_2_HOURS) 00488 { 00489 //If remaining lifetime is less than or equal to 2 hours, ignore 00490 //the Prefix Information option with regards to the valid lifetime 00491 } 00492 else 00493 { 00494 //Otherwise, reset the valid lifetime of the corresponding 00495 //address to 2 hours 00496 entry->validLifetime = SLAAC_LIFETIME_2_HOURS; 00497 00498 //Save current time 00499 entry->timestamp = time; 00500 //Update the state of the IPv6 address 00501 entry->state = IPV6_ADDR_STATE_PREFERRED; 00502 } 00503 } 00504 //Tentative address? 00505 else if(entry->state == IPV6_ADDR_STATE_TENTATIVE) 00506 { 00507 //Do not update the preferred and valid lifetimes of the address 00508 //when Duplicate Address Detection is being performed 00509 found = TRUE; 00510 } 00511 } 00512 } 00513 00514 //The IPv6 address is not yet in the list? 00515 if(!found) 00516 { 00517 //Loop through the list of IPv6 addresses 00518 for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++) 00519 { 00520 //Point to the current entry 00521 entry = &interface->ipv6Context.addrList[i]; 00522 00523 //Check the state of the IPv6 address 00524 if(entry->state == IPV6_ADDR_STATE_INVALID) 00525 { 00526 //If an address is formed successfully and the address is not yet 00527 //in the list, the host adds it to the list of addresses assigned 00528 //to the interface, initializing its preferred and valid lifetime 00529 //values from the Prefix Information option 00530 if(interface->ndpContext.dupAddrDetectTransmits > 0) 00531 { 00532 //Use the IPv6 address as a tentative address 00533 ipv6SetAddr(interface, i, &addr, IPV6_ADDR_STATE_TENTATIVE, 00534 validLifetime, preferredLifetime, FALSE); 00535 } 00536 else 00537 { 00538 //The use of the IPv6 address is now unrestricted 00539 ipv6SetAddr(interface, i, &addr, IPV6_ADDR_STATE_PREFERRED, 00540 validLifetime, preferredLifetime, FALSE); 00541 } 00542 00543 //A new IPv6 address has just been assigned to the interface 00544 context->configUpdated = TRUE; 00545 //We are done 00546 break; 00547 } 00548 } 00549 } 00550 } 00551 00552 00553 /** 00554 * @brief Generate a link-local address 00555 * @param[in] context Pointer to the SLAAC context 00556 * @return Error code 00557 **/ 00558 00559 error_t slaacGenerateLinkLocalAddr(SlaacContext *context) 00560 { 00561 error_t error; 00562 NetInterface *interface; 00563 Ipv6Addr addr; 00564 00565 //Point to the underlying network interface 00566 interface = context->settings.interface; 00567 00568 //Check whether a link-local address has been manually assigned 00569 if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID && 00570 interface->ipv6Context.addrList[0].permanent) 00571 { 00572 //Keep using the current link-local address 00573 error = NO_ERROR; 00574 } 00575 else 00576 { 00577 //A link-local address is formed by combining the well-known 00578 //link-local prefix fe80::/10 with the interface identifier 00579 ipv6GenerateLinkLocalAddr(&interface->eui64, &addr); 00580 00581 //Check whether Duplicate Address Detection should be performed 00582 if(interface->ndpContext.dupAddrDetectTransmits > 0) 00583 { 00584 //Use the link-local address as a tentative address 00585 error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE, 00586 NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE); 00587 } 00588 else 00589 { 00590 //The use of the link-local address is now unrestricted 00591 error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED, 00592 NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE); 00593 } 00594 } 00595 00596 //Return status code 00597 return error; 00598 } 00599 00600 00601 /** 00602 * @brief Dump IPv6 configuration for debugging purpose 00603 * @param[in] context Pointer to the SLAAC context 00604 **/ 00605 00606 void slaacDumpConfig(SlaacContext *context) 00607 { 00608 #if (SLAAC_TRACE_LEVEL >= TRACE_LEVEL_INFO) 00609 uint_t i; 00610 NetInterface *interface; 00611 Ipv6Context *ipv6Context; 00612 00613 //Point to the underlying network interface 00614 interface = context->settings.interface; 00615 //Point to the IPv6 context 00616 ipv6Context = &interface->ipv6Context; 00617 00618 //Debug message 00619 TRACE_INFO("\r\n"); 00620 TRACE_INFO("SLAAC configuration:\r\n"); 00621 00622 //Link-local address 00623 TRACE_INFO(" Link-local Address = %s\r\n", 00624 ipv6AddrToString(&ipv6Context->addrList[0].addr, NULL)); 00625 00626 //Global addresses 00627 for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++) 00628 { 00629 TRACE_INFO(" Global Address %u = %s\r\n", i, 00630 ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL)); 00631 } 00632 00633 //IPv6 prefixes 00634 for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) 00635 { 00636 TRACE_INFO(" Prefix %u = %s/%" PRIu8 "\r\n", i + 1, 00637 ipv6AddrToString(&ipv6Context->prefixList[i].prefix, NULL), 00638 ipv6Context->prefixList[i].prefixLength); 00639 } 00640 00641 //Default routers 00642 for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++) 00643 { 00644 TRACE_INFO(" Default Router %u = %s\r\n", i + 1, 00645 ipv6AddrToString(&ipv6Context->routerList[i].addr, NULL)); 00646 } 00647 00648 //DNS servers 00649 for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++) 00650 { 00651 TRACE_INFO(" DNS Server %u = %s\r\n", i + 1, 00652 ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL)); 00653 } 00654 00655 //Maximum transmit unit 00656 TRACE_INFO(" MTU = %" PRIuSIZE "\r\n", ipv6Context->linkMtu); 00657 TRACE_INFO("\r\n"); 00658 #endif 00659 } 00660 00661 #endif 00662
Generated on Tue Jul 12 2022 17:10:16 by
1.7.2