Webserver+3d print

Dependents:   Nucleo

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