Webserver+3d print
Diff: cyclone_tcp/dhcpv6/dhcpv6_client.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/dhcpv6/dhcpv6_client.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,2609 @@ +/** + * @file dhcpv6_client.c + * @brief DHCPv6 client (Dynamic Host Configuration Protocol for IPv6) + * + * @section License + * + * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. + * + * This file is part of CycloneTCP Open. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @section Description + * + * The Dynamic Host Configuration Protocol for IPv6 enables DHCP servers to + * pass configuration parameters such as IPv6 network addresses to IPv6 + * nodes. This protocol is a stateful counterpart to IPv6 Stateless Address + * Autoconfiguration (RFC 2462), and can be used separately or concurrently + * with the latter to obtain configuration parameters. Refer to RFC 3315 + * + * @author Oryx Embedded SARL (www.oryx-embedded.com) + * @version 1.7.6 + **/ + +//Switch to the appropriate trace level +#define TRACE_LEVEL DHCPV6_TRACE_LEVEL + +//Dependencies +#include <stdlib.h> +#include "core/net.h" +#include "ipv6/ipv6.h" +#include "ipv6/ipv6_misc.h" +#include "ipv6/ndp.h" +#include "dhcpv6/dhcpv6_client.h" +#include "dhcpv6/dhcpv6_common.h" +#include "dhcpv6/dhcpv6_debug.h" +#include "dns/dns_common.h" +#include "date_time.h" +#include "debug.h" + +//Check TCP/IP stack configuration +#if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED) + +//Tick counter to handle periodic operations +systime_t dhcpv6ClientTickCounter; + +//Requested DHCPv6 options +static const uint16_t dhcpv6OptionList[] = +{ + HTONS(DHCPV6_OPTION_DNS_SERVERS), + HTONS(DHCPV6_OPTION_DOMAIN_LIST), + HTONS(DHCPV6_OPTION_FQDN) +}; + + +/** + * @brief Initialize settings with default values + * @param[out] settings Structure that contains DHCPv6 client settings + **/ + +void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings) +{ + //Use default interface + settings->interface = netGetDefaultInterface(); + + //Support for quick configuration using rapid commit + settings->rapidCommit = FALSE; + //Use the DNS servers provided by the DHCPv6 server + settings->manualDnsConfig = FALSE; + //DHCPv6 configuration timeout + settings->timeout = 0; + //DHCPv6 configuration timeout event + settings->timeoutEvent = NULL; + //Link state change event + settings->linkChangeEvent = NULL; + //FSM state change event + settings->stateChangeEvent = NULL; +} + + +/** + * @brief DHCPv6 client initialization + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] settings DHCPv6 client specific settings + * @return Error code + **/ + +error_t dhcpv6ClientInit(Dhcpv6ClientContext *context, const Dhcpv6ClientSettings *settings) +{ + error_t error; + NetInterface *interface; + + //Debug message + TRACE_INFO("Initializing DHCPv6 client...\r\n"); + + //Ensure the parameters are valid + if(context == NULL || settings == NULL) + return ERROR_INVALID_PARAMETER; + + //A valid pointer to the interface being configured is required + if(settings->interface == NULL) + return ERROR_INVALID_PARAMETER; + + //Point to the underlying network interface + interface = settings->interface; + + //Clear the DHCPv6 client context + memset(context, 0, sizeof(Dhcpv6ClientContext)); + //Save user settings + context->settings = *settings; + + //Generate client's DUID + error = dhcpv6ClientGenerateDuid(context); + //any error to report? + if(error) + return error; + + //Generate client's fully qualified domain name + error = dhcpv6ClientGenerateFqdn(context); + //any error to report? + if(error) + return error; + + //Callback function to be called when a DHCPv6 message is received + error = udpAttachRxCallback(interface, DHCPV6_CLIENT_PORT, + dhcpv6ClientProcessMessage, context); + //Failed to register callback function? + if(error) + return error; + + //DHCPv6 client is currently suspended + context->running = FALSE; + //Initialize state machine + context->state = DHCPV6_STATE_INIT; + + //Attach the DHCPv6 client context to the network interface + interface->dhcpv6ClientContext = context; + + //Successful initialization + return NO_ERROR; +} + + +/** + * @brief Start DHCPv6 client + * @param[in] context Pointer to the DHCPv6 client context + * @return Error code + **/ + +error_t dhcpv6ClientStart(Dhcpv6ClientContext *context) +{ + NetInterface *interface; + + //Check parameter + if(context == NULL) + return ERROR_INVALID_PARAMETER; + + //Debug message + TRACE_INFO("Starting DHCPv6 client...\r\n"); + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Point to the underlying network interface + interface = context->settings.interface; + + //Flush the list of IPv6 addresses from the client's IA + dhcpv6ClientFlushAddrList(context); + + //Automatic DNS server configuration? + if(!context->settings.manualDnsConfig) + { + //Clear the list of DNS servers + ipv6FlushDnsServerList(interface); + } + + //Check if the link is up? + if(interface->linkState) + { + //A link-local address is formed by combining the well-known + //link-local prefix fe80::/10 with the interface identifier + dhcpv6ClientGenerateLinkLocalAddr(context); + } + + //Start DHCPv6 client + context->running = TRUE; + //Initialize state machine + context->state = DHCPV6_STATE_INIT; + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Stop DHCPv6 client + * @param[in] context Pointer to the DHCPv6 client context + * @return Error code + **/ + +error_t dhcpv6ClientStop(Dhcpv6ClientContext *context) +{ + //Check parameter + if(context == NULL) + return ERROR_INVALID_PARAMETER; + + //Debug message + TRACE_INFO("Stopping DHCPv6 client...\r\n"); + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Stop DHCPv6 client + context->running = FALSE; + //Reinitialize state machine + context->state = DHCPV6_STATE_INIT; + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Release DHCPv6 lease + * @param[in] context Pointer to the DHCPv6 client context + * @return Error code + **/ + +error_t dhcpv6ClientRelease(Dhcpv6ClientContext *context) +{ + uint_t i; + NetInterface *interface; + Dhcpv6ClientAddrEntry *entry; + + //Check parameter + if(context == NULL) + return ERROR_INVALID_PARAMETER; + + //Debug message + TRACE_INFO("Releasing DHCPv6 lease...\r\n"); + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Point to the underlying network interface + interface = context->settings.interface; + + //Check whether the DHCPv6 client is running + if(context->running) + { + //BOUND state? + if(context->state == DHCPV6_STATE_BOUND) + { + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //The client must stop using the addresses being released as soon + //as the client begins the Release message exchange process + ipv6RemoveAddr(interface, &entry->addr); + } + } + + //Switch to the RELEASE state + dhcpv6ClientChangeState(context, DHCPV6_STATE_RELEASE, 0); + } + else + { + //Stop DHCPv6 client + context->running = FALSE; + //Reinitialize state machine + context->state = DHCPV6_STATE_INIT; + } + } + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Retrieve current state + * @param[in] context Pointer to the DHCPv6 client context + * @return Current DHCPv6 client state + **/ + +Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context) +{ + Dhcpv6State state; + + //Get exclusive access + osAcquireMutex(&netMutex); + //Get current state + state = context->state; + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return current state + return state; +} + + +/** + * @brief DHCPv6 client timer handler + * + * This routine must be periodically called by the TCP/IP stack to + * manage DHCPv6 client operation + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + + +void dhcpv6ClientTick(Dhcpv6ClientContext *context) +{ + //Make sure the DHCPv6 client has been properly instantiated + if(context == NULL) + return; + + //DHCPv6 client finite state machine + switch(context->state) + { + //Process INIT state + case DHCPV6_STATE_INIT: + //This is the initialization state, where a client begins the process of + //acquiring a lease. It also returns here when a lease ends, or when a + //lease negotiation fails + dhcpv6ClientStateInit(context); + break; + //Process SOLICIT state + case DHCPV6_STATE_SOLICIT: + //The client sends a Solicit message to locate servers + dhcpv6ClientStateSolicit(context); + break; + //Process REQUEST state + case DHCPV6_STATE_REQUEST: + //The client sends a Request message to request configuration + //parameters, including IP addresses, from a specific server + dhcpv6ClientStateRequest(context); + break; + //Process INIT-CONFIRM state + case DHCPV6_STATE_INIT_CONFIRM: + //When a client that already has a valid lease starts up after a + //power-down or reboot, it starts here instead of the INIT state + dhcpv6ClientStateInitConfirm(context); + break; + //Process CONFIRM state + case DHCPV6_STATE_CONFIRM: + //The client sends a Confirm message to any available server + //to determine whether the addresses it was assigned are still + //appropriate to the link to which the client is connected + dhcpv6ClientStateConfirm(context); + break; + //Process DAD state + case DHCPV6_STATE_DAD: + //The client should perform duplicate address detection on each + //of the addresses in any IAs it receives in the Reply message + //before using that address for traffic + dhcpv6ClientStateDad(context); + break; + //Process BOUND state + case DHCPV6_STATE_BOUND: + //The client has a valid lease and is in its normal operating state + dhcpv6ClientStateBound(context); + break; + //Process RENEW state + case DHCPV6_STATE_RENEW: + //The client sends a Renew message to the server that originally + //provided the client's addresses and configuration parameters to + //extend the lifetimes on the addresses assigned to the client + //and to update other configuration parameters + dhcpv6ClientStateRenew(context); + break; + //Process REBIND state + case DHCPV6_STATE_REBIND: + //The client sends a Rebind message to any available server to extend + //the lifetimes on the addresses assigned to the client and to update + //other configuration parameters. This message is sent after a client + //receives no response to a Renew message + dhcpv6ClientStateRebind(context); + break; + //Process RELEASE state + case DHCPV6_STATE_RELEASE: + //To release one or more addresses, a client sends a Release message + //to the server + dhcpv6ClientStateRelease(context); + break; + //Process DECLINE state + case DHCPV6_STATE_DECLINE: + //If a client detects that one or more addresses assigned to it by a + //server are already in use by another node, the client sends a Decline + //message to the server to inform it that the address is suspect + dhcpv6ClientStateDecline(context); + break; + //Invalid state... + default: + //Switch to the default state + context->state = DHCPV6_STATE_INIT; + break; + } +} + + +/** + * @brief Callback function for link change event + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context) +{ + NetInterface *interface; + + //Make sure the DHCPv6 client has been properly instantiated + if(context == NULL) + return; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Check whether the DHCPv6 client is running + if(context->running) + { + //Automatic DNS server configuration? + if(!context->settings.manualDnsConfig) + { + //Clear the list of DNS servers + ipv6FlushDnsServerList(interface); + } + + //Link-up event? + if(interface->linkState) + { + //A link-local address is formed by combining the well-known + //link-local prefix fe80::/10 with the interface identifier + dhcpv6ClientGenerateLinkLocalAddr(context); + } + } + + //Check the state of the DHCPv6 client + switch(context->state) + { + case DHCPV6_STATE_INIT_CONFIRM: + case DHCPV6_STATE_CONFIRM : + case DHCPV6_STATE_DAD: + case DHCPV6_STATE_BOUND: + case DHCPV6_STATE_RENEW: + case DHCPV6_STATE_REBIND: + //The client already has a valid lease + context->state = DHCPV6_STATE_INIT_CONFIRM; + break; + case DHCPV6_STATE_RELEASE: + //Stop DHCPv6 client + context->running = FALSE; + //Reinitialize state machine + context->state = DHCPV6_STATE_INIT; + break; + default: + //Switch to the INIT state + context->state = DHCPV6_STATE_INIT; + break; + } + + //Any registered callback? + if(context->settings.linkChangeEvent != NULL) + { + //Release exclusive access + osReleaseMutex(&netMutex); + //Invoke user callback function + context->settings.linkChangeEvent(context, interface, interface->linkState); + //Get exclusive access + osAcquireMutex(&netMutex); + } +} + + +/** + * @brief INIT state + * + * This is the initialization state, where a client begins the process of + * acquiring a lease. It also returns here when a lease ends, or when a + * lease negotiation fails + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateInit(Dhcpv6ClientContext *context) +{ + systime_t delay; + NetInterface *interface; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Check whether the DHCPv6 client is running + if(context->running) + { + //Wait for the link to be up before starting DHCPv6 configuration + if(interface->linkState) + { + //Make sure that a valid link-local address has been assigned to the interface + if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) + { + //Flush the list of IPv6 addresses from the client's IA + dhcpv6ClientFlushAddrList(context); + + //The first Solicit message from the client on the interface must be + //delayed by a random amount of time between 0 and SOL_MAX_DELAY + delay = dhcpv6RandRange(0, DHCPV6_CLIENT_SOL_MAX_DELAY); + + //Record the time at which the client started + //the address acquisition process + context->configStartTime = osGetSystemTime(); + //Clear flag + context->timeoutEventDone = FALSE; + + //Switch to the SOLICIT state + dhcpv6ClientChangeState(context, DHCPV6_STATE_SOLICIT, delay); + } + } + } +} + + +/** + * @brief SOLICIT state + * + * A client uses the Solicit message to discover DHCPv6 servers + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context) +{ + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Reset server preference value + context->serverPreference = -1; + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Solicit message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT); + + //Save the time at which the message was sent + context->exchangeStartTime = time; + context->timestamp = time; + + //If the client is waiting for an Advertise message, the first RT + //must be selected to be strictly greater than IRT + context->timeout = DHCPV6_CLIENT_SOL_TIMEOUT + + abs(dhcpv6Rand(DHCPV6_CLIENT_SOL_TIMEOUT)); + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //Check whether a valid Advertise message has been received + if(context->serverPreference >= 0) + { + //Continue configuration procedure + dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); + } + else + { + //Send a Solicit message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //MRT specifies an upper bound on the value of RT + if(context->timeout > DHCPV6_CLIENT_SOL_MAX_RT) + { + //Compute retransmission timeout + context->timeout = DHCPV6_CLIENT_SOL_MAX_RT + + dhcpv6Rand(DHCPV6_CLIENT_SOL_MAX_RT); + } + + //Increment retransmission counter + context->retransmitCount++; + } + } + } + + //Manage DHCPv6 configuration timeout + dhcpv6ClientCheckTimeout(context); +} + + +/** + * @brief REQUEST state + * + * The client uses a Request message to populate IAs with addresses and obtain + * other configuration information. The client includes one or more more IA + * options in the Request message. The server then returns addresses and other + * information about the IAs to the client in IA options in a Reply message + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context) +{ + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Request message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST); + + //Save the time at which the message was sent + context->exchangeStartTime = time; + context->timestamp = time; + + //Initial retransmission timeout + context->timeout = DHCPV6_CLIENT_REQ_TIMEOUT + + dhcpv6Rand(DHCPV6_CLIENT_REQ_TIMEOUT); + + //Increment retransmission counter + context->retransmitCount++; + } + else if(context->retransmitCount < DHCPV6_CLIENT_REQ_MAX_RC) + { + //Send a Request message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //MRT specifies an upper bound on the value of RT + if(context->timeout > DHCPV6_CLIENT_REQ_MAX_RT) + { + //Compute retransmission timeout + context->timeout = DHCPV6_CLIENT_REQ_MAX_RT + + dhcpv6Rand(DHCPV6_CLIENT_REQ_MAX_RT); + } + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //If the client does not receive a response within a reasonable + //period of time, then it restarts the initialization procedure + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } + } + + //Manage DHCPv6 configuration timeout + dhcpv6ClientCheckTimeout(context); +} + + +/** + * @brief INIT-CONFIRM state + * + * When a client that already has a valid lease starts up after a + * power-down or reboot, it starts here instead of the INIT state + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context) +{ + systime_t delay; + NetInterface *interface; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Check whether the DHCPv6 client is running + if(context->running) + { + //Wait for the link to be up before starting DHCPv6 configuration + if(interface->linkState) + { + //Make sure that a valid link-local address has been assigned to the interface + if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) + { + //The first Confirm message from the client on the interface must be + //delayed by a random amount of time between 0 and CNF_MAX_DELAY + delay = dhcpv6RandRange(0, DHCPV6_CLIENT_CNF_MAX_DELAY); + + //Record the time at which the client started + //the address acquisition process + context->configStartTime = osGetSystemTime(); + //Clear flag + context->timeoutEventDone = FALSE; + + //Switch to the CONFIRM state + dhcpv6ClientChangeState(context, DHCPV6_STATE_CONFIRM, delay); + } + } + } +} + + +/** + * @brief CONFIRM state + * + * Whenever a client may have moved to a new link, the prefixes from + * the addresses assigned to the interfaces on that link may no longer + * be appropriate for the link to which the client is attached. In such + * the client must initiate a Confirm/Reply message exchange + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context) +{ + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Confirm message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM); + + //Save the time at which the client sent the first message + context->exchangeStartTime = time; + context->timestamp = time; + + //Initial retransmission timeout + context->timeout = DHCPV6_CLIENT_CNF_TIMEOUT + + dhcpv6Rand(DHCPV6_CLIENT_CNF_TIMEOUT); + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //Send a Confirm message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //MRT specifies an upper bound on the value of RT + if(context->timeout > DHCPV6_CLIENT_CNF_MAX_RT) + { + //Compute retransmission timeout + context->timeout = DHCPV6_CLIENT_CNF_MAX_RT + + dhcpv6Rand(DHCPV6_CLIENT_CNF_MAX_RT); + } + + //Increment retransmission counter + context->retransmitCount++; + } + } + else + { + //Check retransmission counter + if(context->retransmitCount > 0) + { + //The message exchange fails once MRD seconds have elapsed since + //the client first transmitted the message + if(timeCompare(time, context->exchangeStartTime + DHCPV6_CLIENT_CNF_MAX_RD) >= 0) + { + //If the client receives no responses before the message transmission + //process terminates, the client should continue to use any IP + //addresses using the last known lifetimes for those addresses + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } + } + } + + //Manage DHCPv6 configuration timeout + dhcpv6ClientCheckTimeout(context); +} + + +/** + * @brief DAD state + * + * The client perform duplicate address detection on each + * of the addresses in any IAs it receives in the Reply message + * before using that address for traffic + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateDad(Dhcpv6ClientContext *context) +{ + uint_t i; + NetInterface *interface; + Ipv6AddrState state; + Dhcpv6ClientAddrEntry *entry; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Check the IPv6 address is a tentative address? + if(entry->validLifetime > 0) + { + //Get the state of the current IPv6 address + state = ipv6GetAddrState(interface, &entry->addr); + + //Duplicate Address Detection in progress? + if(state == IPV6_ADDR_STATE_TENTATIVE) + { + //Exit immediately + return; + } + //Duplicate Address Detection failed? + else if(state == IPV6_ADDR_STATE_INVALID) + { + //Switch to the DECLINE state + dhcpv6ClientChangeState(context, DHCPV6_STATE_DECLINE, 0); + //Exit immediately + return; + } + } + } + + //Dump current DHCPv6 configuration for debugging purpose + dhcpv6ClientDumpConfig(context); + //Switch to the BOUND state + dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0); +} + + +/** + * @brief BOUND state + * + * Client has a valid lease and is in its normal operating state + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateBound(Dhcpv6ClientContext *context) +{ + systime_t t1; + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //A client will never attempt to extend the lifetime of any + //address in an IA with T1 set to 0xffffffff + if(context->ia.t1 != DHCPV6_INFINITE_TIME) + { + //Convert T1 to milliseconds + if(context->ia.t1 < (MAX_DELAY / 1000)) + t1 = context->ia.t1 * 1000; + else + t1 = MAX_DELAY; + + //Check the time elapsed since the lease was obtained + if(timeCompare(time, context->leaseStartTime + t1) >= 0) + { + //Record the time at which the client started the address renewal process + context->configStartTime = time; + + //Enter the RENEW state + dhcpv6ClientChangeState(context, DHCPV6_STATE_RENEW, 0); + } + } +} + + +/** + * @brief RENEW state + * + * The client sends a Renew message to the server that originally + * provided the client's addresses and configuration parameters to + * extend the lifetimes on the addresses assigned to the client + * and to update other configuration parameters + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context) +{ + systime_t t2; + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Renew message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW); + + //Save the time at which the message was sent + context->exchangeStartTime = time; + context->timestamp = time; + + //Initial retransmission timeout + context->timeout = DHCPV6_CLIENT_REN_TIMEOUT + + dhcpv6Rand(DHCPV6_CLIENT_REN_TIMEOUT); + } + else + { + //Send a Renew message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //MRT specifies an upper bound on the value of RT + if(context->timeout > DHCPV6_CLIENT_REN_MAX_RT) + { + //Compute retransmission timeout + context->timeout = DHCPV6_CLIENT_REN_MAX_RT + + dhcpv6Rand(DHCPV6_CLIENT_REN_MAX_RT); + } + } + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //A client will never attempt to use a Rebind message to locate a + //different server to extend the lifetime of any address in an IA + //with T2 set to 0xffffffff + if(context->ia.t2 != DHCPV6_INFINITE_TIME) + { + //Convert T2 to milliseconds + if(context->ia.t2 < (MAX_DELAY / 1000)) + t2 = context->ia.t2 * 1000; + else + t2 = MAX_DELAY; + + //Check whether T2 timer has expired + if(timeCompare(time, context->leaseStartTime + t2) >= 0) + { + //Switch to the REBIND state + dhcpv6ClientChangeState(context, DHCPV6_STATE_REBIND, 0); + } + } + } +} + + +/** + * @brief REBIND state + * + * The client sends a Rebind message to any available server to extend + * the lifetimes on the addresses assigned to the client and to update + * other configuration parameters. This message is sent after a client + * receives no response to a Renew message + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context) +{ + uint_t i; + systime_t time; + NetInterface *interface; + Dhcpv6ClientAddrEntry *entry; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Rebind message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND); + + //Save the time at which the message was sent + context->exchangeStartTime = time; + context->timestamp = time; + + //Initial retransmission timeout + context->timeout = DHCPV6_CLIENT_REB_TIMEOUT + + dhcpv6Rand(DHCPV6_CLIENT_REB_TIMEOUT); + } + else + { + //Send a Rebind message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //MRT specifies an upper bound on the value of RT + if(context->timeout > DHCPV6_CLIENT_REB_MAX_RT) + { + //Compute retransmission timeout + context->timeout = DHCPV6_CLIENT_REB_MAX_RT + + dhcpv6Rand(DHCPV6_CLIENT_REB_MAX_RT); + } + } + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //Check whether the valid lifetime has expired + if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_INVALID) + { + //Restart DHCPv6 configuration + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } + } + } + } +} + + +/** + * @brief RELEASE state + * + * To release one or more addresses, a client sends a Release message + * to the server + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context) +{ + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Release message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE); + + //Save the time at which the message was sent + context->exchangeStartTime = time; + context->timestamp = time; + + //Initial retransmission timeout + context->timeout = DHCPV6_CLIENT_REL_TIMEOUT + + dhcpv6Rand(DHCPV6_CLIENT_REL_TIMEOUT); + + //Increment retransmission counter + context->retransmitCount++; + } + else if(context->retransmitCount < DHCPV6_CLIENT_REL_MAX_RC) + { + //Send a Release message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //Implementations should retransmit one or more times, but may + //choose to terminate the retransmission procedure early + context->running = FALSE; + + //Reinitialize state machine + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } + } +} + + +/** + * @brief DECLINE state + * + * If a client detects that one or more addresses assigned to it by a + * server are already in use by another node, the client sends a Decline + * message to the server to inform it that the address is suspect + * + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context) +{ + systime_t time; + + //Get current time + time = osGetSystemTime(); + + //Check current time + if(timeCompare(time, context->timestamp + context->timeout) >= 0) + { + //Check retransmission counter + if(context->retransmitCount == 0) + { + //Generate a 24-bit transaction ID + context->transactionId = netGetRand() & 0x00FFFFFF; + + //Send a Decline message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE); + + //Save the time at which the message was sent + context->exchangeStartTime = time; + context->timestamp = time; + + //Initial retransmission timeout + context->timeout = DHCPV6_CLIENT_DEC_TIMEOUT + + dhcpv6Rand(DHCPV6_CLIENT_DEC_TIMEOUT); + + //Increment retransmission counter + context->retransmitCount++; + } + else if(context->retransmitCount < DHCPV6_CLIENT_DEC_MAX_RC) + { + //Send a Decline message + dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE); + + //Save the time at which the message was sent + context->timestamp = time; + + //The RT is doubled for each subsequent retransmission + context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); + + //Increment retransmission counter + context->retransmitCount++; + } + else + { + //If the client does not receive a response within a reasonable + //period of time, then it restarts the initialization procedure + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } + } +} + + +/** + * @brief Send Solicit message + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] type DHCPv6 message type + * @return Error code + **/ + +error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context, + Dhcpv6MessageType type) +{ + error_t error; + uint_t i; + size_t length; + size_t offset; + NetBuffer *buffer; + NetInterface *interface; + Dhcpv6Message *message; + Dhcpv6Option *option; + Dhcpv6IaNaOption iaNaOption; + Dhcpv6IaAddrOption iaAddrOption; + Dhcpv6FqdnOption *fqdnOption; + Dhcpv6ElapsedTimeOption elapsedTimeOption; + Dhcpv6ClientAddrEntry *entry; + IpAddr destIpAddr; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Allocate a memory buffer to hold the DHCPv6 message + buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset); + //Failed to allocate buffer? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the DHCPv6 message + message = netBufferAt(buffer, offset); + + //Set DHCPv6 message type + message->msgType = type; + + //The transaction ID is chosen by the client + STORE24BE(context->transactionId, message->transactionId); + + //Size of the DHCPv6 message + length = sizeof(Dhcpv6Message); + + //The client must include a Client Identifier option + //to identify itself to the server + dhcpv6AddOption(message, &length, DHCPV6_OPTION_CLIENTID, + context->clientId, context->clientIdLength); + + //Request, Renew, Release or Decline message? + if(type == DHCPV6_MSG_TYPE_REQUEST || + type == DHCPV6_MSG_TYPE_RENEW || + type == DHCPV6_MSG_TYPE_RELEASE || + type == DHCPV6_MSG_TYPE_DECLINE) + { + //The client places the identifier of the destination + //server in a Server Identifier option + dhcpv6AddOption(message, &length, DHCPV6_OPTION_SERVERID, + context->serverId, context->serverIdLength); + } + + //Solicit message? + if(type == DHCPV6_MSG_TYPE_SOLICIT) + { + //Check whether rapid commit is enabled + if(context->settings.rapidCommit) + { + //Include the Rapid Commit option if the client is prepared + //to perform the Solicit/Reply message exchange + dhcpv6AddOption(message, &length, DHCPV6_OPTION_RAPID_COMMIT, NULL, 0); + } + } + + //Solicit, Request, Confirm, Renew or Rebind message? + if(type == DHCPV6_MSG_TYPE_SOLICIT || + type == DHCPV6_MSG_TYPE_REQUEST || + type == DHCPV6_MSG_TYPE_CONFIRM || + type == DHCPV6_MSG_TYPE_RENEW || + type == DHCPV6_MSG_TYPE_REBIND) + { + //Point to the client's fully qualified domain name + fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn; + + //The FQDN option can be used by the client to convey its + //fully qualified domain name to the server + dhcpv6AddOption(message, &length, DHCPV6_OPTION_FQDN, + fqdnOption, sizeof(Dhcpv6FqdnOption) + context->clientFqdnLength); + + //The client should include an Option Request option to indicate + //the options the client is interested in receiving + dhcpv6AddOption(message, &length, DHCPV6_OPTION_ORO, + &dhcpv6OptionList, sizeof(dhcpv6OptionList)); + } + + //Prepare an IA_NA option for a the current interface + iaNaOption.iaId = htonl(interface->id); + + //Solicit, Request or Confirm message? + if(type == DHCPV6_MSG_TYPE_SOLICIT || + type == DHCPV6_MSG_TYPE_REQUEST || + type == DHCPV6_MSG_TYPE_CONFIRM) + { + //The client should set the T1 and T2 fields in any IA_NA options to 0 + iaNaOption.t1 = 0; + iaNaOption.t2 = 0; + } + else + { + //T1 and T2 are provided as a hint + iaNaOption.t1 = htonl(context->ia.t1); + iaNaOption.t2 = htonl(context->ia.t2); + } + + //The client includes IA options for any IAs to which + //it wants the server to assign addresses + option = dhcpv6AddOption(message, &length, DHCPV6_OPTION_IA_NA, + &iaNaOption, sizeof(Dhcpv6IaNaOption)); + + //Request, Confirm, Renew, Rebind, Release or Decline message? + if(type == DHCPV6_MSG_TYPE_REQUEST || + type == DHCPV6_MSG_TYPE_CONFIRM || + type == DHCPV6_MSG_TYPE_RENEW || + type == DHCPV6_MSG_TYPE_REBIND || + type == DHCPV6_MSG_TYPE_RELEASE || + type == DHCPV6_MSG_TYPE_DECLINE) + { + //Loop through the IPv6 addresses recorded by the client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //Prepare an IA Address option + iaAddrOption.address = entry->addr; + + //Confirm message? + if(type == DHCPV6_MSG_TYPE_CONFIRM) + { + //The client should set the preferred and valid lifetimes to 0 + iaAddrOption.preferredLifetime = 0; + iaAddrOption.validLifetime = 0; + } + else + { + //Preferred and valid lifetimes are provided as a hint + iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime); + iaAddrOption.validLifetime = htonl(entry->validLifetime); + } + + //Add the IA Address option + dhcpv6AddSubOption(option, &length, DHCPV6_OPTION_IAADDR, + &iaAddrOption, sizeof(iaAddrOption)); + } + } + } + + //Compute the time elapsed since the client sent the first message + elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context); + + //The client must include an Elapsed Time option in messages to indicate + //how long the client has been trying to complete a DHCP message exchange + dhcpv6AddOption(message, &length, DHCPV6_OPTION_ELAPSED_TIME, + &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption)); + + //Adjust the length of the multi-part buffer + netBufferSetLength(buffer, offset + length); + + //Destination address + destIpAddr.length = sizeof(Ipv6Addr); + destIpAddr.ipv6Addr = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR; + + //Debug message + TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n", + formatSystemTime(osGetSystemTime(), NULL), length); + + //Dump the contents of the message for debugging purpose + dhcpv6DumpMessage(message, length); + + //Send DHCPv6 message + error = udpSendDatagramEx(interface, DHCPV6_CLIENT_PORT, + &destIpAddr, DHCPV6_SERVER_PORT, buffer, offset, 0); + + //Free previously allocated memory + netBufferFree(buffer); + //Return status code + return error; +} + + +/** + * @brief Process incoming DHCPv6 message + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader UDP pseudo header + * @param[in] udpHeader UDP header + * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message + * @param[in] offset Offset to the first byte of the DHCPv6 message + * @param[in] params Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientProcessMessage(NetInterface *interface, + const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, + const NetBuffer *buffer, size_t offset, void *params) +{ + size_t length; + Dhcpv6ClientContext *context; + Dhcpv6Message *message; + + //Point to the DHCPv6 client context + context = (Dhcpv6ClientContext *) params; + + //Retrieve the length of the DHCPv6 message + length = netBufferGetLength(buffer) - offset; + + //Make sure the DHCPv6 message is valid + if(length < sizeof(Dhcpv6Message)) + return; + + //Point to the beginning of the DHCPv6 message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n", + formatSystemTime(osGetSystemTime(), NULL), length); + + //Dump the contents of the message for debugging purpose + dhcpv6DumpMessage(message, length); + + //Check message type + switch(message->msgType) + { + case DHCPV6_MSG_TYPE_ADVERTISE: + //Parse Advertise message + dhcpv6ClientParseAdvertise(context, message, length); + break; + case DHCPV6_MSG_TYPE_REPLY: + //Parse Reply message + dhcpv6ClientParseReply(context, message, length); + break; + default: + //Silently drop incoming message + break; + } +} + + +/** + * @brief Parse Advertise message + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] message Pointer to the incoming message to parse + * @param[in] length Length of the incoming message + **/ + +void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context, + const Dhcpv6Message *message, size_t length) +{ + uint_t i; + int_t serverPreference; + NetInterface *interface; + Dhcpv6StatusCode status; + Dhcpv6Option *option; + Dhcpv6Option *serverIdOption; + Dhcpv6IaNaOption *iaNaOption; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Make sure that the Advertise message is received in response to + //a Solicit message + if(context->state != DHCPV6_STATE_SOLICIT) + return; + + //Discard any received packet that does not match the transaction ID + if(LOAD24BE(message->transactionId) != context->transactionId) + return; + + //Get the length of the Options field + length -= sizeof(Dhcpv6Message); + + //Search for the Client Identifier option + option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID); + + //Discard any received packet that does not include a Client Identifier option + if(option == NULL) + return; + //Check the length of the option + if(ntohs(option->length) != context->clientIdLength) + return; + //Check whether the Client Identifier matches our identifier + if(memcmp(option->value, context->clientId, context->clientIdLength)) + return; + + //Search for the Server Identifier option + serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID); + + //Discard any received packet that does not include a Server Identifier option + if(serverIdOption == NULL) + return; + //Check the length of the server DUID + if(ntohs(serverIdOption->length) == 0) + return; + if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE) + return; + + //Get the status code returned by the server + status = dhcpv6GetStatusCode(message->options, length); + + //If the message contains a Status Code option indicating a failure, + //then the Advertise message is discarded by the client + if(status != DHCPV6_STATUS_SUCCESS) + return; + + //Search for the Preference option + option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_PREFERENCE); + + //Check whether the option has been found + if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption)) + { + //Server server preference value + serverPreference = option->value[0]; + } + else + { + //Any Advertise that does not include a Preference option + //is considered to have a preference value of 0 + serverPreference = 0; + } + + //Select the Advertise message that offers the highest server preference value + if(serverPreference > context->serverPreference) + { + //Save the length of the DUID + context->serverIdLength = ntohs(serverIdOption->length); + //Record the server DUID + memcpy(context->serverId, serverIdOption->value, context->serverIdLength); + + //Flush the list of IPv6 addresses from the client's IA + dhcpv6ClientFlushAddrList(context); + } + + //Point to the first option + i = 0; + + //Loop through DHCPv6 options + while(i < length) + { + //Search for an IA_NA option + option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA); + + //Unable to find the specified option? + if(option == NULL) + break; + + //Make sure the IA_NA option is valid + if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption)) + { + //Get the parameters associated with the IA_NA + iaNaOption = (Dhcpv6IaNaOption *) option->value; + + //Check the IA identifier + if(ntohl(iaNaOption->iaId) == interface->id) + { + //The client examines the status code in each IA individually + status = dhcpv6GetStatusCode(iaNaOption->options, + ntohs(option->length) - sizeof(Dhcpv6IaNaOption)); + + //The client must ignore any Advertise message that includes a Status + //Code option containing the value NoAddrsAvail + if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE) + return; + } + + //Check the server preference value + if(serverPreference > context->serverPreference) + { + //Parse the contents of the IA_NA option + dhcpv6ClientParseIaNaOption(context, option); + } + } + + //Jump to the next option + i += sizeof(Dhcpv6Option) + ntohs(option->length); + } + + //Record the highest server preference value + if(serverPreference > context->serverPreference) + context->serverPreference = serverPreference; + + //If the client receives an Advertise message that includes a + //Preference option with a preference value of 255, the client + //immediately completes the message exchange + if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE) + { + //Continue configuration procedure + dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); + } + //The message exchange is not terminated before the first RT has elapsed + else if(context->retransmitCount > 1) + { + //Continue configuration procedure + dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); + } +} + + +/** + * @brief Parse Reply message + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] message Pointer to the incoming message to parse + * @param[in] length Length of the incoming message + **/ + +void dhcpv6ClientParseReply(Dhcpv6ClientContext *context, + const Dhcpv6Message *message, size_t length) +{ + error_t error; + uint_t i; + uint_t k; + uint_t n; + bool_t iaNaOptionFound; + systime_t minPreferredLifetime; + NetInterface *interface; + Dhcpv6StatusCode status; + Dhcpv6Option *option; + Dhcpv6Option *serverIdOption; + Dhcpv6ClientAddrEntry *entry; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Discard any received packet that does not match the transaction ID + if(LOAD24BE(message->transactionId) != context->transactionId) + return; + + //Get the length of the Options field + length -= sizeof(Dhcpv6Message); + + //Search for the Client Identifier option + option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID); + + //Discard any received packet that does not include a Client Identifier option + if(option == NULL) + return; + //Check the length of the option + if(ntohs(option->length) != context->clientIdLength) + return; + //Check whether the Client Identifier matches our identifier + if(memcmp(option->value, context->clientId, context->clientIdLength)) + return; + + //Search for the Server Identifier option + serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID); + + //Discard any received packet that does not include a Server Identifier option + if(serverIdOption == NULL) + return; + //Check the length of the server DUID + if(ntohs(serverIdOption->length) == 0) + return; + if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE) + return; + + //Get the status code returned by the server + status = dhcpv6GetStatusCode(message->options, length); + + //Check current state + if(context->state == DHCPV6_STATE_SOLICIT) + { + //A Reply message is not acceptable when rapid commit is disallowed + if(!context->settings.rapidCommit) + return; + + //Search for the Rapid Commit option + option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_RAPID_COMMIT); + + //The client discards any message that does not include a Rapid Commit option + if(option == NULL || ntohs(option->length) != 0) + return; + } + else if(context->state == DHCPV6_STATE_REQUEST) + { + //The client must discard the Reply message if the contents of the + //Server Identifier option do not match the server’s DUID + if(!dhcpv6ClientCheckServerId(context, serverIdOption)) + return; + } + else if(context->state == DHCPV6_STATE_CONFIRM) + { + //When the client receives a NotOnLink status from the server in response + //to a Confirm message, the client performs DHCP server solicitation + if(status == DHCPV6_STATUS_NOT_ON_LINK) + { + //Restart the DHCP server discovery process + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + + //Exit immediately + return; + } + } + else if(context->state == DHCPV6_STATE_RENEW) + { + //The client must discard the Reply message if the contents of the + //Server Identifier option do not match the server’s DUID + if(!dhcpv6ClientCheckServerId(context, serverIdOption)) + return; + } + else if(context->state == DHCPV6_STATE_REBIND) + { + //Do not check the server's DUID when the Reply message is + //received in response to a Rebind message + } + else if(context->state == DHCPV6_STATE_RELEASE) + { + //The client must discard the Reply message if the contents of the + //Server Identifier option do not match the server’s DUID + if(!dhcpv6ClientCheckServerId(context, serverIdOption)) + return; + + //When the client receives a valid Reply message in response to a + //Release message, the client considers the Release event completed, + //regardless of the Status Code option(s) returned by the server + context->running = FALSE; + + //Reinitialize state machine + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + + //Exit immediately + return; + } + else if(context->state == DHCPV6_STATE_DECLINE) + { + //The client must discard the Reply message if the contents of the + //Server Identifier option do not match the server’s DUID + if(!dhcpv6ClientCheckServerId(context, serverIdOption)) + return; + + //When the client receives a valid Reply message in response to a + //Decline message, the client considers the Decline event completed, + //regardless of the Status Code option returned by the server + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + + //Exit immediately + return; + } + else + { + //Silently discard the Reply message + return; + } + + //Check status code + if(status == DHCPV6_STATUS_USE_MULTICAST) + { + //When the client receives a Reply message with a Status Code option + //with the value UseMulticast, the client records the receipt of the + //message and sends subsequent messages to the server through the + //interface on which the message was received using multicast + return; + } + else if(status == DHCPV6_STATUS_UNSPEC_FAILURE) + { + //If the client receives a Reply message with a Status Code containing + //UnspecFail, the server is indicating that it was unable to process + //the message due to an unspecified failure condition + return; + } + + //This flag will be set if a valid IA_NA option is found + iaNaOptionFound = FALSE; + //Point to the first option + i = 0; + + //Loop through DHCPv6 options + while(i < length) + { + //Search for an IA_NA option + option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA); + + //Unable to find the specified option? + if(option == NULL) + break; + + //Parse the contents of the IA_NA option + error = dhcpv6ClientParseIaNaOption(context, option); + + //Check error code + if(error == NO_ERROR) + { + //A valid IA_NA option has been found + iaNaOptionFound = TRUE; + } + else if(error == ERROR_NOT_ON_LINK) + { + //When the client receives a NotOnLink status from the server + //in response to a Request, the client can either re-issue the + //Request without specifying any addresses or restart the DHCP + //server discovery process + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + + //Exit immediately + return; + } + else if(error == ERROR_NO_BINDING) + { + //When the client receives a Reply message in response to a Renew + //or Rebind message, the client sends a Request message if any of + //the IAs in the Reply message contains the NoBinding status code + dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); + + //Exit immediately + return; + } + else + { + //If an invalid option is received, the client discards + //the option and process the rest of the message... + } + + //Jump to the next option + i += sizeof(Dhcpv6Option) + ntohs(option->length); + } + + //No usable addresses in any of the IAs? + if(!iaNaOptionFound) + { + //Check whether the client receives a Reply message in response + //to a Renew or Rebind message + if(context->state == DHCPV6_STATE_RENEW || + context->state == DHCPV6_STATE_REBIND) + { + //The client sends a Renew/Rebind if the IA is not in the Reply message + } + else + { + //If the client finds no usable addresses in any of the IAs, it may try + //another server (perhaps restarting the DHCP server discovery process) + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } + + //Exit immediately + return; + } + + //Total number of valid IPv6 in the IA + n = 0; + //Number of new IPv6 addresses in the IA + k = 0; + //Minimum preferred lifetime observed in the IA + minPreferredLifetime = 0; + + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //Total number of valid IPv6 in the IA + n++; + + //Save the minimum preferred lifetime that has been observed so far + if(minPreferredLifetime < entry->preferredLifetime) + minPreferredLifetime = entry->preferredLifetime; + + //Update lifetimes of the current IPv6 address + ipv6AddAddr(interface, &entry->addr, entry->validLifetime, + entry->preferredLifetime); + + //New IPv6 address added? + if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE) + k++; + } + } + + //Make sure that the IA contains at least one IPv6 address + if(n > 0) + { + //Save the length of the DUID + context->serverIdLength = ntohs(serverIdOption->length); + //Record the server DUID + memcpy(context->serverId, serverIdOption->value, context->serverIdLength); + + //Save the time a which the lease was obtained + context->leaseStartTime = osGetSystemTime(); + + //Check the value of T1 + if(context->ia.t1 == 0) + { + //If T1 is set to 0 by the server, the client may send + //a Renew message at the client's discretion + if(minPreferredLifetime == DHCPV6_INFINITE_TIME) + context->ia.t1 = DHCPV6_INFINITE_TIME; + else + context->ia.t1 = minPreferredLifetime / 2; + } + + //Check the value of T2 + if(context->ia.t2 == 0) + { + //If T2 is set to 0 by the server, the client may send + //a Rebind message at the client's discretion + if(context->ia.t1 == DHCPV6_INFINITE_TIME) + context->ia.t2 = DHCPV6_INFINITE_TIME; + else + context->ia.t2 = context->ia.t1 + context->ia.t1 / 2; + } + + //Any addresses added in the IA? + if(k > 0) + { + //Perform Duplicate Address Detection for the new IPv6 addresses + dhcpv6ClientChangeState(context, DHCPV6_STATE_DAD, 0); + } + else + { + //Switch to the BOUND state + dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0); + } + } + else + { + //If the client finds no usable addresses in any of the IAs, it may try + //another server (perhaps restarting the DHCP server discovery process) + dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); + } +} + + +/** + * @brief Parse IA_NA option + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] option Pointer to the IA_NA option to parse + * @return Error code + **/ + +error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context, + const Dhcpv6Option *option) +{ + error_t error; + uint_t n; + size_t i; + size_t length; + NetInterface *interface; + Dhcpv6StatusCode status; + Dhcpv6IaNaOption *iaNaOption; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Number of addresses found in the IA_NA option + n = 0; + + //Make sure the IA_NA option is valid + if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption)) + return ERROR_INVALID_LENGTH; + + //Get the parameters associated with the IA_NA + iaNaOption = (Dhcpv6IaNaOption *) option->value; + //Compute the length of IA_NA Options field + length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption); + + //Check the IA identifier + if(ntohl(iaNaOption->iaId) != interface->id) + return ERROR_WRONG_IDENTIFIER; + + //If a client receives an IA_NA with T1 greater than T2, and both T1 + //and T2 are greater than 0, the client discards the IA_NA option and + //processes the remainder of the message as though the server had not + //included the invalid IA_NA option + if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0) + return ERROR_INVALID_PARAMETER; + + //The client examines the status code in each IA individually + status = dhcpv6GetStatusCode(iaNaOption->options, length); + + //Check error code + if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE) + { + //The client has received no usable address in the IA + return ERROR_NO_ADDRESS; + } + else if(status == DHCPV6_STATUS_NO_BINDING) + { + //Client record (binding) unavailable + return ERROR_NO_BINDING; + } + else if(status == DHCPV6_STATUS_NOT_ON_LINK) + { + //The prefix for the address is not appropriate for the + //link to which the client is attached + return ERROR_NOT_ON_LINK; + } + else if(status != DHCPV6_STATUS_SUCCESS) + { + //Failure, reason unspecified + return ERROR_FAILURE; + } + + //Record T1 and T2 times + context->ia.t1 = ntohl(iaNaOption->t1); + context->ia.t2 = ntohl(iaNaOption->t2); + + //Point to the first option + i = 0; + + //Loop through IA_NA options + while(i < length) + { + //Search for an IA Address option + option = dhcpv6GetOption(iaNaOption->options + i, length - i, DHCPV6_OPTION_IAADDR); + + //Unable to find the specified option? + if(option == NULL) + break; + + //Parse the contents of the IA Address option + error = dhcpv6ClientParseIaAddrOption(context, option); + + //Check status code + if(!error) + { + //Increment the number of addresses found in the IA_NA option + n++; + } + + //Jump to the next option + i += sizeof(Dhcpv6Option) + ntohs(option->length); + } + + //No usable addresses in the IA_NA option? + if(n == 0) + { + //Report an error + return ERROR_NO_ADDRESS; + } + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Parse IA Address option + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] option Pointer to the IA Address option to parse + * @return Error code + **/ + +error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context, + const Dhcpv6Option *option) +{ + size_t length; + uint32_t validLifetime; + uint32_t preferredLifetime; + Dhcpv6StatusCode status; + Dhcpv6IaAddrOption *iaAddrOption; + + //Make sure the IA Address option is valid + if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption)) + return ERROR_INVALID_LENGTH; + + //Point to the contents of the IA Address option + iaAddrOption = (Dhcpv6IaAddrOption *) option->value; + //Compute the length of IA Address Options field + length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption); + + //Convert lifetimes to host byte order + validLifetime = ntohl(iaAddrOption->validLifetime); + preferredLifetime = ntohl(iaAddrOption->preferredLifetime); + + //A client discards any addresses for which the preferred lifetime + //is greater than the valid lifetime + if(preferredLifetime > validLifetime) + return ERROR_INVALID_PARAMETER; + + //The client examines the status code in each IA Address + status = dhcpv6GetStatusCode(iaAddrOption->options, length); + + //Any error to report? + if(status != DHCPV6_STATUS_SUCCESS) + return ERROR_FAILURE; + + //Check the value of the Valid Lifetime + if(iaAddrOption->validLifetime > 0) + { + //Add any new addresses in the IA option to the IA as recorded + //by the client + dhcpv6ClientAddAddr(context, &iaAddrOption->address, + validLifetime, preferredLifetime); + } + else + { + //Discard any addresses from the IA, as recorded by the client, + //that have a valid lifetime of 0 in the IA Address option + dhcpv6ClientRemoveAddr(context, &iaAddrOption->address); + } + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Add an IPv6 address to the IA + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] addr IPv6 address to be added + * @param[in] validLifetime Valid lifetime, in seconds + * @param[in] preferredLifetime Preferred lifetime, in seconds + **/ + +void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr, + uint32_t validLifetime, uint32_t preferredLifetime) +{ + uint_t i; + Dhcpv6ClientAddrEntry *entry; + Dhcpv6ClientAddrEntry *firstFreeEntry; + + //Keep track of the first free entry + firstFreeEntry = NULL; + + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //Check whether the current entry matches the specified address + if(ipv6CompAddr(&entry->addr, addr)) + break; + } + else + { + //Keep track of the first free entry + if(firstFreeEntry == NULL) + firstFreeEntry = entry; + } + } + + //No matching entry found? + if(i >= IPV6_PREFIX_LIST_SIZE) + entry = firstFreeEntry; + + //Update the entry if necessary + if(entry != NULL) + { + //Save IPv6 address + entry->addr = *addr; + + //Save lifetimes + entry->validLifetime = validLifetime; + entry->preferredLifetime = preferredLifetime; + } +} + + +/** + * @brief Remove an IPv6 address from the IA + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] addr IPv6 address to be removed + **/ + +void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr) +{ + uint_t i; + NetInterface *interface; + Dhcpv6ClientAddrEntry *entry; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //Check whether the current entry matches the specified address + if(ipv6CompAddr(&entry->addr, addr)) + { + //The IPv6 address is no more valid and should be removed from + //the list of IPv6 addresses assigned to the interface + ipv6RemoveAddr(interface, addr); + + //Remove the IPv6 address from the IA + entry->validLifetime = 0; + } + } + } +} + + +/** + * @brief Flush the list of IPv6 addresses from the IA + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context) +{ + uint_t i; + NetInterface *interface; + Dhcpv6ClientAddrEntry *entry; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Loop through the IPv6 addresses recorded by the DHCPv6 client + for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) + { + //Point to the current entry + entry = &context->ia.addrList[i]; + + //Valid IPv6 address? + if(entry->validLifetime > 0) + { + //The IPv6 address is no more valid and should be removed from + //the list of IPv6 addresses assigned to the interface + ipv6RemoveAddr(interface, &entry->addr); + + //Remove the IPv6 address from the IA + entry->validLifetime = 0; + } + } +} + + +/** + * @brief Generate client's DUID + * @param[in] context Pointer to the DHCPv6 client context + * @return Error code + **/ + +error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context) +{ + NetInterface *interface; + Dhcpv6DuidLl *duid; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Point to the buffer where to format the client's DUID + duid = (Dhcpv6DuidLl *) context->clientId; + +#if (ETH_SUPPORT == ENABLED) + //Generate a DUID-LL from the MAC address + duid->type = HTONS(DHCPV6_DUID_LL); + duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH); + duid->linkLayerAddr = interface->macAddr; +#else + //Generate a DUID-LL from the EUI-64 identifier + duid->type = HTONS(DHCPV6_DUID_LL); + duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64); + duid->linkLayerAddr = interface->eui64; +#endif + + //Length of the newly generated DUID + context->clientIdLength = sizeof(Dhcpv6DuidLl); + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Generate client's fully qualified domain name + * @param[in] context Pointer to the DHCPv6 client context + * @return Error code + **/ + +error_t dhcpv6ClientGenerateFqdn(Dhcpv6ClientContext *context) +{ + NetInterface *interface; + Dhcpv6FqdnOption *fqdnOption; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Point to the buffer where to format the client's FQDN + fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn; + + //Set flags + fqdnOption->mbz = 0; + fqdnOption->n = FALSE; + fqdnOption->o = FALSE; + fqdnOption->s = FALSE; + + //Encode client's FQDN + context->clientFqdnLength = dnsEncodeName(interface->hostname, + fqdnOption->domainName); + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Generate a link-local address + * @param[in] context Pointer to the DHCPv6 client context + * @return Error code + **/ + +error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context) +{ + error_t error; + NetInterface *interface; + Ipv6Addr addr; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Check whether a link-local address has been manually assigned + if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID && + interface->ipv6Context.addrList[0].permanent) + { + //Keep using the current link-local address + error = NO_ERROR; + } + else + { + //A link-local address is formed by combining the well-known + //link-local prefix fe80::/10 with the interface identifier + ipv6GenerateLinkLocalAddr(&interface->eui64, &addr); + +#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, FALSE); + } + 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, FALSE); + } + } + + //Return status code + return error; +} + + +/** + * @brief Check the Server Identifier option + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] serverIdOption Pointer to the Server Identifier option + * @return TRUE if the option matches the server’s DUID, else FALSE + **/ + +bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context, + Dhcpv6Option *serverIdOption) +{ + bool_t valid = FALSE; + + //Check the length of the Server Identifier option + if(ntohs(serverIdOption->length) == context->serverIdLength) + { + //Check whether the Server Identifier option matches the server’s DUID + if(!memcmp(serverIdOption->value, context->serverId, context->serverIdLength)) + valid = TRUE; + } + + //Return TRUE if the option matches the server’s DUID + return valid; +} + + +/** + * @brief Manage DHCPv6 configuration timeout + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context) +{ + systime_t time; + NetInterface *interface; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Get current time + time = osGetSystemTime(); + + //Any registered callback? + if(context->settings.timeoutEvent != NULL) + { + //DHCPv6 configuration timeout? + if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0) + { + //Ensure the callback function is only called once + if(!context->timeoutEventDone) + { + //Release exclusive access + osReleaseMutex(&netMutex); + //Invoke user callback function + context->settings.timeoutEvent(context, interface); + //Get exclusive access + osAcquireMutex(&netMutex); + + //Set flag + context->timeoutEventDone = TRUE; + } + } + } +} + + +/** + * @brief Compute the time elapsed since the client sent the first message + * @param[in] context Pointer to the DHCPv6 client context + * @return The elapsed time expressed in hundredths of a second + **/ + +uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context) +{ + systime_t time; + + //Check retransmission counter + if(context->retransmitCount == 0) + { + //The elapsed time must be 0 for the first message + time = 0; + } + else + { + //Compute the time elapsed since the client sent the + //first message (in hundredths of a second) + time = (osGetSystemTime() - context->exchangeStartTime) / 10; + + //The value 0xFFFF is used to represent any elapsed time values + //greater than the largest time value that can be represented + time = MIN(time, 0xFFFF); + } + + //Convert the 16-bit value to network byte order + return htons(time); +} + + +/** + * @brief Update DHCPv6 FSM state + * @param[in] context Pointer to the DHCPv6 client context + * @param[in] newState New DHCPv6 state to switch to + * @param[in] delay Initial delay + **/ + +void dhcpv6ClientChangeState(Dhcpv6ClientContext *context, + Dhcpv6State newState, systime_t delay) +{ + systime_t time; + + //Get current time + time = osGetSystemTime(); + +#if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO) + //Sanity check + if(newState <= DHCPV6_STATE_DECLINE) + { + //DHCPv6 FSM states + static const char_t *stateLabel[] = + { + "INIT", + "SOLICIT", + "REQUEST", + "INIT-CONFIRM", + "CONFIRM", + "DAD", + "BOUND", + "RENEW", + "REBIND", + "RELEASE", + "DECLINE" + }; + + //Debug message + TRACE_INFO("%s: DHCPv6 client %s state\r\n", + formatSystemTime(time, NULL), stateLabel[newState]); + } +#endif + + //Set time stamp + context->timestamp = time; + //Set initial delay + context->timeout = delay; + //Reset retransmission counter + context->retransmitCount = 0; + //Switch to the new state + context->state = newState; + + //Any registered callback? + if(context->settings.stateChangeEvent != NULL) + { + NetInterface *interface; + + //Point to the underlying network interface + interface = context->settings.interface; + + //Release exclusive access + osReleaseMutex(&netMutex); + //Invoke user callback function + context->settings.stateChangeEvent(context, interface, newState); + //Get exclusive access + osAcquireMutex(&netMutex); + } +} + + +/** + * @brief Dump DHCPv6 configuration for debugging purpose + * @param[in] context Pointer to the DHCPv6 client context + **/ + +void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context) +{ +#if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO) + uint_t i; + NetInterface *interface; + Ipv6Context *ipv6Context; + + //Point to the underlying network interface + interface = context->settings.interface; + //Point to the IPv6 context + ipv6Context = &interface->ipv6Context; + + //Debug message + TRACE_INFO("\r\n"); + TRACE_INFO("DHCPv6 configuration:\r\n"); + + //Lease start time + TRACE_INFO(" Lease Start Time = %s\r\n", + formatSystemTime(context->leaseStartTime, NULL)); + + //T1 parameter + TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->ia.t1); + //T2 parameter + TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->ia.t2); + + //Global addresses + for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++) + { + TRACE_INFO(" Global Address %u = %s\r\n", i, + ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL)); + } + + //DNS servers + for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++) + { + TRACE_INFO(" DNS Server %u = %s\r\n", i + 1, + ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL)); + } + + //Debug message + TRACE_INFO("\r\n"); +#endif +} + +#endif +