Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ndp_misc.c Source File

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