Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/ipv6/ipv6.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file ipv6.c
 * @brief IPv6 (Internet Protocol Version 6)
 *
 * @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
 *
 * IP version 6 (IPv6) is a new version of the Internet Protocol, designed
 * as the successor to IP version 4 (IPv4). Refer to RFC 2460
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL IPV6_TRACE_LEVEL

//Dependencies
#include <string.h>
#include <ctype.h>
#include "core/net.h"
#include "ipv6/ipv6.h"
#include "ipv6/ipv6_frag.h"
#include "ipv6/ipv6_misc.h"
#include "ipv6/ipv6_pmtu.h"
#include "ipv6/ipv6_routing.h"
#include "ipv6/icmpv6.h"
#include "ipv6/mld.h"
#include "ipv6/ndp.h"
#include "ipv6/ndp_cache.h"
#include "ipv6/ndp_misc.h"
#include "ipv6/ndp_router_adv.h"
#include "ipv6/slaac.h"
#include "dhcpv6/dhcpv6_client.h"
#include "core/udp.h"
#include "core/tcp_fsm.h"
#include "core/raw_socket.h"
#include "debug.h"

//Check TCP/IP stack configuration
#if (IPV6_SUPPORT == ENABLED)

//Unspecified IPv6 address
const Ipv6Addr IPV6_UNSPECIFIED_ADDR =
   IPV6_ADDR(0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000);

//Loopback IPv6 address
const Ipv6Addr IPV6_LOOPBACK_ADDR =
   IPV6_ADDR(0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001);

//Link-local All-Nodes IPv6 address
const Ipv6Addr IPV6_LINK_LOCAL_ALL_NODES_ADDR =
   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001);

//Link-local All-Routers IPv6 address
const Ipv6Addr IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR =
   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002);

//Link-local IPv6 address prefix
const Ipv6Addr IPV6_LINK_LOCAL_ADDR_PREFIX =
   IPV6_ADDR(0xFE80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000);

//Solicited-node IPv6 address prefix
const Ipv6Addr IPV6_SOLICITED_NODE_ADDR_PREFIX =
   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0xFF00, 0x0000);


/**
 * @brief IPv6 related initialization
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t ipv6Init(NetInterface *interface)
{
   Ipv6Context *context;

   //Point to the IPv6 context
   context = &interface->ipv6Context;

   //Clear the IPv6 context
   memset(context, 0, sizeof(Ipv6Context));

   //Initialize interface specific variables
   context->linkMtu = interface->nicDriver->mtu;
   context->isRouter = FALSE;
   context->curHopLimit = IPV6_DEFAULT_HOP_LIMIT;

   //Multicast ICMPv6 Echo Request messages are allowed by default
   context->enableMulticastEchoReq = TRUE;

   //Initialize the list of IPv6 addresses assigned to the interface
   memset(context->addrList, 0, sizeof(context->addrList));
   //Initialize the Prefix List
   memset(context->prefixList, 0, sizeof(context->prefixList));
   //Initialize the Default Router List
   memset(context->routerList, 0, sizeof(context->routerList));
   //Initialize the list of DNS servers
   memset(context->dnsServerList, 0, sizeof(context->dnsServerList));
   //Initialize the multicast filter table
   memset(context->multicastFilter, 0, sizeof(context->multicastFilter));

#if (IPV6_FRAG_SUPPORT == ENABLED)
   //Identification field is used to identify fragments of an original IP datagram
   context->identification = 0;
   //Initialize the reassembly queue
   memset(context->fragQueue, 0, sizeof(context->fragQueue));
#endif

   //Successful initialization
   return NO_ERROR;
}


/**
 * @brief Change the MTU of a network interface
 * @param[in] interface Pointer to the desired network interface
 * @param[in] mtu Maximum transmit unit
 * @return Error code
 **/

