Webserver+3d print
cyclone_tcp/ipv6/ndp_misc.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file ndp_misc.c * @brief Helper functions for NDP (Neighbor Discovery Protocol) * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneTCP Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL NDP_TRACE_LEVEL //Dependencies #include "core/net.h" #include "ipv6/ipv6.h" #include "ipv6/ipv6_misc.h" #include "ipv6/ndp.h" #include "ipv6/ndp_cache.h" #include "ipv6/ndp_misc.h" #include "mdns/mdns_responder.h" #include "debug.h" //Check TCP/IP stack configuration #if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED) /** * @brief Parse Prefix Information Option * @param[in] interface Underlying network interface * @param[in] option Pointer to the Prefix Information option **/ void ndpParsePrefixInfoOption(NetInterface *interface, NdpPrefixInfoOption *option) { //Make sure the Prefix Information option is valid if(option == NULL || option->length != 4) return; //A prefix Information option that have the on-link flag set indicates a //prefix identifying a range of addresses that should be considered on-link if(!option->l) return; //If the prefix is the link-local prefix, silently ignore the //Prefix Information option if(ipv6CompPrefix(&option->prefix, &IPV6_LINK_LOCAL_ADDR_PREFIX, 10)) return; //If the preferred lifetime is greater than the valid lifetime, //silently ignore the Prefix Information option if(ntohl(option->preferredLifetime) > ntohl(option->validLifetime)) return; //Check whether the Valid Lifetime field is non-zero if(ntohl(option->validLifetime) != 0) { //If the prefix is not already present in the Prefix List, create a new //entry for the prefix. If the prefix is already present in the list, //reset its invalidation timer ipv6AddPrefix(interface, &option->prefix, option->prefixLength, ntohl(option->validLifetime), ntohl(option->preferredLifetime)); } else { //If the new Lifetime value is zero, time-out the prefix immediately ipv6RemovePrefix(interface, &option->prefix, option->prefixLength); } } /** * @brief Manage the lifetime of IPv6 addresses * @param[in] interface Underlying network interface **/ void ndpUpdateAddrList(NetInterface *interface) { uint_t i; systime_t time; Ipv6AddrEntry *entry; NdpContext *context; //Point to the NDP context context = &interface->ndpContext; //Get current time time = osGetSystemTime(); //Go through the list of IPv6 addresses for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) { //Point to the current entry entry = &interface->ipv6Context.addrList[i]; //Tentative address? if(entry->state == IPV6_ADDR_STATE_TENTATIVE) { //Check whether the link is up if(interface->linkState) { //To check an address, a node should send Neighbor Solicitation messages if(entry->dadRetransmitCount == 0) { //Set time stamp entry->timestamp = time; //Check whether Duplicate Address Detection should be performed if(context->dupAddrDetectTransmits > 0) { //Link-local address? if(i == 0) { //Delay before transmitting the first solicitation entry->dadTimeout = netGetRandRange(0, NDP_MAX_RTR_SOLICITATION_DELAY); //Prepare to send the first Neighbor Solicitation message entry->dadRetransmitCount = 1; } else { //Valid link-local address? if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) { //Prepare to send the first Neighbor Solicitation message entry->dadTimeout = 0; entry->dadRetransmitCount = 1; } } } else { //Do not perform Duplicate Address Detection entry->state = IPV6_ADDR_STATE_PREFERRED; } } else { //Check current time if(timeCompare(time, entry->timestamp + entry->dadTimeout) >= 0) { //Duplicate Address Detection failed? if(entry->duplicate) { //A tentative address that is determined to be a duplicate //must not be assigned to an interface if(entry->permanent) { //The IPv6 address should be preserved if it has been //manually assigned ipv6SetAddr(interface, i, &entry->addr, IPV6_ADDR_STATE_INVALID, 0, 0, TRUE); } else { //The IPv6 address is no more valid and should be //removed from the list ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR, IPV6_ADDR_STATE_INVALID, 0, 0, FALSE); } } //Duplicate Address Detection is on-going? else if(entry->dadRetransmitCount <= context->dupAddrDetectTransmits) { //Send a multicast Neighbor Solicitation message ndpSendNeighborSol(interface, &entry->addr, TRUE); //Set timeout value entry->dadTimeout += context->retransTimer; //Increment retransmission counter entry->dadRetransmitCount++; } //Duplicate Address Detection is complete? else { //The use of the IPv6 address is now unrestricted entry->state = IPV6_ADDR_STATE_PREFERRED; #if (MDNS_RESPONDER_SUPPORT == ENABLED) //Link-local address? if(i == 0) { //Restart mDNS probing process mdnsResponderStartProbing(interface->mdnsResponderContext); } #endif } } } } } //Preferred address? else if(entry->state == IPV6_ADDR_STATE_PREFERRED) { //An IPv6 address with an infinite preferred lifetime is never timed out if(entry->preferredLifetime != NDP_INFINITE_LIFETIME) { //When the preferred lifetime expires, the address becomes deprecated if(timeCompare(time, entry->timestamp + entry->preferredLifetime) >= 0) { //A deprecated address should continue to be used as a source //address in existing communications, but should not be used //to initiate new communications entry->state = IPV6_ADDR_STATE_DEPRECATED; } } } //Deprecated address? else if(entry->state == IPV6_ADDR_STATE_DEPRECATED) { //An IPv6 address with an infinite valid lifetime is never timed out if(entry->validLifetime != NDP_INFINITE_LIFETIME) { //When the valid lifetime expires, the address becomes invalid if(timeCompare(time, entry->timestamp + entry->validLifetime) >= 0) { //The IPv6 address is no more valid and should be removed from the list ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR, IPV6_ADDR_STATE_INVALID, 0, 0, FALSE); } } } } } /** * @brief Periodically update Prefix List * @param[in] interface Underlying network interface **/ void ndpUpdatePrefixList(NetInterface *interface) { uint_t i; systime_t time; Ipv6PrefixEntry *entry; //Get current time time = osGetSystemTime(); //Go through the Prefix List for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) { //Point to the current entry entry = &interface->ipv6Context.prefixList[i]; //Check the lifetime value if(entry->validLifetime > 0 && entry->validLifetime < INFINITE_DELAY) { //A node should retain entries in the Prefix List until their //lifetimes expire if(timeCompare(time, entry->timestamp + entry->validLifetime) >= 0) { //When removing an entry from the Prefix List, there is no need //to purge any entries from the Destination or Neighbor Caches ipv6RemovePrefix(interface, &entry->prefix, entry->prefixLength); } } } } /** * @brief Periodically update Default Router List * @param[in] interface Underlying network interface **/ void ndpUpdateDefaultRouterList(NetInterface *interface) { uint_t i; bool_t flag; systime_t time; Ipv6RouterEntry *entry; //This flag will be set if any entry has been removed from //the Default Router List flag = FALSE; //Get current time time = osGetSystemTime(); //Go through the Default Router List for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++) { //Point to the current entry entry = &interface->ipv6Context.routerList[i]; //Check the lifetime value if(entry->lifetime > 0 && entry->lifetime < INFINITE_DELAY) { //A node should retain entries in the Default Router List until //their lifetimes expire if(timeCompare(time, entry->timestamp + entry->lifetime) >= 0) { //Immediately time-out the entry entry->addr = IPV6_UNSPECIFIED_ADDR; entry->lifetime = 0; //Set flag flag = TRUE; } } } //Check whether an entry has been removed from the list if(flag) { //When removing an entry from the Default Router List, any entries //in the Destination Cache that go through that router must perform //next-hop determination again to select a new default router ndpFlushDestCache(interface); } } /** * @brief Default Router Selection * @param[in] interface Underlying network interface * @param[in] unreachableAddr IPv6 address of the unreachable router (optional parameter) * @param[out] addr IPv6 address of the default router to be used * @return Error code **/ error_t ndpSelectDefaultRouter(NetInterface *interface, const Ipv6Addr *unreachableAddr, Ipv6Addr *addr) { uint_t i; uint_t j; uint_t k; Ipv6RouterEntry *routerEntry; NdpNeighborCacheEntry *neighborCacheEntry; //Initialize index i = 0; //This parameter is optional... if(unreachableAddr != NULL) { //Search the Default Router List for the router whose reachability is suspect for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++) { //Point to the current entry routerEntry = &interface->ipv6Context.routerList[j]; //Check the lifetime associated with the default router if(routerEntry->lifetime) { //Check the router address against the address whose reachability is suspect if(ipv6CompAddr(&routerEntry->addr, unreachableAddr)) { //Routers should be selected in a round-robin fashion i = j + 1; //We are done break; } } } } //Routers that are reachable or probably reachable should be preferred //over routers whose reachability is unknown or suspect for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++) { //Get current index k = (i + j) % IPV6_ROUTER_LIST_SIZE; //Point to the corresponding entry routerEntry = &interface->ipv6Context.routerList[k]; //Check the lifetime associated with the default router if(routerEntry->lifetime) { //Search the Neighbor Cache for the router address neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &routerEntry->addr); //Check whether the router is reachable or probably reachable if(neighborCacheEntry != NULL) { //Any state other than INCOMPLETE? if(neighborCacheEntry->state != NDP_STATE_INCOMPLETE) { //Return the IPv6 address of the default router *addr = routerEntry->addr; //Successful default router selection return NO_ERROR; } } } } //When no routers on the list are known to be reachable or probably //reachable, routers should be selected in a round-robin fashion, so //that subsequent requests for a default router do not return the //same router until all other routers have been selected for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++) { //Get current index k = (i + j) % IPV6_ROUTER_LIST_SIZE; //Point to the corresponding entry routerEntry = &interface->ipv6Context.routerList[k]; //Check the lifetime associated with the default router if(routerEntry->lifetime) { //Return the IPv6 address of the default router *addr = routerEntry->addr; //Successful default router selection return NO_ERROR; } } //No default router found... return ERROR_NO_ROUTE; } /** * @brief Check whether an address is the first-hop router for the specified destination * @param[in] interface Underlying network interface * @param[in] destAddr Destination address * @param[in] nextHop First-hop address to be checked * @return TRUE if the address is the first-hop router, else FALSE **/ bool_t ndpIsFirstHopRouter(NetInterface *interface, const Ipv6Addr *destAddr, const Ipv6Addr *nextHop) { uint_t i; bool_t isFirstHopRouter; Ipv6RouterEntry *routerEntry; NdpDestCacheEntry *destCacheEntry; //Clear flag isFirstHopRouter = FALSE; //Search the cache for the specified destination address destCacheEntry = ndpFindDestCacheEntry(interface, destAddr); //Any matching entry? if(destCacheEntry != NULL) { //Check if the address is the same as the current first-hop //router for the specified destination if(ipv6CompAddr(&destCacheEntry->nextHop, nextHop)) isFirstHopRouter = TRUE; } else { //Loop through the Default Router List for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++) { //Point to the current entry routerEntry = &interface->ipv6Context.routerList[i]; //Check the lifetime associated with the default router if(routerEntry->lifetime) { //Check whether the current entry matches the specified address if(ipv6CompAddr(&routerEntry->addr, nextHop)) { //The specified address is a valid first-hop router isFirstHopRouter = TRUE; //We are done break; } } } } //Return TRUE if the address is the same as the current first-hop //router for the specified destination return isFirstHopRouter; } /** * @brief Next-hop determination * @param[in] interface Underlying network interface * @param[in] destAddr Destination address * @param[in] unreachableNextHop Address of the unreachable next-hop (optional parameter) * @param[out] nextHop Next-hop address to be used * @return Error code **/ error_t ndpSelectNextHop(NetInterface *interface, const Ipv6Addr *destAddr, const Ipv6Addr *unreachableNextHop, Ipv6Addr *nextHop) { error_t error; //Destination IPv6 address is a multicast address? if(ipv6IsMulticastAddr(destAddr)) { //For multicast packets, the next-hop is always the (multicast) //destination address and is considered to be on-link *nextHop = *destAddr; //Successful next-hop determination error = NO_ERROR; } else { //The sender performs a longest prefix match against the Prefix //List to determine whether the packet's destination is on-link //or off-link if(ipv6IsOnLink(interface, destAddr)) { //If the destination is on-link, the next-hop address is the //same as the packet's destination address *nextHop = *destAddr; //Successful next-hop determination error = NO_ERROR; } else { //If the destination is off-link, the sender selects a router //from the Default Router List error = ndpSelectDefaultRouter(interface, unreachableNextHop, nextHop); } } //Return status code return error; } /** * @brief Update next-hop field of Destination Cache entries * @param[in] interface Underlying network interface * @param[in] unreachableNextHop Address of the unreachable next-hop **/ void ndpUpdateNextHop(NetInterface *interface, const Ipv6Addr *unreachableNextHop) { error_t error; uint_t i; NdpDestCacheEntry *entry; //Go through Destination Cache for(i = 0; i < NDP_DEST_CACHE_SIZE; i++) { //Point to the current entry entry = &interface->ndpContext.destCache[i]; //Check whether the unreachable IPv6 address is used a first-hop router if(ipv6CompAddr(&entry->nextHop, unreachableNextHop)) { //Perform next-hop determination error = ndpSelectNextHop(interface, &entry->destAddr, &entry->nextHop, &entry->nextHop); //Next-hop determination failed? if(error) { //Remove the current entry from the Destination Cache entry->destAddr = IPV6_UNSPECIFIED_ADDR; } } } } /** * @brief Append an option to a NDP message * @param[in] message Pointer to the NDP message * @param[in,out] messageLength Length of the entire message * @param[in] type Option type * @param[in] value Option value * @param[in] length Length of the option value **/ void ndpAddOption(void *message, size_t *messageLength, uint8_t type, const void *value, size_t length) { size_t optionLength; size_t paddingLength; NdpOption *option; //Length of the option in units of 8 bytes including the type and length fields optionLength = (length + sizeof(NdpOption) + 7) / 8; //Sanity check if(optionLength <= UINT8_MAX) { //Point to the buffer where the option is to be written option = (NdpOption *) ((uint8_t *) message + *messageLength); //Option type option->type = type; //Option length option->length = (uint8_t) optionLength; //Option value memcpy(option->value, value, length); //Options should be padded when necessary to ensure that they end on //their natural 64-bit boundaries if((length + sizeof(NdpOption)) < (optionLength * 8)) { //Determine the amount of padding data to append paddingLength = (optionLength * 8) - length - sizeof(NdpOption); //Write padding data memset(option->value + length, 0, paddingLength); } //Adjust the length of the NDP message *messageLength += optionLength * 8; } } /** * @brief Find a specified option in a NDP message * @param[in] options Pointer to the Options field * @param[in] length Length of the Options field * @param[in] type Type of the option to find * @return If the specified option is found, a pointer to the corresponding * option is returned. Otherwise NULL pointer is returned **/ void *ndpGetOption(uint8_t *options, size_t length, uint8_t type) { size_t i; NdpOption *option; //Point to the very first option of the NDP message i = 0; //Parse options while((i + sizeof(NdpOption)) <= length) { //Point to the current option option = (NdpOption *) (options + i); //Nodes must silently discard an NDP message that contains //an option with length zero if(option->length == 0) break; //Check option length if((i + option->length * 8) > length) break; //Current option type matches the specified one? if(option->type == type || type == NDP_OPT_ANY) return option; //Jump to next the next option i += option->length * 8; } //Specified option type not found return NULL; } /** * @brief Check NDP message options * @param[in] options Pointer to the Options field * @param[in] length Length of the Options field * @return Error code **/ error_t ndpCheckOptions(const uint8_t *options, size_t length) { size_t i; NdpOption *option; //Point to the very first option of the NDP message i = 0; //Parse options while((i + sizeof(NdpOption)) <= length) { //Point to the current option option = (NdpOption *) (options + i); //Nodes must silently discard an NDP message that contains //an option with length zero if(option->length == 0) return ERROR_INVALID_OPTION; //Jump to next the next option i += option->length * 8; } //The Options field is valid return NO_ERROR; } #endif