Webserver+3d print
cyclone_tcp/ipv6/ndp_router_adv.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file ndp_router_adv.c * @brief Router advertisement service * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneTCP Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL NDP_TRACE_LEVEL //Dependencies #include "core/net.h" #include "core/ip.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/ndp_router_adv.h" #include "debug.h" //Check TCP/IP stack configuration #if (IPV6_SUPPORT == ENABLED && NDP_ROUTER_ADV_SUPPORT == ENABLED) //Tick counter to handle periodic operations systime_t ndpRouterAdvTickCounter; /** * @brief Initialize settings with default values * @param[out] settings Structure that contains the RA service configuration variables **/ void ndpRouterAdvGetDefaultSettings(NdpRouterAdvSettings *settings) { //Underlying network interface settings->interface = netGetDefaultInterface(); //The maximum time allowed between sending unsolicited multicast //Router Advertisements from the interface settings->maxRtrAdvInterval = NDP_MAX_RTR_ADVERT_INTERVAL; //The minimum time allowed between sending unsolicited multicast //Router Advertisements from the interface settings->minRtrAdvInterval = NDP_MAX_RTR_ADVERT_INTERVAL / 3; //The default value to be placed in the Cur Hop Limit field in the //Router Advertisement messages sent by the router settings->curHopLimit = 0; //The value to be placed in the Managed Address Configuration //flag in the Router Advertisement settings->managedFlag = FALSE; //The value to be placed in the Other Configuration flag //in the Router Advertisement settings->otherConfigFlag = FALSE; //The value to be placed in the Mobile IPv6 Home Agent //flag in the Router Advertisement settings->homeAgentFlag = FALSE; //The value to be placed in the Router Selection Preferences //field in the Router Advertisement settings->preference = NDP_ROUTER_SEL_PREFERENCE_MEDIUM; //The value to be placed in the Neighbor Discovery Proxy //flag in the Router Advertisement settings->proxyFlag = FALSE; //The value to be placed in the Router Lifetime field of //Router Advertisements sent from the interface settings->defaultLifetime = 3 * (NDP_MAX_RTR_ADVERT_INTERVAL / 1000); //The value to be placed in the Reachable Time field in the //Router Advertisement messages sent by the router settings->reachableTime = 0; //The value to be placed in the Retrans Timer field in the //Router Advertisement messages sent by the router settings->retransTimer = 0; //The value to be placed in the MTU option sent by the router settings->linkMtu = 0; //A list of prefixes to be placed in Prefix Information options (PIO) //in Router Advertisement messages sent from the interface settings->prefixList = NULL; settings->prefixListLength = 0; //A list of routes to be placed in Route Information options (RIO) //in Router Advertisement messages sent from the interface settings->routeList = NULL; settings->routeListLength = 0; //A list of header compression contexts to be placed in the 6LoWPAN Context //options (6CO) in Router Advertisement messages sent from the interface settings->contextList = NULL; settings->contextListLength = 0; } /** * @brief RA service initialization * @param[in] context Pointer to the RA service context * @param[in] settings RA service configuration variables * @return Error code **/ error_t ndpRouterAdvInit(NdpRouterAdvContext *context, const NdpRouterAdvSettings *settings) { NetInterface *interface; //Debug message TRACE_INFO("Initializing Router Advertisement service...\r\n"); //Ensure the parameters are valid if(!context || !settings) return ERROR_INVALID_PARAMETER; //Valid network interface? if(!settings->interface) return ERROR_INVALID_PARAMETER; //Get exclusive access osAcquireMutex(&netMutex); //Point to the underlying network interface interface = settings->interface; //Clear the RA service context memset(context, 0, sizeof(NdpRouterAdvContext)); //Save user settings context->settings = *settings; //The RA service is currently disabled on the interface context->running = FALSE; //Attach the RA service context to the network interface interface->ndpRouterAdvContext = context; //Release exclusive access osReleaseMutex(&netMutex); //Successful initialization return NO_ERROR; } /** * @brief Start RA service * @param[in] context Pointer to the RA service context * @return Error code **/ error_t ndpRouterAdvStart(NdpRouterAdvContext *context) { error_t error; NetInterface *interface; //Check parameter if(context == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_INFO("Starting Router Advertisement service...\r\n"); //Get exclusive access osAcquireMutex(&netMutex); //Check whether the service is running if(!context->running) { //Point to the underlying network interface interface = context->settings.interface; //Join the All-Routers multicast address error = ipv6JoinMulticastGroup(interface, &IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR); //Successful membership registration? if(!error) { //Reset variables context->timestamp = osGetSystemTime(); context->timeout = 0; context->routerAdvCount = 0; //Enable the router to forward packets to or from the interface interface->ipv6Context.isRouter = TRUE; //Default Hop Limit value if(context->settings.curHopLimit != 0) interface->ipv6Context.curHopLimit = context->settings.curHopLimit; //The time a node assumes a neighbor is reachable if(context->settings.reachableTime != 0) interface->ndpContext.reachableTime = context->settings.reachableTime; //The time between retransmissions of NS messages if(context->settings.retransTimer != 0) interface->ndpContext.retransTimer = context->settings.retransTimer; //Start transmitting Router Advertisements context->running = TRUE; } } else { //The service is already running... error = NO_ERROR; } //Release exclusive access osReleaseMutex(&netMutex); //Return status code return error; } /** * @brief Stop RA service * @param[in] context Pointer to the RA service context * @return Error code **/ error_t ndpRouterAdvStop(NdpRouterAdvContext *context) { error_t error; NetInterface *interface; //Check parameter if(context == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_INFO("Stopping Router Advertisement service...\r\n"); //Get exclusive access osAcquireMutex(&netMutex); //Check whether the service is running if(context->running) { //Point to the underlying network interface interface = context->settings.interface; //The router should transmit one or more final multicast Router //Advertisements with a Router Lifetime field of zero ndpSendRouterAdv(context, 0); //Leave the All-Routers multicast address error = ipv6LeaveMulticastGroup(interface, &IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR); //Restore default parameters interface->ipv6Context.curHopLimit = IPV6_DEFAULT_HOP_LIMIT; interface->ndpContext.reachableTime = NDP_REACHABLE_TIME; interface->ndpContext.retransTimer = NDP_RETRANS_TIMER; //Stop transmitting Router Advertisements context->running = FALSE; } else { //The service is not running... error = NO_ERROR; } //Release exclusive access osReleaseMutex(&netMutex); //Return status code return error; } /** * @brief RA service timer handler * @param[in] context Pointer to the RA service context **/ void ndpRouterAdvTick(NdpRouterAdvContext *context) { systime_t time; NetInterface *interface; NdpRouterAdvSettings *settings; //Make sure the RA service has been properly instantiated if(context == NULL) return; //Point to the underlying network interface interface = context->settings.interface; //Point to the router configuration variables settings = &context->settings; //Get current time time = osGetSystemTime(); //Make sure that the link is up and the service is running if(interface->linkState && context->running) { //Make sure that a valid link-local address has been assigned to the interface if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) { //Check current time if(timeCompare(time, context->timestamp + context->timeout) >= 0) { //Send an unsolicited Router Advertisement ndpSendRouterAdv(context, context->settings.defaultLifetime); //Save the time at which the message was sent context->timestamp = time; //Whenever a multicast advertisement is sent from an interface, the //timer is reset to a uniformly distributed random value between //MinRtrAdvInterval and MaxRtrAdvInterval context->timeout = netGetRandRange(settings->minRtrAdvInterval, settings->maxRtrAdvInterval); //First Router Advertisements to be sent from this interface? if(context->routerAdvCount < NDP_MAX_INITIAL_RTR_ADVERTISEMENTS) { //For the first few advertisements sent from an interface when it //becomes an advertising interface, the randomly chosen interval //should not be greater than MAX_INITIAL_RTR_ADVERT_INTERVAL context->timeout = MIN(context->timeout, NDP_MAX_INITIAL_RTR_ADVERT_INTERVAL); } //Increment counter context->routerAdvCount++; } } } } /** * @brief Callback function for link change event * @param[in] context Pointer to the RA service context **/ void ndpRouterAdvLinkChangeEvent(NdpRouterAdvContext *context) { NetInterface *interface; //Make sure the RA service has been properly instantiated if(context == NULL) return; //Point to the underlying network interface interface = context->settings.interface; //Reset variables context->timestamp = osGetSystemTime(); context->timeout = 0; context->routerAdvCount = 0; //Default Hop Limit value if(context->settings.curHopLimit != 0) interface->ipv6Context.curHopLimit = context->settings.curHopLimit; //The time a node assumes a neighbor is reachable if(context->settings.reachableTime != 0) interface->ndpContext.reachableTime = context->settings.reachableTime; //The time between retransmissions of NS messages if(context->settings.retransTimer != 0) interface->ndpContext.retransTimer = context->settings.retransTimer; } /** * @brief Router Solicitation 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 ndpProcessRouterSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, uint8_t hopLimit) { error_t error; uint_t n; size_t length; systime_t time; systime_t delay; NdpRouterAdvContext *context; NdpRouterSolMessage *message; NdpLinkLayerAddrOption *option; NdpNeighborCacheEntry *entry; //Point to the RA service context context = interface->ndpRouterAdvContext; //A host must silently discard any received Router Solicitation if(context == NULL) return; //Get current time time = osGetSystemTime(); //Retrieve the length of the message length = netBufferGetLength(buffer) - offset; //Check the length of the Router Solicitation message if(length < sizeof(NdpRouterSolMessage)) return; //Point to the beginning of the message message = netBufferAt(buffer, offset); //Sanity check if(message == NULL) return; //Debug message TRACE_INFO("Router Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpRouterSolMessage(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; //Calculate the length of the Options field length -= sizeof(NdpRouterSolMessage); //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); //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 entry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr); //No matching entry has been found? if(!entry) { //Create an entry entry = ndpCreateNeighborCacheEntry(interface); //Neighbor Cache entry successfully created? if(entry) { //Record the IPv6 and the corresponding MAC address entry->ipAddr = pseudoHeader->srcAddr; entry->macAddr = option->linkLayerAddr; //The IsRouter flag must be set to FALSE entry->isRouter = FALSE; //Save current time entry->timestamp = time; //Enter the STALE state entry->state = NDP_STATE_STALE; } } else { //If a Neighbor Cache entry for the solicitation's sender exists //the entry's IsRouter flag must be set to FALSE entry->isRouter = FALSE; //INCOMPLETE state? if(entry->state == NDP_STATE_INCOMPLETE) { //Record link-layer address entry->macAddr = option->linkLayerAddr; //Send all the packets that are pending for transmission n = ndpSendQueuedPackets(interface, entry); //Save current time entry->timestamp = time; //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, &option->linkLayerAddr)) { //Update link-layer address entry->macAddr = option->linkLayerAddr; //Save current time entry->timestamp = time; //Enter the STALE state entry->state = NDP_STATE_STALE; } } } } //Upon receipt of a Router Solicitation, compute a random delay within the //range 0 through MAX_RA_DELAY_TIME delay = netGetRandRange(0, NDP_MAX_RA_DELAY_TIME); //If the computed value corresponds to a time later than the time the next //multicast Router Advertisement is scheduled to be sent, ignore the random //delay and send the advertisement at the already-scheduled time if(timeCompare(time + delay, context->timestamp + context->timeout) > 0) return; //Check whether the router sent a multicast Router Advertisement (solicited //or unsolicited) within the last MIN_DELAY_BETWEEN_RAS seconds if(timeCompare(time, context->timestamp + NDP_MIN_DELAY_BETWEEN_RAS) < 0) { //Schedule the advertisement to be sent at a time corresponding to //MIN_DELAY_BETWEEN_RAS plus the random value after the previous //advertisement was sent. This ensures that the multicast Router //Advertisements are rate limited context->timeout = NDP_MIN_DELAY_BETWEEN_RAS + delay; } else { //Schedule the sending of a Router Advertisement at the time given //by the random value context->timeout = time + delay - context->timestamp; } } /** * @brief Send a Router Advertisement message * @param[in] context Pointer to the RA service context * @param[in] routerLifetime Router Lifetime field * @return Error code **/ error_t ndpSendRouterAdv(NdpRouterAdvContext *context, uint16_t routerLifetime) { error_t error; uint_t i; uint32_t n; size_t offset; size_t length; NetBuffer *buffer; NetInterface *interface; NdpRouterAdvMessage *message; NdpRouterAdvSettings *settings; Ipv6PseudoHeader pseudoHeader; //Point to the underlying network interface interface = context->settings.interface; //Point to the router configuration variables settings = &context->settings; //The destination address is typically the all-nodes multicast address pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR; //Routers must use their link-local address as the source for Router //Advertisement messages so that hosts can uniquely identify routers error = ipv6SelectSourceAddr(&interface, &pseudoHeader.destAddr, &pseudoHeader.srcAddr); //No link-local address assigned to the interface? if(error) return error; //Compute the maximum size of the Router Advertisement message length = sizeof(NdpRouterAdvMessage) + sizeof(NdpLinkLayerAddrOption) + sizeof(NdpMtuOption) + settings->prefixListLength * sizeof(NdpPrefixInfoOption) + settings->routeListLength * sizeof(NdpRouteInfoOption) + settings->contextListLength * sizeof(NdpContextOption); //Sanity check if((length + sizeof(Ipv6Header)) > IPV6_DEFAULT_MTU) return ERROR_FAILURE; //Allocate a memory buffer to hold the Router 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 Router Advertisement message message->type = ICMPV6_TYPE_ROUTER_ADV; message->code = 0; message->checksum = 0; message->curHopLimit = settings->curHopLimit; message->m = settings->managedFlag; message->o = settings->otherConfigFlag; message->h = settings->homeAgentFlag; message->prf = settings->preference; message->p = settings->proxyFlag; message->reserved = 0; message->routerLifetime = htons(routerLifetime); message->reachableTime = htonl(settings->reachableTime); message->retransTimer = htonl(settings->retransTimer); //If the Router Lifetime is zero, the preference value must be //set to zero by the sender if(routerLifetime == 0) message->prf = NDP_ROUTER_SEL_PREFERENCE_MEDIUM; //Length of the message, excluding any option length = sizeof(NdpRouterAdvMessage); #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 //A value of zero indicates that no MTU option is sent if(settings->linkMtu > 0) { NdpMtuOption mtuOption; //The MTU option specifies the recommended MTU for the link mtuOption.reserved = 0; mtuOption.mtu = htonl(settings->linkMtu); //Add MTU option ndpAddOption(message, &length, NDP_OPT_MTU, (uint8_t *) &mtuOption + sizeof(NdpOption), sizeof(NdpMtuOption) - sizeof(NdpOption)); } //Loop through the list of IPv6 prefixes for(i = 0; i < settings->prefixListLength; i++) { NdpPrefixInfoOption prefixInfoOption; //The Prefix Information option provide hosts with on-link //prefixes and prefixes for Address Autoconfiguration prefixInfoOption.prefixLength = settings->prefixList[i].length; prefixInfoOption.l = settings->prefixList[i].onLinkFlag; prefixInfoOption.a = settings->prefixList[i].autonomousFlag; prefixInfoOption.reserved1 = 0; prefixInfoOption.validLifetime = htonl(settings->prefixList[i].validLifetime); prefixInfoOption.preferredLifetime = htonl(settings->prefixList[i].preferredLifetime); prefixInfoOption.reserved2 = 0; prefixInfoOption.prefix = settings->prefixList[i].prefix; //Add Prefix Information option (PIO) ndpAddOption(message, &length, NDP_OPT_PREFIX_INFORMATION, (uint8_t *) &prefixInfoOption + sizeof(NdpOption), sizeof(NdpPrefixInfoOption) - sizeof(NdpOption)); } //Loop through the list of routes for(i = 0; i < settings->routeListLength; i++) { NdpRouteInfoOption routeInfoOption; //The Route Information option specifies prefixes that are //reachable via the router routeInfoOption.prefixLength = settings->routeList[i].length; routeInfoOption.reserved1 = 0; routeInfoOption.prf = settings->routeList[i].preference; routeInfoOption.reserved2 = 0; routeInfoOption.routeLifetime = htonl(settings->routeList[i].routeLifetime); routeInfoOption.prefix = settings->routeList[i].prefix; //Add Route Information option (RIO) ndpAddOption(message, &length, NDP_OPT_ROUTE_INFORMATION, (uint8_t *) &routeInfoOption + sizeof(NdpOption), sizeof(NdpRouteInfoOption) - sizeof(NdpOption)); } //Loop through the list of 6LoWPAN compression contexts for(i = 0; i < settings->contextListLength; i++) { NdpContextOption contextOption; //The 6LoWPAN Context option (6CO) carries prefix information for //LoWPAN header compression contextOption.contextLength = settings->contextList[i].length; contextOption.reserved1 = 0; contextOption.c = settings->contextList[i].compression; contextOption.cid = settings->contextList[i].cid; contextOption.reserved2 = 0; contextOption.validLifetime = htons(settings->contextList[i].validLifetime); contextOption.contextPrefix = settings->contextList[i].prefix; //Calculate the length of the option in bytes n = sizeof(NdpContextOption) - sizeof(Ipv6Addr) + (contextOption.contextLength / 8); //Add 6LoWPAN Context option (6CO) ndpAddOption(message, &length, NDP_OPT_6LOWPAN_CONTEXT, (uint8_t *) &contextOption + sizeof(NdpOption), n - sizeof(NdpOption)); } //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 Advertisement message (%" PRIuSIZE " bytes)...\r\n", length); //Dump message contents for debugging purpose ndpDumpRouterAdvMessage(message); //Send Router Advertisement message error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT); //Free previously allocated memory netBufferFree(buffer); //Return status code return error; } #endif