Webserver+3d print
Diff: cyclone_tcp/ipv6/ndp_misc.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/ipv6/ndp_misc.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,706 @@ +/** + * @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 +