Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers dhcp_client.c Source File

dhcp_client.c

Go to the documentation of this file.
00001 /**
00002  * @file dhcp_client.c
00003  * @brief DHCP client (Dynamic Host Configuration Protocol)
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 is used to provide configuration
00028  * parameters to hosts. Refer to the following RFCs for complete details:
00029  * - RFC 2131: Dynamic Host Configuration Protocol
00030  * - RFC 2132: DHCP Options and BOOTP Vendor Extensions
00031  * - RFC 4039: Rapid Commit Option for the DHCP version 4
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 DHCP_TRACE_LEVEL
00039 
00040 //Dependencies
00041 #include "core/net.h"
00042 #include "dhcp/dhcp_client.h"
00043 #include "dhcp/dhcp_common.h"
00044 #include "dhcp/dhcp_debug.h"
00045 #include "mdns/mdns_responder.h"
00046 #include "date_time.h"
00047 #include "debug.h"
00048 
00049 //Check TCP/IP stack configuration
00050 #if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
00051 
00052 //Tick counter to handle periodic operations
00053 systime_t dhcpClientTickCounter;
00054 
00055 //Requested DHCP options
00056 const uint8_t dhcpOptionList[] =
00057 {
00058    DHCP_OPT_SUBNET_MASK,
00059    DHCP_OPT_ROUTER,
00060    DHCP_OPT_DNS_SERVER,
00061    DHCP_OPT_INTERFACE_MTU,
00062    DHCP_OPT_IP_ADDRESS_LEASE_TIME,
00063    DHCP_OPT_RENEWAL_TIME_VALUE,
00064    DHCP_OPT_REBINDING_TIME_VALUE
00065 };
00066 
00067 
00068 /**
00069  * @brief Initialize settings with default values
00070  * @param[out] settings Structure that contains DHCP client settings
00071  **/
00072 
00073 void dhcpClientGetDefaultSettings(DhcpClientSettings *settings)
00074 {
00075    //Use default interface
00076    settings->interface = netGetDefaultInterface();
00077 
00078    //Use default host name
00079    strcpy(settings->hostname, "");
00080 
00081    //Support for quick configuration using rapid commit
00082    settings->rapidCommit = FALSE;
00083    //Use the DNS servers provided by the DHCP server
00084    settings->manualDnsConfig = FALSE;
00085    //DHCP configuration timeout
00086    settings->timeout = 0;
00087    //DHCP configuration timeout event
00088    settings->timeoutEvent = NULL;
00089    //Link state change event
00090    settings->linkChangeEvent = NULL;
00091    //FSM state change event
00092    settings->stateChangeEvent = NULL;
00093 }
00094 
00095 
00096 /**
00097  * @brief DHCP client initialization
00098  * @param[in] context Pointer to the DHCP client context
00099  * @param[in] settings DHCP client specific settings
00100  * @return Error code
00101  **/
00102 
00103 error_t dhcpClientInit(DhcpClientContext *context, const DhcpClientSettings *settings)
00104 {
00105    error_t error;
00106    size_t n;
00107    NetInterface *interface;
00108 
00109    //Debug message
00110    TRACE_INFO("Initializing DHCP client...\r\n");
00111 
00112    //Ensure the parameters are valid
00113    if(context == NULL || settings == NULL)
00114       return ERROR_INVALID_PARAMETER;
00115 
00116    //A valid pointer to the interface being configured is required
00117    if(settings->interface == NULL)
00118       return ERROR_INVALID_PARAMETER;
00119 
00120    //Point to the underlying network interface
00121    interface = settings->interface;
00122 
00123    //Clear the DHCP client context
00124    memset(context, 0, sizeof(DhcpClientContext));
00125    //Save user settings
00126    context->settings = *settings;
00127 
00128    //No DHCP host name defined?
00129    if(settings->hostname[0] == '\0')
00130    {
00131       //Use default host name
00132       n = strlen(interface->hostname);
00133       //Limit the length of the string
00134       n = MIN(n, DHCP_CLIENT_MAX_HOSTNAME_LEN);
00135 
00136       //Copy host name
00137       strncpy(context->settings.hostname, interface->hostname, n);
00138       //Properly terminate the string with a NULL character
00139       context->settings.hostname[n] = '\0';
00140    }
00141 
00142    //Callback function to be called when a DHCP message is received
00143    error = udpAttachRxCallback(interface, DHCP_CLIENT_PORT,
00144       dhcpClientProcessMessage, context);
00145    //Failed to register callback function?
00146    if(error)
00147       return error;
00148 
00149    //DHCP client is currently suspended
00150    context->running = FALSE;
00151    //Initialize state machine
00152    context->state = DHCP_STATE_INIT;
00153 
00154    //Attach the DHCP client context to the network interface
00155    interface->dhcpClientContext = context;
00156 
00157    //Successful initialization
00158    return NO_ERROR;
00159 }
00160 
00161 
00162 /**
00163  * @brief Start DHCP client
00164  * @param[in] context Pointer to the DHCP client context
00165  * @return Error code
00166  **/
00167 
00168 error_t dhcpClientStart(DhcpClientContext *context)
00169 {
00170    //Check parameter
00171    if(context == NULL)
00172       return ERROR_INVALID_PARAMETER;
00173 
00174    //Debug message
00175    TRACE_INFO("Starting DHCP client...\r\n");
00176 
00177    //Get exclusive access
00178    osAcquireMutex(&netMutex);
00179 
00180    //Start DHCP client
00181    context->running = TRUE;
00182    //Initialize state machine
00183    context->state = DHCP_STATE_INIT;
00184 
00185    //Release exclusive access
00186    osReleaseMutex(&netMutex);
00187 
00188    //Successful processing
00189    return NO_ERROR;
00190 }
00191 
00192 
00193 /**
00194  * @brief Stop DHCP client
00195  * @param[in] context Pointer to the DHCP client context
00196  * @return Error code
00197  **/
00198 
00199 error_t dhcpClientStop(DhcpClientContext *context)
00200 {
00201    //Check parameter
00202    if(context == NULL)
00203       return ERROR_INVALID_PARAMETER;
00204 
00205    //Debug message
00206    TRACE_INFO("Stopping DHCP client...\r\n");
00207 
00208    //Get exclusive access
00209    osAcquireMutex(&netMutex);
00210 
00211    //Stop DHCP client
00212    context->running = FALSE;
00213    //Reinitialize state machine
00214    context->state = DHCP_STATE_INIT;
00215 
00216    //Release exclusive access
00217    osReleaseMutex(&netMutex);
00218 
00219    //Successful processing
00220    return NO_ERROR;
00221 }
00222 
00223 
00224 /**
00225  * @brief Retrieve current state
00226  * @param[in] context Pointer to the DHCP client context
00227  * @return Current DHCP client state
00228  **/
00229 
00230 DhcpState dhcpClientGetState(DhcpClientContext *context)
00231 {
00232    DhcpState state;
00233 
00234    //Get exclusive access
00235    osAcquireMutex(&netMutex);
00236    //Get current state
00237    state = context->state;
00238    //Release exclusive access
00239    osReleaseMutex(&netMutex);
00240 
00241    //Return current state
00242    return state;
00243 }
00244 
00245 
00246 /**
00247  * @brief DHCP client timer handler
00248  *
00249  * This routine must be periodically called by the TCP/IP stack to
00250  * manage DHCP client operation
00251  *
00252  * @param[in] context Pointer to the DHCP client context
00253  **/
00254 
00255 
00256 void dhcpClientTick(DhcpClientContext *context)
00257 {
00258    //Make sure the DHCP client has been properly instantiated
00259    if(context == NULL)
00260       return;
00261 
00262    //DHCP client finite state machine
00263    switch(context->state)
00264    {
00265    //Process INIT state
00266    case DHCP_STATE_INIT:
00267       //This is the initialization state, where a client begins the process of
00268       //acquiring a lease. It also returns here when a lease ends, or when a
00269       //lease negotiation fails
00270       dhcpClientStateInit(context);
00271       break;
00272    //Process SELECTING state
00273    case DHCP_STATE_SELECTING:
00274       //The client is waiting to receive DHCPOFFER messages from one or more
00275       //DHCP servers, so it can choose one
00276       dhcpClientStateSelecting(context);
00277       break;
00278    //Process REQUESTING state
00279    case DHCP_STATE_REQUESTING:
00280       //The client is waiting to hear back from the server to which
00281       //it sent its request
00282       dhcpClientStateRequesting(context);
00283       break;
00284    //Process INIT REBOOT state
00285    case DHCP_STATE_INIT_REBOOT:
00286       //When a client that already has a valid lease starts up after a
00287       //power-down or reboot, it starts here instead of the INIT state
00288       dhcpClientStateInitReboot(context);
00289       break;
00290    //Process REBOOTING state
00291    case DHCP_STATE_REBOOTING:
00292       //A client that has rebooted with an assigned address is waiting for
00293       //a confirming reply from a server
00294       dhcpClientStateRebooting(context);
00295       break;
00296    //Process PROBING state
00297    case DHCP_STATE_PROBING:
00298       //The client probes the newly received address
00299       dhcpClientStateProbing(context);
00300       break;
00301    //Process BOUND state
00302    case DHCP_STATE_BOUND:
00303       //Client has a valid lease and is in its normal operating state
00304       dhcpClientStateBound(context);
00305       break;
00306    //Process RENEWING state
00307    case DHCP_STATE_RENEWING:
00308       //Client is trying to renew its lease. It regularly sends DHCPREQUEST messages with
00309       //the server that gave it its current lease specified, and waits for a reply
00310       dhcpClientStateRenewing(context);
00311       break;
00312    //Process REBINDING state
00313    case DHCP_STATE_REBINDING:
00314       //The client has failed to renew its lease with the server that originally granted it,
00315       //and now seeks a lease extension with any server that can hear it. It periodically sends
00316       //DHCPREQUEST messages with no server specified until it gets a reply or the lease ends
00317       dhcpClientStateRebinding(context);
00318       break;
00319    //Invalid state...
00320    default:
00321       //Switch to the INIT state
00322       context->state = DHCP_STATE_INIT;
00323       break;
00324    }
00325 }
00326 
00327 
00328 /**
00329  * @brief Callback function for link change event
00330  * @param[in] context Pointer to the DHCP client context
00331  **/
00332 
00333 void dhcpClientLinkChangeEvent(DhcpClientContext *context)
00334 {
00335    NetInterface *interface;
00336 
00337    //Make sure the DHCP client has been properly instantiated
00338    if(context == NULL)
00339       return;
00340 
00341    //Point to the underlying network interface
00342    interface = context->settings.interface;
00343 
00344    //Check whether the DHCP client is running
00345    if(context->running)
00346    {
00347       //The host address is no longer valid
00348       interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
00349       interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
00350 
00351 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
00352       //Restart mDNS probing process
00353       mdnsResponderStartProbing(interface->mdnsResponderContext);
00354 #endif
00355 
00356       //Clear subnet mask
00357       interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
00358    }
00359 
00360    //Check whether the client already has a valid lease
00361    if(context->state >= DHCP_STATE_INIT_REBOOT)
00362    {
00363       //Switch to the INIT-REBOOT state
00364       context->state = DHCP_STATE_INIT_REBOOT;
00365    }
00366    else
00367    {
00368       //Switch to the INIT state
00369       context->state = DHCP_STATE_INIT;
00370    }
00371 
00372    //Any registered callback?
00373    if(context->settings.linkChangeEvent != NULL)
00374    {
00375       //Release exclusive access
00376       osReleaseMutex(&netMutex);
00377       //Invoke user callback function
00378       context->settings.linkChangeEvent(context, interface, interface->linkState);
00379       //Get exclusive access
00380       osAcquireMutex(&netMutex);
00381    }
00382 }
00383 
00384 
00385 /**
00386  * @brief INIT state
00387  *
00388  * This is the initialization state, where a client begins the process of
00389  * acquiring a lease. It also returns here when a lease ends, or when a
00390  * lease negotiation fails
00391  *
00392  * @param[in] context Pointer to the DHCP client context
00393  **/
00394 
00395 void dhcpClientStateInit(DhcpClientContext *context)
00396 {
00397    systime_t delay;
00398    NetInterface *interface;
00399 
00400    //Point to the underlying network interface
00401    interface = context->settings.interface;
00402 
00403    //Check whether the DHCP client is running
00404    if(context->running)
00405    {
00406       //Wait for the link to be up before starting DHCP configuration
00407       if(interface->linkState)
00408       {
00409          //The client should wait for a random time to
00410          //desynchronize the use of DHCP at startup
00411          delay = netGetRandRange(0, DHCP_CLIENT_INIT_DELAY);
00412 
00413          //Record the time at which the client started
00414          //the address acquisition process
00415          context->configStartTime = osGetSystemTime();
00416          //Clear flag
00417          context->timeoutEventDone = FALSE;
00418 
00419          //Switch to the SELECTING state
00420          dhcpClientChangeState(context, DHCP_STATE_SELECTING, delay);
00421       }
00422    }
00423 }
00424 
00425 
00426 /**
00427  * @brief SELECTING state
00428  *
00429  * The client is waiting to receive DHCPOFFER messages from
00430  * one or more DHCP servers, so it can choose one
00431  *
00432  * @param[in] context Pointer to the DHCP client context
00433  **/
00434 
00435 void dhcpClientStateSelecting(DhcpClientContext *context)
00436 {
00437    systime_t time;
00438 
00439    //Get current time
00440    time = osGetSystemTime();
00441 
00442    //Check current time
00443    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00444    {
00445       //Check retransmission counter
00446       if(context->retransmitCount == 0)
00447       {
00448          //A transaction identifier is used by the client to
00449          //match incoming DHCP messages with pending requests
00450          context->transactionId = netGetRand();
00451 
00452          //Send a DHCPDISCOVER message
00453          dhcpClientSendDiscover(context);
00454 
00455          //Initial timeout value
00456          context->retransmitTimeout = DHCP_CLIENT_DISCOVER_INIT_RT;
00457       }
00458       else
00459       {
00460          //Send a DHCPDISCOVER message
00461          dhcpClientSendDiscover(context);
00462 
00463          //The timeout value is doubled for each subsequent retransmission
00464          context->retransmitTimeout *= 2;
00465 
00466          //Limit the timeout value to a maximum of 64 seconds
00467          if(context->retransmitTimeout > DHCP_CLIENT_DISCOVER_MAX_RT)
00468             context->retransmitTimeout = DHCP_CLIENT_DISCOVER_MAX_RT;
00469       }
00470 
00471       //Save the time at which the message was sent
00472       context->timestamp = time;
00473 
00474       //The timeout value should be randomized by the value of a uniform
00475       //number chosen from the range -1 to +1
00476       context->timeout = context->retransmitTimeout +
00477          netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
00478 
00479       //Increment retransmission counter
00480       context->retransmitCount++;
00481    }
00482 
00483    //Manage DHCP configuration timeout
00484    dhcpClientCheckTimeout(context);
00485 }
00486 
00487 
00488 /**
00489  * @brief REQUESTING state
00490  *
00491  * The client is waiting to hear back from the server
00492  * to which it sent its request
00493  *
00494  * @param[in] context Pointer to the DHCP client context
00495  **/
00496 
00497 void dhcpClientStateRequesting(DhcpClientContext *context)
00498 {
00499    systime_t time;
00500 
00501    //Get current time
00502    time = osGetSystemTime();
00503 
00504    //Check current time
00505    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00506    {
00507       //Check retransmission counter
00508       if(context->retransmitCount == 0)
00509       {
00510          //A transaction identifier is used by the client to
00511          //match incoming DHCP messages with pending requests
00512          context->transactionId = netGetRand();
00513 
00514          //Send a DHCPREQUEST message
00515          dhcpClientSendRequest(context);
00516 
00517          //Initial timeout value
00518          context->retransmitTimeout = DHCP_CLIENT_REQUEST_INIT_RT;
00519 
00520          //Save the time at which the message was sent
00521          context->timestamp = time;
00522 
00523          //The timeout value should be randomized by the value of a uniform
00524          //number chosen from the range -1 to +1
00525          context->timeout = context->retransmitTimeout +
00526             netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
00527 
00528          //Increment retransmission counter
00529          context->retransmitCount++;
00530       }
00531       else if(context->retransmitCount < DHCP_CLIENT_REQUEST_MAX_RC)
00532       {
00533          //Send a DHCPREQUEST message
00534          dhcpClientSendRequest(context);
00535 
00536          //The timeout value is doubled for each subsequent retransmission
00537          context->retransmitTimeout *= 2;
00538 
00539          //Limit the timeout value to a maximum of 64 seconds
00540          if(context->retransmitTimeout > DHCP_CLIENT_REQUEST_MAX_RT)
00541             context->retransmitTimeout = DHCP_CLIENT_REQUEST_MAX_RT;
00542 
00543          //Save the time at which the message was sent
00544          context->timestamp = time;
00545 
00546          //The timeout value should be randomized by the value of a uniform
00547          //number chosen from the range -1 to +1
00548          context->timeout = context->retransmitTimeout +
00549             netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
00550 
00551          //Increment retransmission counter
00552          context->retransmitCount++;
00553       }
00554       else
00555       {
00556          //If the client does not receive a response within a reasonable
00557          //period of time, then it restarts the initialization procedure
00558          dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
00559       }
00560    }
00561 
00562    //Manage DHCP configuration timeout
00563    dhcpClientCheckTimeout(context);
00564 }
00565 
00566 
00567 /**
00568  * @brief INIT-REBOOT state
00569  *
00570  * When a client that already has a valid lease starts up after a
00571  * power-down or reboot, it starts here instead of the INIT state
00572  *
00573  * @param[in] context Pointer to the DHCP client context
00574  **/
00575 
00576 void dhcpClientStateInitReboot(DhcpClientContext *context)
00577 {
00578    systime_t delay;
00579    NetInterface *interface;
00580 
00581    //Point to the underlying network interface
00582    interface = context->settings.interface;
00583 
00584    //Check whether the DHCP client is running
00585    if(context->running)
00586    {
00587       //Wait for the link to be up before starting DHCP configuration
00588       if(interface->linkState)
00589       {
00590          //The client should wait for a random time to
00591          //desynchronize the use of DHCP at startup
00592          delay = netGetRandRange(0, DHCP_CLIENT_INIT_DELAY);
00593 
00594          //Record the time at which the client started
00595          //the address acquisition process
00596          context->configStartTime = osGetSystemTime();
00597          //Clear flag
00598          context->timeoutEventDone = FALSE;
00599 
00600          //Switch to the REBOOTING state
00601          dhcpClientChangeState(context, DHCP_STATE_REBOOTING, delay);
00602       }
00603    }
00604 }
00605 
00606 
00607 /**
00608  * @brief REBOOTING state
00609  *
00610  * A client that has rebooted with an assigned address is
00611  * waiting for a confirming reply from a server
00612  *
00613  * @param[in] context Pointer to the DHCP client context
00614  **/
00615 
00616 void dhcpClientStateRebooting(DhcpClientContext *context)
00617 {
00618    systime_t time;
00619 
00620    //Get current time
00621    time = osGetSystemTime();
00622 
00623    //Check current time
00624    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00625    {
00626       //Check retransmission counter
00627       if(context->retransmitCount == 0)
00628       {
00629          //A transaction identifier is used by the client to
00630          //match incoming DHCP messages with pending requests
00631          context->transactionId = netGetRand();
00632 
00633          //Send a DHCPREQUEST message
00634          dhcpClientSendRequest(context);
00635 
00636          //Initial timeout value
00637          context->retransmitTimeout = DHCP_CLIENT_REQUEST_INIT_RT;
00638 
00639          //Save the time at which the message was sent
00640          context->timestamp = time;
00641 
00642          //The timeout value should be randomized by the value of a uniform
00643          //number chosen from the range -1 to +1
00644          context->timeout = context->retransmitTimeout +
00645             netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
00646 
00647          //Increment retransmission counter
00648          context->retransmitCount++;
00649       }
00650       else if(context->retransmitCount < DHCP_CLIENT_REQUEST_MAX_RC)
00651       {
00652          //Send a DHCPREQUEST message
00653          dhcpClientSendRequest(context);
00654 
00655          //The timeout value is doubled for each subsequent retransmission
00656          context->retransmitTimeout *= 2;
00657 
00658          //Limit the timeout value to a maximum of 64 seconds
00659          if(context->retransmitTimeout > DHCP_CLIENT_REQUEST_MAX_RT)
00660             context->retransmitTimeout = DHCP_CLIENT_REQUEST_MAX_RT;
00661 
00662          //Save the time at which the message was sent
00663          context->timestamp = time;
00664 
00665          //The timeout value should be randomized by the value of a uniform
00666          //number chosen from the range -1 to +1
00667          context->timeout = context->retransmitTimeout +
00668             netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
00669 
00670          //Increment retransmission counter
00671          context->retransmitCount++;
00672       }
00673       else
00674       {
00675          //If the client does not receive a response within a reasonable
00676          //period of time, then it restarts the initialization procedure
00677          dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
00678       }
00679    }
00680 
00681    //Manage DHCP configuration timeout
00682    dhcpClientCheckTimeout(context);
00683 }
00684 
00685 
00686 /**
00687  * @brief PROBING state
00688  *
00689  * The client probes the newly received address
00690  *
00691  * @param[in] context Pointer to the DHCP client context
00692  **/
00693 
00694 void dhcpClientStateProbing(DhcpClientContext *context)
00695 {
00696    systime_t time;
00697    NetInterface *interface;
00698 
00699    //Point to the underlying network interface
00700    interface = context->settings.interface;
00701    //Get current time
00702    time = osGetSystemTime();
00703 
00704    //Check current time
00705    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00706    {
00707       //The address is already in use?
00708       if(interface->ipv4Context.addrConflict)
00709       {
00710          //If the client detects that the address is already in use, the
00711          //client must send a DHCPDECLINE message to the server and
00712          //restarts the configuration process
00713          dhcpClientSendDecline(context);
00714 
00715          //The client should wait a minimum of ten seconds before
00716          //restarting the configuration process to avoid excessive
00717          //network traffic in case of looping
00718          dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
00719       }
00720       //Probing is on-going?
00721       else if(context->retransmitCount < DHCP_CLIENT_PROBE_NUM)
00722       {
00723          //Conflict detection is done using ARP probes
00724          arpSendProbe(interface, interface->ipv4Context.addr);
00725 
00726          //Save the time at which the packet was sent
00727          context->timestamp = time;
00728          //Delay until repeated probe
00729          context->timeout = DHCP_CLIENT_PROBE_DELAY;
00730          //Increment retransmission counter
00731          context->retransmitCount++;
00732       }
00733       //Probing is complete?
00734       else
00735       {
00736          //The use of the IPv4 address is now unrestricted
00737          interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
00738 
00739 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
00740          //Restart mDNS probing process
00741          mdnsResponderStartProbing(interface->mdnsResponderContext);
00742 #endif
00743          //Dump current DHCP configuration for debugging purpose
00744          dhcpClientDumpConfig(context);
00745 
00746          //The client transitions to the BOUND state
00747          dhcpClientChangeState(context, DHCP_STATE_BOUND, 0);
00748       }
00749    }
00750 }
00751 
00752 
00753 /**
00754  * @brief BOUND state
00755  *
00756  * Client has a valid lease and is in its normal operating state
00757  *
00758  * @param[in] context Pointer to the DHCP client context
00759  **/
00760 
00761 void dhcpClientStateBound(DhcpClientContext *context)
00762 {
00763    systime_t t1;
00764    systime_t time;
00765 
00766    //Get current time
00767    time = osGetSystemTime();
00768 
00769    //A client will never attempt to extend the lifetime
00770    //of the address when T1 set to 0xFFFFFFFF
00771    if(context->t1 != DHCP_INFINITE_TIME)
00772    {
00773       //Convert T1 to milliseconds
00774       if(context->t1 < (MAX_DELAY / 1000))
00775          t1 = context->t1 * 1000;
00776       else
00777          t1 = MAX_DELAY;
00778 
00779       //Check the time elapsed since the lease was obtained
00780       if(timeCompare(time, context->leaseStartTime + t1) >= 0)
00781       {
00782          //Record the time at which the client started the address renewal process
00783          context->configStartTime = time;
00784 
00785          //Enter the RENEWING state
00786          dhcpClientChangeState(context, DHCP_STATE_RENEWING, 0);
00787       }
00788    }
00789 }
00790 
00791 
00792 /**
00793  * @brief RENEWING state
00794  *
00795  * Client is trying to renew its lease. It regularly sends
00796  * DHCPREQUEST messages with the server that gave it its current
00797  * lease specified, and waits for a reply
00798  *
00799  * @param[in] context Pointer to the DHCP client context
00800  **/
00801 
00802 void dhcpClientStateRenewing(DhcpClientContext *context)
00803 {
00804    systime_t t2;
00805    systime_t time;
00806 
00807    //Get current time
00808    time = osGetSystemTime();
00809 
00810    //Check current time
00811    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00812    {
00813       //Convert T2 to milliseconds
00814       if(context->t2 < (MAX_DELAY / 1000))
00815          t2 = context->t2 * 1000;
00816       else
00817          t2 = MAX_DELAY;
00818 
00819       //Check whether T2 timer has expired
00820       if(timeCompare(time, context->leaseStartTime + t2) < 0)
00821       {
00822          //First DHCPREQUEST message?
00823          if(context->retransmitCount == 0)
00824          {
00825             //A transaction identifier is used by the client to
00826             //match incoming DHCP messages with pending requests
00827             context->transactionId = netGetRand();
00828          }
00829 
00830          //Send a DHCPREQUEST message
00831          dhcpClientSendRequest(context);
00832 
00833          //Save the time at which the message was sent
00834          context->timestamp = time;
00835 
00836          //Compute the remaining time until T2 expires
00837          context->timeout = context->leaseStartTime + t2 - time;
00838 
00839          //The client should wait one-half of the remaining time until T2, down to
00840          //a minimum of 60 seconds, before retransmitting the DHCPREQUEST message
00841          if(context->timeout > (2 * DHCP_CLIENT_REQUEST_MIN_DELAY))
00842             context->timeout /= 2;
00843 
00844          //Increment retransmission counter
00845          context->retransmitCount++;
00846       }
00847       else
00848       {
00849          //If no DHCPACK arrives before time T2, the client moves to REBINDING
00850          dhcpClientChangeState(context, DHCP_STATE_REBINDING, 0);
00851       }
00852    }
00853 }
00854 
00855 
00856 /**
00857  * @brief REBINDING state
00858  *
00859  * The client has failed to renew its lease with the server that originally
00860  * granted it, and now seeks a lease extension with any server that can
00861  * hear it. It periodically sends DHCPREQUEST messages with no server specified
00862  * until it gets a reply or the lease ends
00863  *
00864  * @param[in] context Pointer to the DHCP client context
00865  **/
00866 
00867 void dhcpClientStateRebinding(DhcpClientContext *context)
00868 {
00869    systime_t time;
00870    systime_t leaseTime;
00871    NetInterface *interface;
00872 
00873    //Point to the underlying network interface
00874    interface = context->settings.interface;
00875 
00876    //Get current time
00877    time = osGetSystemTime();
00878 
00879    //Check current time
00880    if(timeCompare(time, context->timestamp + context->timeout) >= 0)
00881    {
00882       //Convert the lease time to milliseconds
00883       if(context->leaseTime < (MAX_DELAY / 1000))
00884          leaseTime = context->leaseTime * 1000;
00885       else
00886          leaseTime = MAX_DELAY;
00887 
00888       //Check whether the lease has expired
00889       if(timeCompare(time, context->leaseStartTime + leaseTime) < 0)
00890       {
00891          //First DHCPREQUEST message?
00892          if(context->retransmitCount == 0)
00893          {
00894             //A transaction identifier is used by the client to
00895             //match incoming DHCP messages with pending requests
00896             context->transactionId = netGetRand();
00897          }
00898 
00899          //Send a DHCPREQUEST message
00900          dhcpClientSendRequest(context);
00901 
00902          //Save the time at which the message was sent
00903          context->timestamp = time;
00904 
00905          //Compute the remaining time until the lease expires
00906          context->timeout = context->leaseStartTime + leaseTime - time;
00907 
00908          //The client should wait one-half of the remaining lease time, down to a
00909          //minimum of 60 seconds, before retransmitting the DHCPREQUEST message
00910          if(context->timeout > (2 * DHCP_CLIENT_REQUEST_MIN_DELAY))
00911             context->timeout /= 2;
00912 
00913          //Increment retransmission counter
00914          context->retransmitCount++;
00915       }
00916       else
00917       {
00918          //The host address is no longer valid...
00919          interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
00920          interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
00921 
00922          //Clear subnet mask
00923          interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
00924 
00925 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
00926          //Restart mDNS probing process
00927          mdnsResponderStartProbing(interface->mdnsResponderContext);
00928 #endif
00929 
00930          //If the lease expires before the client receives
00931          //a DHCPACK, the client moves to INIT state
00932          dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
00933       }
00934    }
00935 }
00936 
00937 
00938 /**
00939  * @brief Send DHCPDISCOVER message
00940  * @param[in] context Pointer to the DHCP client context
00941  * @return Error code
00942  **/
00943 
00944 error_t dhcpClientSendDiscover(DhcpClientContext *context)
00945 {
00946    error_t error;
00947    size_t length;
00948    size_t offset;
00949    NetBuffer *buffer;
00950    NetInterface *interface;
00951    DhcpMessage *message;
00952    IpAddr destIpAddr;
00953 
00954    //DHCP message type
00955    const uint8_t messageType = DHCP_MESSAGE_TYPE_DISCOVER;
00956 
00957    //Point to the underlying network interface
00958    interface = context->settings.interface;
00959 
00960    //Allocate a memory buffer to hold the DHCP message
00961    buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
00962    //Failed to allocate buffer?
00963    if(buffer == NULL)
00964       return ERROR_OUT_OF_MEMORY;
00965 
00966    //Point to the beginning of the DHCP message
00967    message = netBufferAt(buffer, offset);
00968    //Clear memory buffer contents
00969    memset(message, 0, DHCP_MIN_MSG_SIZE);
00970 
00971    //Format DHCPDISCOVER message
00972    message->op = DHCP_OPCODE_BOOTREQUEST;
00973    message->htype = DHCP_HARDWARE_TYPE_ETH;
00974    message->hlen = sizeof(MacAddr);
00975    message->xid = htonl(context->transactionId);
00976    message->secs = dhcpClientComputeElapsedTime(context);
00977    message->flags = HTONS(DHCP_FLAG_BROADCAST);
00978    message->ciaddr = IPV4_UNSPECIFIED_ADDR;
00979    message->chaddr = interface->macAddr;
00980 
00981    //Write magic cookie before setting any option
00982    message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
00983    //Properly terminate options field
00984    message->options[0] = DHCP_OPT_END;
00985 
00986    //DHCP Message Type option
00987    dhcpAddOption(message, DHCP_OPT_DHCP_MESSAGE_TYPE,
00988       &messageType, sizeof(messageType));
00989 
00990    //Retrieve the length of the host name
00991    length = strlen(context->settings.hostname);
00992 
00993    //Any host name defined?
00994    if(length > 0)
00995    {
00996       //The Host Name option specifies the name of the client
00997       dhcpAddOption(message, DHCP_OPT_HOST_NAME,
00998          context->settings.hostname, length);
00999    }
01000 
01001    //Check whether rapid commit is enabled
01002    if(context->settings.rapidCommit)
01003    {
01004       //Include the Rapid Commit option if the client is prepared
01005       //to perform the DHCPDISCOVER-DHCPACK message exchange
01006       dhcpAddOption(message, DHCP_OPT_RAPID_COMMIT, NULL, 0);
01007    }
01008 
01009    //Set destination IP address
01010    destIpAddr.length = sizeof(Ipv4Addr);
01011    destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
01012 
01013    //Debug message
01014    TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
01015       formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
01016 
01017    //Dump the contents of the message for debugging purpose
01018    dhcpDumpMessage(message, DHCP_MIN_MSG_SIZE);
01019 
01020    //Broadcast DHCPDISCOVER message
01021    error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
01022       DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
01023 
01024    //Free previously allocated memory
01025    netBufferFree(buffer);
01026    //Return status code
01027    return error;
01028 }
01029 
01030 
01031 /**
01032  * @brief Send DHCPREQUEST message
01033  * @param[in] context Pointer to the DHCP client context
01034  * @return Error code
01035  **/
01036 
01037 error_t dhcpClientSendRequest(DhcpClientContext *context)
01038 {
01039    error_t error;
01040    size_t length;
01041    size_t offset;
01042    NetBuffer *buffer;
01043    NetInterface *interface;
01044    DhcpMessage *message;
01045    IpAddr destIpAddr;
01046 
01047    //DHCP message type
01048    const uint8_t messageType = DHCP_MESSAGE_TYPE_REQUEST;
01049 
01050    //Point to the underlying network interface
01051    interface = context->settings.interface;
01052 
01053    //Allocate a memory buffer to hold the DHCP message
01054    buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
01055    //Failed to allocate buffer?
01056    if(buffer == NULL)
01057       return ERROR_OUT_OF_MEMORY;
01058 
01059    //Point to the beginning of the DHCP message
01060    message = netBufferAt(buffer, offset);
01061    //Clear memory buffer contents
01062    memset(message, 0, DHCP_MIN_MSG_SIZE);
01063 
01064    //Format DHCPREQUEST message
01065    message->op = DHCP_OPCODE_BOOTREQUEST;
01066    message->htype = DHCP_HARDWARE_TYPE_ETH;
01067    message->hlen = sizeof(MacAddr);
01068    message->xid = htonl(context->transactionId);
01069    message->secs = dhcpClientComputeElapsedTime(context);
01070 
01071    //The client IP address must be included if the client
01072    //is fully configured and can respond to ARP requests
01073    if(context->state == DHCP_STATE_RENEWING ||
01074       context->state == DHCP_STATE_REBINDING)
01075    {
01076       message->flags = 0;
01077       message->ciaddr = interface->ipv4Context.addr;
01078    }
01079    else
01080    {
01081       message->flags = HTONS(DHCP_FLAG_BROADCAST);
01082       message->ciaddr = IPV4_UNSPECIFIED_ADDR;
01083    }
01084 
01085    //Client hardware address
01086    message->chaddr = interface->macAddr;
01087    //Write magic cookie before setting any option
01088    message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
01089    //Properly terminate options field
01090    message->options[0] = DHCP_OPT_END;
01091 
01092    //DHCP Message Type option
01093    dhcpAddOption(message, DHCP_OPT_DHCP_MESSAGE_TYPE,
01094       &messageType, sizeof(messageType));
01095 
01096    //Retrieve the length of the host name
01097    length = strlen(context->settings.hostname);
01098 
01099    //Any host name defined?
01100    if(length > 0)
01101    {
01102       //The Host Name option specifies the name of the client
01103       dhcpAddOption(message, DHCP_OPT_HOST_NAME,
01104          context->settings.hostname, length);
01105    }
01106 
01107    //Server Identifier option
01108    if(context->state == DHCP_STATE_REQUESTING)
01109    {
01110       dhcpAddOption(message, DHCP_OPT_SERVER_IDENTIFIER,
01111          &context->serverIpAddr, sizeof(Ipv4Addr));
01112    }
01113 
01114    //Requested IP Address option
01115    if(context->state == DHCP_STATE_REQUESTING ||
01116       context->state == DHCP_STATE_REBOOTING)
01117    {
01118       dhcpAddOption(message, DHCP_OPT_REQUESTED_IP_ADDRESS,
01119          &context->requestedIpAddr, sizeof(Ipv4Addr));
01120    }
01121 
01122    //Parameter Request List option
01123    dhcpAddOption(message, DHCP_OPT_PARAM_REQUEST_LIST,
01124       dhcpOptionList, sizeof(dhcpOptionList));
01125 
01126    //IP address is being renewed?
01127    if(context->state == DHCP_STATE_RENEWING)
01128    {
01129       //The client transmits the message directly to the
01130       //server that initially granted the lease
01131       destIpAddr.length = sizeof(Ipv4Addr);
01132       destIpAddr.ipv4Addr = context->serverIpAddr;
01133    }
01134    else
01135    {
01136       //Broadcast the message
01137       destIpAddr.length = sizeof(Ipv4Addr);
01138       destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
01139    }
01140 
01141    //Debug message
01142    TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
01143       formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
01144 
01145    //Dump the contents of the message for debugging purpose
01146    dhcpDumpMessage(message, DHCP_MIN_MSG_SIZE);
01147 
01148    //Send DHCPREQUEST message
01149    error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
01150       DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
01151 
01152    //Free previously allocated memory
01153    netBufferFree(buffer);
01154    //Return status code
01155    return error;
01156 }
01157 
01158 
01159 /**
01160  * @brief Send DHCPDECLINE message
01161  * @param[in] context Pointer to the DHCP client context
01162  * @return Error code
01163  **/
01164 
01165 error_t dhcpClientSendDecline(DhcpClientContext *context)
01166 {
01167    error_t error;
01168    size_t offset;
01169    NetBuffer *buffer;
01170    NetInterface *interface;
01171    DhcpMessage *message;
01172    IpAddr destIpAddr;
01173 
01174    //DHCP message type
01175    const uint8_t messageType = DHCP_MESSAGE_TYPE_DECLINE;
01176 
01177    //Point to the underlying network interface
01178    interface = context->settings.interface;
01179 
01180    //Allocate a memory buffer to hold the DHCP message
01181    buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
01182    //Failed to allocate buffer?
01183    if(buffer == NULL)
01184       return ERROR_OUT_OF_MEMORY;
01185 
01186    //Point to the beginning of the DHCP message
01187    message = netBufferAt(buffer, offset);
01188    //Clear memory buffer contents
01189    memset(message, 0, DHCP_MIN_MSG_SIZE);
01190 
01191    //Format DHCPDECLINE message
01192    message->op = DHCP_OPCODE_BOOTREQUEST;
01193    message->htype = DHCP_HARDWARE_TYPE_ETH;
01194    message->hlen = sizeof(MacAddr);
01195    message->xid = htonl(context->transactionId);
01196    message->secs = 0;
01197    message->flags = 0;
01198    message->ciaddr = IPV4_UNSPECIFIED_ADDR;
01199    message->chaddr = interface->macAddr;
01200 
01201    //Write magic cookie before setting any option
01202    message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
01203    //Properly terminate options field
01204    message->options[0] = DHCP_OPT_END;
01205 
01206    //DHCP Message Type option
01207    dhcpAddOption(message, DHCP_OPT_DHCP_MESSAGE_TYPE,
01208       &messageType, sizeof(messageType));
01209    //Server Identifier option
01210    dhcpAddOption(message, DHCP_OPT_SERVER_IDENTIFIER,
01211       &context->serverIpAddr, sizeof(Ipv4Addr));
01212    //Requested IP Address option
01213    dhcpAddOption(message, DHCP_OPT_REQUESTED_IP_ADDRESS,
01214       &context->requestedIpAddr, sizeof(Ipv4Addr));
01215 
01216    //Set destination IP address
01217    destIpAddr.length = sizeof(Ipv4Addr);
01218    destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
01219 
01220    //Debug message
01221    TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
01222       formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
01223 
01224    //Dump the contents of the message for debugging purpose
01225    dhcpDumpMessage(message, DHCP_MIN_MSG_SIZE);
01226 
01227    //Broadcast DHCPDECLINE message
01228    error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
01229       DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
01230 
01231    //Free previously allocated memory
01232    netBufferFree(buffer);
01233    //Return status code
01234    return error;
01235 }
01236 
01237 
01238 /**
01239  * @brief Process incoming DHCP message
01240  * @param[in] interface Underlying network interface
01241  * @param[in] pseudoHeader UDP pseudo header
01242  * @param[in] udpHeader UDP header
01243  * @param[in] buffer Multi-part buffer containing the incoming DHCP message
01244  * @param[in] offset Offset to the first byte of the DHCP message
01245  * @param[in] params Pointer to the DHCP client context
01246  **/
01247 
01248 void dhcpClientProcessMessage(NetInterface *interface,
01249    const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
01250    const NetBuffer *buffer, size_t offset, void *params)
01251 {
01252    size_t length;
01253    DhcpClientContext *context;
01254    DhcpMessage *message;
01255    DhcpOption *option;
01256 
01257    //Point to the DHCP client context
01258    context = (DhcpClientContext *) params;
01259 
01260    //Retrieve the length of the DHCP message
01261    length = netBufferGetLength(buffer) - offset;
01262 
01263    //Make sure the DHCP message is valid
01264    if(length < sizeof(DhcpMessage))
01265       return;
01266    if(length > DHCP_MAX_MSG_SIZE)
01267       return;
01268 
01269    //Point to the beginning of the DHCP message
01270    message = netBufferAt(buffer, offset);
01271    //Sanity check
01272    if(message == NULL)
01273       return;
01274 
01275    //Debug message
01276    TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
01277       formatSystemTime(osGetSystemTime(), NULL), length);
01278 
01279    //Dump the contents of the message for debugging purpose
01280    dhcpDumpMessage(message, length);
01281 
01282    //The DHCP server shall respond with a BOOTREPLY opcode
01283    if(message->op != DHCP_OPCODE_BOOTREPLY)
01284       return;
01285    //Enforce hardware type
01286    if(message->htype != DHCP_HARDWARE_TYPE_ETH)
01287       return;
01288    //Check the length of the hardware address
01289    if(message->hlen != sizeof(MacAddr))
01290       return;
01291    //Check magic cookie
01292    if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
01293       return;
01294 
01295    //The DHCP Message Type option must be included in every DHCP message
01296    option = dhcpGetOption(message, length, DHCP_OPT_DHCP_MESSAGE_TYPE);
01297 
01298    //Failed to retrieve the Message Type option?
01299    if(option == NULL || option->length != 1)
01300       return;
01301 
01302    //Check message type
01303    switch(option->value[0])
01304    {
01305    case DHCP_MESSAGE_TYPE_OFFER:
01306       //Parse DHCPOFFER message
01307       dhcpClientParseOffer(context, message, length);
01308       break;
01309    case DHCP_MESSAGE_TYPE_ACK:
01310       //Parse DHCPACK message
01311       dhcpClientParseAck(context, message, length);
01312       break;
01313    case DHCP_MESSAGE_TYPE_NAK:
01314       //Parse DHCPNAK message
01315       dhcpClientParseNak(context, message, length);
01316       break;
01317    default:
01318       //Silently drop incoming message
01319       break;
01320    }
01321 }
01322 
01323 
01324 /**
01325  * @brief Parse DHCPOFFER message
01326  * @param[in] context Pointer to the DHCP client context
01327  * @param[in] message Pointer to the incoming DHCP message
01328  * @param[in] length Length of the incoming message to parse
01329  **/
01330 
01331 void dhcpClientParseOffer(DhcpClientContext *context,
01332    const DhcpMessage *message, size_t length)
01333 {
01334    DhcpOption *serverIdOption;
01335    NetInterface *interface;
01336 
01337    //Point to the underlying network interface
01338    interface = context->settings.interface;
01339 
01340    //Discard any received packet that does not match the transaction ID
01341    if(ntohl(message->xid) != context->transactionId)
01342       return;
01343    //Make sure the IP address offered to the client is valid
01344    if(message->yiaddr == IPV4_UNSPECIFIED_ADDR)
01345       return;
01346    //Check MAC address
01347    if(!macCompAddr(&message->chaddr, &interface->macAddr))
01348       return;
01349 
01350    //Make sure that the DHCPOFFER message is received in response to
01351    //a DHCPDISCOVER message
01352    if(context->state != DHCP_STATE_SELECTING)
01353       return;
01354 
01355    //A DHCP server always returns its own address in the Server Identifier option
01356    serverIdOption = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
01357 
01358    //Failed to retrieve the Server Identifier option?
01359    if(serverIdOption == NULL || serverIdOption->length != 4)
01360       return;
01361 
01362    //Record the IP address of the DHCP server
01363    ipv4CopyAddr(&context->serverIpAddr, serverIdOption->value);
01364    //Record the IP address offered to the client
01365    context->requestedIpAddr = message->yiaddr;
01366 
01367    //Switch to the REQUESTING state
01368    dhcpClientChangeState(context, DHCP_STATE_REQUESTING, 0);
01369 }
01370 
01371 
01372 /**
01373  * @brief Parse DHCPACK message
01374  * @param[in] context Pointer to the DHCP client context
01375  * @param[in] message Pointer to the incoming DHCP message
01376  * @param[in] length Length of the incoming message to parse
01377  * @return Error code
01378  **/
01379 
01380 void dhcpClientParseAck(DhcpClientContext *context,
01381    const DhcpMessage *message, size_t length)
01382 {
01383    uint_t i;
01384    uint_t n;
01385    DhcpOption *option;
01386    DhcpOption *serverIdOption;
01387    NetInterface *interface;
01388 
01389    //Point to the underlying network interface
01390    interface = context->settings.interface;
01391 
01392    //Discard any received packet that does not match the transaction ID
01393    if(ntohl(message->xid) != context->transactionId)
01394       return;
01395    //Make sure the IP address assigned to the client is valid
01396    if(message->yiaddr == IPV4_UNSPECIFIED_ADDR)
01397       return;
01398    //Check MAC address
01399    if(!macCompAddr(&message->chaddr, &interface->macAddr))
01400       return;
01401 
01402    //A DHCP server always returns its own address in the Server Identifier option
01403    serverIdOption = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
01404 
01405    //Failed to retrieve the Server Identifier option?
01406    if(serverIdOption == NULL || serverIdOption->length != 4)
01407       return;
01408 
01409    //Check current state
01410    if(context->state == DHCP_STATE_SELECTING)
01411    {
01412       //A DHCPACK message is not acceptable when rapid commit is disallowed
01413       if(!context->settings.rapidCommit)
01414          return;
01415 
01416       //Search for the Rapid Commit option
01417       option = dhcpGetOption(message, length, DHCP_OPT_RAPID_COMMIT);
01418 
01419       //A server must include this option in a DHCPACK message sent
01420       //in a response to a DHCPDISCOVER message when completing the
01421       //DHCPDISCOVER-DHCPACK message exchange
01422       if(option == NULL || option->length != 0)
01423          return;
01424    }
01425    else if(context->state == DHCP_STATE_REQUESTING ||
01426       context->state == DHCP_STATE_RENEWING)
01427    {
01428       //Check the server identifier
01429       if(!ipv4CompAddr(serverIdOption->value, &context->serverIpAddr))
01430          return;
01431    }
01432    else if(context->state == DHCP_STATE_REBOOTING ||
01433       context->state == DHCP_STATE_REBINDING)
01434    {
01435       //Do not check the server identifier
01436    }
01437    else
01438    {
01439       //Silently discard the DHCPACK message
01440       return;
01441    }
01442 
01443    //Retrieve IP Address Lease Time option
01444    option = dhcpGetOption(message, length, DHCP_OPT_IP_ADDRESS_LEASE_TIME);
01445 
01446    //Failed to retrieve specified option?
01447    if(option == NULL  || option->length != 4)
01448       return;
01449 
01450    //Record the lease time
01451    context->leaseTime = LOAD32BE(option->value);
01452 
01453    //Retrieve Renewal Time Value option
01454    option = dhcpGetOption(message, length, DHCP_OPT_RENEWAL_TIME_VALUE);
01455 
01456    //Specified option found?
01457    if(option != NULL && option->length == 4)
01458    {
01459       //This option specifies the time interval from address assignment
01460       //until the client transitions to the RENEWING state
01461       context->t1 = LOAD32BE(option->value);
01462    }
01463    else if(context->leaseTime != DHCP_INFINITE_TIME)
01464    {
01465       //By default, T1 is set to 50% of the lease time
01466       context->t1 = context->leaseTime / 2;
01467    }
01468    else
01469    {
01470       //Infinite lease
01471       context->t1 = DHCP_INFINITE_TIME;
01472    }
01473 
01474    //Retrieve Rebinding Time value option
01475    option = dhcpGetOption(message, length, DHCP_OPT_REBINDING_TIME_VALUE);
01476 
01477    //Specified option found?
01478    if(option != NULL && option->length == 4)
01479    {
01480       //This option specifies the time interval from address assignment
01481       //until the client transitions to the REBINDING state
01482       context->t2 = LOAD32BE(option->value);
01483    }
01484    else if(context->leaseTime != DHCP_INFINITE_TIME)
01485    {
01486       //By default, T2 is set to 87.5% of the lease time
01487       context->t2 = context->leaseTime * 7 / 8;
01488    }
01489    else
01490    {
01491       //Infinite lease
01492       context->t2 = DHCP_INFINITE_TIME;
01493    }
01494 
01495    //Retrieve Subnet Mask option
01496    option = dhcpGetOption(message, length, DHCP_OPT_SUBNET_MASK);
01497 
01498    //The specified option has been found?
01499    if(option != NULL && option->length == sizeof(Ipv4Addr))
01500    {
01501       //Record subnet mask
01502       ipv4CopyAddr(&interface->ipv4Context.subnetMask, option->value);
01503    }
01504 
01505    //Retrieve Router option
01506    option = dhcpGetOption(message, length, DHCP_OPT_ROUTER);
01507 
01508    //The specified option has been found?
01509    if(option != NULL && !(option->length % sizeof(Ipv4Addr)))
01510    {
01511       //Save default gateway
01512       if(option->length >= sizeof(Ipv4Addr))
01513          ipv4CopyAddr(&interface->ipv4Context.defaultGateway, option->value);
01514    }
01515 
01516    //Use the DNS servers provided by the DHCP server?
01517    if(!context->settings.manualDnsConfig)
01518    {
01519       //Retrieve DNS Server option
01520       option = dhcpGetOption(message, length, DHCP_OPT_DNS_SERVER);
01521 
01522        //The specified option has been found?
01523       if(option != NULL && !(option->length % sizeof(Ipv4Addr)))
01524       {
01525          //Get the number of addresses provided in the response
01526          n = option->length / sizeof(Ipv4Addr);
01527 
01528          //Loop through the list of addresses
01529          for(i = 0; i < n && i < IPV4_DNS_SERVER_LIST_SIZE; i++)
01530          {
01531             //Record DNS server address
01532             ipv4CopyAddr(&interface->ipv4Context.dnsServerList[i],
01533                option->value + i * sizeof(Ipv4Addr));
01534          }
01535       }
01536    }
01537 
01538    //Retrieve MTU option
01539    option = dhcpGetOption(message, length, DHCP_OPT_INTERFACE_MTU);
01540 
01541    //The specified option has been found?
01542    if(option != NULL && option->length == 2)
01543    {
01544       //This option specifies the MTU to use on this interface
01545       n = LOAD16BE(option->value);
01546 
01547       //Make sure that the option's value is acceptable
01548       if(n >= IPV4_MINIMUM_MTU && n <= interface->nicDriver->mtu)
01549       {
01550          //Set the MTU to be used on the interface
01551          interface->ipv4Context.linkMtu = n;
01552       }
01553    }
01554 
01555    //Record the IP address of the DHCP server
01556    ipv4CopyAddr(&context->serverIpAddr, serverIdOption->value);
01557    //Record the IP address assigned to the client
01558    context->requestedIpAddr = message->yiaddr;
01559 
01560    //Save the time a which the lease was obtained
01561    context->leaseStartTime = osGetSystemTime();
01562 
01563    //Check current state
01564    if(context->state == DHCP_STATE_REQUESTING ||
01565       context->state == DHCP_STATE_REBOOTING)
01566    {
01567       //Use the IP address as a tentative address
01568       interface->ipv4Context.addr = message->yiaddr;
01569       interface->ipv4Context.addrState = IPV4_ADDR_STATE_TENTATIVE;
01570 
01571       //Clear conflict flag
01572       interface->ipv4Context.addrConflict = FALSE;
01573 
01574       //The client should probe the newly received address
01575       dhcpClientChangeState(context, DHCP_STATE_PROBING, 0);
01576    }
01577    else
01578    {
01579       //Assign the IP address to the client
01580       interface->ipv4Context.addr = message->yiaddr;
01581       interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
01582 
01583 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
01584       //Restart mDNS probing process
01585       mdnsResponderStartProbing(interface->mdnsResponderContext);
01586 #endif
01587       //The client transitions to the BOUND state
01588       dhcpClientChangeState(context, DHCP_STATE_BOUND, 0);
01589    }
01590 }
01591 
01592 
01593 /**
01594  * @brief Parse DHCPNAK message
01595  * @param[in] context Pointer to the DHCP client context
01596  * @param[in] message Pointer to the incoming DHCP message
01597  * @param[in] length Length of the incoming message to parse
01598  * @return Error code
01599  **/
01600 
01601 void dhcpClientParseNak(DhcpClientContext *context,
01602    const DhcpMessage *message, size_t length)
01603 {
01604    DhcpOption *serverIdOption;
01605    NetInterface *interface;
01606 
01607    //Point to the underlying network interface
01608    interface = context->settings.interface;
01609 
01610    //Discard any received packet that does not match the transaction ID
01611    if(ntohl(message->xid) != context->transactionId)
01612       return;
01613    //Check MAC address
01614    if(!macCompAddr(&message->chaddr, &interface->macAddr))
01615       return;
01616 
01617    //A DHCP server always returns its own address in the Server Identifier option
01618    serverIdOption = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
01619 
01620    //Failed to retrieve the Server Identifier option?
01621    if(serverIdOption == NULL || serverIdOption->length != 4)
01622       return;
01623 
01624    //Check current state
01625    if(context->state == DHCP_STATE_REQUESTING ||
01626       context->state == DHCP_STATE_RENEWING)
01627    {
01628       //Check the server identifier
01629       if(!ipv4CompAddr(serverIdOption->value, &context->serverIpAddr))
01630          return;
01631    }
01632    else if(context->state == DHCP_STATE_REBOOTING ||
01633       context->state == DHCP_STATE_REBINDING)
01634    {
01635       //Do not check the server identifier
01636    }
01637    else
01638    {
01639       //Silently discard the DHCPNAK message
01640       return;
01641    }
01642 
01643    //The host address is no longer appropriate for the link
01644    interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
01645    interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
01646 
01647    //Clear subnet mask
01648    interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
01649 
01650 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
01651    //Restart mDNS probing process
01652    mdnsResponderStartProbing(interface->mdnsResponderContext);
01653 #endif
01654 
01655    //Restart DHCP configuration
01656    dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
01657 }
01658 
01659 
01660 /**
01661  * @brief Manage DHCP configuration timeout
01662  * @param[in] context Pointer to the DHCP client context
01663  **/
01664 
01665 void dhcpClientCheckTimeout(DhcpClientContext *context)
01666 {
01667    systime_t time;
01668    NetInterface *interface;
01669 
01670    //Point to the underlying network interface
01671    interface = context->settings.interface;
01672 
01673    //Get current time
01674    time = osGetSystemTime();
01675 
01676    //Any registered callback?
01677    if(context->settings.timeoutEvent != NULL)
01678    {
01679       //DHCP configuration timeout?
01680       if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
01681       {
01682          //Ensure the callback function is only called once
01683          if(!context->timeoutEventDone)
01684          {
01685             //Release exclusive access
01686             osReleaseMutex(&netMutex);
01687             //Invoke user callback function
01688             context->settings.timeoutEvent(context, interface);
01689             //Get exclusive access
01690             osAcquireMutex(&netMutex);
01691 
01692             //Set flag
01693             context->timeoutEventDone = TRUE;
01694          }
01695       }
01696    }
01697 }
01698 
01699 
01700 /**
01701  * @brief Compute the appropriate secs field
01702  *
01703  * Compute the number of seconds elapsed since the client began
01704  * address acquisition or renewal process
01705  *
01706  * @param[in] context Pointer to the DHCP client context
01707  * @return The elapsed time expressed in seconds
01708  **/
01709 
01710 uint16_t dhcpClientComputeElapsedTime(DhcpClientContext *context)
01711 {
01712    systime_t time;
01713 
01714    //Compute the time elapsed since the DHCP configuration process started
01715    time = (osGetSystemTime() - context->configStartTime) / 1000;
01716 
01717    //The value 0xFFFF is used to represent any elapsed time values
01718    //greater than the largest time value that can be represented
01719    time = MIN(time, 0xFFFF);
01720 
01721    //Convert the 16-bit value to network byte order
01722    return htons(time);
01723 }
01724 
01725 
01726 /**
01727  * @brief Update DHCP FSM state
01728  * @param[in] context Pointer to the DHCP client context
01729  * @param[in] newState New DHCP state to switch to
01730  * @param[in] delay Initial delay
01731  **/
01732 
01733 void dhcpClientChangeState(DhcpClientContext *context,
01734    DhcpState newState, systime_t delay)
01735 {
01736    systime_t time;
01737 
01738    //Get current time
01739    time = osGetSystemTime();
01740 
01741 #if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
01742    //Sanity check
01743    if(newState <= DHCP_STATE_REBINDING)
01744    {
01745       //DHCP FSM states
01746       static const char_t *stateLabel[] =
01747       {
01748          "INIT",
01749          "SELECTING",
01750          "REQUESTING",
01751          "INIT-REBOOT",
01752          "REBOOTING",
01753          "PROBING",
01754          "BOUND",
01755          "RENEWING",
01756          "REBINDING"
01757       };
01758 
01759       //Debug message
01760       TRACE_INFO("%s: DHCP client %s state\r\n",
01761          formatSystemTime(time, NULL), stateLabel[newState]);
01762    }
01763 #endif
01764 
01765    //Set time stamp
01766    context->timestamp = time;
01767    //Set initial delay
01768    context->timeout = delay;
01769    //Reset retransmission counter
01770    context->retransmitCount = 0;
01771    //Switch to the new state
01772    context->state = newState;
01773 
01774    //Any registered callback?
01775    if(context->settings.stateChangeEvent != NULL)
01776    {
01777       NetInterface *interface;
01778 
01779       //Point to the underlying network interface
01780       interface = context->settings.interface;
01781 
01782       //Release exclusive access
01783       osReleaseMutex(&netMutex);
01784       //Invoke user callback function
01785       context->settings.stateChangeEvent(context, interface, newState);
01786       //Get exclusive access
01787       osAcquireMutex(&netMutex);
01788    }
01789 }
01790 
01791 
01792 /**
01793  * @brief Dump DHCP configuration for debugging purpose
01794  * @param[in] context Pointer to the DHCP client context
01795  **/
01796 
01797 void dhcpClientDumpConfig(DhcpClientContext *context)
01798 {
01799 #if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
01800    uint_t i;
01801    NetInterface *interface;
01802    Ipv4Context *ipv4Context;
01803 
01804    //Point to the underlying network interface
01805    interface = context->settings.interface;
01806    //Point to the IPv4 context
01807    ipv4Context = &interface->ipv4Context;
01808 
01809    //Debug message
01810    TRACE_INFO("\r\n");
01811    TRACE_INFO("DHCP configuration:\r\n");
01812 
01813    //Lease start time
01814    TRACE_INFO("  Lease Start Time = %s\r\n",
01815       formatSystemTime(context->leaseStartTime, NULL));
01816 
01817    //Lease time
01818    TRACE_INFO("  Lease Time = %" PRIu32 "s\r\n", context->leaseTime);
01819    //Renewal time
01820    TRACE_INFO("  T1 = %" PRIu32 "s\r\n", context->t1);
01821    //Rebinding time
01822    TRACE_INFO("  T2 = %" PRIu32 "s\r\n", context->t2);
01823 
01824    //Host address
01825    TRACE_INFO("  IPv4 Address = %s\r\n",
01826       ipv4AddrToString(ipv4Context->addr, NULL));
01827 
01828    //Subnet mask
01829    TRACE_INFO("  Subnet Mask = %s\r\n",
01830       ipv4AddrToString(ipv4Context->subnetMask, NULL));
01831 
01832    //Default gateway
01833    TRACE_INFO("  Default Gateway = %s\r\n",
01834       ipv4AddrToString(ipv4Context->defaultGateway, NULL));
01835 
01836    //DNS servers
01837    for(i = 0; i < IPV4_DNS_SERVER_LIST_SIZE; i++)
01838    {
01839       TRACE_INFO("  DNS Server %u = %s\r\n", i + 1,
01840          ipv4AddrToString(ipv4Context->dnsServerList[i], NULL));
01841    }
01842 
01843    //Maximum transmit unit
01844    TRACE_INFO("  MTU = %" PRIuSIZE "\r\n", interface->ipv4Context.linkMtu);
01845    TRACE_INFO("\r\n");
01846 #endif
01847 }
01848 
01849 #endif
01850