error_t ipv6SetMtu(NetInterface *interface, size_t mtu)
{
   error_t error;

   //Check parameters
   if(interface == NULL)
      return ERROR_INVALID_PARAMETER;

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Make sure the specified MTU is greater than or equal to the minimum
   //IPv6 MTU and does not exceed the maximum MTU of the interface
   if(mtu >= IPV6_DEFAULT_MTU && mtu <= interface->nicDriver->mtu)
   {
      //Set the MTU to be used
      interface->ipv6Context.linkMtu = mtu;
      //Successful processing
      error = NO_ERROR;
   }
   else
   {
      //The specified MTU is not valid
      error = ERROR_OUT_OF_RANGE;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Return status code
   return error;
}


/**
 * @brief Retrieve the MTU for the specified interface
 * @param[in] interface Pointer to the desired network interface
 * @param[out] mtu Maximum transmit unit
 * @return Error code
 **/

error_t ipv6GetMtu(NetInterface *interface, size_t *mtu)
{
   //Check parameters
   if(interface == NULL || mtu == NULL)
      return ERROR_INVALID_PARAMETER;

   //Get exclusive access
   osAcquireMutex(&netMutex);
   //Return the current MTU value
   *mtu = interface->ipv6Context.linkMtu;
   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Assign link-local address
 * @param[in] interface Pointer to the desired network interface
 * @param[in] addr Link-local address
 * @return Error code
 **/

error_t ipv6SetLinkLocalAddr(NetInterface *interface, const Ipv6Addr *addr)
{
   error_t error;

   //Get exclusive access
   osAcquireMutex(&netMutex);

#if (NDP_SUPPORT == ENABLED)
   //Check whether Duplicate Address Detection should be performed
   if(interface->ndpContext.dupAddrDetectTransmits > 0)
   {
      //Use the link-local address as a tentative address
      error = ipv6SetAddr(interface, 0, addr, IPV6_ADDR_STATE_TENTATIVE,
         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
   }
   else
#endif
   {
      //The use of the link-local address is now unrestricted
      error = ipv6SetAddr(interface, 0, addr, IPV6_ADDR_STATE_PREFERRED,
         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Return status code
   return error;
}


/**
 * @brief Retrieve link-local address
 * @param[in] interface Pointer to the desired network interface
 * @param[out] addr link-local address
 * @return Error code
 **/

error_t ipv6GetLinkLocalAddr(NetInterface *interface, Ipv6Addr *addr)
{
   Ipv6AddrEntry *entry;

   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the corresponding address entry
   entry = &interface->ipv6Context.addrList[0];

   //Check whether the IPv6 address is valid
   if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
      entry->state == IPV6_ADDR_STATE_DEPRECATED)
   {
      //Get IPv6 address
      *addr = entry->addr;
   }
   else
   {
      //Return the unspecified address when no address has been assigned
      *addr = IPV6_UNSPECIFIED_ADDR;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Assign global address
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[in] addr Global address
 * @return Error code
 **/

error_t ipv6SetGlobalAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
{
   error_t error;

   //Get exclusive access
   osAcquireMutex(&netMutex);

#if (NDP_SUPPORT == ENABLED)
   //Check whether Duplicate Address Detection should be performed
   if(interface->ndpContext.dupAddrDetectTransmits > 0)
   {
      //Use the global address as a tentative address
      error = ipv6SetAddr(interface, index + 1, addr, IPV6_ADDR_STATE_TENTATIVE,
         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
   }
   else
#endif
   {
      //The use of the global address is now unrestricted
      error = ipv6SetAddr(interface, index + 1, addr, IPV6_ADDR_STATE_PREFERRED,
         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Return status code
   return error;
}


/**
 * @brief Retrieve global address
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[out] addr Global address
 * @return Error code
 **/

error_t ipv6GetGlobalAddr(NetInterface *interface, uint_t index, Ipv6Addr *addr)
{
   Ipv6AddrEntry *entry;

   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if((index + 1) >= IPV6_ADDR_LIST_SIZE)
   {
      //Return the unspecified address when the index is out of range
      *addr = IPV6_UNSPECIFIED_ADDR;
      //Report an error
      return ERROR_OUT_OF_RANGE;
   }

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the corresponding address entry
   entry = &interface->ipv6Context.addrList[index + 1];

   //Check whether the IPv6 address is valid
   if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
      entry->state == IPV6_ADDR_STATE_DEPRECATED)
   {
      //Get IPv6 address
      *addr = entry->addr;
   }
   else
   {
      //Return the unspecified address when no address has been assigned
      *addr = IPV6_UNSPECIFIED_ADDR;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Assign anycast address
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[in] addr Anycast address
 * @return Error code
 **/

error_t ipv6SetAnycastAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
{
   error_t error;
   Ipv6Addr *anycastAddrList;
   Ipv6Addr solicitedNodeAddr;

   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_ANYCAST_ADDR_LIST_SIZE)
      return ERROR_OUT_OF_RANGE;

   //The IPv6 address must be a valid unicast address
   if(ipv6IsMulticastAddr(addr))
      return ERROR_INVALID_ADDRESS;

   //Initialize status code
   error = NO_ERROR;

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the list of anycast addresses assigned to the interface
   anycastAddrList = interface->ipv6Context.anycastAddrList;

   //Check whether an anycast address is already assigned
   if(!ipv6CompAddr(&anycastAddrList[index], &IPV6_UNSPECIFIED_ADDR))
   {
      //Ethernet interface?
      if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
      {
         //Form the Solicited-Node address
         ipv6ComputeSolicitedNodeAddr(&anycastAddrList[index], &solicitedNodeAddr);
         //Leave the Solicited-Node multicast group
         ipv6LeaveMulticastGroup(interface, &solicitedNodeAddr);
      }
   }

   //Assign the specified anycast address to the interface
   anycastAddrList[index] = *addr;

   //Check whether the anycast address is valid
   if(!ipv6CompAddr(addr, &IPV6_UNSPECIFIED_ADDR))
   {
      //Ethernet interface?
      if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
      {
         //Form the Solicited-Node address for the link-local address
         ipv6ComputeSolicitedNodeAddr(addr, &solicitedNodeAddr);
         //Join the Solicited-Node multicast group for each assigned address
         error = ipv6JoinMulticastGroup(interface, &solicitedNodeAddr);
      }
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Return status code
   return error;
}


/**
 * @brief Retrieve anycast address
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[out] addr Anycast address
 * @return Error code
 **/

error_t ipv6GetAnycastAddr(NetInterface *interface, uint_t index, Ipv6Addr *addr)
{
   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_ANYCAST_ADDR_LIST_SIZE)
   {
      //Return the unspecified address when the index is out of range
      *addr = IPV6_UNSPECIFIED_ADDR;
      //Report an error
      return ERROR_OUT_OF_RANGE;
   }

   //Get exclusive access
   osAcquireMutex(&netMutex);
   //Return the corresponding address entry
   *addr = interface->ipv6Context.anycastAddrList[index];
   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Configure IPv6 prefix
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[in] prefix IPv6 prefix
 * @param[in] length The number of leading bits in the prefix that are valid
 **/

error_t ipv6SetPrefix(NetInterface *interface,
   uint_t index, const Ipv6Addr *prefix, uint_t length)
{
   Ipv6PrefixEntry *entry;

   //Check parameters
   if(interface == NULL || prefix == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_PREFIX_LIST_SIZE)
      return ERROR_OUT_OF_RANGE;

   //Make sure the prefix length is valid
   if(length >= 128)
      return ERROR_INVALID_PARAMETER;

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the corresponding entry
   entry = &interface->ipv6Context.prefixList[index];

   //Set up IPv6 prefix
   entry->prefix = *prefix;
   entry->prefixLength = length;

   //Check prefix length
   if(length > 0)
   {
      //Manually assigned prefixes have infinite lifetime
      entry->validLifetime = INFINITE_DELAY;
      entry->preferredLifetime = INFINITE_DELAY;
      entry->permanent = TRUE;
   }
   else
   {
      //Immediately time-out the entry
      entry->validLifetime = 0;
      entry->preferredLifetime = 0;
      entry->permanent = FALSE;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Retrieve IPv6 prefix
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[out] prefix IPv6 prefix
 * @param[out] length The number of leading bits in the prefix that are valid
 * @return Error code
 **/

error_t ipv6GetPrefix(NetInterface *interface,
   uint_t index, Ipv6Addr *prefix, uint_t *length)
{
   Ipv6PrefixEntry *entry;

   //Check parameters
   if(interface == NULL || prefix == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_PREFIX_LIST_SIZE)
   {
      //Return the ::/0 prefix when the index is out of range
      *prefix = IPV6_UNSPECIFIED_ADDR;
      *length = 0;
      //Report an error
      return ERROR_OUT_OF_RANGE;
   }

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the corresponding entry
   entry = &interface->ipv6Context.prefixList[index];

   //Check whether the prefix is valid
   if(entry->validLifetime > 0)
   {
      //Get IPv6 prefix
      *prefix = entry->prefix;
      *length = entry->prefixLength;
   }
   else
   {
      //Return the ::/0 prefix when the valid lifetime has expired
      *prefix = IPV6_UNSPECIFIED_ADDR;
      *length = 0;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Configure default router
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[in] addr Default router address
 * @return Error code
 **/

error_t ipv6SetDefaultRouter(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
{
   Ipv6RouterEntry *entry;

   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_ROUTER_LIST_SIZE)
      return ERROR_OUT_OF_RANGE;

   //The IPv6 address must be a valid unicast address
   if(ipv6IsMulticastAddr(addr))
      return ERROR_INVALID_ADDRESS;

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the corresponding entry
   entry = &interface->ipv6Context.routerList[index];

   //Set up router address
   entry->addr = *addr;

   //Valid IPv6 address?
   if(!ipv6CompAddr(addr, &IPV6_UNSPECIFIED_ADDR))
   {
      //Manually assigned routers have infinite lifetime
      entry->lifetime = INFINITE_DELAY;
      entry->permanent = TRUE;
   }
   else
   {
      //Immediately time-out the entry
      entry->lifetime = 0;
      entry->permanent = FALSE;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Retrieve default router
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index Zero-based index
 * @param[out] addr Default router address
 * @return Error code
 **/

error_t ipv6GetDefaultRouter(NetInterface *interface, uint_t index, Ipv6Addr *addr)
{
   Ipv6RouterEntry *entry;

   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_ROUTER_LIST_SIZE)
   {
      //Return the unspecified address when the index is out of range
      *addr = IPV6_UNSPECIFIED_ADDR;
      //Report an error
      return ERROR_OUT_OF_RANGE;
   }

   //Get exclusive access
   osAcquireMutex(&netMutex);

   //Point to the corresponding entry
   entry = &interface->ipv6Context.routerList[index];

   //Check the lifetime of the entry
   if(entry->lifetime > 0)
   {
      //Get router address
      *addr = entry->addr;
   }
   else
   {
      //Return the unspecified address when the lifetime has expired
      *addr = IPV6_UNSPECIFIED_ADDR;
   }

   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Configure DNS server
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index This parameter selects between the primary and secondary DNS server
 * @param[in] addr DNS server address
 * @return Error code
 **/

error_t ipv6SetDnsServer(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
{
   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_DNS_SERVER_LIST_SIZE)
      return ERROR_OUT_OF_RANGE;

   //The IPv6 address must be a valid unicast address
   if(ipv6IsMulticastAddr(addr))
      return ERROR_INVALID_ADDRESS;

   //Get exclusive access
   osAcquireMutex(&netMutex);
   //Set up DNS server address
   interface->ipv6Context.dnsServerList[index] = *addr;
   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Retrieve DNS server
 * @param[in] interface Pointer to the desired network interface
 * @param[in] index This parameter selects between the primary and secondary DNS server
 * @param[out] addr DNS server address
 * @return Error code
 **/

error_t ipv6GetDnsServer(NetInterface *interface, uint_t index, Ipv6Addr *addr)
{
   //Check parameters
   if(interface == NULL || addr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure that the index is valid
   if(index >= IPV6_DNS_SERVER_LIST_SIZE)
   {
      //Return the unspecified address when the index is out of range
      *addr = IPV6_UNSPECIFIED_ADDR;
      //Report an error
      return ERROR_OUT_OF_RANGE;
   }

   //Get exclusive access
   osAcquireMutex(&netMutex);
   //Get DNS server address
   *addr = interface->ipv6Context.dnsServerList[index];
   //Release exclusive access
   osReleaseMutex(&netMutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Callback function for link change event
 * @param[in] interface Underlying network interface
 **/

void ipv6LinkChangeEvent(NetInterface *interface)
{
   uint_t i;
   Ipv6Context *context;
   Ipv6AddrEntry *entry;

   //Point to the IPv6 context
   context = &interface->ipv6Context;

   //Restore default parameters
   context->linkMtu = interface->nicDriver->mtu;
   context->curHopLimit = IPV6_DEFAULT_HOP_LIMIT;

   //Clear the list of IPv6 addresses
   ipv6FlushAddrList(interface);
   //Clear the Prefix List
   ipv6FlushPrefixList(interface);
   //Clear the Default Router List
   ipv6FlushDefaultRouterList(interface);

#if (IPV6_FRAG_SUPPORT == ENABLED)
   //Flush the reassembly queue
   ipv6FlushFragQueue(interface);
#endif

#if (MLD_SUPPORT == ENABLED)
   //Notify MLD of link state changes
   mldLinkChangeEvent(interface);
#endif

#if (NDP_SUPPORT == ENABLED)
   //Notify NDP of link state changes
   ndpLinkChangeEvent(interface);
#endif

#if (NDP_ROUTER_ADV_SUPPORT == ENABLED)
   //Notify the RA service of link state changes
   ndpRouterAdvLinkChangeEvent(interface->ndpRouterAdvContext);
#endif

#if (SLAAC_SUPPORT == ENABLED)
   //Notify the SLAAC service of link state changes
   slaacLinkChangeEvent(interface->slaacContext);
#endif

#if (DHCPV6_CLIENT_SUPPORT == ENABLED)
   //Notify the DHCPv6 client of link state changes
   dhcpv6ClientLinkChangeEvent(interface->dhcpv6ClientContext);
#endif

   //Go through the list of IPv6 addresses
   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
   {
      //Point to the current entry
      entry = &context->addrList[i];

      //Check whether the IPv6 address has been manually assigned
      if(entry->permanent)
      {
#if (NDP_SUPPORT == ENABLED)
         //Check whether Duplicate Address Detection should be performed
         if(interface->ndpContext.dupAddrDetectTransmits > 0)
         {
            //Use the IPv6 address as a tentative address
            ipv6SetAddr(interface, i, &entry->addr, IPV6_ADDR_STATE_TENTATIVE,
               NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
         }
         else
#endif
         {
            //The use of the IPv6 address is now unrestricted
            ipv6SetAddr(interface, i, &entry->addr, IPV6_ADDR_STATE_PREFERRED,
               NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
         }
      }
   }
}


/**
 * @brief Incoming IPv6 packet processing
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer that holds the incoming IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 **/

void ipv6ProcessPacket(NetInterface *interface,
   NetBuffer *ipPacket, size_t ipPacketOffset)
{
   error_t error;
   size_t i;
   size_t length;
   size_t nextHeaderOffset;
   uint8_t *type;
   Ipv6Header *ipHeader;
   IpPseudoHeader pseudoHeader;

   //Retrieve the length of the IPv6 packet
   length = netBufferGetLength(ipPacket);

   //Ensure the packet length is greater than 40 bytes
   if(length < sizeof(Ipv6Header))
      return;

   //Point to the IPv6 header
   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
   //Sanity check
   if(ipHeader == NULL)
      return;

   //Debug message
   TRACE_INFO("IPv6 packet received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump IPv6 header contents for debugging purpose
   ipv6DumpHeader(ipHeader);

   //Check IP version number
   if(ipHeader->version != IPV6_VERSION)
      return;
   //Ensure the payload length is correct before processing the packet
   if(ntohs(ipHeader->payloadLength) > (length - sizeof(Ipv6Header)))
      return;
   //Source address filtering
   if(ipv6CheckSourceAddr(interface, &ipHeader->srcAddr))
      return;

#if defined(IPV6_PACKET_FORWARD_HOOK)
   IPV6_PACKET_FORWARD_HOOK(interface, ipPacket, ipPacketOffset);
#else
   //Destination address filtering
   if(ipv6CheckDestAddr(interface, &ipHeader->destAddr))
   {
#if(IPV6_ROUTING_SUPPORT == ENABLED)
      //Forward the packet according to the routing table
      ipv6ForwardPacket(interface, ipPacket, ipPacketOffset);
#endif
      //We are done
      return;
   }
#endif

   //Calculate the effective length of the multi-part buffer
   length = ipPacketOffset + sizeof(Ipv6Header) +
      ntohs(ipHeader->payloadLength);

   //Adjust the length of the multi-part buffer if necessary
   netBufferSetLength(ipPacket, length);

   //Form the IPv6 pseudo header
   pseudoHeader.length = sizeof(Ipv6PseudoHeader);
   pseudoHeader.ipv6Data.srcAddr = ipHeader->srcAddr;
   pseudoHeader.ipv6Data.destAddr = ipHeader->destAddr;
   pseudoHeader.ipv6Data.reserved = 0;

   //Keep track of Next Header field
   nextHeaderOffset = ipPacketOffset + &ipHeader->nextHeader -
      (uint8_t *) ipHeader;

   //Point to the first extension header
   i = ipPacketOffset + sizeof(Ipv6Header);

   //Parse extension headers
   while(i < length)
   {
      //Retrieve the Next Header field of preceding header
      type = netBufferAt(ipPacket, nextHeaderOffset);
      //Sanity check
      if(type == NULL)
         return;

      //Update IPv6 pseudo header
      pseudoHeader.ipv6Data.length = htonl(length - i);
      pseudoHeader.ipv6Data.nextHeader = *type;

      //Each extension header is identified by the
      //Next Header field of the preceding header
      switch(*type)
      {
      //Hop-by-Hop Options header?
      case IPV6_HOP_BY_HOP_OPT_HEADER:
         //Parse current extension header
         error = ipv6ParseHopByHopOptHeader(interface,
            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
         //Continue processing
         break;

      //Destination Options header?
      case IPV6_DEST_OPT_HEADER:
         //Parse current extension header
         error = ipv6ParseDestOptHeader(interface,
            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
         //Continue processing
         break;

      //Routing header?
      case IPV6_ROUTING_HEADER:
         //Parse current extension header
         error = ipv6ParseRoutingHeader(interface,
            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
         //Continue processing
         break;

      //Fragment header?
      case IPV6_FRAGMENT_HEADER:
#if (IPV6_FRAG_SUPPORT == ENABLED)
         //Parse current extension header
         ipv6ParseFragmentHeader(interface,
            ipPacket, ipPacketOffset, i, nextHeaderOffset);
#endif
         //Exit immediately
         return;

      //Authentication header?
      case IPV6_AUTH_HEADER:
         //Parse current extension header
         error = ipv6ParseAuthHeader(interface,
            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
         //Continue processing
         break;

      //Encapsulating Security Payload header?
      case IPV6_ESP_HEADER:
         //Parse current extension header
         error = ipv6ParseEspHeader(interface,
            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
         //Continue processing
         break;

      //ICMPv6 header?
      case IPV6_ICMPV6_HEADER:
         //Process incoming ICMPv6 message
         icmpv6ProcessMessage(interface, &pseudoHeader.ipv6Data,
            ipPacket, i, ipHeader->hopLimit);

#if (RAW_SOCKET_SUPPORT == ENABLED)
         //Packets addressed to the tentative address should be silently discarded
         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
         {
            //Allow raw sockets to process ICMPv6 messages
            rawSocketProcessIpPacket(interface, &pseudoHeader, ipPacket, i);
         }
#endif
         //Exit immediately
         return;

#if (TCP_SUPPORT == ENABLED)
      //TCP header?
      case IPV6_TCP_HEADER:
         //Packets addressed to the tentative address should be silently discarded
         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
         {
            //Process incoming TCP segment
            tcpProcessSegment(interface, &pseudoHeader, ipPacket, i);
         }
         //Exit immediately
         return;
#endif

#if (UDP_SUPPORT == ENABLED)
      //UDP header?
      case IPV6_UDP_HEADER:
         //Packets addressed to the tentative address should be silently discarded
         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
         {
            //Process incoming UDP datagram
            error = udpProcessDatagram(interface, &pseudoHeader, ipPacket, i);

            //Unreachable port?
            if(error == ERROR_PORT_UNREACHABLE)
            {
               //A destination node should originate a Destination Unreachable
               //message with Code 4 in response to a packet for which the
               //transport protocol has no listener
               icmpv6SendErrorMessage(interface, ICMPV6_TYPE_DEST_UNREACHABLE,
                  ICMPV6_CODE_PORT_UNREACHABLE, 0, ipPacket, ipPacketOffset);
            }
         }
         //Exit immediately
         return;
#endif

      //No next header?
      case IPV6_NO_NEXT_HEADER:
         //If the payload length field of the IPv6 header indicates the presence of
         //octets past the end of the previous header, these octets must be ignored
         return;

      //Unrecognized header type?
      default:
         //Debug message
         TRACE_WARNING("Unrecognized Next Header type\r\n");

         //Packets addressed to the tentative address should be silently discarded
         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
         {
            //Compute the offset of the unrecognized Next Header field within the packet
            size_t n = nextHeaderOffset - ipPacketOffset;

            //Send an ICMP Parameter Problem message
            icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
               ICMPV6_CODE_UNKNOWN_NEXT_HEADER, n, ipPacket, ipPacketOffset);
         }

         //Discard incoming packet
         return;
      }

      //Any error while processing the current extension header?
      if(error)
         return;
   }
}


/**
 * @brief Parse Hop-by-Hop Options header
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 * @param[in,out] headerOffset Offset to the Hop-by-Hop Options header
 * @param[in,out] nextHeaderOffset Offset to the Next Header field
 * @brief Error code
 **/

error_t ipv6ParseHopByHopOptHeader(NetInterface *interface, const NetBuffer *ipPacket,
   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
{
   error_t error;
   size_t n;
   size_t length;
   size_t headerLength;
   Ipv6HopByHopOptHeader *header;

   //Remaining bytes to process in the IPv6 packet
   length = netBufferGetLength(ipPacket) - *headerOffset;

   //Make sure the extension header is valid
   if(length < sizeof(Ipv6HopByHopOptHeader))
      return ERROR_INVALID_HEADER;

   //Point to the Hop-by-Hop Options header
   header = netBufferAt(ipPacket, *headerOffset);
   //Sanity check
   if(header == NULL)
      return ERROR_FAILURE;

   //Calculate the length of the entire header
   headerLength = (header->hdrExtLen * 8) + 8;

   //Check header length
   if(headerLength > length)
      return ERROR_INVALID_HEADER;

   //Debug message
   TRACE_DEBUG("  Hop-by-Hop Options header\r\n");

   //The Hop-by-Hop Options header, when present, must immediately follow
   //the IPv6 header
   if(*headerOffset != (ipPacketOffset + sizeof(Ipv6Header)))
   {
      //Compute the offset of the unrecognized Next Header field within the packet
      n = *nextHeaderOffset - ipPacketOffset;

      //Send an ICMP Parameter Problem message to the source of the packet
      icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
         ICMPV6_CODE_UNKNOWN_NEXT_HEADER, n, ipPacket, ipPacketOffset);

      //Discard incoming packet
      return ERROR_INVALID_HEADER;
   }

   //Compute the length of the Options field
   n = headerLength - sizeof(Ipv6HopByHopOptHeader);

   //Parse options
   error = ipv6ParseOptions(interface, ipPacket, ipPacketOffset,
      *headerOffset + sizeof(Ipv6HopByHopOptHeader), n);

   //Any error to report?
   if(error)
      return error;

   //Keep track of Next Header field
   *nextHeaderOffset = *headerOffset + &header->nextHeader - (uint8_t *) header;
   //Point to the next extension header
   *headerOffset += headerLength;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse Destination Options header
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 * @param[in,out] headerOffset Offset to the Destination Options header
 * @param[in,out] nextHeaderOffset Offset to the Next Header field
 * @brief Error code
 **/

error_t ipv6ParseDestOptHeader(NetInterface *interface, const NetBuffer *ipPacket,
   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
{
   error_t error;
   size_t n;
   size_t length;
   size_t headerLength;
   Ipv6DestOptHeader *header;

   //Remaining bytes to process in the IPv6 packet
   length = netBufferGetLength(ipPacket) - *headerOffset;

   //Make sure the extension header is valid
   if(length < sizeof(Ipv6DestOptHeader))
      return ERROR_INVALID_HEADER;

   //Point to the Destination Options header
   header = netBufferAt(ipPacket, *headerOffset);
   //Sanity check
   if(header == NULL)
      return ERROR_FAILURE;

   //Calculate the length of the entire header
   headerLength = (header->hdrExtLen * 8) + 8;

   //Check header length
   if(headerLength > length)
      return ERROR_INVALID_HEADER;

   //Debug message
   TRACE_DEBUG("  Destination Options header\r\n");

   //Compute the length of the Options field
   n = headerLength - sizeof(Ipv6DestOptHeader);

   //Parse options
   error = ipv6ParseOptions(interface, ipPacket, ipPacketOffset,
      *headerOffset + sizeof(Ipv6DestOptHeader), n);

   //Any error to report?
   if(error)
      return error;

   //Keep track of Next Header field
   *nextHeaderOffset = *headerOffset + &header->nextHeader - (uint8_t *) header;
   //Point to the next extension header
   *headerOffset += headerLength;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse Routing header
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 * @param[in,out] headerOffset Offset to the Routing header
 * @param[in,out] nextHeaderOffset Offset to the Next Header field
 * @brief Error code
 **/

error_t ipv6ParseRoutingHeader(NetInterface *interface, const NetBuffer *ipPacket,
   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
{
   size_t n;
   size_t length;
   size_t headerLength;
   Ipv6RoutingHeader *header;

   //Remaining bytes to process in the IPv6 packet
   length = netBufferGetLength(ipPacket) - *headerOffset;

   //Make sure the extension header is valid
   if(length < sizeof(Ipv6RoutingHeader))
      return ERROR_INVALID_HEADER;

   //Point to the Routing header
   header = netBufferAt(ipPacket, *headerOffset);
   //Sanity check
   if(header == NULL)
      return ERROR_FAILURE;

   //Calculate the length of the entire header
   headerLength = (header->hdrExtLen * 8) + 8;

   //Check header length
   if(headerLength > length)
      return ERROR_INVALID_HEADER;

   //Debug message
   TRACE_DEBUG("  Routing header\r\n");

   //If, while processing a received packet, a node encounters a Routing
   //header with an unrecognized Routing Type value, the required behavior
   //of the node depends on the value of the Segments Left field
   if(header->segmentsLeft != 0)
   {
      //Retrieve the offset of the Routing header within the packet
      n = *headerOffset - ipPacketOffset;
      //Compute the exact offset of the Routing Type field
      n += (uint8_t *) &header->routingType - (uint8_t *) header;

      //If Segments Left is non-zero, send an ICMP Parameter Problem,
      //Code 0, message to the packet's Source Address, pointing to
      //the unrecognized Routing Type
      icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
         ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset);

      //The node must discard the packet
      return ERROR_INVALID_TYPE;
   }

   //Keep track of Next Header field
   *nextHeaderOffset = *headerOffset + &header->nextHeader - (uint8_t *) header;
   //Point to the next extension header
   *headerOffset += headerLength;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse Authentication header
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 * @param[in,out] headerOffset Offset to the Authentication header
 * @param[in,out] nextHeaderOffset Offset to the Next Header field
 * @brief Error code
 **/

error_t ipv6ParseAuthHeader(NetInterface *interface, const NetBuffer *ipPacket,
   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
{
   //Debug message
   TRACE_DEBUG("  Authentication header\r\n");
   //Authentication not supported
   return ERROR_FAILURE;
}


/**
 * @brief Parse Encapsulating Security Payload header
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 * @param[in,out] headerOffset Offset to the Encapsulating Security Payload header
 * @param[in,out] nextHeaderOffset Offset to the Next Header field
 * @brief Error code
 **/

error_t ipv6ParseEspHeader(NetInterface *interface, const NetBuffer *ipPacket,
   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
{
   //Debug message
   TRACE_DEBUG("  Encapsulating Security Payload header\r\n");
   //Authentication not supported
   return ERROR_FAILURE;
}


/**
 * @brief Parse IPv6 options
 * @param[in] interface Underlying network interface
 * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
 * @param[in] optOffset Offset to the first byte of the Options field
 * @param[in] optLength Length of the Options field
 * @brief Error code
 **/

error_t ipv6ParseOptions(NetInterface *interface, const NetBuffer *ipPacket,
   size_t ipPacketOffset, size_t optOffset, size_t optLength)
{
   size_t i;
   size_t n;
   uint8_t type;
   uint8_t action;
   uint8_t *options;
   Ipv6Option *option;
   Ipv6Header *ipHeader;

   //Point to the first byte of the Options field
   options = netBufferAt(ipPacket, optOffset);

   //Sanity check
   if(options == NULL)
      return ERROR_FAILURE;

   //Parse options
   for(i = 0; i < optLength; )
   {
      //Point to the current option
      option = (Ipv6Option *) (options + i);
      //Get option type
      type = option->type & IPV6_OPTION_TYPE_MASK;

      //Pad1 option?
      if(type == IPV6_OPTION_TYPE_PAD1)
      {
         //Advance data pointer
         i++;
      }
      //PadN option?
      else if(type == IPV6_OPTION_TYPE_PADN)
      {
         //Malformed IPv6 packet?
         if((i + sizeof(Ipv6Option)) > optLength)
            return ERROR_INVALID_LENGTH;

         //Advance data pointer
         i += sizeof(Ipv6Option) + option->length;
      }
      //Unrecognized option?
      else
      {
         //Point to the IPv6 header
         ipHeader = netBufferAt(ipPacket, ipPacketOffset);

         //Sanity check
         if(ipHeader == NULL)
            return ERROR_FAILURE;

         //Get the value of the highest-order two bits
         action = option->type & IPV6_ACTION_MASK;

         //The highest-order two bits specify the action that must be taken
         //if the processing IPv6 node does not recognize the option type
         if(action == IPV6_ACTION_SKIP_OPTION)
         {
            //Skip over this option and continue processing the header
         }
         else if(action == IPV6_ACTION_DISCARD_PACKET)
         {
            //Discard the packet
            return ERROR_INVALID_OPTION;
         }
         else if(action == IPV6_ACTION_SEND_ICMP_ERROR_ALL)
         {
            //Calculate the octet offset within the invoking packet
            //where the error was detected
            n = optOffset + i - ipPacketOffset;

            //Send an ICMP Parameter Problem message to the source of the
            //packet, regardless of whether or not the destination address
            //was a multicast address
            icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
               ICMPV6_CODE_UNKNOWN_IPV6_OPTION, n, ipPacket, ipPacketOffset);

            //Discard the packet
            return ERROR_INVALID_OPTION;
         }
         else if(action == IPV6_ACTION_SEND_ICMP_ERROR_UNI)
         {
            //Send an ICMP Parameter Problem message to the source of the
            //packet, only if the destination address was not a multicast
            //address
            if(!ipv6IsMulticastAddr(&ipHeader->destAddr))
            {
               //Calculate the octet offset within the invoking packet
               //where the error was detected
               n = optOffset + i - ipPacketOffset;

               //Send the ICMP Parameter Problem message
               icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
                  ICMPV6_CODE_UNKNOWN_IPV6_OPTION, n, ipPacket, ipPacketOffset);
            }

            //Discard the packet
            return ERROR_INVALID_OPTION;
         }

         //Malformed IPv6 packet?
         if((i + sizeof(Ipv6Option)) > optLength)
            return ERROR_INVALID_LENGTH;

         //Advance data pointer
         i += sizeof(Ipv6Option) + option->length;
      }
   }

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Send an IPv6 datagram
 * @param[in] interface Underlying network interface
 * @param[in] pseudoHeader IPv6 pseudo header
 * @param[in] buffer Multi-part buffer containing the payload
 * @param[in] offset Offset to the first byte of the payload
 * @param[in] hopLimit Hop Limit value. Default value is used when this parameter is zero
 * @return Error code
 **/

error_t ipv6SendDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   NetBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   error_t error;
   size_t length;
   size_t pathMtu;

   //Retrieve the length of payload
   length = netBufferGetLength(buffer) - offset;

   //Check whether the Hop Limit value is zero
   if(hopLimit == 0)
   {
      //Use default Hop Limit value
      hopLimit = interface->ipv6Context.curHopLimit;
   }

#if (IPV6_PMTU_SUPPORT == ENABLED)
   //Retrieve the PMTU for the specified destination address
   pathMtu = ipv6GetPathMtu(interface, &pseudoHeader->destAddr);

   //The PMTU should not exceed the MTU of the first-hop link
   if(pathMtu > interface->ipv6Context.linkMtu)
      pathMtu = interface->ipv6Context.linkMtu;
#else
   //The PMTU value for the path is assumed to be the MTU of the first-hop link
   pathMtu = interface->ipv6Context.linkMtu;
#endif

   //If the payload is smaller than the PMTU then no fragmentation is needed
   if((length + sizeof(Ipv6Header)) <= pathMtu)
   {
      //Send data as is
      error = ipv6SendPacket(interface, pseudoHeader,
         0, 0, buffer, offset, hopLimit);
   }
   //If the payload length exceeds the PMTU then the device must fragment the data
   else
   {
#if (IPV6_FRAG_SUPPORT == ENABLED)
      //Fragment IP datagram into smaller packets
      error = ipv6FragmentDatagram(interface, pseudoHeader,
         buffer, offset, pathMtu, hopLimit);
#else
      //Fragmentation is not supported
      error = ERROR_MESSAGE_TOO_LONG;
#endif
   }

   //Return status code
   return error;
}


/**
 * @brief Send an IPv6 packet
 * @param[in] interface Underlying network interface
 * @param[in] pseudoHeader IPv6 pseudo header
 * @param[in] fragId Fragment identification field
 * @param[in] fragOffset Fragment offset field
 * @param[in] buffer Multi-part buffer containing the payload
 * @param[in] offset Offset to the first byte of the payload
 * @param[in] hopLimit Hop Limit value
 * @return Error code
 **/

error_t ipv6SendPacket(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   uint32_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   error_t error;
   size_t length;
   Ipv6Header *packet;

   //Calculate the length of the payload
   length = netBufferGetLength(buffer) - offset;

   //Add Fragment header?
   if(fragOffset != 0)
   {
      Ipv6FragmentHeader *header;

      //Is there enough space for the IPv6 header and the Fragment header?
      if(offset < (sizeof(Ipv6Header) + sizeof(Ipv6FragmentHeader)))
         return ERROR_INVALID_PARAMETER;

      //Make room for the Fragment header
      offset -= sizeof(Ipv6FragmentHeader);
      length += sizeof(Ipv6FragmentHeader);

      //Point to the Fragment header
      header = netBufferAt(buffer, offset);
      //Format the Fragment header
      header->nextHeader = pseudoHeader->nextHeader;
      header->reserved = 0;
      header->fragmentOffset = htons(fragOffset);
      header->identification = htonl(fragId);

      //Make room for the IPv6 header
      offset -= sizeof(Ipv6Header);
      length += sizeof(Ipv6Header);

      //Point to the IPv6 header
      packet = netBufferAt(buffer, offset);
      //Properly set the Next Header field
      packet->nextHeader = IPV6_FRAGMENT_HEADER;
   }
   else
   {
      //Is there enough space for the IPv6 header?
      if(offset < sizeof(Ipv6Header))
         return ERROR_INVALID_PARAMETER;

      //Make room for the IPv6 header
      offset -= sizeof(Ipv6Header);
      length += sizeof(Ipv6Header);

      //Point to the IPv6 header
      packet = netBufferAt(buffer, offset);
      //Properly set the Next Header field
      packet->nextHeader = pseudoHeader->nextHeader;
   }

   //Format IPv6 header
   packet->version = IPV6_VERSION;
   packet->trafficClassH = 0;
   packet->trafficClassL = 0;
   packet->flowLabelH = 0;
   packet->flowLabelL = 0;
   packet->payloadLength = htons(length - sizeof(Ipv6Header));
   packet->hopLimit = hopLimit;
   packet->srcAddr = pseudoHeader->srcAddr;
   packet->destAddr = pseudoHeader->destAddr;

   //Check whether the source address is acceptable
   error = ipv6CheckSourceAddr(interface, &pseudoHeader->srcAddr);
   //Invalid source address?
   if(error)
      return error;

   //Destination IPv6 address is the unspecified address?
   if(ipv6CompAddr(&pseudoHeader->destAddr, &IPV6_UNSPECIFIED_ADDR))
   {
      //Destination address is not acceptable
      return ERROR_INVALID_ADDRESS;
   }
   //Destination address is the loopback address?
   else if(ipv6CompAddr(&pseudoHeader->destAddr, &IPV6_LOOPBACK_ADDR))
   {
      //Not yet implemented...
      return ERROR_NOT_IMPLEMENTED;
   }

#if (ETH_SUPPORT == ENABLED)
   //Ethernet interface?
   if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
   {
      Ipv6Addr destIpAddr;
      MacAddr destMacAddr;
      NdpDestCacheEntry *entry;

      //When the sending node has a packet to send, it first examines
      //the Destination Cache
      entry = ndpFindDestCacheEntry(interface, &pseudoHeader->destAddr);

      //Check whether a matching entry exists
      if(entry != NULL)
      {
         //Retrieve the address of the next-hop
         destIpAddr = entry->nextHop;
         //Update timestamp
         entry->timestamp = osGetSystemTime();
         //No error to report
         error = NO_ERROR;
      }
      else
      {
         //Perform next-hop determination
         error = ndpSelectNextHop(interface,
            &pseudoHeader->destAddr, NULL, &destIpAddr);

         //Check status code
         if(error == NO_ERROR)
         {
            //Create a new Destination Cache entry
            entry = ndpCreateDestCacheEntry(interface);

            //Destination cache entry successfully created?
            if(entry != NULL)
            {
               //Destination address
               entry->destAddr = pseudoHeader->destAddr;
               //Address of the next hop
               entry->nextHop = destIpAddr;

               //Initially, the PMTU value for a path is assumed to be
               //the MTU of the first-hop link
               entry->pathMtu = interface->ipv6Context.linkMtu;

               //Set timestamp
               entry->timestamp = osGetSystemTime();
            }
         }
      }

      //Successful next-hop determination?
      if(error == NO_ERROR)
      {
         //Destination IPv6 address is a multicast address?
         if(ipv6IsMulticastAddr(&destIpAddr))
         {
            //Map IPv6 multicast address to MAC-layer multicast address
            error = ipv6MapMulticastAddrToMac(&destIpAddr, &destMacAddr);
         }
         else
         {
            //Resolve host address using Neighbor Discovery protocol
            error = ndpResolve(interface, &destIpAddr, &destMacAddr);
         }

         //Successful address resolution?
         if(error == NO_ERROR)
         {
            //Debug message
            TRACE_INFO("Sending IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
            //Dump IP header contents for debugging purpose
            ipv6DumpHeader(packet);

            //Send Ethernet frame
            error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV6);
         }
         //Address resolution is in progress?
         else if(error == ERROR_IN_PROGRESS)
         {
            //Debug message
            TRACE_INFO("Enqueuing IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
            //Dump IP header contents for debugging purpose
            ipv6DumpHeader(packet);

            //Enqueue packets waiting for address resolution
            error = ndpEnqueuePacket(NULL, interface, &destIpAddr, buffer, offset);
         }
         //Address resolution failed?
         else
         {
            //Debug message
            TRACE_WARNING("Cannot map IPv6 address to Ethernet address!\r\n");
         }
      }
   }
   else
#endif
#if (PPP_SUPPORT == ENABLED)
   //PPP interface?
   if(interface->nicDriver->type == NIC_TYPE_PPP)
   {
      //Debug message
      TRACE_INFO("Sending IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
      //Dump IP header contents for debugging purpose
      ipv6DumpHeader(packet);

      //Send PPP frame
      error = pppSendFrame(interface, buffer, offset, PPP_PROTOCOL_IPV6);
   }
   else
#endif
   //6LoWPAN interface?
   if(interface->nicDriver->type == NIC_TYPE_6LOWPAN)
   {
      //Debug message
      TRACE_INFO("Sending IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
      //Dump IP header contents for debugging purpose
      ipv6DumpHeader(packet);

      //Send the packet over the specified link
      error = nicSendPacket(interface, buffer, offset);
   }
   else
   //Unknown interface type?
   {
      //Report an error
      error = ERROR_INVALID_INTERFACE;
   }

   //Return status code
   return error;
}


/**
 * @brief Join an IPv6 multicast group
 * @param[in] interface Underlying network interface
 * @param[in] groupAddr IPv6 Multicast address to join
 * @return Error code
 **/

error_t ipv6JoinMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
{
   error_t error;
   uint_t i;
   Ipv6FilterEntry *entry;
   Ipv6FilterEntry *firstFreeEntry;
#if (ETH_SUPPORT == ENABLED)
   MacAddr macAddr;
#endif

   //The IPv6 address must be a valid multicast address
   if(!ipv6IsMulticastAddr(groupAddr))
      return ERROR_INVALID_ADDRESS;

   //Initialize error code
   error = NO_ERROR;
   //Keep track of the first free entry
   firstFreeEntry = NULL;

   //Go through the multicast filter table
   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->ipv6Context.multicastFilter[i];

      //Valid entry?
      if(entry->refCount > 0)
      {
         //Check whether the table already contains the specified IPv6 address
         if(ipv6CompAddr(&entry->addr, groupAddr))
         {
            //Increment the reference count
            entry->refCount++;
            //Successful processing
            return NO_ERROR;
         }
      }
      else
      {
         //Keep track of the first free entry
         if(firstFreeEntry == NULL)
            firstFreeEntry = entry;
      }
   }

   //Check whether the multicast filter table is full
   if(firstFreeEntry == NULL)
   {
      //A new entry cannot be added
      return ERROR_FAILURE;
   }

#if (ETH_SUPPORT == ENABLED)
   //Map the IPv6 multicast address to a MAC-layer address
   ipv6MapMulticastAddrToMac(groupAddr, &macAddr);
   //Add the corresponding address to the MAC filter table
   error = ethAcceptMulticastAddr(interface, &macAddr);
#endif

   //MAC filter table successfully updated?
   if(!error)
   {
      //Now we can safely add a new entry to the table
      firstFreeEntry->addr = *groupAddr;
      //Initialize the reference count
      firstFreeEntry->refCount = 1;

#if (MLD_SUPPORT == ENABLED)
      //Start listening to the multicast address
      mldStartListening(interface, firstFreeEntry);
#endif
   }

   //Return status code
   return error;
}


/**
 * @brief Leave an IPv6 multicast group
 * @param[in] interface Underlying network interface
 * @param[in] groupAddr IPv6 multicast address to drop
 * @return Error code
 **/

error_t ipv6LeaveMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
{
   uint_t i;
   Ipv6FilterEntry *entry;
#if (ETH_SUPPORT == ENABLED)
   MacAddr macAddr;
#endif

   //The IPv6 address must be a valid multicast address
   if(!ipv6IsMulticastAddr(groupAddr))
      return ERROR_INVALID_ADDRESS;

   //Go through the multicast filter table
   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->ipv6Context.multicastFilter[i];

      //Valid entry?
      if(entry->refCount > 0)
      {
         //Specified IPv6 address found?
         if(ipv6CompAddr(&entry->addr, groupAddr))
         {
            //Decrement the reference count
            entry->refCount--;

            //Remove the entry if the reference count drops to zero
            if(entry->refCount == 0)
            {
#if (MLD_SUPPORT == ENABLED)
               //Stop listening to the multicast address
               mldStopListening(interface, entry);
#endif
#if (ETH_SUPPORT == ENABLED)
               //Map the IPv6 multicast address to a MAC-layer address
               ipv6MapMulticastAddrToMac(groupAddr, &macAddr);
               //Drop the corresponding address from the MAC filter table
               ethDropMulticastAddr(interface, &macAddr);
#endif
               //Remove the multicast address from the list
               entry->addr = IPV6_UNSPECIFIED_ADDR;
            }

            //Successful processing
            return NO_ERROR;
         }
      }
   }

   //The specified IPv6 address does not exist
   return ERROR_ADDRESS_NOT_FOUND;
}


/**
 * @brief Convert a string representation of an IPv6 address to a binary IPv6 address
 * @param[in] str NULL-terminated string representing the IPv6 address
 * @param[out] ipAddr Binary representation of the IPv6 address
 * @return Error code
 **/

error_t ipv6StringToAddr(const char_t *str, Ipv6Addr *ipAddr)
{
   error_t error;
   int_t i = 0;
   int_t j = -1;
   int_t k = 0;
   int32_t value = -1;

   //Parse input string
   while(1)
   {
      //Hexadecimal digit found?
      if(isxdigit((uint8_t) *str))
      {
         //First digit to be decoded?
         if(value < 0)
            value = 0;

         //Update the value of the current 16-bit word
         if(isdigit((uint8_t) *str))
            value = (value * 16) + (*str - '0');
         else if(isupper((uint8_t) *str))
            value = (value * 16) + (*str - 'A' + 10);
         else
            value = (value * 16) + (*str - 'a' + 10);

         //Check resulting value
         if(value > 0xFFFF)
         {
            //The conversion failed
            error = ERROR_INVALID_SYNTAX;
            break;
         }
      }
      //"::" symbol found?
      else if(!strncmp(str, "::", 2))
      {
         //The "::" can only appear once in an IPv6 address
         if(j >= 0)
         {
            //The conversion failed
            error = ERROR_INVALID_SYNTAX;
            break;
         }

         //The "::" symbol is preceded by a number?
         if(value >= 0)
         {
            //Save the current 16-bit word
            ipAddr->w[i++] = htons(value);
            //Prepare to decode the next 16-bit word
            value = -1;
         }

         //Save the position of the "::" symbol
         j = i;
         //Point to the next character
         str++;
      }
      //":" symbol found?
      else if(*str == ':' && i < 8)
      {
         //Each ":" must be preceded by a valid number
         if(value < 0)
         {
            //The conversion failed
            error = ERROR_INVALID_SYNTAX;
            break;
         }

         //Save the current 16-bit word
         ipAddr->w[i++] = htons(value);
         //Prepare to decode the next 16-bit word
         value = -1;
      }
      //End of string detected?
      else if(*str == '\0' && i == 7 && j < 0)
      {
         //The NULL character must be preceded by a valid number
         if(value < 0)
         {
            //The conversion failed
            error = ERROR_INVALID_SYNTAX;
         }
         else
         {
            //Save the last 16-bit word of the IPv6 address
            ipAddr->w[i] = htons(value);
            //The conversion succeeded
            error = NO_ERROR;
         }

         //We are done
         break;
      }
      else if(*str == '\0' && i < 7 && j >= 0)
      {
         //Save the last 16-bit word of the IPv6 address
         if(value >= 0)
            ipAddr->w[i++] = htons(value);

         //Move the part of the address that follows the "::" symbol
         for(k = 0; k < (i - j); k++)
            ipAddr->w[7 - k] = ipAddr->w[i - 1 - k];
         //A sequence of zeroes can now be written in place of "::"
         for(k = 0; k < (8 - i); k++)
            ipAddr->w[j + k] = 0;

         //The conversion succeeded
         error = NO_ERROR;
         break;
      }
      //Invalid character...
      else
      {
         //The conversion failed
         error = ERROR_INVALID_SYNTAX;
         break;
      }

      //Point to the next character
      str++;
   }

   //Return status code
   return error;
}


/**
 * @brief Convert a binary IPv6 address to a string representation
 *
 * Call ipv6AddrToString() to convert an IPv6 address to a text representation. The
 * implementation of ipv6AddrToString() function follows RFC 5952 recommendations
 *
 * @param[in] ipAddr Binary representation of the IPv6 address
 * @param[out] str NULL-terminated string representing the IPv6 address
 * @return Pointer to the formatted string
 **/

char_t *ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
{
   static char_t buffer[40];
   uint_t i;
   uint_t j;
   char_t *p;

   //Best run of zeroes
   uint_t zeroRunStart = 0;
   uint_t zeroRunEnd = 0;

   //If the NULL pointer is given as parameter, then the internal buffer is used
   if(str == NULL)
      str = buffer;

   //Find the longest run of zeros for "::" short-handing
   for(i = 0; i < 8; i++)
   {
      //Compute the length of the current sequence of zeroes
      for(j = i; j < 8 && !ipAddr->w[j]; j++);

      //Keep track of the longest one
      if((j - i) > 1 && (j - i) > (zeroRunEnd - zeroRunStart))
      {
         //The symbol "::" should not be used to shorten just one zero field
         zeroRunStart = i;
         zeroRunEnd = j;
      }
   }

   //Format IPv6 address
   for(p = str, i = 0; i < 8; i++)
   {
      //Are we inside the best run of zeroes?
      if(i >= zeroRunStart && i < zeroRunEnd)
      {
         //Append a separator
         *(p++) = ':';
         //Skip the sequence of zeroes
         i = zeroRunEnd - 1;
      }
      else
      {
         //Add a separator between each 16-bit word
         if(i > 0)
            *(p++) = ':';

         //Convert the current 16-bit word to string
         p += sprintf(p, "%" PRIx16, ntohs(ipAddr->w[i]));
      }
   }

   //A trailing run of zeroes has been found?
   if(zeroRunEnd == 8)
      *(p++) = ':';

   //Properly terminate the string
   *p = '\0';

   //Return a pointer to the formatted string
   return str;
}


/**
 * @brief Dump IPv6 header for debugging purpose
 * @param[in] ipHeader IPv6 header
 **/

void ipv6DumpHeader(const Ipv6Header *ipHeader)
{
   //Dump IPv6 header contents
   TRACE_DEBUG("  Version = %" PRIu8 "\r\n", ipHeader->version);
   TRACE_DEBUG("  Traffic Class = %u\r\n", (ipHeader->trafficClassH << 4) | ipHeader->trafficClassL);
   TRACE_DEBUG("  Flow Label = 0x%05X\r\n", (ipHeader->flowLabelH << 16) | ntohs(ipHeader->flowLabelL));
   TRACE_DEBUG("  Payload Length = %" PRIu16 "\r\n", ntohs(ipHeader->payloadLength));
   TRACE_DEBUG("  Next Header = %" PRIu8 "\r\n", ipHeader->nextHeader);
   TRACE_DEBUG("  Hop Limit = %" PRIu8 "\r\n", ipHeader->hopLimit);
   TRACE_DEBUG("  Src Addr = %s\r\n", ipv6AddrToString(&ipHeader->srcAddr, NULL));
   TRACE_DEBUG("  Dest Addr = %s\r\n", ipv6AddrToString(&ipHeader->destAddr, NULL));
}

#endif