Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers slaac.c Source File

slaac.c

Go to the documentation of this file.
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