Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 17:10:13 by
