Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers dhcpv6_client.c Source File

dhcpv6_client.c

Go to the documentation of this file.
00001 /**
00002  * @file dhcpv6_client.c
00003  * @brief DHCPv6 client (Dynamic Host Configuration Protocol for IPv6)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * The Dynamic Host Configuration Protocol for IPv6 enables DHCP servers to
00028  * pass configuration parameters such as IPv6 network addresses to IPv6
00029  * nodes. This protocol is a stateful counterpart to IPv6 Stateless Address
00030  * Autoconfiguration (RFC 2462), and can be used separately or concurrently
00031  * with the latter to obtain configuration parameters. Refer to RFC 3315
00032  *
00033  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00034  * @version 1.7.6
00035  **/
00036 
00037 //Switch to the appropriate trace level
00038 #define TRACE_LEVEL DHCPV6_TRACE_LEVEL
00039 
00040 //Dependencies
00041 #include <stdlib.h>
00042 #include "core/net.h"
00043 #include "ipv6/ipv6.h"
00044 #include "ipv6/ipv6_misc.h"
00045 #include "ipv6/ndp.h"
00046 #include "dhcpv6/dhcpv6_client.h"
00047 #include "dhcpv6/dhcpv6_common.h"
00048 #include "dhcpv6/dhcpv6_debug.h"
00049 #include "dns/dns_common.h"
00050 #include "date_time.h"
00051 #include "debug.h"
00052 
00053 //Check TCP/IP stack configuration
00054 #if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
00055 
00056 //Tick counter to handle periodic operations
00057 systime_t dhcpv6ClientTickCounter;
00058 
00059 //Requested DHCPv6 options
00060 static const uint16_t dhcpv6OptionList[] =
00061 {
00062    HTONS(DHCPV6_OPTION_DNS_SERVERS),
00063    HTONS(DHCPV6_OPTION_DOMAIN_LIST),
00064    HTONS(DHCPV6_OPTION_FQDN)
00065 };
00066 
00067 
00068 /**
00069  * @brief Initialize settings with default values
00070  * @param[out] settings Structure that contains DHCPv6 client settings
00071  **/
00072 
00073 void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings)
00074 {
00075    //Use default interface
00076    settings->interface = netGetDefaultInterface();
00077 
00078    //Support for quick configuration using rapid commit
00079    settings->rapidCommit = FALSE;
00080    //Use the DNS servers provided by the DHCPv6 server
00081    settings->manualDnsConfig = FALSE;
00082    //DHCPv6 configuration timeout
00083    settings->timeout = 0;
00084    //DHCPv6 configuration timeout event
00085    settings->timeoutEvent = NULL;
00086    //Link state change event
00087    settings->linkChangeEvent = NULL;
00088    //FSM state change event
00089    settings->stateChangeEvent = NULL;
00090 }
00091 
00092 
00093 /**
00094  * @brief DHCPv6 client initialization
00095  * @param[in] context Pointer to the DHCPv6 client context
00096  * @param[in] settings DHCPv6 client specific settings
00097  * @return Error code
00098  **/
00099 
00100 error_t dhcpv6ClientInit(Dhcpv6ClientContext *context, const Dhcpv6ClientSettings *settings)
00101 {
00102    error_t error;
00103    NetInterface *interface;
00104 
00105    //Debug message
00106    TRACE_INFO("Initializing DHCPv6 client...\r\n");
00107 
00108    //Ensure the parameters are valid
00109    if(context == NULL || settings == NULL)
00110       return ERROR_INVALID_PARAMETER;
00111 
00112    //A valid pointer to the interface being configured is required
00113    if(settings->interface == NULL)
00114       return ERROR_INVALID_PARAMETER;
00115 
00116    //Point to the underlying network interface
00117    interface = settings->interface;
00118 
00119    //Clear the DHCPv6 client context
00120    memset(context, 0, sizeof(Dhcpv6ClientContext));
00121    //Save user settings
00122    context->settings = *settings;
00123 
00124    //Generate client's DUID
00125    error = dhcpv6ClientGenerateDuid(context);
00126    //any error to report?
00127    if(error)
00128       return error;
00129 
00130    //Generate client's fully qualified domain name
00131    error = dhcpv6ClientGenerateFqdn(context);
00132    //any error to report?
00133    if(error)
00134       return error;
00135 
00136    //Callback function to be called when a DHCPv6 message is received
00137    error = udpAttachRxCallback(interface, DHCPV6_CLIENT_PORT,
00138       dhcpv6ClientProcessMessage, context);
00139    //Failed to register callback function?
00140    if(error)
00141       return error;
00142 
00143    //DHCPv6 client is currently suspended
00144    context->running = FALSE;
00145    //Initialize state machine
00146    context->state = DHCPV6_STATE_INIT;
00147 
00148    //Attach the DHCPv6 client context to the network interface
00149    interface->dhcpv6ClientContext = context;
00150 
00151    //Successful initialization
00152    return NO_ERROR;
00153 }
00154 
00155 
00156 /**
00157  * @brief Start DHCPv6 client
00158  * @param[in] context Pointer to the DHCPv6 client context
00159  * @return Error code
00160  **/
00161 
00162 error_t dhcpv6ClientStart(Dhcpv6ClientContext *context)
00163 {
00164    NetInterface *interface;
00165 
00166    //Check parameter
00167    if(context == NULL)
00168       return ERROR_INVALID_PARAMETER;
00169 
00170    //Debug message
00171    TRACE_INFO("Starting DHCPv6 client...\r\n");
00172 
00173    //Get exclusive access
00174    osAcquireMutex(&netMutex);
00175 
00176    //Point to the underlying network interface
00177    interface = context->settings.interface;
00178 
00179    //Flush the list of IPv6 addresses from the client's IA
00180    dhcpv6ClientFlushAddrList(context);
00181 
00182    //Automatic DNS server configuration?
00183    if(!context->settings.manualDnsConfig)
00184    {
00185       //Clear the list of DNS servers
00186       ipv6FlushDnsServerList(interface);
00187    }
00188 
00189    //Check if the link is up?
00190    if(interface->linkState)
00191    {
00192       //A link-local address is formed by combining the well-known
00193       //link-local prefix fe80::/10 with the interface identifier
00194       dhcpv6ClientGenerateLinkLocalAddr(context);
00195    }
00196 
00197    //Start DHCPv6 client
00198    context->running = TRUE;
00199    //Initialize state machine
00200    context->state = DHCPV6_STATE_INIT;
00201 
00202    //Release exclusive access
00203    osReleaseMutex(&netMutex);
00204 
00205    //Successful processing
00206    return NO_ERROR;
00207 }
00208 
00209 
00210 /**
00211  * @brief Stop DHCPv6 client
00212  * @param[in] context Pointer to the DHCPv6 client context
00213  * @return Error code
00214  **/
00215 
00216 error_t dhcpv6ClientStop(Dhcpv6ClientContext *context)
00217 {
00218    //Check parameter
00219    if(context == NULL)
00220       return ERROR_INVALID_PARAMETER;
00221 
00222    //Debug message
00223    TRACE_INFO("Stopping DHCPv6 client...\r\n");
00224 
00225    //Get exclusive access
00226    osAcquireMutex(&netMutex);
00227 
00228    //Stop DHCPv6 client
00229    context->running = FALSE;
00230    //Reinitialize state machine
00231    context->state = DHCPV6_STATE_INIT;
00232 
00233    //Release exclusive access
00234    osReleaseMutex(&netMutex);
00235 
00236    //Successful processing
00237    return NO_ERROR;
00238 }
00239 
00240 
00241 /**
00242  * @brief Release DHCPv6 lease
00243  * @param[in] context Pointer to the DHCPv6 client context
00244  * @return Error code
00245  **/
00246 
00247 error_t dhcpv6ClientRelease(Dhcpv6ClientContext *context)
00248 {
00249    uint_t i;
00250    NetInterface *interface;
00251    Dhcpv6ClientAddrEntry *entry;
00252 
00253    //Check parameter
00254    if(context == NULL)
00255       return ERROR_INVALID_PARAMETER;
00256 
00257    //Debug message
00258    TRACE_INFO("Releasing DHCPv6 lease...\r\n");
00259 
00260    //Get exclusive access
00261    osAcquireMutex(&netMutex);
00262 
00263    //Point to the underlying network interface
00264    interface = context->settings.interface;
00265 
00266    //Check whether the DHCPv6 client is running
00267    if(context->running)
00268    {
00269       //BOUND state?
00270       if(context->state == DHCPV6_STATE_BOUND)
00271       {
00272          //Loop through the IPv6 addresses recorded by the DHCPv6 client
00273          for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
00274          {
00275             //Point to the current entry
00276             entry = &context->ia.addrList[i];
00277 
00278             //Valid IPv6 address?
00279             if(entry->validLifetime > 0)
00280             {
00281                //The client must stop using the addresses being released as soon
00282                //as the client begins the Release message exchange process
00283                ipv6RemoveAddr(interface, &entry->addr);
00284             }
00285          }
00286 
00287          //Switch to the RELEASE state
00288          dhcpv6ClientChangeState(context, DHCPV6_STATE_RELEASE, 0);
00289       }
00290       else
00291       {
00292          //Stop DHCPv6 client
00293          context->running = FALSE;
00294          //Reinitialize state machine
00295          context->state = DHCPV6_STATE_INIT;
00296       }
00297    }
00298 
00299    //Release exclusive access
00300    osReleaseMutex(&netMutex);
00301 
00302    //Successful processing
00303    return NO_ERROR;
00304 }
00305 
00306 
00307 /**
00308  * @brief Retrieve current state
00309  * @param[in] context Pointer to the DHCPv6 client context
00310  * @return Current DHCPv6 client state
00311  **/
00312 
00313 Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context)
00314 {
00315    Dhcpv6State state;
00316 
00317    //Get exclusive access
00318    osAcquireMutex(&netMutex);
00319    //Get current state
00320    state = context->state;
00321    //Release exclusive access
00322    osReleaseMutex(&netMutex);
00323 
00324    //Return current state
00325    return state;
00326 }
00327 
00328 
00329 /**
00330  * @brief DHCPv6 client timer handler
00331  *
00332  * This routine must be periodically called by the TCP/IP stack to
00333  * manage DHCPv6 client operation
00334  *
00335  * @param[in] context Pointer to the DHCPv6 client context
00336  **/
00337 
00338 
00339 void dhcpv6ClientTick(Dhcpv6ClientContext *context)
00340 {
00341    //Make sure the DHCPv6 client has been properly instantiated
00342    if(context == NULL)
00343       return;
00344 
00345    //DHCPv6 client finite state machine
00346    switch(context->state)
00347    {
00348    //Process INIT state
00349    case DHCPV6_STATE_INIT:
00350       //This is the initialization state, where a client begins the process of
00351       //acquiring a lease. It also returns here when a lease ends, or when a
00352       //lease negotiation fails
00353       dhcpv6ClientStateInit(context);
00354       break;
00355    //Process SOLICIT state
00356    case DHCPV6_STATE_SOLICIT:
00357       //The client sends a Solicit message to locate servers
00358       dhcpv6ClientStateSolicit(context);
00359       break;
00360    //Process REQUEST state
00361    case DHCPV6_STATE_REQUEST:
00362       //The client sends a Request message to request configuration
00363       //parameters, including IP addresses, from a specific server
00364       dhcpv6ClientStateRequest(context);
00365       break;
00366    //Process INIT-CONFIRM state
00367    case DHCPV6_STATE_INIT_CONFIRM:
00368       //When a client that already has a valid lease starts up after a
00369       //power-down or reboot, it starts here instead of the INIT state
00370       dhcpv6ClientStateInitConfirm(context);
00371       break;
00372    //Process CONFIRM state
00373    case DHCPV6_STATE_CONFIRM:
00374       //The client sends a Confirm message to any available server
00375       //to determine whether the addresses it was assigned are still
00376       //appropriate to the link to which the client is connected
00377       dhcpv6ClientStateConfirm(context);
00378       break;
00379    //Process DAD state
00380    case DHCPV6_STATE_DAD:
00381       //The client should perform duplicate address detection on each
00382       //of the addresses in any IAs it receives in the Reply message
00383       //before using that address for traffic
00384       dhcpv6ClientStateDad(context);
00385       break;
00386    //Process BOUND state
00387    case DHCPV6_STATE_BOUND:
00388       //The client has a valid lease and is in its normal operating state
00389       dhcpv6ClientStateBound(context);
00390       break;
00391    //Process RENEW state
00392    case DHCPV6_STATE_RENEW:
00393       //The client sends a Renew message to the server that originally
00394       //provided the client's addresses and configuration parameters to
00395       //extend the lifetimes on the addresses assigned to the client
00396       //and to update other configuration parameters
00397       dhcpv6ClientStateRenew(context);
00398       break;
00399    //Process REBIND state
00400    case DHCPV6_STATE_REBIND:
00401       //The client sends a Rebind message to any available server to extend
00402       //the lifetimes on the addresses assigned to the client and to update
00403       //other configuration parameters. This message is sent after a client
00404       //receives no response to a Renew message
00405       dhcpv6ClientStateRebind(context);
00406       break;
00407    //Process RELEASE state
00408    case DHCPV6_STATE_RELEASE:
00409       //To release one or more addresses, a client sends a Release message
00410       //to the server
00411       dhcpv6ClientStateRelease(context);
00412       break;
00413    //Process DECLINE state
00414    case DHCPV6_STATE_DECLINE:
00415       //If a client detects that one or more addresses assigned to it by a
00416       //server are already in use by another node, the client sends a Decline
00417       //message to the server to inform it that the address is suspect
00418       dhcpv6ClientStateDecline(context);
00419       break;
00420    //Invalid state...
00421    default:
00422       //Switch to the default state
00423       context->state = DHCPV6_STATE_INIT;
00424       break;
00425    }
00426 }
00427 
00428 
00429 /**
00430  * @brief Callback function for link change event
00431  * @param[in] context Pointer to the DHCPv6 client context
00432  **/
00433 
00434 void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context)
00435 {
00436    NetInterface *interface;
00437 
00438    //Make sure the DHCPv6 client has been properly instantiated
00439    if(context == NULL)
00440       return;
00441 
00442    //Point to the underlying network interface
00443    interface = context->settings.interface;
00444 
00445    //Check whether the DHCPv6 client is running
00446    if(context->running)
00447    {
00448       //Automatic DNS server configuration?
00449       if(!context->settings.manualDnsConfig)
00450       {
00451          //Clear the list of DNS servers
00452          ipv6FlushDnsServerList(interface);
00453       }
00454 
00455       //Link-up event?
00456       if(interface->linkState)
00457       {
00458          //A link-local address is formed by combining the well-known
00459          //link-local prefix fe80::/10 with the interface identifier
00460          dhcpv6ClientGenerateLinkLocalAddr(context);
00461       }
00462    }
00463 
00464    //Check the state of the DHCPv6 client
00465    switch(context->state)
00466    {
00467    case DHCPV6_STATE_INIT_CONFIRM:
00468    case DHCPV6_STATE_CONFIRM :
00469    case DHCPV6_STATE_DAD:
00470    case DHCPV6_STATE_BOUND:
00471    case DHCPV6_STATE_RENEW:
00472    case DHCPV6_STATE_REBIND:
00473       //The client already has a valid lease
00474       context->state = DHCPV6_STATE_INIT_CONFIRM;
00475       break;
00476    case DHCPV6_STATE_RELEASE:
00477       //Stop DHCPv6 client
00478       context->running = FALSE;
00479       //Reinitialize state machine
00480       context->state = DHCPV6_STATE_INIT;
00481       break;
00482    default:
00483       //Switch to the INIT state
00484       context->state = DHCPV6_STATE_INIT;
00485       break;
00486    }
00487 
00488    //Any registered callback?
00489    if(context->settings.linkChangeEvent != NULL)
00490    {
00491       //Release exclusive access
00492       osReleaseMutex(&netMutex);
00493       //Invoke user callback function
00494       context->settings.linkChangeEvent(context, interface, interface->linkState);
00495       //Get exclusive access
00496       osAcquireMutex(&netMutex);
00497    }
00498 }
00499 
00500 
00501 /**
00502  * @brief INIT state
00503  *
00504  * This is the initialization state, where a client begins the process of
00505  * acquiring a lease. It also returns here when a lease ends, or when a
00506  * lease negotiation fails
00507  *
00508  * @param[in] context Pointer to the DHCPv6 client context
00509  **/
00510 
00511 void dhcpv6ClientStateInit(Dhcpv6ClientContext *context)
00512 {
00513    systime_t delay;
00514    NetInterface *interface;
00515 
00516    //Point to the underlying network interface
00517    interface = context->settings.interface;
00518 
00519    //Check whether the DHCPv6 client is running
00520    if(context->running)
00521    {
00522       //Wait for the link to be up before starting DHCPv6 configuration
00523       if(interface->linkState)
00524       {
00525          //Make sure that a valid link-local address has been assigned to the interface
00526          if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
00527          {
00528             //Flush the list of IPv6 addresses from the client's IA
00529             dhcpv6ClientFlushAddrList(context);
00530 
00531             //The first Solicit message from the client on the interface must be
00532             //delayed by a random amount of time between 0 and SOL_MAX_DELAY
00533             delay = dhcpv6RandRange(0, DHCPV6_CLIENT_SOL_MAX_DELAY);
00534 
00535             //Record the time at which the client started
00536             //the address acquisition process
00537             context->configStartTime = osGetSystemTime();
00538             //Clear flag
00539             context->timeoutEventDone = FALSE;
00540 
00541             //Switch to the SOLICIT state
00542             dhcpv6ClientChangeState(context, DHCPV6_STATE_SOLICIT, delay);
00543          }
00544       }
00545    }
00546 }
00547 
00548 
00549 /**
00550  * @brief SOLICIT state
00551  *
00552  * A client uses the Solicit message to discover DHCPv6 servers
00553  *
00554  * @param[in] context Pointer to the DHCPv6 client context
00555  **/
00556 
00557 void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context)
00558 {
00559    systime_t time;
00560 
00561    //Get current time
00562    time = osGetSystemTime();
00563 
00564    //Check current time
00565    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00566    {
00567       //Check retransmission counter
00568       if(context->retransmitCount == 0)
00569       {
00570          //Reset server preference value
00571          context->serverPreference = -1;
00572          //Generate a 24-bit transaction ID
00573          context->transactionId = netGetRand() & 0x00FFFFFF;
00574 
00575          //Send a Solicit message
00576          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT);
00577 
00578          //Save the time at which the message was sent
00579          context->exchangeStartTime = time;
00580          context->timestamp = time;
00581 
00582          //If the client is waiting for an Advertise message, the first RT
00583          //must be selected to be strictly greater than IRT
00584          context->timeout = DHCPV6_CLIENT_SOL_TIMEOUT +
00585             abs(dhcpv6Rand(DHCPV6_CLIENT_SOL_TIMEOUT));
00586 
00587          //Increment retransmission counter
00588          context->retransmitCount++;
00589       }
00590       else
00591       {
00592          //Check whether a valid Advertise message has been received
00593          if(context->serverPreference >= 0)
00594          {
00595             //Continue configuration procedure
00596             dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
00597          }
00598          else
00599          {
00600             //Send a Solicit message
00601             dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT);
00602 
00603             //Save the time at which the message was sent
00604             context->timestamp = time;
00605 
00606             //The RT is doubled for each subsequent retransmission
00607             context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
00608 
00609             //MRT specifies an upper bound on the value of RT
00610             if(context->timeout > DHCPV6_CLIENT_SOL_MAX_RT)
00611             {
00612                //Compute retransmission timeout
00613                context->timeout = DHCPV6_CLIENT_SOL_MAX_RT +
00614                   dhcpv6Rand(DHCPV6_CLIENT_SOL_MAX_RT);
00615             }
00616 
00617             //Increment retransmission counter
00618             context->retransmitCount++;
00619          }
00620       }
00621    }
00622 
00623    //Manage DHCPv6 configuration timeout
00624    dhcpv6ClientCheckTimeout(context);
00625 }
00626 
00627 
00628 /**
00629  * @brief REQUEST state
00630  *
00631  * The client uses a Request message to populate IAs with addresses and obtain
00632  * other configuration information. The client includes one or more more IA
00633  * options in the Request message. The server then returns addresses and other
00634  * information about the IAs to the client in IA options in a Reply message
00635  *
00636  * @param[in] context Pointer to the DHCPv6 client context
00637  **/
00638 
00639 void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context)
00640 {
00641    systime_t time;
00642 
00643    //Get current time
00644    time = osGetSystemTime();
00645 
00646    //Check current time
00647    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00648    {
00649       //Check retransmission counter
00650       if(context->retransmitCount == 0)
00651       {
00652          //Generate a 24-bit transaction ID
00653          context->transactionId = netGetRand() & 0x00FFFFFF;
00654 
00655          //Send a Request message
00656          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST);
00657 
00658          //Save the time at which the message was sent
00659          context->exchangeStartTime = time;
00660          context->timestamp = time;
00661 
00662          //Initial retransmission timeout
00663          context->timeout = DHCPV6_CLIENT_REQ_TIMEOUT +
00664             dhcpv6Rand(DHCPV6_CLIENT_REQ_TIMEOUT);
00665 
00666          //Increment retransmission counter
00667          context->retransmitCount++;
00668       }
00669       else if(context->retransmitCount < DHCPV6_CLIENT_REQ_MAX_RC)
00670       {
00671          //Send a Request message
00672          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST);
00673 
00674          //Save the time at which the message was sent
00675          context->timestamp = time;
00676 
00677          //The RT is doubled for each subsequent retransmission
00678          context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
00679 
00680          //MRT specifies an upper bound on the value of RT
00681          if(context->timeout > DHCPV6_CLIENT_REQ_MAX_RT)
00682          {
00683             //Compute retransmission timeout
00684             context->timeout = DHCPV6_CLIENT_REQ_MAX_RT +
00685                dhcpv6Rand(DHCPV6_CLIENT_REQ_MAX_RT);
00686          }
00687 
00688          //Increment retransmission counter
00689          context->retransmitCount++;
00690       }
00691       else
00692       {
00693          //If the client does not receive a response within a reasonable
00694          //period of time, then it restarts the initialization procedure
00695          dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
00696       }
00697    }
00698 
00699    //Manage DHCPv6 configuration timeout
00700    dhcpv6ClientCheckTimeout(context);
00701 }
00702 
00703 
00704 /**
00705  * @brief INIT-CONFIRM state
00706  *
00707  * When a client that already has a valid lease starts up after a
00708  * power-down or reboot, it starts here instead of the INIT state
00709  *
00710  * @param[in] context Pointer to the DHCPv6 client context
00711  **/
00712 
00713 void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context)
00714 {
00715    systime_t delay;
00716    NetInterface *interface;
00717 
00718    //Point to the underlying network interface
00719    interface = context->settings.interface;
00720 
00721    //Check whether the DHCPv6 client is running
00722    if(context->running)
00723    {
00724       //Wait for the link to be up before starting DHCPv6 configuration
00725       if(interface->linkState)
00726       {
00727          //Make sure that a valid link-local address has been assigned to the interface
00728          if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
00729          {
00730             //The first Confirm message from the client on the interface must be
00731             //delayed by a random amount of time between 0 and CNF_MAX_DELAY
00732             delay = dhcpv6RandRange(0, DHCPV6_CLIENT_CNF_MAX_DELAY);
00733 
00734             //Record the time at which the client started
00735             //the address acquisition process
00736             context->configStartTime = osGetSystemTime();
00737             //Clear flag
00738             context->timeoutEventDone = FALSE;
00739 
00740             //Switch to the CONFIRM state
00741             dhcpv6ClientChangeState(context, DHCPV6_STATE_CONFIRM, delay);
00742          }
00743       }
00744    }
00745 }
00746 
00747 
00748 /**
00749  * @brief CONFIRM state
00750  *
00751  * Whenever a client may have moved to a new link, the prefixes from
00752  * the addresses assigned to the interfaces on that link may no longer
00753  * be appropriate for the link to which the client is attached. In such
00754  * the client must initiate a Confirm/Reply message exchange
00755  *
00756  * @param[in] context Pointer to the DHCPv6 client context
00757  **/
00758 
00759 void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context)
00760 {
00761    systime_t time;
00762 
00763    //Get current time
00764    time = osGetSystemTime();
00765 
00766    //Check current time
00767    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00768    {
00769       //Check retransmission counter
00770       if(context->retransmitCount == 0)
00771       {
00772          //Generate a 24-bit transaction ID
00773          context->transactionId = netGetRand() & 0x00FFFFFF;
00774 
00775          //Send a Confirm message
00776          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM);
00777 
00778          //Save the time at which the client sent the first message
00779          context->exchangeStartTime = time;
00780          context->timestamp = time;
00781 
00782          //Initial retransmission timeout
00783          context->timeout = DHCPV6_CLIENT_CNF_TIMEOUT +
00784             dhcpv6Rand(DHCPV6_CLIENT_CNF_TIMEOUT);
00785 
00786          //Increment retransmission counter
00787          context->retransmitCount++;
00788       }
00789       else
00790       {
00791          //Send a Confirm message
00792          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM);
00793 
00794          //Save the time at which the message was sent
00795          context->timestamp = time;
00796 
00797          //The RT is doubled for each subsequent retransmission
00798          context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
00799 
00800          //MRT specifies an upper bound on the value of RT
00801          if(context->timeout > DHCPV6_CLIENT_CNF_MAX_RT)
00802          {
00803             //Compute retransmission timeout
00804             context->timeout = DHCPV6_CLIENT_CNF_MAX_RT +
00805                dhcpv6Rand(DHCPV6_CLIENT_CNF_MAX_RT);
00806          }
00807 
00808          //Increment retransmission counter
00809          context->retransmitCount++;
00810       }
00811    }
00812    else
00813    {
00814       //Check retransmission counter
00815       if(context->retransmitCount > 0)
00816       {
00817          //The message exchange fails once MRD seconds have elapsed since
00818          //the client first transmitted the message
00819          if(timeCompare(time, context->exchangeStartTime + DHCPV6_CLIENT_CNF_MAX_RD) >= 0)
00820          {
00821             //If the client receives no responses before the message transmission
00822             //process terminates, the client should continue to use any IP
00823             //addresses using the last known lifetimes for those addresses
00824             dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
00825          }
00826       }
00827    }
00828 
00829    //Manage DHCPv6 configuration timeout
00830    dhcpv6ClientCheckTimeout(context);
00831 }
00832 
00833 
00834 /**
00835  * @brief DAD state
00836  *
00837  * The client perform duplicate address detection on each
00838  * of the addresses in any IAs it receives in the Reply message
00839  * before using that address for traffic
00840  *
00841  * @param[in] context Pointer to the DHCPv6 client context
00842  **/
00843 
00844 void dhcpv6ClientStateDad(Dhcpv6ClientContext *context)
00845 {
00846    uint_t i;
00847    NetInterface *interface;
00848    Ipv6AddrState state;
00849    Dhcpv6ClientAddrEntry *entry;
00850 
00851    //Point to the underlying network interface
00852    interface = context->settings.interface;
00853 
00854    //Loop through the IPv6 addresses recorded by the DHCPv6 client
00855    for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
00856    {
00857       //Point to the current entry
00858       entry = &context->ia.addrList[i];
00859 
00860       //Check the IPv6 address is a tentative address?
00861       if(entry->validLifetime > 0)
00862       {
00863          //Get the state of the current IPv6 address
00864          state = ipv6GetAddrState(interface, &entry->addr);
00865 
00866          //Duplicate Address Detection in progress?
00867          if(state == IPV6_ADDR_STATE_TENTATIVE)
00868          {
00869             //Exit immediately
00870             return;
00871          }
00872          //Duplicate Address Detection failed?
00873          else if(state == IPV6_ADDR_STATE_INVALID)
00874          {
00875             //Switch to the DECLINE state
00876             dhcpv6ClientChangeState(context, DHCPV6_STATE_DECLINE, 0);
00877             //Exit immediately
00878             return;
00879          }
00880       }
00881    }
00882 
00883    //Dump current DHCPv6 configuration for debugging purpose
00884    dhcpv6ClientDumpConfig(context);
00885    //Switch to the BOUND state
00886    dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0);
00887 }
00888 
00889 
00890 /**
00891  * @brief BOUND state
00892  *
00893  * Client has a valid lease and is in its normal operating state
00894  *
00895  * @param[in] context Pointer to the DHCPv6 client context
00896  **/
00897 
00898 void dhcpv6ClientStateBound(Dhcpv6ClientContext *context)
00899 {
00900    systime_t t1;
00901    systime_t time;
00902 
00903    //Get current time
00904    time = osGetSystemTime();
00905 
00906    //A client will never attempt to extend the lifetime of any
00907    //address in an IA with T1 set to 0xffffffff
00908    if(context->ia.t1 != DHCPV6_INFINITE_TIME)
00909    {
00910       //Convert T1 to milliseconds
00911       if(context->ia.t1 < (MAX_DELAY / 1000))
00912          t1 = context->ia.t1 * 1000;
00913       else
00914          t1 = MAX_DELAY;
00915 
00916       //Check the time elapsed since the lease was obtained
00917       if(timeCompare(time, context->leaseStartTime + t1) >= 0)
00918       {
00919          //Record the time at which the client started the address renewal process
00920          context->configStartTime = time;
00921 
00922          //Enter the RENEW state
00923          dhcpv6ClientChangeState(context, DHCPV6_STATE_RENEW, 0);
00924       }
00925    }
00926 }
00927 
00928 
00929 /**
00930  * @brief RENEW state
00931  *
00932  * The client sends a Renew message to the server that originally
00933  * provided the client's addresses and configuration parameters to
00934  * extend the lifetimes on the addresses assigned to the client
00935  * and to update other configuration parameters
00936  *
00937  * @param[in] context Pointer to the DHCPv6 client context
00938  **/
00939 
00940 void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context)
00941 {
00942    systime_t t2;
00943    systime_t time;
00944 
00945    //Get current time
00946    time = osGetSystemTime();
00947 
00948    //Check current time
00949    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00950    {
00951       //Check retransmission counter
00952       if(context->retransmitCount == 0)
00953       {
00954          //Generate a 24-bit transaction ID
00955          context->transactionId = netGetRand() & 0x00FFFFFF;
00956 
00957          //Send a Renew message
00958          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW);
00959 
00960          //Save the time at which the message was sent
00961          context->exchangeStartTime = time;
00962          context->timestamp = time;
00963 
00964          //Initial retransmission timeout
00965          context->timeout = DHCPV6_CLIENT_REN_TIMEOUT +
00966             dhcpv6Rand(DHCPV6_CLIENT_REN_TIMEOUT);
00967       }
00968       else
00969       {
00970          //Send a Renew message
00971          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW);
00972 
00973          //Save the time at which the message was sent
00974          context->timestamp = time;
00975 
00976          //The RT is doubled for each subsequent retransmission
00977          context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
00978 
00979          //MRT specifies an upper bound on the value of RT
00980          if(context->timeout > DHCPV6_CLIENT_REN_MAX_RT)
00981          {
00982             //Compute retransmission timeout
00983             context->timeout = DHCPV6_CLIENT_REN_MAX_RT +
00984                dhcpv6Rand(DHCPV6_CLIENT_REN_MAX_RT);
00985          }
00986       }
00987 
00988       //Increment retransmission counter
00989       context->retransmitCount++;
00990    }
00991    else
00992    {
00993       //A client will never attempt to use a Rebind message to locate a
00994       //different server to extend the lifetime of any address in an IA
00995       //with T2 set to 0xffffffff
00996       if(context->ia.t2 != DHCPV6_INFINITE_TIME)
00997       {
00998          //Convert T2 to milliseconds
00999          if(context->ia.t2 < (MAX_DELAY / 1000))
01000             t2 = context->ia.t2 * 1000;
01001          else
01002             t2 = MAX_DELAY;
01003 
01004          //Check whether T2 timer has expired
01005          if(timeCompare(time, context->leaseStartTime + t2) >= 0)
01006          {
01007             //Switch to the REBIND state
01008             dhcpv6ClientChangeState(context, DHCPV6_STATE_REBIND, 0);
01009          }
01010       }
01011    }
01012 }
01013 
01014 
01015 /**
01016  * @brief REBIND state
01017  *
01018  * The client sends a Rebind message to any available server to extend
01019  * the lifetimes on the addresses assigned to the client and to update
01020  * other configuration parameters. This message is sent after a client
01021  * receives no response to a Renew message
01022  *
01023  * @param[in] context Pointer to the DHCPv6 client context
01024  **/
01025 
01026 void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context)
01027 {
01028    uint_t i;
01029    systime_t time;
01030    NetInterface *interface;
01031    Dhcpv6ClientAddrEntry *entry;
01032 
01033    //Point to the underlying network interface
01034    interface = context->settings.interface;
01035 
01036    //Get current time
01037    time = osGetSystemTime();
01038 
01039    //Check current time
01040    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
01041    {
01042       //Check retransmission counter
01043       if(context->retransmitCount == 0)
01044       {
01045          //Generate a 24-bit transaction ID
01046          context->transactionId = netGetRand() & 0x00FFFFFF;
01047 
01048          //Send a Rebind message
01049          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND);
01050 
01051          //Save the time at which the message was sent
01052          context->exchangeStartTime = time;
01053          context->timestamp = time;
01054 
01055          //Initial retransmission timeout
01056          context->timeout = DHCPV6_CLIENT_REB_TIMEOUT +
01057             dhcpv6Rand(DHCPV6_CLIENT_REB_TIMEOUT);
01058       }
01059       else
01060       {
01061          //Send a Rebind message
01062          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND);
01063 
01064          //Save the time at which the message was sent
01065          context->timestamp = time;
01066 
01067          //The RT is doubled for each subsequent retransmission
01068          context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
01069 
01070          //MRT specifies an upper bound on the value of RT
01071          if(context->timeout > DHCPV6_CLIENT_REB_MAX_RT)
01072          {
01073             //Compute retransmission timeout
01074             context->timeout = DHCPV6_CLIENT_REB_MAX_RT +
01075                dhcpv6Rand(DHCPV6_CLIENT_REB_MAX_RT);
01076          }
01077       }
01078 
01079       //Increment retransmission counter
01080       context->retransmitCount++;
01081    }
01082    else
01083    {
01084       //Loop through the IPv6 addresses recorded by the DHCPv6 client
01085       for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
01086       {
01087          //Point to the current entry
01088          entry = &context->ia.addrList[i];
01089 
01090          //Valid IPv6 address?
01091          if(entry->validLifetime > 0)
01092          {
01093             //Check whether the valid lifetime has expired
01094             if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_INVALID)
01095             {
01096                //Restart DHCPv6 configuration
01097                dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01098             }
01099          }
01100       }
01101    }
01102 }
01103 
01104 
01105 /**
01106  * @brief RELEASE state
01107  *
01108  * To release one or more addresses, a client sends a Release message
01109  * to the server
01110  *
01111  * @param[in] context Pointer to the DHCPv6 client context
01112  **/
01113 
01114 void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context)
01115 {
01116    systime_t time;
01117 
01118    //Get current time
01119    time = osGetSystemTime();
01120 
01121    //Check current time
01122    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
01123    {
01124       //Check retransmission counter
01125       if(context->retransmitCount == 0)
01126       {
01127          //Generate a 24-bit transaction ID
01128          context->transactionId = netGetRand() & 0x00FFFFFF;
01129 
01130          //Send a Release message
01131          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE);
01132 
01133          //Save the time at which the message was sent
01134          context->exchangeStartTime = time;
01135          context->timestamp = time;
01136 
01137          //Initial retransmission timeout
01138          context->timeout = DHCPV6_CLIENT_REL_TIMEOUT +
01139             dhcpv6Rand(DHCPV6_CLIENT_REL_TIMEOUT);
01140 
01141          //Increment retransmission counter
01142          context->retransmitCount++;
01143       }
01144       else if(context->retransmitCount < DHCPV6_CLIENT_REL_MAX_RC)
01145       {
01146          //Send a Release message
01147          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE);
01148 
01149          //Save the time at which the message was sent
01150          context->timestamp = time;
01151 
01152          //The RT is doubled for each subsequent retransmission
01153          context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
01154 
01155          //Increment retransmission counter
01156          context->retransmitCount++;
01157       }
01158       else
01159       {
01160          //Implementations should retransmit one or more times, but may
01161          //choose to terminate the retransmission procedure early
01162          context->running = FALSE;
01163 
01164          //Reinitialize state machine
01165          dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01166       }
01167    }
01168 }
01169 
01170 
01171 /**
01172  * @brief DECLINE state
01173  *
01174  * If a client detects that one or more addresses assigned to it by a
01175  * server are already in use by another node, the client sends a Decline
01176  * message to the server to inform it that the address is suspect
01177  *
01178  * @param[in] context Pointer to the DHCPv6 client context
01179  **/
01180 
01181 void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context)
01182 {
01183    systime_t time;
01184 
01185    //Get current time
01186    time = osGetSystemTime();
01187 
01188    //Check current time
01189    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
01190    {
01191       //Check retransmission counter
01192       if(context->retransmitCount == 0)
01193       {
01194          //Generate a 24-bit transaction ID
01195          context->transactionId = netGetRand() & 0x00FFFFFF;
01196 
01197          //Send a Decline message
01198          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE);
01199 
01200          //Save the time at which the message was sent
01201          context->exchangeStartTime = time;
01202          context->timestamp = time;
01203 
01204          //Initial retransmission timeout
01205          context->timeout = DHCPV6_CLIENT_DEC_TIMEOUT +
01206             dhcpv6Rand(DHCPV6_CLIENT_DEC_TIMEOUT);
01207 
01208          //Increment retransmission counter
01209          context->retransmitCount++;
01210       }
01211       else if(context->retransmitCount < DHCPV6_CLIENT_DEC_MAX_RC)
01212       {
01213          //Send a Decline message
01214          dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE);
01215 
01216          //Save the time at which the message was sent
01217          context->timestamp = time;
01218 
01219          //The RT is doubled for each subsequent retransmission
01220          context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
01221 
01222          //Increment retransmission counter
01223          context->retransmitCount++;
01224       }
01225       else
01226       {
01227          //If the client does not receive a response within a reasonable
01228          //period of time, then it restarts the initialization procedure
01229          dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01230       }
01231    }
01232 }
01233 
01234 
01235 /**
01236  * @brief Send Solicit message
01237  * @param[in] context Pointer to the DHCPv6 client context
01238  * @param[in] type DHCPv6 message type
01239  * @return Error code
01240  **/
01241 
01242 error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context,
01243    Dhcpv6MessageType type)
01244 {
01245    error_t error;
01246    uint_t i;
01247    size_t length;
01248    size_t offset;
01249    NetBuffer *buffer;
01250    NetInterface *interface;
01251    Dhcpv6Message *message;
01252    Dhcpv6Option *option;
01253    Dhcpv6IaNaOption iaNaOption;
01254    Dhcpv6IaAddrOption iaAddrOption;
01255    Dhcpv6FqdnOption *fqdnOption;
01256    Dhcpv6ElapsedTimeOption elapsedTimeOption;
01257    Dhcpv6ClientAddrEntry *entry;
01258    IpAddr destIpAddr;
01259 
01260    //Point to the underlying network interface
01261    interface = context->settings.interface;
01262 
01263    //Allocate a memory buffer to hold the DHCPv6 message
01264    buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset);
01265    //Failed to allocate buffer?
01266    if(buffer == NULL)
01267       return ERROR_OUT_OF_MEMORY;
01268 
01269    //Point to the beginning of the DHCPv6 message
01270    message = netBufferAt(buffer, offset);
01271 
01272    //Set DHCPv6 message type
01273    message->msgType = type;
01274 
01275    //The transaction ID is chosen by the client
01276    STORE24BE(context->transactionId, message->transactionId);
01277 
01278    //Size of the DHCPv6 message
01279    length = sizeof(Dhcpv6Message);
01280 
01281    //The client must include a Client Identifier option
01282    //to identify itself to the server
01283    dhcpv6AddOption(message, &length, DHCPV6_OPTION_CLIENTID,
01284       context->clientId, context->clientIdLength);
01285 
01286    //Request, Renew, Release or Decline message?
01287    if(type == DHCPV6_MSG_TYPE_REQUEST ||
01288       type == DHCPV6_MSG_TYPE_RENEW ||
01289       type == DHCPV6_MSG_TYPE_RELEASE ||
01290       type == DHCPV6_MSG_TYPE_DECLINE)
01291    {
01292       //The client places the identifier of the destination
01293       //server in a Server Identifier option
01294       dhcpv6AddOption(message, &length, DHCPV6_OPTION_SERVERID,
01295          context->serverId, context->serverIdLength);
01296    }
01297 
01298    //Solicit message?
01299    if(type == DHCPV6_MSG_TYPE_SOLICIT)
01300    {
01301       //Check whether rapid commit is enabled
01302       if(context->settings.rapidCommit)
01303       {
01304          //Include the Rapid Commit option if the client is prepared
01305          //to perform the Solicit/Reply message exchange
01306          dhcpv6AddOption(message, &length, DHCPV6_OPTION_RAPID_COMMIT, NULL, 0);
01307       }
01308    }
01309 
01310    //Solicit, Request, Confirm, Renew or Rebind message?
01311    if(type == DHCPV6_MSG_TYPE_SOLICIT ||
01312       type == DHCPV6_MSG_TYPE_REQUEST ||
01313       type == DHCPV6_MSG_TYPE_CONFIRM ||
01314       type == DHCPV6_MSG_TYPE_RENEW ||
01315       type == DHCPV6_MSG_TYPE_REBIND)
01316    {
01317       //Point to the client's fully qualified domain name
01318       fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
01319 
01320       //The FQDN option can be used by the client to convey its
01321       //fully qualified domain name to the server
01322       dhcpv6AddOption(message, &length, DHCPV6_OPTION_FQDN,
01323          fqdnOption, sizeof(Dhcpv6FqdnOption) + context->clientFqdnLength);
01324 
01325       //The client should include an Option Request option to indicate
01326       //the options the client is interested in receiving
01327       dhcpv6AddOption(message, &length, DHCPV6_OPTION_ORO,
01328          &dhcpv6OptionList, sizeof(dhcpv6OptionList));
01329    }
01330 
01331    //Prepare an IA_NA option for a the current interface
01332    iaNaOption.iaId = htonl(interface->id);
01333 
01334    //Solicit, Request or Confirm message?
01335    if(type == DHCPV6_MSG_TYPE_SOLICIT ||
01336       type == DHCPV6_MSG_TYPE_REQUEST ||
01337       type == DHCPV6_MSG_TYPE_CONFIRM)
01338    {
01339       //The client should set the T1 and T2 fields in any IA_NA options to 0
01340       iaNaOption.t1 = 0;
01341       iaNaOption.t2 = 0;
01342    }
01343    else
01344    {
01345       //T1 and T2 are provided as a hint
01346       iaNaOption.t1 = htonl(context->ia.t1);
01347       iaNaOption.t2 = htonl(context->ia.t2);
01348    }
01349 
01350    //The client includes IA options for any IAs to which
01351    //it wants the server to assign addresses
01352    option = dhcpv6AddOption(message, &length, DHCPV6_OPTION_IA_NA,
01353       &iaNaOption, sizeof(Dhcpv6IaNaOption));
01354 
01355    //Request, Confirm, Renew, Rebind, Release or Decline message?
01356    if(type == DHCPV6_MSG_TYPE_REQUEST ||
01357       type == DHCPV6_MSG_TYPE_CONFIRM ||
01358       type == DHCPV6_MSG_TYPE_RENEW ||
01359       type == DHCPV6_MSG_TYPE_REBIND ||
01360       type == DHCPV6_MSG_TYPE_RELEASE ||
01361       type == DHCPV6_MSG_TYPE_DECLINE)
01362    {
01363       //Loop through the IPv6 addresses recorded by the client
01364       for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
01365       {
01366          //Point to the current entry
01367          entry = &context->ia.addrList[i];
01368 
01369          //Valid IPv6 address?
01370          if(entry->validLifetime > 0)
01371          {
01372             //Prepare an IA Address option
01373             iaAddrOption.address = entry->addr;
01374 
01375             //Confirm message?
01376             if(type == DHCPV6_MSG_TYPE_CONFIRM)
01377             {
01378                //The client should set the preferred and valid lifetimes to 0
01379                iaAddrOption.preferredLifetime = 0;
01380                iaAddrOption.validLifetime = 0;
01381             }
01382             else
01383             {
01384                //Preferred and valid lifetimes are provided as a hint
01385                iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime);
01386                iaAddrOption.validLifetime = htonl(entry->validLifetime);
01387             }
01388 
01389             //Add the IA Address option
01390             dhcpv6AddSubOption(option, &length, DHCPV6_OPTION_IAADDR,
01391                &iaAddrOption, sizeof(iaAddrOption));
01392          }
01393       }
01394    }
01395 
01396    //Compute the time elapsed since the client sent the first message
01397    elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context);
01398 
01399    //The client must include an Elapsed Time option in messages to indicate
01400    //how long the client has been trying to complete a DHCP message exchange
01401    dhcpv6AddOption(message, &length, DHCPV6_OPTION_ELAPSED_TIME,
01402       &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption));
01403 
01404    //Adjust the length of the multi-part buffer
01405    netBufferSetLength(buffer, offset + length);
01406 
01407    //Destination address
01408    destIpAddr.length = sizeof(Ipv6Addr);
01409    destIpAddr.ipv6Addr = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR;
01410 
01411    //Debug message
01412    TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n",
01413       formatSystemTime(osGetSystemTime(), NULL), length);
01414 
01415    //Dump the contents of the message for debugging purpose
01416    dhcpv6DumpMessage(message, length);
01417 
01418    //Send DHCPv6 message
01419    error = udpSendDatagramEx(interface, DHCPV6_CLIENT_PORT,
01420       &destIpAddr, DHCPV6_SERVER_PORT, buffer, offset, 0);
01421 
01422    //Free previously allocated memory
01423    netBufferFree(buffer);
01424    //Return status code
01425    return error;
01426 }
01427 
01428 
01429 /**
01430  * @brief Process incoming DHCPv6 message
01431  * @param[in] interface Underlying network interface
01432  * @param[in] pseudoHeader UDP pseudo header
01433  * @param[in] udpHeader UDP header
01434  * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message
01435  * @param[in] offset Offset to the first byte of the DHCPv6 message
01436  * @param[in] params Pointer to the DHCPv6 client context
01437  **/
01438 
01439 void dhcpv6ClientProcessMessage(NetInterface *interface,
01440    const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
01441    const NetBuffer *buffer, size_t offset, void *params)
01442 {
01443    size_t length;
01444    Dhcpv6ClientContext *context;
01445    Dhcpv6Message *message;
01446 
01447    //Point to the DHCPv6 client context
01448    context = (Dhcpv6ClientContext *) params;
01449 
01450    //Retrieve the length of the DHCPv6 message
01451    length = netBufferGetLength(buffer) - offset;
01452 
01453    //Make sure the DHCPv6 message is valid
01454    if(length < sizeof(Dhcpv6Message))
01455       return;
01456 
01457    //Point to the beginning of the DHCPv6 message
01458    message = netBufferAt(buffer, offset);
01459    //Sanity check
01460    if(message == NULL)
01461       return;
01462 
01463    //Debug message
01464    TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n",
01465       formatSystemTime(osGetSystemTime(), NULL), length);
01466 
01467    //Dump the contents of the message for debugging purpose
01468    dhcpv6DumpMessage(message, length);
01469 
01470    //Check message type
01471    switch(message->msgType)
01472    {
01473    case DHCPV6_MSG_TYPE_ADVERTISE:
01474       //Parse Advertise message
01475       dhcpv6ClientParseAdvertise(context, message, length);
01476       break;
01477    case DHCPV6_MSG_TYPE_REPLY:
01478       //Parse Reply message
01479       dhcpv6ClientParseReply(context, message, length);
01480       break;
01481    default:
01482       //Silently drop incoming message
01483       break;
01484    }
01485 }
01486 
01487 
01488 /**
01489  * @brief Parse Advertise message
01490  * @param[in] context Pointer to the DHCPv6 client context
01491  * @param[in] message Pointer to the incoming message to parse
01492  * @param[in] length Length of the incoming message
01493  **/
01494 
01495 void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context,
01496    const Dhcpv6Message *message, size_t length)
01497 {
01498    uint_t i;
01499    int_t serverPreference;
01500    NetInterface *interface;
01501    Dhcpv6StatusCode status;
01502    Dhcpv6Option *option;
01503    Dhcpv6Option *serverIdOption;
01504    Dhcpv6IaNaOption *iaNaOption;
01505 
01506    //Point to the underlying network interface
01507    interface = context->settings.interface;
01508 
01509    //Make sure that the Advertise message is received in response to
01510    //a Solicit message
01511    if(context->state != DHCPV6_STATE_SOLICIT)
01512       return;
01513 
01514    //Discard any received packet that does not match the transaction ID
01515    if(LOAD24BE(message->transactionId) != context->transactionId)
01516       return;
01517 
01518    //Get the length of the Options field
01519    length -= sizeof(Dhcpv6Message);
01520 
01521    //Search for the Client Identifier option
01522    option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID);
01523 
01524    //Discard any received packet that does not include a Client Identifier option
01525    if(option == NULL)
01526       return;
01527    //Check the length of the option
01528    if(ntohs(option->length) != context->clientIdLength)
01529       return;
01530    //Check whether the Client Identifier matches our identifier
01531    if(memcmp(option->value, context->clientId, context->clientIdLength))
01532       return;
01533 
01534    //Search for the Server Identifier option
01535    serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
01536 
01537    //Discard any received packet that does not include a Server Identifier option
01538    if(serverIdOption == NULL)
01539       return;
01540    //Check the length of the server DUID
01541    if(ntohs(serverIdOption->length) == 0)
01542       return;
01543    if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
01544       return;
01545 
01546    //Get the status code returned by the server
01547    status = dhcpv6GetStatusCode(message->options, length);
01548 
01549    //If the message contains a Status Code option indicating a failure,
01550    //then the Advertise message is discarded by the client
01551    if(status != DHCPV6_STATUS_SUCCESS)
01552       return;
01553 
01554    //Search for the Preference option
01555    option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_PREFERENCE);
01556 
01557    //Check whether the option has been found
01558    if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption))
01559    {
01560       //Server server preference value
01561       serverPreference = option->value[0];
01562    }
01563    else
01564    {
01565       //Any Advertise that does not include a Preference option
01566       //is considered to have a preference value of 0
01567       serverPreference = 0;
01568    }
01569 
01570    //Select the Advertise message that offers the highest server preference value
01571    if(serverPreference > context->serverPreference)
01572    {
01573       //Save the length of the DUID
01574       context->serverIdLength = ntohs(serverIdOption->length);
01575       //Record the server DUID
01576       memcpy(context->serverId, serverIdOption->value, context->serverIdLength);
01577 
01578       //Flush the list of IPv6 addresses from the client's IA
01579       dhcpv6ClientFlushAddrList(context);
01580    }
01581 
01582    //Point to the first option
01583    i = 0;
01584 
01585    //Loop through DHCPv6 options
01586    while(i < length)
01587    {
01588       //Search for an IA_NA option
01589       option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
01590 
01591       //Unable to find the specified option?
01592       if(option == NULL)
01593          break;
01594 
01595       //Make sure the IA_NA option is valid
01596       if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption))
01597       {
01598          //Get the parameters associated with the IA_NA
01599          iaNaOption = (Dhcpv6IaNaOption *) option->value;
01600 
01601          //Check the IA identifier
01602          if(ntohl(iaNaOption->iaId) == interface->id)
01603          {
01604             //The client examines the status code in each IA individually
01605             status = dhcpv6GetStatusCode(iaNaOption->options,
01606                ntohs(option->length) - sizeof(Dhcpv6IaNaOption));
01607 
01608             //The client must ignore any Advertise message that includes a Status
01609             //Code option containing the value NoAddrsAvail
01610             if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
01611                return;
01612          }
01613 
01614          //Check the server preference value
01615          if(serverPreference > context->serverPreference)
01616          {
01617             //Parse the contents of the IA_NA option
01618             dhcpv6ClientParseIaNaOption(context, option);
01619          }
01620       }
01621 
01622       //Jump to the next option
01623       i += sizeof(Dhcpv6Option) + ntohs(option->length);
01624    }
01625 
01626    //Record the highest server preference value
01627    if(serverPreference > context->serverPreference)
01628       context->serverPreference = serverPreference;
01629 
01630    //If the client receives an Advertise message that includes a
01631    //Preference option with a preference value of 255, the client
01632    //immediately completes the message exchange
01633    if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE)
01634    {
01635       //Continue configuration procedure
01636       dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
01637    }
01638    //The message exchange is not terminated before the first RT has elapsed
01639    else if(context->retransmitCount > 1)
01640    {
01641       //Continue configuration procedure
01642       dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
01643    }
01644 }
01645 
01646 
01647 /**
01648  * @brief Parse Reply message
01649  * @param[in] context Pointer to the DHCPv6 client context
01650  * @param[in] message Pointer to the incoming message to parse
01651  * @param[in] length Length of the incoming message
01652  **/
01653 
01654 void dhcpv6ClientParseReply(Dhcpv6ClientContext *context,
01655    const Dhcpv6Message *message, size_t length)
01656 {
01657    error_t error;
01658    uint_t i;
01659    uint_t k;
01660    uint_t n;
01661    bool_t iaNaOptionFound;
01662    systime_t minPreferredLifetime;
01663    NetInterface *interface;
01664    Dhcpv6StatusCode status;
01665    Dhcpv6Option *option;
01666    Dhcpv6Option *serverIdOption;
01667    Dhcpv6ClientAddrEntry *entry;
01668 
01669    //Point to the underlying network interface
01670    interface = context->settings.interface;
01671 
01672    //Discard any received packet that does not match the transaction ID
01673    if(LOAD24BE(message->transactionId) != context->transactionId)
01674       return;
01675 
01676    //Get the length of the Options field
01677    length -= sizeof(Dhcpv6Message);
01678 
01679    //Search for the Client Identifier option
01680    option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID);
01681 
01682    //Discard any received packet that does not include a Client Identifier option
01683    if(option == NULL)
01684       return;
01685    //Check the length of the option
01686    if(ntohs(option->length) != context->clientIdLength)
01687       return;
01688    //Check whether the Client Identifier matches our identifier
01689    if(memcmp(option->value, context->clientId, context->clientIdLength))
01690       return;
01691 
01692    //Search for the Server Identifier option
01693    serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
01694 
01695    //Discard any received packet that does not include a Server Identifier option
01696    if(serverIdOption == NULL)
01697       return;
01698    //Check the length of the server DUID
01699    if(ntohs(serverIdOption->length) == 0)
01700       return;
01701    if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
01702       return;
01703 
01704    //Get the status code returned by the server
01705    status = dhcpv6GetStatusCode(message->options, length);
01706 
01707    //Check current state
01708    if(context->state == DHCPV6_STATE_SOLICIT)
01709    {
01710       //A Reply message is not acceptable when rapid commit is disallowed
01711       if(!context->settings.rapidCommit)
01712          return;
01713 
01714       //Search for the Rapid Commit option
01715       option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_RAPID_COMMIT);
01716 
01717       //The client discards any message that does not include a Rapid Commit option
01718       if(option == NULL || ntohs(option->length) != 0)
01719          return;
01720    }
01721    else if(context->state == DHCPV6_STATE_REQUEST)
01722    {
01723       //The client must discard the Reply message if the contents of the
01724       //Server Identifier option do not match the server’s DUID
01725       if(!dhcpv6ClientCheckServerId(context, serverIdOption))
01726          return;
01727    }
01728    else if(context->state == DHCPV6_STATE_CONFIRM)
01729    {
01730       //When the client receives a NotOnLink status from the server in response
01731       //to a Confirm message, the client performs DHCP server solicitation
01732       if(status == DHCPV6_STATUS_NOT_ON_LINK)
01733       {
01734          //Restart the DHCP server discovery process
01735          dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01736 
01737          //Exit immediately
01738          return;
01739       }
01740    }
01741    else if(context->state == DHCPV6_STATE_RENEW)
01742    {
01743       //The client must discard the Reply message if the contents of the
01744       //Server Identifier option do not match the server’s DUID
01745       if(!dhcpv6ClientCheckServerId(context, serverIdOption))
01746          return;
01747    }
01748    else if(context->state == DHCPV6_STATE_REBIND)
01749    {
01750       //Do not check the server's DUID when the Reply message is
01751       //received in response to a Rebind message
01752    }
01753    else if(context->state == DHCPV6_STATE_RELEASE)
01754    {
01755       //The client must discard the Reply message if the contents of the
01756       //Server Identifier option do not match the server’s DUID
01757       if(!dhcpv6ClientCheckServerId(context, serverIdOption))
01758          return;
01759 
01760       //When the client receives a valid Reply message in response to a
01761       //Release message, the client considers the Release event completed,
01762       //regardless of the Status Code option(s) returned by the server
01763       context->running = FALSE;
01764 
01765       //Reinitialize state machine
01766       dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01767 
01768       //Exit immediately
01769       return;
01770    }
01771    else if(context->state == DHCPV6_STATE_DECLINE)
01772    {
01773       //The client must discard the Reply message if the contents of the
01774       //Server Identifier option do not match the server’s DUID
01775       if(!dhcpv6ClientCheckServerId(context, serverIdOption))
01776          return;
01777 
01778       //When the client receives a valid Reply message in response to a
01779       //Decline message, the client considers the Decline event completed,
01780       //regardless of the Status Code option returned by the server
01781       dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01782 
01783       //Exit immediately
01784       return;
01785    }
01786    else
01787    {
01788       //Silently discard the Reply message
01789       return;
01790    }
01791 
01792    //Check status code
01793    if(status == DHCPV6_STATUS_USE_MULTICAST)
01794    {
01795       //When the client receives a Reply message with a Status Code option
01796       //with the value UseMulticast, the client records the receipt of the
01797       //message and sends subsequent messages to the server through the
01798       //interface on which the message was received using multicast
01799       return;
01800    }
01801    else if(status == DHCPV6_STATUS_UNSPEC_FAILURE)
01802    {
01803       //If the client receives a Reply message with a Status Code containing
01804       //UnspecFail, the server is indicating that it was unable to process
01805       //the message due to an unspecified failure condition
01806       return;
01807    }
01808 
01809    //This flag will be set if a valid IA_NA option is found
01810    iaNaOptionFound = FALSE;
01811    //Point to the first option
01812    i = 0;
01813 
01814    //Loop through DHCPv6 options
01815    while(i < length)
01816    {
01817       //Search for an IA_NA option
01818       option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
01819 
01820       //Unable to find the specified option?
01821       if(option == NULL)
01822          break;
01823 
01824       //Parse the contents of the IA_NA option
01825       error = dhcpv6ClientParseIaNaOption(context, option);
01826 
01827       //Check error code
01828       if(error == NO_ERROR)
01829       {
01830          //A valid IA_NA option has been found
01831          iaNaOptionFound = TRUE;
01832       }
01833       else if(error == ERROR_NOT_ON_LINK)
01834       {
01835          //When the client receives a NotOnLink status from the server
01836          //in response to a Request, the client can either re-issue the
01837          //Request without specifying any addresses or restart the DHCP
01838          //server discovery process
01839          dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01840 
01841          //Exit immediately
01842          return;
01843       }
01844       else if(error == ERROR_NO_BINDING)
01845       {
01846          //When the client receives a Reply message in response to a Renew
01847          //or Rebind message, the client sends a Request message if any of
01848          //the IAs in the Reply message contains the NoBinding status code
01849          dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
01850 
01851          //Exit immediately
01852          return;
01853       }
01854       else
01855       {
01856          //If an invalid option is received, the client discards
01857          //the option and process the rest of the message...
01858       }
01859 
01860       //Jump to the next option
01861       i += sizeof(Dhcpv6Option) + ntohs(option->length);
01862    }
01863 
01864    //No usable addresses in any of the IAs?
01865    if(!iaNaOptionFound)
01866    {
01867       //Check whether the client receives a Reply message in response
01868       //to a Renew or Rebind message
01869       if(context->state == DHCPV6_STATE_RENEW ||
01870          context->state == DHCPV6_STATE_REBIND)
01871       {
01872          //The client sends a Renew/Rebind if the IA is not in the Reply message
01873       }
01874       else
01875       {
01876          //If the client finds no usable addresses in any of the IAs, it may try
01877          //another server (perhaps restarting the DHCP server discovery process)
01878          dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01879       }
01880 
01881       //Exit immediately
01882       return;
01883    }
01884 
01885    //Total number of valid IPv6 in the IA
01886    n = 0;
01887    //Number of new IPv6 addresses in the IA
01888    k = 0;
01889    //Minimum preferred lifetime observed in the IA
01890    minPreferredLifetime = 0;
01891 
01892    //Loop through the IPv6 addresses recorded by the DHCPv6 client
01893    for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
01894    {
01895       //Point to the current entry
01896       entry = &context->ia.addrList[i];
01897 
01898       //Valid IPv6 address?
01899       if(entry->validLifetime > 0)
01900       {
01901          //Total number of valid IPv6 in the IA
01902          n++;
01903 
01904          //Save the minimum preferred lifetime that has been observed so far
01905          if(minPreferredLifetime < entry->preferredLifetime)
01906             minPreferredLifetime = entry->preferredLifetime;
01907 
01908          //Update lifetimes of the current IPv6 address
01909          ipv6AddAddr(interface, &entry->addr, entry->validLifetime,
01910             entry->preferredLifetime);
01911 
01912          //New IPv6 address added?
01913          if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE)
01914             k++;
01915       }
01916    }
01917 
01918    //Make sure that the IA contains at least one IPv6 address
01919    if(n > 0)
01920    {
01921       //Save the length of the DUID
01922       context->serverIdLength = ntohs(serverIdOption->length);
01923       //Record the server DUID
01924       memcpy(context->serverId, serverIdOption->value, context->serverIdLength);
01925 
01926       //Save the time a which the lease was obtained
01927       context->leaseStartTime = osGetSystemTime();
01928 
01929       //Check the value of T1
01930       if(context->ia.t1 == 0)
01931       {
01932          //If T1 is set to 0 by the server, the client may send
01933          //a Renew message at the client's discretion
01934          if(minPreferredLifetime == DHCPV6_INFINITE_TIME)
01935             context->ia.t1 = DHCPV6_INFINITE_TIME;
01936          else
01937             context->ia.t1 = minPreferredLifetime / 2;
01938       }
01939 
01940       //Check the value of T2
01941       if(context->ia.t2 == 0)
01942       {
01943          //If T2 is set to 0 by the server, the client may send
01944          //a Rebind message at the client's discretion
01945          if(context->ia.t1 == DHCPV6_INFINITE_TIME)
01946             context->ia.t2 = DHCPV6_INFINITE_TIME;
01947          else
01948             context->ia.t2 = context->ia.t1 + context->ia.t1 / 2;
01949       }
01950 
01951       //Any addresses added in the IA?
01952       if(k > 0)
01953       {
01954          //Perform Duplicate Address Detection for the new IPv6 addresses
01955          dhcpv6ClientChangeState(context, DHCPV6_STATE_DAD, 0);
01956       }
01957       else
01958       {
01959          //Switch to the BOUND state
01960          dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0);
01961       }
01962    }
01963    else
01964    {
01965       //If the client finds no usable addresses in any of the IAs, it may try
01966       //another server (perhaps restarting the DHCP server discovery process)
01967       dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
01968    }
01969 }
01970 
01971 
01972 /**
01973  * @brief Parse IA_NA option
01974  * @param[in] context Pointer to the DHCPv6 client context
01975  * @param[in] option Pointer to the IA_NA option to parse
01976  * @return Error code
01977  **/
01978 
01979 error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context,
01980    const Dhcpv6Option *option)
01981 {
01982    error_t error;
01983    uint_t n;
01984    size_t i;
01985    size_t length;
01986    NetInterface *interface;
01987    Dhcpv6StatusCode status;
01988    Dhcpv6IaNaOption *iaNaOption;
01989 
01990    //Point to the underlying network interface
01991    interface = context->settings.interface;
01992 
01993    //Number of addresses found in the IA_NA option
01994    n = 0;
01995 
01996    //Make sure the IA_NA option is valid
01997    if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
01998       return ERROR_INVALID_LENGTH;
01999 
02000    //Get the parameters associated with the IA_NA
02001    iaNaOption = (Dhcpv6IaNaOption *) option->value;
02002    //Compute the length of IA_NA Options field
02003    length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption);
02004 
02005    //Check the IA identifier
02006    if(ntohl(iaNaOption->iaId) != interface->id)
02007       return ERROR_WRONG_IDENTIFIER;
02008 
02009    //If a client receives an IA_NA with T1 greater than T2, and both T1
02010    //and T2 are greater than 0, the client discards the IA_NA option and
02011    //processes the remainder of the message as though the server had not
02012    //included the invalid IA_NA option
02013    if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0)
02014       return ERROR_INVALID_PARAMETER;
02015 
02016    //The client examines the status code in each IA individually
02017    status = dhcpv6GetStatusCode(iaNaOption->options, length);
02018 
02019    //Check error code
02020    if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
02021    {
02022       //The client has received no usable address in the IA
02023       return ERROR_NO_ADDRESS;
02024    }
02025    else if(status == DHCPV6_STATUS_NO_BINDING)
02026    {
02027       //Client record (binding) unavailable
02028       return ERROR_NO_BINDING;
02029    }
02030    else if(status == DHCPV6_STATUS_NOT_ON_LINK)
02031    {
02032       //The prefix for the address is not appropriate for the
02033       //link to which the client is attached
02034       return ERROR_NOT_ON_LINK;
02035    }
02036    else if(status != DHCPV6_STATUS_SUCCESS)
02037    {
02038       //Failure, reason unspecified
02039       return ERROR_FAILURE;
02040    }
02041 
02042    //Record T1 and T2 times
02043    context->ia.t1 = ntohl(iaNaOption->t1);
02044    context->ia.t2 = ntohl(iaNaOption->t2);
02045 
02046    //Point to the first option
02047    i = 0;
02048 
02049    //Loop through IA_NA options
02050    while(i < length)
02051    {
02052       //Search for an IA Address option
02053       option = dhcpv6GetOption(iaNaOption->options + i, length - i, DHCPV6_OPTION_IAADDR);
02054 
02055       //Unable to find the specified option?
02056       if(option == NULL)
02057          break;
02058 
02059       //Parse the contents of the IA Address option
02060       error = dhcpv6ClientParseIaAddrOption(context, option);
02061 
02062       //Check status code
02063       if(!error)
02064       {
02065          //Increment the number of addresses found in the IA_NA option
02066          n++;
02067       }
02068 
02069       //Jump to the next option
02070       i += sizeof(Dhcpv6Option) + ntohs(option->length);
02071    }
02072 
02073    //No usable addresses in the IA_NA option?
02074    if(n == 0)
02075    {
02076       //Report an error
02077       return ERROR_NO_ADDRESS;
02078    }
02079 
02080    //Successful processing
02081    return NO_ERROR;
02082 }
02083 
02084 
02085 /**
02086  * @brief Parse IA Address option
02087  * @param[in] context Pointer to the DHCPv6 client context
02088  * @param[in] option Pointer to the IA Address option to parse
02089  * @return Error code
02090  **/
02091 
02092 error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context,
02093    const Dhcpv6Option *option)
02094 {
02095    size_t length;
02096    uint32_t validLifetime;
02097    uint32_t preferredLifetime;
02098    Dhcpv6StatusCode status;
02099    Dhcpv6IaAddrOption *iaAddrOption;
02100 
02101    //Make sure the IA Address option is valid
02102    if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
02103       return ERROR_INVALID_LENGTH;
02104 
02105    //Point to the contents of the IA Address option
02106    iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
02107    //Compute the length of IA Address Options field
02108    length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption);
02109 
02110    //Convert lifetimes to host byte order
02111    validLifetime = ntohl(iaAddrOption->validLifetime);
02112    preferredLifetime = ntohl(iaAddrOption->preferredLifetime);
02113 
02114    //A client discards any addresses for which the preferred lifetime
02115    //is greater than the valid lifetime
02116    if(preferredLifetime > validLifetime)
02117       return ERROR_INVALID_PARAMETER;
02118 
02119    //The client examines the status code in each IA Address
02120    status = dhcpv6GetStatusCode(iaAddrOption->options, length);
02121 
02122    //Any error to report?
02123    if(status != DHCPV6_STATUS_SUCCESS)
02124       return ERROR_FAILURE;
02125 
02126    //Check the value of the Valid Lifetime
02127    if(iaAddrOption->validLifetime > 0)
02128    {
02129       //Add any new addresses in the IA option to the IA as recorded
02130       //by the client
02131       dhcpv6ClientAddAddr(context, &iaAddrOption->address,
02132          validLifetime, preferredLifetime);
02133    }
02134    else
02135    {
02136       //Discard any addresses from the IA, as recorded by the client,
02137       //that have a valid lifetime of 0 in the IA Address option
02138       dhcpv6ClientRemoveAddr(context, &iaAddrOption->address);
02139    }
02140 
02141    //Successful processing
02142    return NO_ERROR;
02143 }
02144 
02145 
02146 /**
02147  * @brief Add an IPv6 address to the IA
02148  * @param[in] context Pointer to the DHCPv6 client context
02149  * @param[in] addr IPv6 address to be added
02150  * @param[in] validLifetime Valid lifetime, in seconds
02151  * @param[in] preferredLifetime Preferred lifetime, in seconds
02152  **/
02153 
02154 void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr,
02155    uint32_t validLifetime, uint32_t preferredLifetime)
02156 {
02157    uint_t i;
02158    Dhcpv6ClientAddrEntry *entry;
02159    Dhcpv6ClientAddrEntry *firstFreeEntry;
02160 
02161    //Keep track of the first free entry
02162    firstFreeEntry = NULL;
02163 
02164    //Loop through the IPv6 addresses recorded by the DHCPv6 client
02165    for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
02166    {
02167       //Point to the current entry
02168       entry = &context->ia.addrList[i];
02169 
02170       //Valid IPv6 address?
02171       if(entry->validLifetime > 0)
02172       {
02173          //Check whether the current entry matches the specified address
02174          if(ipv6CompAddr(&entry->addr, addr))
02175             break;
02176       }
02177       else
02178       {
02179          //Keep track of the first free entry
02180          if(firstFreeEntry == NULL)
02181             firstFreeEntry = entry;
02182       }
02183    }
02184 
02185    //No matching entry found?
02186    if(i >= IPV6_PREFIX_LIST_SIZE)
02187       entry = firstFreeEntry;
02188 
02189    //Update the entry if necessary
02190    if(entry != NULL)
02191    {
02192       //Save IPv6 address
02193       entry->addr = *addr;
02194 
02195       //Save lifetimes
02196       entry->validLifetime = validLifetime;
02197       entry->preferredLifetime = preferredLifetime;
02198    }
02199 }
02200 
02201 
02202 /**
02203  * @brief Remove an IPv6 address from the IA
02204  * @param[in] context Pointer to the DHCPv6 client context
02205  * @param[in] addr IPv6 address to be removed
02206  **/
02207 
02208 void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr)
02209 {
02210    uint_t i;
02211    NetInterface *interface;
02212    Dhcpv6ClientAddrEntry *entry;
02213 
02214    //Point to the underlying network interface
02215    interface = context->settings.interface;
02216 
02217    //Loop through the IPv6 addresses recorded by the DHCPv6 client
02218    for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
02219    {
02220       //Point to the current entry
02221       entry = &context->ia.addrList[i];
02222 
02223       //Valid IPv6 address?
02224       if(entry->validLifetime > 0)
02225       {
02226          //Check whether the current entry matches the specified address
02227          if(ipv6CompAddr(&entry->addr, addr))
02228          {
02229             //The IPv6 address is no more valid and should be removed from
02230             //the list of IPv6 addresses assigned to the interface
02231             ipv6RemoveAddr(interface, addr);
02232 
02233             //Remove the IPv6 address from the IA
02234             entry->validLifetime = 0;
02235          }
02236       }
02237    }
02238 }
02239 
02240 
02241 /**
02242  * @brief Flush the list of IPv6 addresses from the IA
02243  * @param[in] context Pointer to the DHCPv6 client context
02244  **/
02245 
02246 void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context)
02247 {
02248    uint_t i;
02249    NetInterface *interface;
02250    Dhcpv6ClientAddrEntry *entry;
02251 
02252    //Point to the underlying network interface
02253    interface = context->settings.interface;
02254 
02255    //Loop through the IPv6 addresses recorded by the DHCPv6 client
02256    for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
02257    {
02258       //Point to the current entry
02259       entry = &context->ia.addrList[i];
02260 
02261       //Valid IPv6 address?
02262       if(entry->validLifetime > 0)
02263       {
02264          //The IPv6 address is no more valid and should be removed from
02265          //the list of IPv6 addresses assigned to the interface
02266          ipv6RemoveAddr(interface, &entry->addr);
02267 
02268          //Remove the IPv6 address from the IA
02269          entry->validLifetime = 0;
02270       }
02271    }
02272 }
02273 
02274 
02275 /**
02276  * @brief Generate client's DUID
02277  * @param[in] context Pointer to the DHCPv6 client context
02278  * @return Error code
02279  **/
02280 
02281 error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context)
02282 {
02283    NetInterface *interface;
02284    Dhcpv6DuidLl *duid;
02285 
02286    //Point to the underlying network interface
02287    interface = context->settings.interface;
02288 
02289    //Point to the buffer where to format the client's DUID
02290    duid = (Dhcpv6DuidLl *) context->clientId;
02291 
02292 #if (ETH_SUPPORT == ENABLED)
02293    //Generate a DUID-LL from the MAC address
02294    duid->type = HTONS(DHCPV6_DUID_LL);
02295    duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH);
02296    duid->linkLayerAddr = interface->macAddr;
02297 #else
02298    //Generate a DUID-LL from the EUI-64 identifier
02299    duid->type = HTONS(DHCPV6_DUID_LL);
02300    duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64);
02301    duid->linkLayerAddr = interface->eui64;
02302 #endif
02303 
02304    //Length of the newly generated DUID
02305    context->clientIdLength = sizeof(Dhcpv6DuidLl);
02306 
02307    //Successful processing
02308    return NO_ERROR;
02309 }
02310 
02311 
02312 /**
02313  * @brief Generate client's fully qualified domain name
02314  * @param[in] context Pointer to the DHCPv6 client context
02315  * @return Error code
02316  **/
02317 
02318 error_t dhcpv6ClientGenerateFqdn(Dhcpv6ClientContext *context)
02319 {
02320    NetInterface *interface;
02321    Dhcpv6FqdnOption *fqdnOption;
02322 
02323    //Point to the underlying network interface
02324    interface = context->settings.interface;
02325 
02326    //Point to the buffer where to format the client's FQDN
02327    fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
02328 
02329    //Set flags
02330    fqdnOption->mbz = 0;
02331    fqdnOption->n = FALSE;
02332    fqdnOption->o = FALSE;
02333    fqdnOption->s = FALSE;
02334 
02335    //Encode client's FQDN
02336    context->clientFqdnLength = dnsEncodeName(interface->hostname,
02337       fqdnOption->domainName);
02338 
02339    //Successful processing
02340    return NO_ERROR;
02341 }
02342 
02343 
02344 /**
02345  * @brief Generate a link-local address
02346  * @param[in] context Pointer to the DHCPv6 client context
02347  * @return Error code
02348  **/
02349 
02350 error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context)
02351 {
02352    error_t error;
02353    NetInterface *interface;
02354    Ipv6Addr addr;
02355 
02356    //Point to the underlying network interface
02357    interface = context->settings.interface;
02358 
02359    //Check whether a link-local address has been manually assigned
02360    if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
02361       interface->ipv6Context.addrList[0].permanent)
02362    {
02363       //Keep using the current link-local address
02364       error = NO_ERROR;
02365    }
02366    else
02367    {
02368       //A link-local address is formed by combining the well-known
02369       //link-local prefix fe80::/10 with the interface identifier
02370       ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
02371 
02372 #if (NDP_SUPPORT == ENABLED)
02373       //Check whether Duplicate Address Detection should be performed
02374       if(interface->ndpContext.dupAddrDetectTransmits > 0)
02375       {
02376          //Use the link-local address as a tentative address
02377          error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
02378             NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE);
02379       }
02380       else
02381 #endif
02382       {
02383          //The use of the link-local address is now unrestricted
02384          error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
02385             NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE);
02386       }
02387    }
02388 
02389    //Return status code
02390    return error;
02391 }
02392 
02393 
02394 /**
02395  * @brief Check the Server Identifier option
02396  * @param[in] context Pointer to the DHCPv6 client context
02397  * @param[in] serverIdOption Pointer to the Server Identifier option
02398  * @return TRUE if the option matches the server’s DUID, else FALSE
02399  **/
02400 
02401 bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context,
02402    Dhcpv6Option *serverIdOption)
02403 {
02404    bool_t valid = FALSE;
02405 
02406    //Check the length of the Server Identifier option
02407    if(ntohs(serverIdOption->length) == context->serverIdLength)
02408    {
02409       //Check whether the Server Identifier option matches the server’s DUID
02410       if(!memcmp(serverIdOption->value, context->serverId, context->serverIdLength))
02411          valid = TRUE;
02412    }
02413 
02414    //Return TRUE if the option matches the server’s DUID
02415    return valid;
02416 }
02417 
02418 
02419 /**
02420  * @brief Manage DHCPv6 configuration timeout
02421  * @param[in] context Pointer to the DHCPv6 client context
02422  **/
02423 
02424 void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context)
02425 {
02426    systime_t time;
02427    NetInterface *interface;
02428 
02429    //Point to the underlying network interface
02430    interface = context->settings.interface;
02431 
02432    //Get current time
02433    time = osGetSystemTime();
02434 
02435    //Any registered callback?
02436    if(context->settings.timeoutEvent != NULL)
02437    {
02438       //DHCPv6 configuration timeout?
02439       if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
02440       {
02441          //Ensure the callback function is only called once
02442          if(!context->timeoutEventDone)
02443          {
02444             //Release exclusive access
02445             osReleaseMutex(&netMutex);
02446             //Invoke user callback function
02447             context->settings.timeoutEvent(context, interface);
02448             //Get exclusive access
02449             osAcquireMutex(&netMutex);
02450 
02451             //Set flag
02452             context->timeoutEventDone = TRUE;
02453          }
02454       }
02455    }
02456 }
02457 
02458 
02459 /**
02460  * @brief Compute the time elapsed since the client sent the first message
02461  * @param[in] context Pointer to the DHCPv6 client context
02462  * @return The elapsed time expressed in hundredths of a second
02463  **/
02464 
02465 uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context)
02466 {
02467    systime_t time;
02468 
02469    //Check retransmission counter
02470    if(context->retransmitCount == 0)
02471    {
02472       //The elapsed time must be 0 for the first message
02473       time = 0;
02474    }
02475    else
02476    {
02477       //Compute the time elapsed since the client sent the
02478       //first message (in hundredths of a second)
02479       time = (osGetSystemTime() - context->exchangeStartTime) / 10;
02480 
02481       //The value 0xFFFF is used to represent any elapsed time values
02482       //greater than the largest time value that can be represented
02483       time = MIN(time, 0xFFFF);
02484    }
02485 
02486    //Convert the 16-bit value to network byte order
02487    return htons(time);
02488 }
02489 
02490 
02491 /**
02492  * @brief Update DHCPv6 FSM state
02493  * @param[in] context Pointer to the DHCPv6 client context
02494  * @param[in] newState New DHCPv6 state to switch to
02495  * @param[in] delay Initial delay
02496  **/
02497 
02498 void dhcpv6ClientChangeState(Dhcpv6ClientContext *context,
02499    Dhcpv6State newState, systime_t delay)
02500 {
02501    systime_t time;
02502 
02503    //Get current time
02504    time = osGetSystemTime();
02505 
02506 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
02507    //Sanity check
02508    if(newState <= DHCPV6_STATE_DECLINE)
02509    {
02510       //DHCPv6 FSM states
02511       static const char_t *stateLabel[] =
02512       {
02513          "INIT",
02514          "SOLICIT",
02515          "REQUEST",
02516          "INIT-CONFIRM",
02517          "CONFIRM",
02518          "DAD",
02519          "BOUND",
02520          "RENEW",
02521          "REBIND",
02522          "RELEASE",
02523          "DECLINE"
02524       };
02525 
02526       //Debug message
02527       TRACE_INFO("%s: DHCPv6 client %s state\r\n",
02528          formatSystemTime(time, NULL), stateLabel[newState]);
02529    }
02530 #endif
02531 
02532    //Set time stamp
02533    context->timestamp = time;
02534    //Set initial delay
02535    context->timeout = delay;
02536    //Reset retransmission counter
02537    context->retransmitCount = 0;
02538    //Switch to the new state
02539    context->state = newState;
02540 
02541    //Any registered callback?
02542    if(context->settings.stateChangeEvent != NULL)
02543    {
02544       NetInterface *interface;
02545 
02546       //Point to the underlying network interface
02547       interface = context->settings.interface;
02548 
02549       //Release exclusive access
02550       osReleaseMutex(&netMutex);
02551       //Invoke user callback function
02552       context->settings.stateChangeEvent(context, interface, newState);
02553       //Get exclusive access
02554       osAcquireMutex(&netMutex);
02555    }
02556 }
02557 
02558 
02559 /**
02560  * @brief Dump DHCPv6 configuration for debugging purpose
02561  * @param[in] context Pointer to the DHCPv6 client context
02562  **/
02563 
02564 void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context)
02565 {
02566 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
02567    uint_t i;
02568    NetInterface *interface;
02569    Ipv6Context *ipv6Context;
02570 
02571    //Point to the underlying network interface
02572    interface = context->settings.interface;
02573    //Point to the IPv6 context
02574    ipv6Context = &interface->ipv6Context;
02575 
02576    //Debug message
02577    TRACE_INFO("\r\n");
02578    TRACE_INFO("DHCPv6 configuration:\r\n");
02579 
02580    //Lease start time
02581    TRACE_INFO("  Lease Start Time = %s\r\n",
02582       formatSystemTime(context->leaseStartTime, NULL));
02583 
02584    //T1 parameter
02585    TRACE_INFO("  T1 = %" PRIu32 "s\r\n", context->ia.t1);
02586    //T2 parameter
02587    TRACE_INFO("  T2 = %" PRIu32 "s\r\n", context->ia.t2);
02588 
02589    //Global addresses
02590    for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
02591    {
02592       TRACE_INFO("  Global Address %u = %s\r\n", i,
02593          ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
02594    }
02595 
02596    //DNS servers
02597    for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
02598    {
02599       TRACE_INFO("  DNS Server %u = %s\r\n", i + 1,
02600          ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
02601    }
02602 
02603    //Debug message
02604    TRACE_INFO("\r\n");
02605 #endif
02606 }
02607 
02608 #endif
02609