Webserver+3d print
Diff: cyclone_tcp/ipv6/ipv6_misc.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/ipv6/ipv6_misc.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,1346 @@ +/** + * @file ipv6_misc.c + * @brief Helper functions for IPv6 + * + * @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 IPV6_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 "mdns/mdns_responder.h" +#include "debug.h" + +//Check TCP/IP stack configuration +#if (IPV6_SUPPORT == ENABLED) + + +/** + * @brief Get the state of the specified IPv6 address + * @param[in] interface Underlying network interface + * @param[in] addr IPv6 address + * @return Address state + **/ + +Ipv6AddrState ipv6GetAddrState(NetInterface *interface, const Ipv6Addr *addr) +{ + uint_t i; + Ipv6AddrEntry *entry; + + //Loop through the list of IPv6 addresses assigned to the interface + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.addrList[i]; + + //Valid IPv6 address + if(entry->state != IPV6_ADDR_STATE_INVALID) + { + //Check whether the current entry matches the specified address + if(ipv6CompAddr(&entry->addr, addr)) + { + //Return the state of the IPv6 address + return entry->state; + } + } + } + + //The specified IPv6 address is not valid + return IPV6_ADDR_STATE_INVALID; +} + + +/** + * @brief Set IPv6 address and address state + * @param[in] interface Pointer to the desired network interface + * @param[in] index Zero-based index + * @param[in] addr IPv6 address + * @param[in] state State of the IPv6 address + * @param[in] validLifetime Valid lifetime + * @param[in] preferredLifetime Preferred lifetime + * @param[in] permanent Permanently assigned address + * @return Error code + **/ + +error_t ipv6SetAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr, + Ipv6AddrState state, systime_t validLifetime, systime_t preferredLifetime, bool_t permanent) +{ + error_t error; + Ipv6AddrEntry *entry; + Ipv6Addr solicitedNodeAddr; + + //Check parameters + if(interface == NULL || addr == NULL) + return ERROR_INVALID_PARAMETER; + + //Make sure that the index is valid + if(index >= IPV6_ADDR_LIST_SIZE) + return ERROR_OUT_OF_RANGE; + + //The IPv6 address must be a valid unicast address + if(ipv6IsMulticastAddr(addr)) + return ERROR_INVALID_ADDRESS; + + //Initialize status code + error = NO_ERROR; + + //Point to the corresponding entry + entry = &interface->ipv6Context.addrList[index]; + + //Check whether an IPv6 address is already assigned + if(!ipv6CompAddr(&entry->addr, &IPV6_UNSPECIFIED_ADDR)) + { + //Check the state of the IPv6 address + if(entry->state != IPV6_ADDR_STATE_INVALID) + { + //Ethernet interface? + if(interface->nicDriver->type == NIC_TYPE_ETHERNET) + { + //Form the Solicited-Node address + ipv6ComputeSolicitedNodeAddr(&entry->addr, &solicitedNodeAddr); + //Leave the Solicited-Node multicast group + ipv6LeaveMulticastGroup(interface, &solicitedNodeAddr); + } + } + } + + //The current IPv6 address is no more valid + entry->state = IPV6_ADDR_STATE_INVALID; + entry->validLifetime = 0; + entry->preferredLifetime = 0; + entry->permanent = FALSE; + + //Assign the new IPv6 address + entry->addr = *addr; + + //Check whether the new IPv6 address is valid + if(!ipv6CompAddr(addr, &IPV6_UNSPECIFIED_ADDR)) + { + //Check the state of the IPv6 address + if(state != IPV6_ADDR_STATE_INVALID) + { + //Ethernet interface? + if(interface->nicDriver->type == NIC_TYPE_ETHERNET) + { + //Form the Solicited-Node address for the link-local address + ipv6ComputeSolicitedNodeAddr(addr, &solicitedNodeAddr); + //Join the Solicited-Node multicast group for each assigned address + error = ipv6JoinMulticastGroup(interface, &solicitedNodeAddr); + } + //6LoWPAN interface? + else if(interface->nicDriver->type == NIC_TYPE_6LOWPAN) + { + //There is no need to join the solicited-node multicast address, + //since nobody multicasts Neighbor Solicitations in this type of + //network (refer to RFC 6775 section 5.2) + } + } + + //Check status code + if(!error) + { + //Set the state of the IPv6 address + entry->state = state; + + //Clear duplicate flag + entry->duplicate = FALSE; + + //Save preferred and valid lifetimes + entry->preferredLifetime = preferredLifetime; + entry->validLifetime = validLifetime; + + //Set time stamp + entry->timestamp = osGetSystemTime(); + + //Initialize DAD related variables + entry->dadTimeout = 0; + entry->dadRetransmitCount = 0; + } + + //This flag tells whether the IPv6 address should be permanently assigned + entry->permanent = permanent; + } + +#if (MDNS_RESPONDER_SUPPORT == ENABLED) + //Link-local address? + if(index == 0) + { + //Restart mDNS probing process + mdnsResponderStartProbing(interface->mdnsResponderContext); + } +#endif + + //Return status code + return error; +} + + +/** + * @brief Add a new entry to the list of IPv6 addresses + * @param[in] interface Underlying network interface + * @param[in] addr IPv6 address + * @param[in] validLifetime Valid lifetime, in seconds + * @param[in] preferredLifetime Preferred lifetime, in seconds + **/ + +void ipv6AddAddr(NetInterface *interface, const Ipv6Addr *addr, + uint32_t validLifetime, uint32_t preferredLifetime) +{ + uint_t i; + Ipv6AddrEntry *entry; + + //Check the valid lifetime + if(validLifetime != NDP_INFINITE_LIFETIME) + { + //The length of time in seconds that the address is valid + if(validLifetime < (MAX_DELAY / 1000)) + validLifetime *= 1000; + else + validLifetime = MAX_DELAY; + } + else + { + //A value of all one bits (0xffffffff) represents infinity + validLifetime = INFINITE_DELAY; + } + + //Check the preferred lifetime + if(preferredLifetime != NDP_INFINITE_LIFETIME) + { + //The length of time in seconds that the address remains preferred + if(preferredLifetime < (MAX_DELAY / 1000)) + preferredLifetime *= 1000; + else + preferredLifetime = MAX_DELAY; + } + else + { + //A value of all one bits (0xffffffff) represents infinity + preferredLifetime = INFINITE_DELAY; + } + + //Loop through the list of IPv6 addresses assigned to the interface + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.addrList[i]; + + //Check the state of the IPv6 address + if(entry->state == IPV6_ADDR_STATE_PREFERRED || + entry->state == IPV6_ADDR_STATE_DEPRECATED) + { + //Check whether the current entry matches the specified address + if(ipv6CompAddr(&entry->addr, addr)) + { + //The IPv6 address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Update the lifetimes of the entry + entry->validLifetime = validLifetime; + entry->preferredLifetime = preferredLifetime; + + //Save current time + entry->timestamp = osGetSystemTime(); + //Update the state of the IPv6 address + entry->state = IPV6_ADDR_STATE_PREFERRED; + } + + //Exit immediately + return; + } + } + } + + //If no matching entry was found, then try to create a new entry + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.addrList[i]; + + //Check the state of the IPv6 address + if(entry->state == IPV6_ADDR_STATE_INVALID) + { + //The IPv6 address should be preserved if it has been manually assigned + if(!entry->permanent) + { +#if (NDP_SUPPORT == ENABLED) + //Check whether Duplicate Address Detection should be performed + if(interface->ndpContext.dupAddrDetectTransmits > 0) + { + //Use the IPv6 address as a tentative address + ipv6SetAddr(interface, i, addr, IPV6_ADDR_STATE_TENTATIVE, + validLifetime, preferredLifetime, FALSE); + } + else +#endif + { + //The use of the IPv6 address is now unrestricted + ipv6SetAddr(interface, i, addr, IPV6_ADDR_STATE_PREFERRED, + validLifetime, preferredLifetime, FALSE); + } + + //Exit immediately + return; + } + } + } +} + + +/** + * @brief Remove an entry from the list of IPv6 addresses + * @param[in] interface Underlying network interface + * @param[in] addr IPv6 address + **/ + +void ipv6RemoveAddr(NetInterface *interface, const Ipv6Addr *addr) +{ + uint_t i; + Ipv6AddrEntry *entry; + + //Loop through the list of IPv6 addresses assigned to the interface + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime) + { + //Check whether the current entry matches the specified address + if(ipv6CompAddr(&entry->addr, addr)) + { + //The IPv6 address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Remove the IPv6 address from the list + ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR, + IPV6_ADDR_STATE_INVALID, 0, 0, FALSE); + } + } + } + } +} + + +/** + * @brief Add a new entry to the Prefix List + * @param[in] interface Underlying network interface + * @param[in] prefix IPv6 prefix + * @param[in] length The number of leading bits in the prefix that are valid + * @param[in] validLifetime Valid lifetime, in seconds + * @param[in] preferredLifetime Preferred lifetime, in seconds + **/ + +void ipv6AddPrefix(NetInterface *interface, const Ipv6Addr *prefix, + uint_t length, uint32_t validLifetime, uint32_t preferredLifetime) +{ + uint_t i; + Ipv6PrefixEntry *entry; + Ipv6PrefixEntry *firstFreeEntry; + + //Keep track of the first free entry + firstFreeEntry = NULL; + + //Loop through the Prefix List + for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.prefixList[i]; + + //Valid prefix? + if(entry->validLifetime) + { + //Compare prefix length against the specified value + if(entry->prefixLength == length) + { + //Check whether the current entry matches the specified prefix + if(ipv6CompPrefix(&entry->prefix, prefix, length)) + break; + } + } + else + { + //The IPv6 prefix should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Keep track of the first free entry + if(firstFreeEntry == NULL) + firstFreeEntry = entry; + } + } + } + + //No matching entry found? + if(i >= IPV6_PREFIX_LIST_SIZE) + entry = firstFreeEntry; + + //Update the entry if necessary + if(entry != NULL) + { + //The IPv6 prefix should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Save the IPv6 prefix + entry->prefix = *prefix; + entry->prefixLength = length; + + //Check the valid lifetime + if(validLifetime != NDP_INFINITE_LIFETIME) + { + //The length of time in seconds that the prefix is valid + //for the purpose of on-link determination + if(validLifetime < (MAX_DELAY / 1000)) + entry->validLifetime = validLifetime * 1000; + else + entry->validLifetime = MAX_DELAY; + } + else + { + //A value of all one bits (0xffffffff) represents infinity + entry->validLifetime = INFINITE_DELAY; + } + + //Check the preferred lifetime + if(preferredLifetime != NDP_INFINITE_LIFETIME) + { + //The length of time in seconds that addresses generated from the + //prefix via stateless address autoconfiguration remain preferred + if(preferredLifetime < (MAX_DELAY / 1000)) + entry->preferredLifetime = preferredLifetime * 1000; + else + entry->preferredLifetime = MAX_DELAY; + } + else + { + //A value of all one bits (0xffffffff) represents infinity + entry->preferredLifetime = INFINITE_DELAY; + } + + //Save current time + entry->timestamp = osGetSystemTime(); + } + } +} + + +/** + * @brief Remove an entry from the Prefix List + * @param[in] interface Underlying network interface + * @param[in] prefix IPv6 prefix + * @param[in] length The number of leading bits in the prefix that are valid + **/ + +void ipv6RemovePrefix(NetInterface *interface, const Ipv6Addr *prefix, uint_t length) +{ + uint_t i; + Ipv6PrefixEntry *entry; + + //Loop through the Prefix List + for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.prefixList[i]; + + //Valid prefix? + if(entry->validLifetime) + { + //Compare prefix length against the specified value + if(entry->prefixLength == length) + { + //Check whether the current entry matches the specified prefix + if(ipv6CompPrefix(&entry->prefix, prefix, length)) + { + //The IPv6 prefix should be preserved if it has been manually assigned + if(!entry->permanent) + { + //When removing an entry from the Prefix List, there is no need + //to purge any entries from the Destination or Neighbor Caches + entry->prefix = IPV6_UNSPECIFIED_ADDR; + entry->prefixLength = 0; + entry->validLifetime = 0; + } + } + } + } + } +} + + +/** + * @brief Add a new entry to the Default Router List + * @param[in] interface Underlying network interface + * @param[in] addr IPv6 address of the router + * @param[in] lifetime Router lifetime, in seconds + **/ + +void ipv6AddDefaultRouter(NetInterface *interface, + const Ipv6Addr *addr, uint16_t lifetime) +{ + uint_t i; + Ipv6RouterEntry *entry; + Ipv6RouterEntry *firstFreeEntry; + + //Keep track of the first free entry + firstFreeEntry = NULL; + + //Loop 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 associated with the default router + if(entry->lifetime) + { + //Check whether the current entry matches the specified router address + if(ipv6CompAddr(&entry->addr, addr)) + break; + } + else + { + //The router address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Keep track of the first free entry + if(firstFreeEntry == NULL) + firstFreeEntry = entry; + } + } + } + + //No matching entry found? + if(i >= IPV6_ROUTER_LIST_SIZE) + entry = firstFreeEntry; + + //Update the entry if necessary + if(entry != NULL) + { + //The router address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Save the IPv6 address of the router + entry->addr = *addr; + //The lifetime associated with the default router + entry->lifetime = lifetime * 1000; + //Save current time + entry->timestamp = osGetSystemTime(); + } + } +} + + +/** + * @brief Remove an entry from the Default Router List + * @param[in] interface Underlying network interface + * @param[in] addr IPv6 address of the router to be removed from the list + **/ + +void ipv6RemoveDefaultRouter(NetInterface *interface, const Ipv6Addr *addr) +{ + uint_t i; + bool_t flag; + Ipv6RouterEntry *entry; + + //This flag will be set if any entry has been removed from + //the Default Router List + flag = FALSE; + + //Loop 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 associated with the default router + if(entry->lifetime) + { + //Check whether the current entry matches the specified router address + if(ipv6CompAddr(&entry->addr, addr)) + { + //The router address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //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) + { +#if (NDP_SUPPORT == ENABLED) + //When removing a router from the Default Router list, the node must + //update the Destination Cache in such a way that all entries using + //the router perform next-hop determination again + ndpFlushDestCache(interface); +#endif + } +} + + +/** + * @brief Flush the list of IPv6 addresses + * @param[in] interface Underlying network interface + **/ + +void ipv6FlushAddrList(NetInterface *interface) +{ + uint_t i; + Ipv6AddrEntry *entry; + + //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]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //The IPv6 address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //The IPv6 address is not longer valid + ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR, + IPV6_ADDR_STATE_INVALID, 0, 0, FALSE); + } + } + } +} + + +/** + * @brief Flush the Prefix List + * @param[in] interface Underlying network interface + **/ + +void ipv6FlushPrefixList(NetInterface *interface) +{ + uint_t i; + Ipv6PrefixEntry *entry; + + //Go through the Prefix List + for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.prefixList[i]; + + //Valid IPv6 prefix? + if(entry->validLifetime > 0) + { + //The IPv6 prefix should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Clear the current entry + entry->prefix = IPV6_UNSPECIFIED_ADDR; + entry->prefixLength = 0; + //Remove the entry from the Prefix List + entry->validLifetime = 0; + } + } + } +} + + +/** + * @brief Flush the Default Router List + * @param[in] interface Underlying network interface + **/ + +void ipv6FlushDefaultRouterList(NetInterface *interface) +{ + uint_t i; + Ipv6RouterEntry *entry; + + //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]; + + //Valid entry? + if(entry->lifetime > 0) + { + //The router address should be preserved if it has been manually assigned + if(!entry->permanent) + { + //Clear the current entry + entry->addr = IPV6_UNSPECIFIED_ADDR; + //Remove the entry from the Default Router List + entry->lifetime = 0; + } + } + } +} + + +/** + * @brief Flush the list of DNS servers + * @param[in] interface Underlying network interface + **/ + +void ipv6FlushDnsServerList(NetInterface *interface) +{ + //Clear the list of DNS servers + memset(interface->ipv6Context.dnsServerList, 0, + sizeof(interface->ipv6Context.dnsServerList)); +} + + +/** + * @brief Source IPv6 address filtering + * @param[in] interface Underlying network interface + * @param[in] ipAddr Source IPv6 address to be checked + * @return Error code + **/ + +error_t ipv6CheckSourceAddr(NetInterface *interface, const Ipv6Addr *ipAddr) +{ + //Multicast addresses cannot be used as source address + if(ipv6IsMulticastAddr(ipAddr)) + { + //Debug message + TRACE_WARNING("Wrong source IPv6 address!\r\n"); + //The source address not is acceptable + return ERROR_INVALID_ADDRESS; + } + + //The source address is acceptable + return NO_ERROR; +} + + +/** + * @brief Destination IPv6 address filtering + * @param[in] interface Underlying network interface + * @param[in] ipAddr Destination IPv6 address to be checked + * @return Error code + **/ + +error_t ipv6CheckDestAddr(NetInterface *interface, const Ipv6Addr *ipAddr) +{ + error_t error; + uint_t i; + + //Filter out any invalid addresses + error = ERROR_INVALID_ADDRESS; + + //Multicast address? + if(ipv6IsMulticastAddr(ipAddr)) + { + //Go through the multicast filter table + for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++) + { + Ipv6FilterEntry *entry; + + //Point to the current entry + entry = &interface->ipv6Context.multicastFilter[i]; + + //Valid entry? + if(entry->refCount > 0) + { + //Check whether the destination IPv6 address matches + //a relevant multicast address + if(ipv6CompAddr(&entry->addr, ipAddr)) + { + //The multicast address is acceptable + error = NO_ERROR; + //Stop immediately + break; + } + } + } + } + //Unicast address? + else + { + //Loop through the list of IPv6 unicast addresses + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + Ipv6AddrEntry *entry; + + //Point to the current entry + entry = &interface->ipv6Context.addrList[i]; + + //Valid entry? + if(entry->state != IPV6_ADDR_STATE_INVALID) + { + //Check whether the destination address matches a valid unicast + //address assigned to the interface + if(ipv6CompAddr(&entry->addr, ipAddr)) + { + //The destination address is acceptable + error = NO_ERROR; + //We are done + break; + } + } + } + + //Check whether the specified is a valid unicast address + if(error == ERROR_INVALID_ADDRESS) + { + Ipv6Addr *anycastAddrList; + + //Point to the list of anycast addresses assigned to the interface + anycastAddrList = interface->ipv6Context.anycastAddrList; + + //Loop through the list of IPv6 anycast addresses + for(i = 0; i < IPV6_ANYCAST_ADDR_LIST_SIZE; i++) + { + //Valid entry? + if(!ipv6CompAddr(&anycastAddrList[i], &IPV6_UNSPECIFIED_ADDR)) + { + //Check whether the destination address matches a valid anycast + //address assigned to the interface + if(ipv6CompAddr(&anycastAddrList[i], ipAddr)) + { + //The destination address is acceptable + error = NO_ERROR; + //We are done + break; + } + } + } + } + } + + //Return status code + return error; +} + + +/** + * @brief IPv6 source address selection + * + * This function selects the source address and the relevant network interface + * to be used in order to join the specified destination address + * + * @param[in,out] interface A pointer to a valid network interface may be provided as + * a hint. The function returns a pointer identifying the interface to be used + * @param[in] destAddr Destination IPv6 address + * @param[out] srcAddr Local IPv6 address to be used + * @return Error code + **/ + +error_t ipv6SelectSourceAddr(NetInterface **interface, + const Ipv6Addr *destAddr, Ipv6Addr *srcAddr) +{ + uint_t i; + uint_t j; + NetInterface *currentInterface; + NetInterface *bestInterface; + Ipv6AddrEntry *currentAddr; + Ipv6AddrEntry *bestAddr; + + //Initialize variables + bestInterface = NULL; + bestAddr = NULL; + + //Loop through network interfaces + for(i = 0; i < NET_INTERFACE_COUNT; i++) + { + //Point to the current interface + currentInterface = &netInterface[i]; + + //A network interface may be provided as a hint... + if(*interface != currentInterface && *interface != NULL) + { + //Select the next interface in the list + continue; + } + + //A sort of the candidate source addresses is being performed, where a + //set of rules define the ordering among addresses (refer to RFC 6724) + for(j = 0; j < IPV6_ADDR_LIST_SIZE; j++) + { + //Point to the current entry + currentAddr = ¤tInterface->ipv6Context.addrList[j]; + + //Check the state of the address + if(currentAddr->state == IPV6_ADDR_STATE_PREFERRED || + currentAddr->state == IPV6_ADDR_STATE_DEPRECATED) + { + //Select the first address as default + if(bestAddr == NULL) + { + //Give the current source address the higher precedence + bestInterface = currentInterface; + bestAddr = currentAddr; + + //Select the next address in the list + continue; + } + + //Rule 1: Prefer same address + if(ipv6CompAddr(&bestAddr->addr, destAddr)) + { + //Select the next address in the list + continue; + } + else if(ipv6CompAddr(¤tAddr->addr, destAddr)) + { + //Give the current source address the higher precedence + bestInterface = currentInterface; + bestAddr = currentAddr; + + //Select the next address in the list + continue; + } + + //Rule 2: Prefer appropriate scope + if(ipv6GetAddrScope(¤tAddr->addr) < ipv6GetAddrScope(&bestAddr->addr)) + { + if(ipv6GetAddrScope(¤tAddr->addr) >= ipv6GetAddrScope(destAddr)) + { + //Give the current source address the higher precedence + bestInterface = currentInterface; + bestAddr = currentAddr; + } + + //Select the next address in the list + continue; + } + else if(ipv6GetAddrScope(&bestAddr->addr) < ipv6GetAddrScope(¤tAddr->addr)) + { + if(ipv6GetAddrScope(&bestAddr->addr) < ipv6GetAddrScope(destAddr)) + { + //Give the current source address the higher precedence + bestInterface = currentInterface; + bestAddr = currentAddr; + } + + //Select the next address in the list + continue; + } + + //Rule 3: Avoid deprecated addresses + if(bestAddr->state == IPV6_ADDR_STATE_PREFERRED && + currentAddr->state == IPV6_ADDR_STATE_DEPRECATED) + { + //Select the next address in the list + continue; + } + else if(currentAddr->state == IPV6_ADDR_STATE_PREFERRED && + bestAddr->state == IPV6_ADDR_STATE_DEPRECATED) + { + //Give the current source address the higher precedence + bestInterface = currentInterface; + bestAddr = currentAddr; + + //Select the next address in the list + continue; + } + + //Rule 8: Use longest matching prefix + if(ipv6GetCommonPrefixLength(¤tAddr->addr, destAddr) > + ipv6GetCommonPrefixLength(&bestAddr->addr, destAddr)) + { + //Give the current source address the higher precedence + bestInterface = currentInterface; + bestAddr = currentAddr; + } + } + } + } + + //Source address selection failed? + if(bestAddr == NULL) + { + //Report an error + return ERROR_NO_ADDRESS; + } + + //Return the out-going interface and the source address to be used + *interface = bestInterface; + *srcAddr = bestAddr->addr; + + //Successful source address selection + return NO_ERROR; +} + + +/** + * @brief Check whether an IPv6 address is on-link + * @param[in] interface Underlying network interface + * @param[in] ipAddr IPv6 address to be checked + * @return TRUE if the IPv6 address is on-link, else FALSE + **/ + +bool_t ipv6IsOnLink(NetInterface *interface, const Ipv6Addr *ipAddr) +{ + uint_t i; + Ipv6PrefixEntry *entry; + + //Link-local prefix? + if(ipv6IsLinkLocalUnicastAddr(ipAddr)) + { + //The link-local prefix is considered to be on the prefix + //list with an infinite invalidation timer regardless of + //whether routers are advertising a prefix for it + return TRUE; + } + + //Loop through the Prefix List + for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.prefixList[i]; + + //Valid prefix? + if(entry->validLifetime > 0) + { + //Check the specified address against the prefix + if(ipv6CompPrefix(ipAddr, &entry->prefix, entry->prefixLength)) + { + //The specified IPv6 address is on-link + return TRUE; + } + } + } + + //The specified IPv6 address is off-link + return FALSE; +} + + +/** + * @brief Check whether an IPv6 address is a tentative address + * @param[in] interface Underlying network interface + * @param[in] ipAddr IPv6 address to be checked + * @return TRUE if the IPv6 address is a tentative address, else FALSE + **/ + +bool_t ipv6IsTentativeAddr(NetInterface *interface, const Ipv6Addr *ipAddr) +{ + uint_t i; + Ipv6AddrEntry *entry; + + //Loop through the list of IPv6 unicast 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 specified address matches a valid unicast + //address assigned to the interface + if(ipv6CompAddr(&entry->addr, ipAddr)) + { + //The specified IPv6 address is a tentative address + return TRUE; + } + } + } + + //The specified IPv6 address is not a tentative address + return FALSE; +} + + +/** + * @brief Check whether an IPv6 address is an anycast address + * @param[in] interface Underlying network interface + * @param[in] ipAddr IPv6 address to be checked + * @return TRUE if the IPv6 address is an anycast address, else FALSE + **/ + +bool_t ipv6IsAnycastAddr(NetInterface *interface, const Ipv6Addr *ipAddr) +{ + uint_t i; + Ipv6Addr *anycastAddrList; + + //Point to the list of anycast addresses assigned to the interface + anycastAddrList = interface->ipv6Context.anycastAddrList; + + //Loop through the list of IPv6 anycast addresses + for(i = 0; i < IPV6_ANYCAST_ADDR_LIST_SIZE; i++) + { + //Valid entry? + if(!ipv6CompAddr(&anycastAddrList[i], &IPV6_UNSPECIFIED_ADDR)) + { + //Check whether the specified address matches a valid anycast + //address assigned to the interface + if(ipv6CompAddr(&anycastAddrList[i], ipAddr)) + { + //The specified IPv6 address is an anycast address + return TRUE; + } + } + } + + //The specified IPv6 address is not an anycast address + return FALSE; +} + + +/** + * @brief Compare IPv6 address prefixes + * @param[in] ipAddr1 Pointer to the first IPv6 address + * @param[in] ipAddr2 Pointer to the second IPv6 address + * @param[in] length Prefix length + * @return TRUE if the prefixes match each other, else FALSE + **/ + +bool_t ipv6CompPrefix(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2, size_t length) +{ + size_t n = length / 8; + size_t m = length % 8; + + //Ensure the prefix length is valid + if(length > 128) + return FALSE; + + //Compare the first part + if(n > 0) + { + if(memcmp(ipAddr1, ipAddr2, n)) + return FALSE; + } + + //Compare the remaining bits, if any + if(m > 0) + { + //Calculate the mask to be applied + uint8_t mask = ((1 << m) - 1) << (8 - m); + + //Check remaining bits + if((ipAddr1->b[n] & mask) != (ipAddr2->b[n] & mask)) + return FALSE; + } + + //The prefixes match each other + return TRUE; +} + + +/** + * @brief Retrieve the scope of an IPv6 address + * @param[in] ipAddr Pointer to an IPv6 address + * @return IPv6 address scope + **/ + +uint_t ipv6GetAddrScope(const Ipv6Addr *ipAddr) +{ + uint_t scope; + + //Multicast address? + if(ipv6IsMulticastAddr(ipAddr)) + { + //Retrieve the scope of the multicast address + scope = ipv6GetMulticastAddrScope(ipAddr); + } + //Loopback address? + else if(ipv6CompAddr(ipAddr, &IPV6_LOOPBACK_ADDR)) + { + //The loopback address may be used by a node to send an IPv6 packet to itself + scope = IPV6_ADDR_SCOPE_INTERFACE_LOCAL; + } + //Link-local unicast address? + else if(ipv6IsLinkLocalUnicastAddr(ipAddr)) + { + //A link-local address is for use on a single link + scope = IPV6_ADDR_SCOPE_LINK_LOCAL; + } + //Site-local unicast address? + else if(ipv6IsSiteLocalUnicastAddr(ipAddr)) + { + //A site-local address is for use in a single site + scope = IPV6_ADDR_SCOPE_SITE_LOCAL; + } + //Global address? + else + { + //Global scope + scope = IPV6_ADDR_SCOPE_GLOBAL; + } + + //Return the scope of the specified IPv6 address + return scope; +} + + +/** + * @brief Retrieve the scope of an IPv6 multicast address + * @param[in] ipAddr Pointer to an IPv6 multicast address + * @return IPv6 address scope + **/ + +uint_t ipv6GetMulticastAddrScope(const Ipv6Addr *ipAddr) +{ + uint_t scope; + + //The scope field is a 4-bit value + scope = ipAddr->b[1] & 0x0F; + + //If the scope field contains the reserved value F, an IPv6 packet + //must be treated the same as packets destined to a global multicast + //address (refer to RFC 3513 section 2.7) + if(scope == 0x0F) + scope = IPV6_ADDR_SCOPE_GLOBAL; + + //Return the scope of the specified IPv6 multicast address + return scope; +} + + +/** + * @brief Compute the length of the longest common prefix + * @param[in] ipAddr1 Pointer to the first IPv6 address + * @param[in] ipAddr2 Pointer to the second IPv6 address + * @return The length of the longest common prefix, in bits + **/ + +uint_t ipv6GetCommonPrefixLength(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2) +{ + uint_t i; + uint_t j; + + //Clear bit counter + j = 0; + + //Perform a byte-for-byte comparison + for(i = 0; i < sizeof(Ipv6Addr); i++) + { + //Loop as long as prefixes match + if(ipAddr1->b[i] != ipAddr2->b[i]) + break; + } + + //Mismatch? + if(i < sizeof(Ipv6Addr)) + { + //Perform a bit-for-bit comparison + for(j = 0; j < 8; j++) + { + //Calculate the mask to be applied + uint8_t mask = 1 << (7 - j); + + //Loop as long as prefixes match + if((ipAddr1->b[i] & mask) != (ipAddr2->b[i] & mask)) + break; + } + } + + //Return the length of the longest common prefix, in bits + return i * 8 + j; +} + + +/** + * @brief Form a solicited-node address from an IPv6 address + * @param[in] ipAddr Unicast or anycast address + * @param[out] solicitedNodeAddr Corresponding solicited-node address + * @return Error code + **/ + +error_t ipv6ComputeSolicitedNodeAddr(const Ipv6Addr *ipAddr, + Ipv6Addr *solicitedNodeAddr) +{ + //Ensure the specified address is a valid unicast or anycast address + if(ipv6IsMulticastAddr(ipAddr)) + return ERROR_INVALID_ADDRESS; + + //Copy the 104-bit prefix + ipv6CopyAddr(solicitedNodeAddr, &IPV6_SOLICITED_NODE_ADDR_PREFIX); + + //Take the low-order 24 bits of the address (unicast or + //anycast) and append those bits to the prefix + solicitedNodeAddr->b[13] = ipAddr->b[13]; + solicitedNodeAddr->b[14] = ipAddr->b[14]; + solicitedNodeAddr->b[15] = ipAddr->b[15]; + + //No error to report + return NO_ERROR; +} + + +/** + * @brief Map an IPv6 multicast address to a MAC-layer multicast address + * @param[in] ipAddr IPv6 multicast address + * @param[out] macAddr Corresponding MAC-layer multicast address + * @return Error code + **/ + +error_t ipv6MapMulticastAddrToMac(const Ipv6Addr *ipAddr, MacAddr *macAddr) +{ + //Ensure the specified IPv6 address is a multicast address + if(!ipv6IsMulticastAddr(ipAddr)) + return ERROR_INVALID_ADDRESS; + + //To support IPv6 multicasting, MAC address range of 33-33-00-00-00-00 + //to 33-33-FF-FF-FF-FF is reserved (refer to RFC 2464) + macAddr->b[0] = 0x33; + macAddr->b[1] = 0x33; + + //The low-order 32 bits of the IPv6 multicast address are mapped directly + //to the low-order 32 bits in the MAC-layer multicast address + macAddr->b[2] = ipAddr->b[12]; + macAddr->b[3] = ipAddr->b[13]; + macAddr->b[4] = ipAddr->b[14]; + macAddr->b[5] = ipAddr->b[15]; + + //The specified IPv6 multicast address was successfully + //mapped to a MAC-layer address + return NO_ERROR; +} + + +/** + * @brief Generate a IPv6 link-local address from an interface identifier + * @param[in] interfaceId Interface identifier + * @param[out] ipAddr Corresponding IPv6 link-local address + **/ + +void ipv6GenerateLinkLocalAddr(const Eui64* interfaceId, Ipv6Addr *ipAddr) +{ + //A link-local address is formed by combining the well-known + //link-local prefix fe80::/10 with the interface identifier + ipAddr->w[0] = HTONS(0xFE80); + ipAddr->w[1] = HTONS(0x0000); + ipAddr->w[2] = HTONS(0x0000); + ipAddr->w[3] = HTONS(0x0000); + ipAddr->w[4] = interfaceId->w[0]; + ipAddr->w[5] = interfaceId->w[1]; + ipAddr->w[6] = interfaceId->w[2]; + ipAddr->w[7] = interfaceId->w[3]; +} + +#endif +