Webserver+3d print
Diff: cyclone_tcp/ipv6/ndp.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/ipv6/ndp.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,1927 @@ +/** + * @file ndp.c + * @brief 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. + * + * @section Description + * + * The Neighbor Discovery Protocol is responsible for address autoconfiguration + * of nodes, discovery of the link-layer addresses of other nodes, duplicate + * address detection, finding available routers and address prefix discovery. + * Refer to RFC 4861 for more details + * + * @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 <limits.h> +#include <string.h> +#include "core/net.h" +#include "ipv6/ipv6.h" +#include "ipv6/ipv6_misc.h" +#include "ipv6/icmpv6.h" +#include "ipv6/ndp.h" +#include "ipv6/ndp_cache.h" +#include "ipv6/ndp_misc.h" +#include "ipv6/slaac.h" +#include "debug.h" + +//Check TCP/IP stack configuration +#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED) + +//Tick counter to handle periodic operations +systime_t ndpTickCounter; + + +/** + * @brief Neighbor cache initialization + * @param[in] interface Underlying network interface + * @return Error code + **/ + +error_t ndpInit(NetInterface *interface) +{ + NdpContext *context; + + //Point to the NDP context + context = &interface->ndpContext; + + //Clear the NDP context + memset(context, 0, sizeof(NdpContext)); + + //Initialize interface specific variables + context->reachableTime = NDP_REACHABLE_TIME; + context->retransTimer = NDP_RETRANS_TIMER; + context->dupAddrDetectTransmits = NDP_DUP_ADDR_DETECT_TRANSMITS; + context->minRtrSolicitationDelay = NDP_MIN_RTR_SOLICITATION_DELAY; + context->maxRtrSolicitationDelay = NDP_MAX_RTR_SOLICITATION_DELAY; + context->rtrSolicitationInterval = NDP_RTR_SOLICITATION_INTERVAL; + context->maxRtrSolicitations = NDP_MAX_RTR_SOLICITATIONS; + + //Successful initialization + return NO_ERROR; +} + + +/** + * @brief Address resolution using Neighbor Discovery protocol + * @param[in] interface Underlying network interface + * @param[in] ipAddr IPv6 address + * @param[in] macAddr Physical address matching the specified IPv6 address + * @return Error code + **/ + +error_t ndpResolve(NetInterface *interface, const Ipv6Addr *ipAddr, MacAddr *macAddr) +{ + error_t error; + NdpNeighborCacheEntry *entry; + + //Search the ndpCacheMutex cache for the specified IPv6 address + entry = ndpFindNeighborCacheEntry(interface, ipAddr); + + //Check whether a matching entry has been found + if(entry != NULL) + { + //Check the state of the Neighbor cache entry + if(entry->state == NDP_STATE_INCOMPLETE) + { + //The address resolution is already in progress + error = ERROR_IN_PROGRESS; + } + else if(entry->state == NDP_STATE_STALE) + { + //Copy the MAC address associated with the specified IPv6 address + *macAddr = entry->macAddr; + + //Start delay timer + entry->timestamp = osGetSystemTime(); + //Delay before sending the first probe + entry->timeout = NDP_DELAY_FIRST_PROBE_TIME; + //Switch to the DELAY state + entry->state = NDP_STATE_DELAY; + + //Successful address resolution + error = NO_ERROR; + } + else + { + //Copy the MAC address associated with the specified IPv6 address + *macAddr = entry->macAddr; + + //Successful address resolution + error = NO_ERROR; + } + } + else + { + //If no entry exists, then create a new one + entry = ndpCreateNeighborCacheEntry(interface); + + //Neighbor Cache entry successfully created? + if(entry != NULL) + { + //Record the IPv6 address whose MAC address is unknown + entry->ipAddr = *ipAddr; + + //Reset retransmission counter + entry->retransmitCount = 0; + //No packet are pending in the transmit queue + entry->queueSize = 0; + + //Send a multicast Neighbor Solicitation message + ndpSendNeighborSol(interface, ipAddr, TRUE); + + //Save the time at which the message was sent + entry->timestamp = osGetSystemTime(); + //Set timeout value + entry->timeout = interface->ndpContext.retransTimer; + //Enter INCOMPLETE state + entry->state = NDP_STATE_INCOMPLETE; + + //The address resolution is in progress + error = ERROR_IN_PROGRESS; + } + else + { + //Failed to create Neighbor Cache entry... + error = ERROR_OUT_OF_RESOURCES; + } + } + + //Return status code + return error; +} + + +/** + * @brief Enqueue an IPv6 packet waiting for address resolution + * @param[in] srcInterface Interface from which the packet has been received + * @param[in] destInterface Interface on which the packet should be sent + * @param[in] ipAddr IPv6 address of the destination host + * @param[in] buffer Multi-part buffer containing the packet to be enqueued + * @param[in] offset Offset to the first byte of the packet + * @return Error code + **/ + +error_t ndpEnqueuePacket(NetInterface *srcInterface, NetInterface *destInterface, + const Ipv6Addr *ipAddr, NetBuffer *buffer, size_t offset) +{ + error_t error; + uint_t i; + size_t length; + NdpNeighborCacheEntry *entry; + + //Retrieve the length of the multi-part buffer + length = netBufferGetLength(buffer); + + //Search the Neighbor cache for the specified IPv6 address + entry = ndpFindNeighborCacheEntry(destInterface, ipAddr); + + //Check whether a matching entry exists + if(entry != NULL) + { + //Check current state + if(entry->state == NDP_STATE_INCOMPLETE) + { + //Check whether the packet queue is full + if(entry->queueSize >= NDP_MAX_PENDING_PACKETS) + { + //When the queue overflows, the new arrival should replace the oldest entry + netBufferFree(entry->queue[0].buffer); + + //Make room for the new packet + for(i = 1; i < NDP_MAX_PENDING_PACKETS; i++) + entry->queue[i - 1] = entry->queue[i]; + + //Adjust the number of pending packets + entry->queueSize--; + } + + //Index of the entry to be filled in + i = entry->queueSize; + //Allocate a memory buffer to store the packet + entry->queue[i].buffer = netBufferAlloc(length); + + //Successful memory allocation? + if(entry->queue[i].buffer != NULL) + { + //If the IPv6 packet has been forwarded, record the network + //interface from which the packet has been received + entry->queue[i].srcInterface = srcInterface; + + //Copy the contents of the IPv6 packet + netBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length); + //Offset to the first byte of the IPv6 header + entry->queue[i].offset = offset; + + //Increment the number of queued packets + entry->queueSize++; + //The packet was successfully enqueued + error = NO_ERROR; + } + else + { + //Failed to allocate memory + error = ERROR_OUT_OF_MEMORY; + } + } + else + { + //The address is already resolved + error = ERROR_UNEXPECTED_STATE; + } + } + else + { + //No matching entry in Neighbor Cache + error = ERROR_NOT_FOUND; + } + + //Return status code + return error; +} + + +/** + * @brief NDP timer handler + * @param[in] interface Underlying network interface + **/ + +void ndpTick(NetInterface *interface) +{ + systime_t time; + NdpContext *context; + + //Point to the NDP context + context = &interface->ndpContext; + + //Get current time + time = osGetSystemTime(); + + //When an interface becomes enabled, a host may send some Router + //Solicitation messages to obtain Router Advertisements quickly + if(interface->linkState && !interface->ipv6Context.isRouter) + { + //Make sure that a valid link-local address has been assigned to the interface + if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) + { + //The host should transmit up to MAX_RTR_SOLICITATIONS Router + //Solicitation messages + if(context->rtrSolicitationCount == 0) + { + //Set time stamp + context->timestamp = time; + + //Check whether the host has already performed Duplicate Address + //Detection for the link-local address + if(context->dupAddrDetectTransmits > 0) + { + //If a host has already performed a random delay since the interface + //became enabled, there is no need to delay again before sending the + //first Router Solicitation message + context->timeout = 0; + } + else + { + //Before a host sends an initial solicitation, it should delay the + //transmission for a random amount of time in order to alleviate + //congestion when many hosts start up on a link at the same time + context->timeout = netGetRandRange(context->minRtrSolicitationDelay, + context->maxRtrSolicitationDelay); + } + + //Prepare to send the first Router Solicitation message + context->rtrSolicitationCount = 1; + } + else if(context->rtrSolicitationCount <= context->maxRtrSolicitations) + { + //Once the host sends a Router Solicitation, and receives a valid + //Router Advertisement with a non-zero Router Lifetime, the host must + //desist from sending additional solicitations on that interface + if(!context->rtrAdvReceived) + { + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Send Router Solicitation message + ndpSendRouterSol(interface); + + //Save the time at which the message was sent + context->timestamp = time; + //Set timeout value + context->timeout = context->rtrSolicitationInterval; + //Increment retransmission counter + context->rtrSolicitationCount++; + } + } + } + } + } + + //Periodically update the Neighbor Cache + ndpUpdateNeighborCache(interface); + + //Manage the lifetime of IPv6 addresses + ndpUpdateAddrList(interface); + + //Periodically update the Prefix List + ndpUpdatePrefixList(interface); + + //Periodically update the Default Router List + ndpUpdateDefaultRouterList(interface); +} + + +/** + * @brief Callback function for link change event + * @param[in] interface Underlying network interface + **/ + +void ndpLinkChangeEvent(NetInterface *interface) +{ + NdpContext *context; + + //Point to the NDP context + context = &interface->ndpContext; + + //Restore default parameters + context->reachableTime = NDP_REACHABLE_TIME; + context->retransTimer = NDP_RETRANS_TIMER; + context->dupAddrDetectTransmits = NDP_DUP_ADDR_DETECT_TRANSMITS; + context->minRtrSolicitationDelay = NDP_MIN_RTR_SOLICITATION_DELAY; + context->maxRtrSolicitationDelay = NDP_MAX_RTR_SOLICITATION_DELAY; + context->rtrSolicitationInterval = NDP_RTR_SOLICITATION_INTERVAL; + context->maxRtrSolicitations = NDP_MAX_RTR_SOLICITATIONS; + + //Reset retransmission counter for RS messages + context->rtrSolicitationCount = 0; + //Valid RA message not yet received + context->rtrAdvReceived = FALSE; + + //Flush the Neighbor Cache + ndpFlushNeighborCache(interface); + //Flush the Destination Cache + ndpFlushDestCache(interface); +} + + +/** + * @brief Router Advertisement message processing + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IPv6 pseudo header + * @param[in] buffer Multi-part buffer containing the Router Advertisement message + * @param[in] offset Offset to the first byte of the message + * @param[in] hopLimit Hop Limit field from IPv6 header + **/ + +void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, + const NetBuffer *buffer, size_t offset, uint8_t hopLimit) +{ + error_t error; + uint32_t n; + size_t length; + NdpRouterAdvMessage *message; + NdpMtuOption *mtuOption; + NdpPrefixInfoOption *prefixInfoOption; +#if (ETH_SUPPORT == ENABLED) + NdpLinkLayerAddrOption *linkLayerAddrOption; + NdpNeighborCacheEntry *entry; +#endif + + //Retrieve the length of the message + length = netBufferGetLength(buffer) - offset; + + //Check the length of the Router Advertisement message + if(length < sizeof(NdpRouterAdvMessage)) + return; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_INFO("Router Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpRouterAdvMessage(message); + + //Routers must use their link-local address as the source for the + //Router Advertisement so that hosts can uniquely identify routers + if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) + return; + + //The IPv6 Hop Limit field must have a value of 255 to ensure + //that the packet has not been forwarded by a router + if(hopLimit != NDP_HOP_LIMIT) + return; + + //ICMPv6 Code must be 0. An advertisement that passes the validity + //checks is called a valid advertisement + if(message->code) + return; + + //Calculate the length of the Options field + length -= sizeof(NdpRouterAdvMessage); + + //Parse Options field + error = ndpCheckOptions(message->options, length); + //All included options must have a length that is greater than zero + if(error) + return; + + //Check the Router Lifetime value + if(ntohs(message->routerLifetime) != 0) + { + //Add a new entry in the Default Router List + ipv6AddDefaultRouter(interface, &pseudoHeader->srcAddr, + ntohs(message->routerLifetime)); + + //The host should send at least one solicitation in the case where + //an advertisement is received prior to having sent a solicitation + if(interface->ndpContext.rtrSolicitationCount > 1) + { + //Once the host sends a Router Solicitation, and receives a valid + //Router Advertisement with a non-zero Router Lifetime, the host must + //desist from sending additional solicitations on that interface + interface->ndpContext.rtrAdvReceived = TRUE; + } + } + else + { + //Immediately time-out the entry + ipv6RemoveDefaultRouter(interface, &pseudoHeader->srcAddr); + } + + //6LoWPAN interface? + if(interface->nicDriver->type == NIC_TYPE_6LOWPAN) + { + //In all cases, the Router Solicitation retransmissions are terminated + //when a Router Advertisement is received (refer to RFC 6675 5.3) + interface->ndpContext.rtrAdvReceived = TRUE; + } + + //A Router Advertisement field (Cur Hop Limit, Reachable Time, and + //Retrans Timer) may contain a value denoting that it is unspecified. + //In such cases, the parameter should be ignored and the host should + //continue using whatever value it is already using + if(message->curHopLimit != 0) + { + //Get the default value that should be placed in the Hop Count + //field of the IP header for outgoing IP packets + interface->ipv6Context.curHopLimit = message->curHopLimit; + } + + //A value of zero means unspecified... + if(message->reachableTime != 0) + { + //The Reachable Time field holds the time, in milliseconds, that + //a node assumes a neighbor is reachable after having received a + //reachability confirmation + interface->ndpContext.reachableTime = ntohl(message->reachableTime); + } + + //A value of zero means unspecified... + if(message->retransTimer != 0) + { + //The Retrans Timer field holds the time, in milliseconds, + //between retransmitted Neighbor Solicitation messages + interface->ndpContext.retransTimer = ntohl(message->retransTimer); + } + +#if (ETH_SUPPORT == ENABLED) + //Search for the Source Link-Layer Address option + linkLayerAddrOption = ndpGetOption(message->options, + length, NDP_OPT_SOURCE_LINK_LAYER_ADDR); + + //Source Link-Layer Address option found? + if(linkLayerAddrOption != NULL && linkLayerAddrOption->length == 1) + { + //Debug message + TRACE_DEBUG(" Source Link-Layer Address = %s\r\n", + macAddrToString(&linkLayerAddrOption->linkLayerAddr, NULL)); + } + else + { + //No valid Source Link-Layer Address option... + linkLayerAddrOption = NULL; + } + + //Search the Neighbor cache for the router + entry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr); + + //No matching entry has been found? + if(!entry) + { + //If the advertisement contains a Source Link-Layer Address option, + //the link-layer address should be recorded in the Neighbor cache + if(linkLayerAddrOption) + { + //Create an entry for the router + entry = ndpCreateNeighborCacheEntry(interface); + + //Neighbor cache entry successfully created? + if(entry) + { + //Record the IPv6 address and the corresponding MAC address + entry->ipAddr = pseudoHeader->srcAddr; + entry->macAddr = linkLayerAddrOption->linkLayerAddr; + //The IsRouter flag must be set to TRUE + entry->isRouter = TRUE; + //Save current time + entry->timestamp = osGetSystemTime(); + //The reachability state must be set to STALE + entry->state = NDP_STATE_STALE; + } + } + } + else + { + //The sender of a Router Advertisement is implicitly assumed to be a router + entry->isRouter = TRUE; + + //Check if the advertisement contains a Source Link-Layer Address option + if(linkLayerAddrOption) + { + //INCOMPLETE state? + if(entry->state == NDP_STATE_INCOMPLETE) + { + //Record link-layer address + entry->macAddr = linkLayerAddrOption->linkLayerAddr; + //Send all the packets that are pending for transmission + n = ndpSendQueuedPackets(interface, entry); + //Save current time + entry->timestamp = osGetSystemTime(); + + //Check whether any packets have been sent + if(n > 0) + { + //Start delay timer + entry->timeout = NDP_DELAY_FIRST_PROBE_TIME; + //Switch to the DELAY state + entry->state = NDP_STATE_DELAY; + } + else + { + //Enter the STALE state + entry->state = NDP_STATE_STALE; + } + } + //REACHABLE, STALE, DELAY or PROBE state? + else + { + //Different link-layer address than cached? + if(!macCompAddr(&entry->macAddr, &linkLayerAddrOption->linkLayerAddr)) + { + //Update link-layer address + entry->macAddr = linkLayerAddrOption->linkLayerAddr; + //Save current time + entry->timestamp = osGetSystemTime(); + //The reachability state must be set to STALE + entry->state = NDP_STATE_STALE; + } + } + } + } +#endif + + //Search for the MTU option + mtuOption = ndpGetOption(message->options, length, NDP_OPT_MTU); + + //MTU option found? + if(mtuOption != NULL && mtuOption->length == 1) + { + //This option specifies the recommended MTU for the link + n = ntohl(mtuOption->mtu); + + //The host should copy the option's value so long as the value is greater + //than or equal to the minimum IPv6 MTU and does not exceed the maximum + //MTU of the interface + if(n >= IPV6_DEFAULT_MTU && n <= interface->nicDriver->mtu) + { + //Save the MTU value + interface->ipv6Context.linkMtu = n; + } + } + + //Point to the beginning of the Options field + n = 0; + + //Parse Options field + while(1) + { + //Search the Options field for any Prefix Information options + prefixInfoOption = ndpGetOption(message->options + n, + length - n, NDP_OPT_PREFIX_INFORMATION); + + //No more option of the specified type? + if(prefixInfoOption == NULL) + break; + + //Hosts use the advertised on-link prefixes to build and maintain + //a list that is used in deciding when a packet's destination is + //on-link or beyond a router + ndpParsePrefixInfoOption(interface, prefixInfoOption); + + //Retrieve the offset to the current position + n = (uint8_t *) prefixInfoOption - message->options; + //Jump to the next option + n += prefixInfoOption->length * 8; + } + +#if (SLAAC_SUPPORT == ENABLED) + //Stateless Address Autoconfiguration is currently used? + if(interface->slaacContext != NULL) + { + //Process the valid advertisement + slaacParseRouterAdv(interface->slaacContext, message, + length + sizeof(NdpRouterAdvMessage)); + } +#endif +} + + +/** + * @brief Neighbor Solicitation message processing + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IPv6 pseudo header + * @param[in] buffer Multi-part buffer containing the Neighbor Solicitation message + * @param[in] offset Offset to the first byte of the message + * @param[in] hopLimit Hop Limit field from IPv6 header + **/ + +void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, + const NetBuffer *buffer, size_t offset, uint8_t hopLimit) +{ +#if (ETH_SUPPORT == ENABLED) + error_t error; + uint_t i; + uint_t n; + size_t length; + bool_t validTarget; + NdpNeighborSolMessage *message; + NdpLinkLayerAddrOption *option; + NdpNeighborCacheEntry *neighborCacheEntry; + Ipv6AddrEntry *addrEntry; + + //Retrieve the length of the message + length = netBufferGetLength(buffer) - offset; + + //Check the length of the Neighbor Solicitation message + if(length < sizeof(NdpNeighborSolMessage)) + return; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_INFO("Neighbor Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpNeighborSolMessage(message); + + //The IPv6 Hop Limit field must have a value of 255 to ensure + //that the packet has not been forwarded by a router + if(hopLimit != NDP_HOP_LIMIT) + return; + + //ICMPv6 Code must be 0 + if(message->code) + return; + + //If the IP source address is the unspecified address, the IP + //destination address must be a solicited-node multicast address + if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) && + !ipv6IsSolicitedNodeAddr(&pseudoHeader->destAddr)) + { + //Debug message + TRACE_WARNING("Destination address must be a solicited-node address!\r\n"); + //Exit immediately + return; + } + + //Calculate the length of the Options field + length -= sizeof(NdpNeighborSolMessage); + + //Parse Options field + error = ndpCheckOptions(message->options, length); + //All included options must have a length that is greater than zero + if(error) + return; + + //Search for the Source Link-Layer Address option + option = ndpGetOption(message->options, + length, NDP_OPT_SOURCE_LINK_LAYER_ADDR); + + //The target address must a valid unicast or anycast address assigned to + //the interface or a tentative address on which DAD is being performed + validTarget = FALSE; + + //Loop through the IPv6 addresses assigned to the interface + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + addrEntry = &interface->ipv6Context.addrList[i]; + + //Compare target address + if(ipv6CompAddr(&addrEntry->addr, &message->targetAddr)) + { + //Check address state + if(addrEntry->state == IPV6_ADDR_STATE_TENTATIVE) + { + //If the source address of the Neighbor Solicitation is the + //unspecified address, the solicitation is from a node + //performing Duplicate Address Detection + if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) + { + //The source link-layer address must not be included when the + //source IP address is the unspecified address... + if(option == NULL) + { + //Debug message + TRACE_WARNING("The tentative address %s is a duplicate!\r\n", + ipv6AddrToString(&addrEntry->addr, NULL)); + + //The tentative address is a duplicate and should not be used + addrEntry->duplicate = TRUE; + } + } + + //In all cases, a node must not respond to a Neighbor Solicitation + //for a tentative address + return; + } + else if(addrEntry->state != IPV6_ADDR_STATE_INVALID) + { + //The target address is a valid address assigned to the interface + validTarget = TRUE; + //We are done + break; + } + } + } + + //Invalid target address? + if(!validTarget) + { + //The Neighbor Solicitation must be discarded if the target address + //is not a valid anycast address assigned to the interface + if(!ipv6IsAnycastAddr(interface, &message->targetAddr)) + { + //Debug message + TRACE_WARNING("Wrong target address!\r\n"); + //Exit immediately + return; + } + } + + //Source Link-Layer Address option found? + if(option != NULL && option->length == 1) + { + //Debug message + TRACE_DEBUG(" Source Link-Layer Address = %s\r\n", + macAddrToString(&option->linkLayerAddr, NULL)); + + //The Source Link-Layer Address option must not be included when the + //source IP address is the unspecified address + if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR)) + return; + + //Search the Neighbor Cache for the source address of the solicitation + neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr); + + //No matching entry has been found? + if(!neighborCacheEntry) + { + //Create an entry + neighborCacheEntry = ndpCreateNeighborCacheEntry(interface); + + //Neighbor Cache entry successfully created? + if(neighborCacheEntry) + { + //Record the IPv6 and the corresponding MAC address + neighborCacheEntry->ipAddr = pseudoHeader->srcAddr; + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //Enter the STALE state + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + else + { + //INCOMPLETE state? + if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE) + { + //Record link-layer address + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Send all the packets that are pending for transmission + n = ndpSendQueuedPackets(interface, neighborCacheEntry); + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + + //Check whether any packets have been sent + if(n > 0) + { + //Start delay timer + neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME; + //Switch to the DELAY state + neighborCacheEntry->state = NDP_STATE_DELAY; + } + else + { + //Enter the STALE state + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + //REACHABLE, STALE, DELAY or PROBE state? + else + { + //Different link-layer address than cached? + if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr)) + { + //Update link-layer address + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //Enter the STALE state + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + } + } + //Source Link-Layer Address option not found? + else + { + //The Source Link-Layer Address option must not be included when the + //source IP address is the unspecified address. Otherwise, this option + //must be included in multicast solicitations + if(!ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) && + ipv6IsMulticastAddr(&pseudoHeader->destAddr)) + { + //Debug message + TRACE_WARNING("The Source Link-Layer Address must be included!\r\n"); + //Exit immediately + return; + } + } + + //After any updates to the Neighbor cache, the node sends a Neighbor + //Advertisement response as described in RFC 4861 7.2.4 + ndpSendNeighborAdv(interface, &message->targetAddr, &pseudoHeader->srcAddr); +#endif +} + + +/** + * @brief Neighbor Advertisement message processing + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IPv6 pseudo header + * @param[in] buffer Multi-part buffer containing the Neighbor Advertisement message + * @param[in] offset Offset to the first byte of the message + * @param[in] hopLimit Hop Limit field from IPv6 header + **/ + +void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, + const NetBuffer *buffer, size_t offset, uint8_t hopLimit) +{ +#if (ETH_SUPPORT == ENABLED) + error_t error; + uint_t i; + uint_t n; + size_t length; + bool_t differentLinkLayerAddr; + NdpNeighborAdvMessage *message; + NdpLinkLayerAddrOption *option; + NdpNeighborCacheEntry *neighborCacheEntry; + Ipv6AddrEntry *addrEntry; + + //Retrieve the length of the message + length = netBufferGetLength(buffer) - offset; + + //Check the length of the Neighbor Advertisement message + if(length < sizeof(NdpNeighborAdvMessage)) + return; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_INFO("Neighbor Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpNeighborAdvMessage(message); + + //The IPv6 Hop Limit field must have a value of 255 to ensure + //that the packet has not been forwarded by a router + if(hopLimit != NDP_HOP_LIMIT) + return; + + //ICMPv6 Code must be 0 + if(message->code) + return; + + //The target address must not be a multicast address + if(ipv6IsMulticastAddr(&message->targetAddr)) + { + //Debug message + TRACE_WARNING("Target address must not be a multicast address!\r\n"); + //Exit immediately + return; + } + + //If the destination address is a multicast address + //then the Solicited flag must be zero + if(ipv6IsMulticastAddr(&pseudoHeader->destAddr) && message->s) + { + //Debug message + TRACE_WARNING("Solicited flag must be zero!\r\n"); + //Exit immediately + return; + } + + //Calculate the length of the Options field + length -= sizeof(NdpNeighborAdvMessage); + + //Parse Options field + error = ndpCheckOptions(message->options, length); + //All included options must have a length that is greater than zero + if(error) + return; + + //Duplicate address detection + for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + addrEntry = &interface->ipv6Context.addrList[i]; + + //Valid entry? + if(addrEntry->state != IPV6_ADDR_STATE_INVALID) + { + //Check whether the target address is tentative or matches + //a unicast address assigned to the interface + if(ipv6CompAddr(&addrEntry->addr, &message->targetAddr)) + { + //Debug message + TRACE_WARNING("The address %s is a duplicate!\r\n", + ipv6AddrToString(&addrEntry->addr, NULL)); + + //The address is a duplicate and should not be used + addrEntry->duplicate = TRUE; + //Exit immediately + return; + } + } + } + + //Search the Neighbor cache for the specified target address + neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &message->targetAddr); + + //If no entry exists, the advertisement should be silently discarded + if(neighborCacheEntry) + { + //This flag tells whether the supplied link-layer + //address differs from that in the cache + differentLinkLayerAddr = FALSE; + + //Search for the Target Link-Layer Address option + option = ndpGetOption(message->options, + length, NDP_OPT_TARGET_LINK_LAYER_ADDR); + + //Target Link-Layer Address option found? + if(option != NULL && option->length == 1) + { + //Debug message + TRACE_DEBUG(" Target Link-Layer Address = %s\r\n", + macAddrToString(&option->linkLayerAddr, NULL)); + + //Different link-layer address than cached? + if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr)) + differentLinkLayerAddr = TRUE; + } + + //INCOMPLETE state? + if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE) + { + //If no Target Link-Layer Address option is included, the receiving + //node should silently discard the received advertisement + if(option != NULL && option->length == 1) + { + //Record the link-layer address + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Send all the packets that are pending for transmission + n = ndpSendQueuedPackets(interface, neighborCacheEntry); + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + + //Solicited flag is set? + if(message->s) + { + //Computing the random ReachableTime value + neighborCacheEntry->timeout = interface->ndpContext.reachableTime; + //Switch to the REACHABLE state + neighborCacheEntry->state = NDP_STATE_REACHABLE; + } + else + { + //Check whether any packets have been sent + if(n > 0) + { + //Start delay timer + neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME; + //Switch to the DELAY state + neighborCacheEntry->state = NDP_STATE_DELAY; + } + else + { + //Enter the STALE state + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + } + } + //REACHABLE, STALE, DELAY or PROBE state? + else + { + //Check whether the Override flag is clear and the supplied + //link-layer address differs from that in the cache + if(!message->o && differentLinkLayerAddr) + { + //REACHABLE state? + if(neighborCacheEntry->state == NDP_STATE_REACHABLE) + { + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //Enter the STALE state + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + else + { + //Solicited flag is set? + if(message->s) + { + //Different link-layer address than cached? + if(differentLinkLayerAddr) + { + //The link-layer address must be inserted in the cache + neighborCacheEntry->macAddr = option->linkLayerAddr; + } + + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //Computing the random ReachableTime value + neighborCacheEntry->timeout = interface->ndpContext.reachableTime; + //Switch to the REACHABLE state + neighborCacheEntry->state = NDP_STATE_REACHABLE; + } + else + { + //Different link-layer address than cached? + if(differentLinkLayerAddr) + { + //The link-layer address must be inserted in the cache + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //The state must be set to STALE + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + } + } + + //The IsRouter flag in the cache entry must be set based + //on the Router flag in the received advertisement + if(message->r) + { + //The neighbor is a router + neighborCacheEntry->isRouter = TRUE; + } + else + { + //Check whether the IsRouter flag changes from TRUE to FALSE + //as a result of this update + if(neighborCacheEntry->isRouter) + { + //The node must remove that router from the Default Router list + //and update the Destination cache entries for all destinations + //using that neighbor as a router + ipv6RemoveDefaultRouter(interface, &neighborCacheEntry->ipAddr); + } + + //The neighbor is a host + neighborCacheEntry->isRouter = FALSE; + } + } +#endif +} + + +/** + * @brief Redirect message processing + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IPv6 pseudo header + * @param[in] buffer Multi-part buffer containing the Redirect message + * @param[in] offset Offset to the first byte of the message + * @param[in] hopLimit Hop Limit field from IPv6 header + **/ + +void ndpProcessRedirect(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, + const NetBuffer *buffer, size_t offset, uint8_t hopLimit) +{ +#if (ETH_SUPPORT == ENABLED) + error_t error; + uint_t n; + size_t length; + NdpRedirectMessage *message; + NdpLinkLayerAddrOption *option; + NdpNeighborCacheEntry *neighborCacheEntry; + NdpDestCacheEntry *destCacheEntry; + + //Retrieve the length of the message + length = netBufferGetLength(buffer) - offset; + + //Check the length of the Redirect message + if(length < sizeof(NdpRedirectMessage)) + return; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_INFO("Redirect message received (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpRedirectMessage(message); + + //The IPv6 Hop Limit field must have a value of 255 to ensure + //that the packet has not been forwarded by a router + if(hopLimit != NDP_HOP_LIMIT) + return; + + //ICMPv6 Code must be 0 + if(message->code) + return; + + //Routers must use their link-local address as the source for Redirect + //messages so that hosts can uniquely identify routers + if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) + return; + + //The IP source address of the Redirect must be the same as the current + //first-hop router for the specified Destination address + if(!ndpIsFirstHopRouter(interface, &message->destAddr, &pseudoHeader->srcAddr)) + return; + + //The Destination Address field in the Redirect message must not + //contain a multicast address + if(ipv6IsMulticastAddr(&message->destAddr)) + return; + + //The Target Address must be either a link-local address (when redirected + //to a router) or the same as the Destination Address (when redirected to + //the on-link destination) + if(!ipv6IsLinkLocalUnicastAddr(&message->targetAddr) && + !ipv6CompAddr(&message->targetAddr, &message->destAddr)) + { + //Silently discard the received Redirect message + return; + } + + //Calculate the length of the Options field + length -= sizeof(NdpNeighborAdvMessage); + + //Parse Options field + error = ndpCheckOptions(message->options, length); + //All included options must have a length that is greater than zero + if(error) + return; + + //Search the Destination cache for the specified address + destCacheEntry = ndpFindDestCacheEntry(interface, &message->destAddr); + + //Check whether a corresponding Destination cache entry exists + if(destCacheEntry) + { + //The entry is updated with information learned from Redirect messages + destCacheEntry->nextHop = message->targetAddr; + //Save current time + destCacheEntry->timestamp = osGetSystemTime(); + } + else + { + //If no Destination Cache entry exists for the destination, an + //implementation should create such an entry + destCacheEntry = ndpCreateDestCacheEntry(interface); + + //Destination cache entry successfully created? + if(destCacheEntry) + { + //Destination address + destCacheEntry->destAddr = message->destAddr; + //Address of the next hop + destCacheEntry->nextHop = message->targetAddr; + + //Initially, the PMTU value for a path is assumed to be + //the MTU of the first-hop link + destCacheEntry->pathMtu = interface->ipv6Context.linkMtu; + + //Save current time + destCacheEntry->timestamp = osGetSystemTime(); + } + } + + //Search for the Target Link-Layer Address option + option = ndpGetOption(message->options, + length, NDP_OPT_TARGET_LINK_LAYER_ADDR); + + //If the Redirect contains a Target Link-Layer Address option, the host + //either creates or updates the Neighbor Cache entry for the target + if(option != NULL && option->length == 1) + { + //Debug message + TRACE_DEBUG(" Target Link-Layer Address = %s\r\n", + macAddrToString(&option->linkLayerAddr, NULL)); + + //Search the Neighbor cache for the specified target address + neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &message->targetAddr); + + //No matching entry has been found? + if(!neighborCacheEntry) + { + //Create an entry for the target + neighborCacheEntry = ndpCreateNeighborCacheEntry(interface); + + //Neighbor cache entry successfully created? + if(neighborCacheEntry) + { + //Record the Target address + neighborCacheEntry->ipAddr = message->targetAddr; + //The cached link-layer address is copied from the option + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Newly created Neighbor Cache entries should set the IsRouter flag to FALSE + neighborCacheEntry->isRouter = FALSE; + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //The reachability state must be set to STALE + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + else + { + //If the Target Address is not the same as the Destination Address, + //the host must set IsRouter to TRUE for the target + if(!ipv6CompAddr(&message->targetAddr, &message->destAddr)) + neighborCacheEntry->isRouter = TRUE; + + //INCOMPLETE state? + if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE) + { + //Record link-layer address + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Send all the packets that are pending for transmission + n = ndpSendQueuedPackets(interface, neighborCacheEntry); + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + + //Check whether any packets have been sent + if(n > 0) + { + //Start delay timer + neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME; + //Switch to the DELAY state + neighborCacheEntry->state = NDP_STATE_DELAY; + } + else + { + //Enter the STALE state + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + //REACHABLE, STALE, DELAY or PROBE state? + else + { + //Different link-layer address than cached? + if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr)) + { + //Update link-layer address + neighborCacheEntry->macAddr = option->linkLayerAddr; + //Save current time + neighborCacheEntry->timestamp = osGetSystemTime(); + //The reachability state must be set to STALE + neighborCacheEntry->state = NDP_STATE_STALE; + } + } + } + } +#endif +} + + +/** + * @brief Send a Router Solicitation message + * @param[in] interface Underlying network interface + * @return Error code + **/ + +error_t ndpSendRouterSol(NetInterface *interface) +{ + error_t error; + size_t offset; + size_t length; + NetBuffer *buffer; + NdpRouterSolMessage *message; + Ipv6PseudoHeader pseudoHeader; + + //The destination address is typically the all-routers multicast address + pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR; + + //Select the most appropriate source address to be used when + //sending the Router Solicitation message + error = ipv6SelectSourceAddr(&interface, + &pseudoHeader.destAddr, &pseudoHeader.srcAddr); + + //No address assigned to the interface? + if(error) + { + //Use the unspecified address if no address is assigned + //to the sending interface + pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR; + } + + //The only defined option that may appear in a Router Solicitation + //message is the Source Link-Layer Address option + length = sizeof(NdpRouterSolMessage) + sizeof(NdpLinkLayerAddrOption); + + //Allocate a memory buffer to hold the Router Solicitation message + buffer = ipAllocBuffer(length, &offset); + //Failed to allocate memory? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + + //Format Router Solicitation message + message->type = ICMPV6_TYPE_ROUTER_SOL; + message->code = 0; + message->checksum = 0; + message->reserved = 0; + + //Length of the message, excluding any option + length = sizeof(NdpRouterSolMessage); + + //The Source Link-Layer Address option must not be included + //when the source IPv6 address is the unspecified address + if(!ipv6CompAddr(&pseudoHeader.srcAddr, &IPV6_UNSPECIFIED_ADDR)) + { +#if (ETH_SUPPORT == ENABLED) + //Check whether a MAC address has been assigned to the interface + if(!macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR)) + { + //Add Source Link-Layer Address option + ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR, + &interface->macAddr, sizeof(MacAddr)); + } +#endif + } + + //Adjust the length of the multi-part buffer + netBufferSetLength(buffer, offset + length); + + //Format IPv6 pseudo header + pseudoHeader.length = htonl(length); + pseudoHeader.reserved = 0; + pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; + + //Calculate ICMPv6 header checksum + message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, + sizeof(Ipv6PseudoHeader), buffer, offset, length); + + //Debug message + TRACE_INFO("Sending Router Solicitation message (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpRouterSolMessage(message); + + //Send Router Solicitation message + error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); + + //Free previously allocated memory + netBufferFree(buffer); + //Return status code + return error; +} + + +/** + * @brief Send a Neighbor Solicitation message + * @param[in] interface Underlying network interface + * @param[in] targetIpAddr Target IPv6 address + * @param[in] multicast Unicast or unicast Neighbor Solicitation message + * @return Error code + **/ + +error_t ndpSendNeighborSol(NetInterface *interface, + const Ipv6Addr *targetIpAddr, bool_t multicast) +{ + error_t error; + size_t offset; + size_t length; + NetBuffer *buffer; + NdpNeighborSolMessage *message; + Ipv6PseudoHeader pseudoHeader; + + //Multicast Neighbor Solicitation message? + if(multicast) + { + //Compute the solicited-node multicast address that + //corresponds to the target IPv6 address + ipv6ComputeSolicitedNodeAddr(targetIpAddr, &pseudoHeader.destAddr); + } + else + { + //Unicast Neighbor Solicitation message + pseudoHeader.destAddr = *targetIpAddr; + } + + //Check whether the target address is a tentative address + if(ipv6IsTentativeAddr(interface, targetIpAddr)) + { + //The IPv6 source is set to the unspecified address + pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR; + } + else + { + //Select the most appropriate source address to be used + //when sending the Neighbor Solicitation message + error = ipv6SelectSourceAddr(&interface, + targetIpAddr, &pseudoHeader.srcAddr); + + //No address assigned to the interface? + if(error) + return error; + } + + //The only defined option that may appear in a Neighbor Solicitation + //message is the Source Link-Layer Address option + length = sizeof(NdpNeighborSolMessage) + sizeof(NdpLinkLayerAddrOption); + + //Allocate a memory buffer to hold the Neighbor Solicitation message + buffer = ipAllocBuffer(length, &offset); + //Failed to allocate memory? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + + //Format Neighbor Solicitation message + message->type = ICMPV6_TYPE_NEIGHBOR_SOL; + message->code = 0; + message->checksum = 0; + message->reserved = 0; + message->targetAddr = *targetIpAddr; + + //Length of the message, excluding any option + length = sizeof(NdpNeighborSolMessage); + + //The Source Link-Layer Address option must not be included + //when the source IPv6 address is the unspecified address + if(!ipv6CompAddr(&pseudoHeader.srcAddr, &IPV6_UNSPECIFIED_ADDR)) + { +#if (ETH_SUPPORT == ENABLED) + //Add Source Link-Layer Address option + ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR, + &interface->macAddr, sizeof(MacAddr)); +#endif + } + + //Adjust the length of the multi-part buffer + netBufferSetLength(buffer, offset + length); + + //Format IPv6 pseudo header + pseudoHeader.length = htonl(length); + pseudoHeader.reserved = 0; + pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; + + //Calculate ICMPv6 header checksum + message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, + sizeof(Ipv6PseudoHeader), buffer, offset, length); + + //Debug message + TRACE_INFO("Sending Neighbor Solicitation message (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpNeighborSolMessage(message); + + //Send Neighbor Solicitation message + error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); + + //Free previously allocated memory + netBufferFree(buffer); + //Return status code + return error; +} + + +/** + * @brief Send a Neighbor Advertisement message + * @param[in] interface Underlying network interface + * @param[in] targetIpAddr Target IPv6 address + * @param[in] destIpAddr Destination IPv6 address + * @return Error code + **/ + +error_t ndpSendNeighborAdv(NetInterface *interface, + const Ipv6Addr *targetIpAddr, const Ipv6Addr *destIpAddr) +{ + error_t error; + size_t offset; + size_t length; + NetBuffer *buffer; + NdpNeighborAdvMessage *message; + Ipv6PseudoHeader pseudoHeader; + + //Destination IP address is the unspecified address? + if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR)) + { + //If the destination is the unspecified address, the node must + //multicast the advertisement to the all-nodes address + pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR; + } + else + { + //Otherwise, the node must unicast the advertisement to + //the destination IP address + pseudoHeader.destAddr = *destIpAddr; + } + + //Check whether the target address is a valid anycast address + //assigned to the interface + if(ipv6IsAnycastAddr(interface, targetIpAddr)) + { + //Select the most appropriate source address to be used + //when sending the Neighbor Advertisement message + error = ipv6SelectSourceAddr(&interface, + targetIpAddr, &pseudoHeader.srcAddr); + + //No address assigned to the interface? + if(error) + return error; + } + else + { + //Set the source IP address + pseudoHeader.srcAddr = *targetIpAddr; + } + + //The only defined option that may appear in a Neighbor Advertisement + //message is the Target Link-Layer Address option + length = sizeof(NdpNeighborAdvMessage) + sizeof(NdpLinkLayerAddrOption); + + //Allocate a memory buffer to hold the Neighbor Advertisement message + buffer = ipAllocBuffer(length, &offset); + //Failed to allocate memory? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + + //Format Neighbor Advertisement message + message->type = ICMPV6_TYPE_NEIGHBOR_ADV; + message->code = 0; + message->checksum = 0; + message->reserved1 = 0; + message->reserved2 = 0; + message->targetAddr = *targetIpAddr; + + //The Router flag indicates that the sender is a router + if(interface->ipv6Context.isRouter) + message->r = TRUE; + else + message->r = FALSE; + + //If the destination is the unspecified address, the node must set + //the Solicited flag to zero + if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR)) + message->s = FALSE; + else + message->s = TRUE; + + //The Override flag should not be set in solicited advertisements + //for anycast addresses + if(ipv6IsAnycastAddr(interface, targetIpAddr)) + message->o = FALSE; + else + message->o = TRUE; + + //Length of the message, excluding any option + length = sizeof(NdpNeighborAdvMessage); + +#if (ETH_SUPPORT == ENABLED) + //Add Target Link-Layer Address option + ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR, + &interface->macAddr, sizeof(MacAddr)); +#endif + + //Adjust the length of the multi-part buffer + netBufferSetLength(buffer, offset + length); + + //Format IPv6 pseudo header + pseudoHeader.length = htonl(length); + pseudoHeader.reserved = 0; + pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; + + //Calculate ICMPv6 header checksum + message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, + sizeof(Ipv6PseudoHeader), buffer, offset, length); + + //Debug message + TRACE_INFO("Sending Neighbor Advertisement message (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpNeighborAdvMessage(message); + + //Send Neighbor Advertisement message + error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); + + //Free previously allocated memory + netBufferFree(buffer); + //Return status code + return error; +} + + +/** + * @brief Send a Redirect message + * @param[in] interface Underlying network interface + * @param[in] targetAddr IPv6 address that is a better first hop to use + * for the destination address + * @param[in] ipPacket Multi-part buffer that holds the IPv6 packet that + * triggered the sending of the Redirect + * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet + * @return Error code + **/ + +error_t ndpSendRedirect(NetInterface *interface, const Ipv6Addr *targetAddr, + const NetBuffer *ipPacket, size_t ipPacketOffset) +{ + error_t error; + size_t offset; + size_t length; + size_t ipPacketLength; + size_t optionLength; + size_t paddingLength; + NetBuffer *buffer; + NdpRedirectMessage *message; + NdpRedirectedHeaderOption *option; + NdpNeighborCacheEntry *entry; + Ipv6Header *ipHeader; + Ipv6PseudoHeader pseudoHeader; + uint8_t padding[8]; + + //Retrieve the length of the forwarded IPv6 packet + ipPacketLength = netBufferGetLength(ipPacket) - ipPacketOffset; + + //Check the length of the IPv6 packet + if(ipPacketLength < sizeof(Ipv6Header)) + return ERROR_INVALID_LENGTH; + + //Point to the header of the invoking packet + ipHeader = netBufferAt(ipPacket, ipPacketOffset); + //Sanity check + if(ipHeader == NULL) + return ERROR_FAILURE; + + //The only defined options that may appear in a Redirect message are the + //Target Link-Layer Address option and the Redirected Header option + length = sizeof(NdpRedirectMessage) + sizeof(NdpLinkLayerAddrOption) + + sizeof(NdpRedirectedHeaderOption); + + //Allocate a memory buffer to hold the Redirect message + buffer = ipAllocBuffer(length, &offset); + //Failed to allocate memory? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the message + message = netBufferAt(buffer, offset); + + //Format Redirect message + message->type = ICMPV6_TYPE_REDIRECT; + message->code = 0; + message->checksum = 0; + message->reserved = 0; + message->targetAddr = *targetAddr; + message->destAddr = ipHeader->destAddr; + + //Length of the message, excluding any option + length = sizeof(NdpRedirectMessage); + + //Search the Neighbor cache for the specified target address + entry = ndpFindNeighborCacheEntry(interface, targetAddr); + + //Include the link-layer address of the target, if known + if(entry != NULL) + { + //Add Target Link-Layer Address option + ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR, + &entry->macAddr, sizeof(MacAddr)); + } + + //Retrieve the length of the IPv6 packet that triggered the sending + //of the Redirect + ipPacketLength = netBufferGetLength(ipPacket) - ipPacketOffset; + + //Return as much of the forwarded IPv6 packet as can fit without + //the redirect packet exceeding the minimum IPv6 MTU + ipPacketLength = MIN(ipPacketLength, IPV6_DEFAULT_MTU - + sizeof(NdpRedirectedHeaderOption) - length); + + //Length of the Redirected Header option in units of 8 bytes including + //the type and length fields + optionLength = (ipPacketLength + sizeof(NdpOption) + 7) / 8; + + //Add Redirected Header option + option = (NdpRedirectedHeaderOption *) ((uint8_t *) message + length); + + //Format Redirected Header option + option->type = NDP_OPT_REDIRECTED_HEADER; + option->length = (uint8_t) optionLength; + option->reserved1 = 0; + option->reserved2 = 0; + + //Update the length of Redirect message + length += sizeof(NdpRedirectedHeaderOption); + + //Adjust the length of the multi-part buffer + netBufferSetLength(buffer, offset + length); + + //Copy the contents of the forwarded IPv6 packet + error = netBufferConcat(buffer, ipPacket, ipPacketOffset, ipPacketLength); + + //Check status code + if(!error) + { + //Options should be padded when necessary to ensure that they end on + //their natural 64-bit boundaries + if((ipPacketLength + sizeof(NdpRedirectedHeaderOption)) < (optionLength * 8)) + { + //Determine the amount of padding data to append + paddingLength = (optionLength * 8) - ipPacketLength - + sizeof(NdpRedirectedHeaderOption); + + //Prepare padding data + memset(padding, 0, paddingLength); + //Append padding bytes + error = netBufferAppend(buffer, padding, paddingLength); + } + } + + //Check status code + if(!error) + { + //Get the length of the resulting message + length = netBufferGetLength(buffer) - offset; + + //Format IPv6 pseudo header + pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr; + pseudoHeader.destAddr = ipHeader->srcAddr; + pseudoHeader.length = htonl(length); + pseudoHeader.reserved = 0; + pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; + + //Message checksum calculation + message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, + sizeof(Ipv6PseudoHeader), buffer, offset, length); + + //Debug message + TRACE_INFO("Sending Redirect message (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + ndpDumpRedirectMessage(message); + + //Send Redirect message + error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); + } + + //Free previously allocated memory + netBufferFree(buffer); + + //Return status code + return error; +} + + +/** + * @brief Dump Router Solicitation message for debugging purpose + * @param[in] message Router Solicitation message + **/ + +void ndpDumpRouterSolMessage(const NdpRouterSolMessage *message) +{ + //Dump Router Solicitation message + TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); + TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); + TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); +} + + +/** + * @brief Dump Router Advertisement message for debugging purpose + * @param[in] message Router Advertisement message + **/ + +void ndpDumpRouterAdvMessage(const NdpRouterAdvMessage *message) +{ + //Dump Router Advertisement message + TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); + TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); + TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); + TRACE_DEBUG(" Cur Hop Limit = %" PRIu8 "\r\n", message->curHopLimit); + TRACE_DEBUG(" M = %" PRIu8 "\r\n", message->m); + TRACE_DEBUG(" O = %" PRIu8 "\r\n", message->o); + TRACE_DEBUG(" Router Lifetime = %" PRIu16 "\r\n", ntohs(message->routerLifetime)); + TRACE_DEBUG(" Reachable Time = %" PRIu32 "\r\n", ntohl(message->reachableTime)); + TRACE_DEBUG(" Retrans Timer = %" PRIu32 "\r\n", ntohl(message->retransTimer)); +} + + +/** + * @brief Dump Neighbor Solicitation message for debugging purpose + * @param[in] message Neighbor Solicitation message + **/ + +void ndpDumpNeighborSolMessage(const NdpNeighborSolMessage *message) +{ + //Dump Neighbor Solicitation message + TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); + TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); + TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); + TRACE_DEBUG(" Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL)); +} + + +/** + * @brief Dump Neighbor Advertisement message for debugging purpose + * @param[in] message Neighbor Advertisement message + **/ + +void ndpDumpNeighborAdvMessage(const NdpNeighborAdvMessage *message) +{ + //Dump Neighbor Advertisement message + TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); + TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); + TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); + TRACE_DEBUG(" R = %" PRIu8 "\r\n", message->r); + TRACE_DEBUG(" S = %" PRIu8 "\r\n", message->s); + TRACE_DEBUG(" O = %" PRIu8 "\r\n", message->o); + TRACE_DEBUG(" Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL)); +} + + +/** + * @brief Dump Redirect message for debugging purpose + * @param[in] message Redirect message + **/ + +void ndpDumpRedirectMessage(const NdpRedirectMessage *message) +{ + //Dump Neighbor Advertisement message + TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); + TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); + TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); + TRACE_DEBUG(" Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL)); + TRACE_DEBUG(" Destination Address = %s\r\n", ipv6AddrToString(&message->destAddr, NULL)); +} + +#endif +