dhcp_client.c
Go to the documentation of this file.
1 /**
2  * @file dhcp_client.c
3  * @brief DHCP client (Dynamic Host Configuration Protocol)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @section Description
26  *
27  * The Dynamic Host Configuration Protocol is used to provide configuration
28  * parameters to hosts. Refer to the following RFCs for complete details:
29  * - RFC 2131: Dynamic Host Configuration Protocol
30  * - RFC 2132: DHCP Options and BOOTP Vendor Extensions
31  * - RFC 4039: Rapid Commit Option for the DHCP version 4
32  *
33  * @author Oryx Embedded SARL (www.oryx-embedded.com)
34  * @version 1.9.0
35  **/
36 
37 //Switch to the appropriate trace level
38 #define TRACE_LEVEL DHCP_TRACE_LEVEL
39 
40 //Dependencies
41 #include "core/net.h"
42 #include "dhcp/dhcp_client.h"
43 #include "dhcp/dhcp_common.h"
44 #include "dhcp/dhcp_debug.h"
45 #include "mdns/mdns_responder.h"
46 #include "date_time.h"
47 #include "debug.h"
48 
49 //Check TCP/IP stack configuration
50 #if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
51 
52 //Tick counter to handle periodic operations
54 
55 //Requested DHCP options
56 const uint8_t dhcpOptionList[] =
57 {
65 };
66 
67 
68 /**
69  * @brief Initialize settings with default values
70  * @param[out] settings Structure that contains DHCP client settings
71  **/
72 
74 {
75  //Use default interface
76  settings->interface = netGetDefaultInterface();
77 
78 #if (DHCP_CLIENT_HOSTNAME_OPTION_SUPPORT == ENABLED)
79  //Use default host name
80  strcpy(settings->hostname, "");
81 #endif
82 
83 #if (DHCP_CLIENT_ID_OPTION_SUPPORT == ENABLED)
84  //Use default client identifier
85  settings->clientIdLength = 0;
86 #endif
87 
88  //Support for quick configuration using rapid commit
89  settings->rapidCommit = FALSE;
90  //Use the DNS servers provided by the DHCP server
91  settings->manualDnsConfig = FALSE;
92  //DHCP configuration timeout
93  settings->timeout = 0;
94  //DHCP configuration timeout event
95  settings->timeoutEvent = NULL;
96  //Link state change event
97  settings->linkChangeEvent = NULL;
98  //FSM state change event
99  settings->stateChangeEvent = NULL;
100 }
101 
102 
103 /**
104  * @brief DHCP client initialization
105  * @param[in] context Pointer to the DHCP client context
106  * @param[in] settings DHCP client specific settings
107  * @return Error code
108  **/
109 
111 {
112  error_t error;
113  size_t n;
115 
116  //Debug message
117  TRACE_INFO("Initializing DHCP client...\r\n");
118 
119  //Ensure the parameters are valid
120  if(context == NULL || settings == NULL)
122 
123  //A valid pointer to the interface being configured is required
124  if(settings->interface == NULL)
126 
127  //Point to the underlying network interface
128  interface = settings->interface;
129 
130  //Clear the DHCP client context
131  memset(context, 0, sizeof(DhcpClientContext));
132  //Save user settings
133  context->settings = *settings;
134 
135 #if (DHCP_CLIENT_HOSTNAME_OPTION_SUPPORT == ENABLED)
136  //No DHCP host name defined?
137  if(settings->hostname[0] == '\0')
138  {
139  //Use default host name
140  n = strlen(interface->hostname);
141  //Limit the length of the string
143 
144  //Copy host name
145  strncpy(context->settings.hostname, interface->hostname, n);
146  //Properly terminate the string with a NULL character
147  context->settings.hostname[n] = '\0';
148  }
149 #endif
150 
151  //Callback function to be called when a DHCP message is received
152  error = udpAttachRxCallback(interface, DHCP_CLIENT_PORT,
153  dhcpClientProcessMessage, context);
154  //Failed to register callback function?
155  if(error)
156  return error;
157 
158  //DHCP client is currently suspended
159  context->running = FALSE;
160  //Initialize state machine
161  context->state = DHCP_STATE_INIT;
162 
163  //Attach the DHCP client context to the network interface
164  interface->dhcpClientContext = context;
165 
166  //Successful initialization
167  return NO_ERROR;
168 }
169 
170 
171 /**
172  * @brief Start DHCP client
173  * @param[in] context Pointer to the DHCP client context
174  * @return Error code
175  **/
176 
178 {
179  NetInterface *interface;
180 
181  //Check parameter
182  if(context == NULL)
184 
185  //Debug message
186  TRACE_INFO("Starting DHCP client...\r\n");
187 
188  //Get exclusive access
190 
191  //Point to the underlying network interface
192  interface = context->settings.interface;
193 
194  //The host address is not longer valid
195  interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
196  interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
197 
198  //Clear subnet mask
199  interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
200 
201  //Start DHCP client
202  context->running = TRUE;
203  //Initialize state machine
204  context->state = DHCP_STATE_INIT;
205 
206  //Release exclusive access
208 
209  //Successful processing
210  return NO_ERROR;
211 }
212 
213 
214 /**
215  * @brief Stop DHCP client
216  * @param[in] context Pointer to the DHCP client context
217  * @return Error code
218  **/
219 
221 {
222  //Check parameter
223  if(context == NULL)
225 
226  //Debug message
227  TRACE_INFO("Stopping DHCP client...\r\n");
228 
229  //Get exclusive access
231 
232  //Stop DHCP client
233  context->running = FALSE;
234  //Reinitialize state machine
235  context->state = DHCP_STATE_INIT;
236 
237  //Release exclusive access
239 
240  //Successful processing
241  return NO_ERROR;
242 }
243 
244 
245 /**
246  * @brief Retrieve current state
247  * @param[in] context Pointer to the DHCP client context
248  * @return Current DHCP client state
249  **/
250 
252 {
253  DhcpState state;
254 
255  //Get exclusive access
257  //Get current state
258  state = context->state;
259  //Release exclusive access
261 
262  //Return current state
263  return state;
264 }
265 
266 
267 /**
268  * @brief DHCP client timer handler
269  *
270  * This routine must be periodically called by the TCP/IP stack to
271  * manage DHCP client operation
272  *
273  * @param[in] context Pointer to the DHCP client context
274  **/
275 
276 
278 {
279  //Make sure the DHCP client has been properly instantiated
280  if(context == NULL)
281  return;
282 
283  //DHCP client finite state machine
284  switch(context->state)
285  {
286  //Process INIT state
287  case DHCP_STATE_INIT:
288  //This is the initialization state, where a client begins the process of
289  //acquiring a lease. It also returns here when a lease ends, or when a
290  //lease negotiation fails
291  dhcpClientStateInit(context);
292  break;
293  //Process SELECTING state
295  //The client is waiting to receive DHCPOFFER messages from one or more
296  //DHCP servers, so it can choose one
297  dhcpClientStateSelecting(context);
298  break;
299  //Process REQUESTING state
301  //The client is waiting to hear back from the server to which
302  //it sent its request
303  dhcpClientStateRequesting(context);
304  break;
305  //Process INIT REBOOT state
307  //When a client that already has a valid lease starts up after a
308  //power-down or reboot, it starts here instead of the INIT state
309  dhcpClientStateInitReboot(context);
310  break;
311  //Process REBOOTING state
313  //A client that has rebooted with an assigned address is waiting for
314  //a confirming reply from a server
315  dhcpClientStateRebooting(context);
316  break;
317  //Process PROBING state
318  case DHCP_STATE_PROBING:
319  //The client probes the newly received address
320  dhcpClientStateProbing(context);
321  break;
322  //Process BOUND state
323  case DHCP_STATE_BOUND:
324  //Client has a valid lease and is in its normal operating state
325  dhcpClientStateBound(context);
326  break;
327  //Process RENEWING state
328  case DHCP_STATE_RENEWING:
329  //Client is trying to renew its lease. It regularly sends DHCPREQUEST messages with
330  //the server that gave it its current lease specified, and waits for a reply
331  dhcpClientStateRenewing(context);
332  break;
333  //Process REBINDING state
335  //The client has failed to renew its lease with the server that originally granted it,
336  //and now seeks a lease extension with any server that can hear it. It periodically sends
337  //DHCPREQUEST messages with no server specified until it gets a reply or the lease ends
338  dhcpClientStateRebinding(context);
339  break;
340  //Invalid state...
341  default:
342  //Switch to the INIT state
343  context->state = DHCP_STATE_INIT;
344  break;
345  }
346 }
347 
348 
349 /**
350  * @brief Callback function for link change event
351  * @param[in] context Pointer to the DHCP client context
352  **/
353 
355 {
356  NetInterface *interface;
357 
358  //Make sure the DHCP client has been properly instantiated
359  if(context == NULL)
360  return;
361 
362  //Point to the underlying network interface
363  interface = context->settings.interface;
364 
365  //Check whether the DHCP client is running
366  if(context->running)
367  {
368  //The host address is no longer valid
369  interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
370  interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
371 
372  //Clear subnet mask
373  interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
374 
375 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
376  //Restart mDNS probing process
377  mdnsResponderStartProbing(interface->mdnsResponderContext);
378 #endif
379  }
380 
381  //Check whether the client already has a valid lease
382  if(context->state >= DHCP_STATE_INIT_REBOOT)
383  {
384  //Switch to the INIT-REBOOT state
385  context->state = DHCP_STATE_INIT_REBOOT;
386  }
387  else
388  {
389  //Switch to the INIT state
390  context->state = DHCP_STATE_INIT;
391  }
392 
393  //Any registered callback?
394  if(context->settings.linkChangeEvent != NULL)
395  {
396  //Release exclusive access
398  //Invoke user callback function
399  context->settings.linkChangeEvent(context, interface, interface->linkState);
400  //Get exclusive access
402  }
403 }
404 
405 
406 /**
407  * @brief INIT state
408  *
409  * This is the initialization state, where a client begins the process of
410  * acquiring a lease. It also returns here when a lease ends, or when a
411  * lease negotiation fails
412  *
413  * @param[in] context Pointer to the DHCP client context
414  **/
415 
417 {
418  systime_t delay;
419  NetInterface *interface;
420 
421  //Point to the underlying network interface
422  interface = context->settings.interface;
423 
424  //Check whether the DHCP client is running
425  if(context->running)
426  {
427  //Wait for the link to be up before starting DHCP configuration
428  if(interface->linkState)
429  {
430  //The client should wait for a random time to desynchronize
431  //the use of DHCP at startup
433 
434  //Record the time at which the client started the address
435  //acquisition process
436  context->configStartTime = osGetSystemTime();
437  //Clear flag
438  context->timeoutEventDone = FALSE;
439 
440  //Switch to the SELECTING state
442  }
443  }
444 }
445 
446 
447 /**
448  * @brief SELECTING state
449  *
450  * The client is waiting to receive DHCPOFFER messages from
451  * one or more DHCP servers, so it can choose one
452  *
453  * @param[in] context Pointer to the DHCP client context
454  **/
455 
457 {
458  systime_t time;
459 
460  //Get current time
461  time = osGetSystemTime();
462 
463  //Check current time
464  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
465  {
466  //Check retransmission counter
467  if(context->retransmitCount == 0)
468  {
469  //A transaction identifier is used by the client to
470  //match incoming DHCP messages with pending requests
471  context->transactionId = netGetRand();
472 
473  //Send a DHCPDISCOVER message
474  dhcpClientSendDiscover(context);
475 
476  //Initial timeout value
477  context->retransmitTimeout = DHCP_CLIENT_DISCOVER_INIT_RT;
478  }
479  else
480  {
481  //Send a DHCPDISCOVER message
482  dhcpClientSendDiscover(context);
483 
484  //The timeout value is doubled for each subsequent retransmission
485  context->retransmitTimeout *= 2;
486 
487  //Limit the timeout value to a maximum of 64 seconds
488  if(context->retransmitTimeout > DHCP_CLIENT_DISCOVER_MAX_RT)
489  context->retransmitTimeout = DHCP_CLIENT_DISCOVER_MAX_RT;
490  }
491 
492  //Save the time at which the message was sent
493  context->timestamp = time;
494 
495  //The timeout value should be randomized by the value of a uniform
496  //number chosen from the range -1 to +1
497  context->timeout = context->retransmitTimeout +
499 
500  //Increment retransmission counter
501  context->retransmitCount++;
502  }
503 
504  //Manage DHCP configuration timeout
505  dhcpClientCheckTimeout(context);
506 }
507 
508 
509 /**
510  * @brief REQUESTING state
511  *
512  * The client is waiting to hear back from the server
513  * to which it sent its request
514  *
515  * @param[in] context Pointer to the DHCP client context
516  **/
517 
519 {
520  systime_t time;
521 
522  //Get current time
523  time = osGetSystemTime();
524 
525  //Check current time
526  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
527  {
528  //Check retransmission counter
529  if(context->retransmitCount == 0)
530  {
531  //A transaction identifier is used by the client to
532  //match incoming DHCP messages with pending requests
533  context->transactionId = netGetRand();
534 
535  //Send a DHCPREQUEST message
536  dhcpClientSendRequest(context);
537 
538  //Initial timeout value
539  context->retransmitTimeout = DHCP_CLIENT_REQUEST_INIT_RT;
540 
541  //Save the time at which the message was sent
542  context->timestamp = time;
543 
544  //The timeout value should be randomized by the value of a uniform
545  //number chosen from the range -1 to +1
546  context->timeout = context->retransmitTimeout +
548 
549  //Increment retransmission counter
550  context->retransmitCount++;
551  }
552  else if(context->retransmitCount < DHCP_CLIENT_REQUEST_MAX_RC)
553  {
554  //Send a DHCPREQUEST message
555  dhcpClientSendRequest(context);
556 
557  //The timeout value is doubled for each subsequent retransmission
558  context->retransmitTimeout *= 2;
559 
560  //Limit the timeout value to a maximum of 64 seconds
561  if(context->retransmitTimeout > DHCP_CLIENT_REQUEST_MAX_RT)
562  context->retransmitTimeout = DHCP_CLIENT_REQUEST_MAX_RT;
563 
564  //Save the time at which the message was sent
565  context->timestamp = time;
566 
567  //The timeout value should be randomized by the value of a uniform
568  //number chosen from the range -1 to +1
569  context->timeout = context->retransmitTimeout +
571 
572  //Increment retransmission counter
573  context->retransmitCount++;
574  }
575  else
576  {
577  //If the client does not receive a response within a reasonable
578  //period of time, then it restarts the initialization procedure
580  }
581  }
582 
583  //Manage DHCP configuration timeout
584  dhcpClientCheckTimeout(context);
585 }
586 
587 
588 /**
589  * @brief INIT-REBOOT state
590  *
591  * When a client that already has a valid lease starts up after a
592  * power-down or reboot, it starts here instead of the INIT state
593  *
594  * @param[in] context Pointer to the DHCP client context
595  **/
596 
598 {
599  systime_t delay;
600  NetInterface *interface;
601 
602  //Point to the underlying network interface
603  interface = context->settings.interface;
604 
605  //Check whether the DHCP client is running
606  if(context->running)
607  {
608  //Wait for the link to be up before starting DHCP configuration
609  if(interface->linkState)
610  {
611  //The client should wait for a random time to desynchronize
612  //the use of DHCP at startup
614 
615  //Record the time at which the client started the address
616  //acquisition process
617  context->configStartTime = osGetSystemTime();
618  //Clear flag
619  context->timeoutEventDone = FALSE;
620 
621  //Switch to the REBOOTING state
623  }
624  }
625 }
626 
627 
628 /**
629  * @brief REBOOTING state
630  *
631  * A client that has rebooted with an assigned address is
632  * waiting for a confirming reply from a server
633  *
634  * @param[in] context Pointer to the DHCP client context
635  **/
636 
638 {
639  systime_t time;
640 
641  //Get current time
642  time = osGetSystemTime();
643 
644  //Check current time
645  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
646  {
647  //Check retransmission counter
648  if(context->retransmitCount == 0)
649  {
650  //A transaction identifier is used by the client to
651  //match incoming DHCP messages with pending requests
652  context->transactionId = netGetRand();
653 
654  //Send a DHCPREQUEST message
655  dhcpClientSendRequest(context);
656 
657  //Initial timeout value
658  context->retransmitTimeout = DHCP_CLIENT_REQUEST_INIT_RT;
659 
660  //Save the time at which the message was sent
661  context->timestamp = time;
662 
663  //The timeout value should be randomized by the value of a uniform
664  //number chosen from the range -1 to +1
665  context->timeout = context->retransmitTimeout +
667 
668  //Increment retransmission counter
669  context->retransmitCount++;
670  }
671  else if(context->retransmitCount < DHCP_CLIENT_REQUEST_MAX_RC)
672  {
673  //Send a DHCPREQUEST message
674  dhcpClientSendRequest(context);
675 
676  //The timeout value is doubled for each subsequent retransmission
677  context->retransmitTimeout *= 2;
678 
679  //Limit the timeout value to a maximum of 64 seconds
680  if(context->retransmitTimeout > DHCP_CLIENT_REQUEST_MAX_RT)
681  context->retransmitTimeout = DHCP_CLIENT_REQUEST_MAX_RT;
682 
683  //Save the time at which the message was sent
684  context->timestamp = time;
685 
686  //The timeout value should be randomized by the value of a uniform
687  //number chosen from the range -1 to +1
688  context->timeout = context->retransmitTimeout +
690 
691  //Increment retransmission counter
692  context->retransmitCount++;
693  }
694  else
695  {
696  //If the client does not receive a response within a reasonable
697  //period of time, then it restarts the initialization procedure
699  }
700  }
701 
702  //Manage DHCP configuration timeout
703  dhcpClientCheckTimeout(context);
704 }
705 
706 
707 /**
708  * @brief PROBING state
709  *
710  * The client probes the newly received address
711  *
712  * @param[in] context Pointer to the DHCP client context
713  **/
714 
716 {
717  systime_t time;
718  NetInterface *interface;
719 
720  //Point to the underlying network interface
721  interface = context->settings.interface;
722  //Get current time
723  time = osGetSystemTime();
724 
725  //Check current time
726  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
727  {
728  //The address is already in use?
729  if(interface->ipv4Context.addrConflict)
730  {
731  //If the client detects that the address is already in use, the
732  //client must send a DHCPDECLINE message to the server and
733  //restarts the configuration process
734  dhcpClientSendDecline(context);
735 
736  //The client should wait a minimum of ten seconds before
737  //restarting the configuration process to avoid excessive
738  //network traffic in case of looping
740  }
741  //Probing is on-going?
742  else if(context->retransmitCount < DHCP_CLIENT_PROBE_NUM)
743  {
744  //Conflict detection is done using ARP probes
745  arpSendProbe(interface, interface->ipv4Context.addr);
746 
747  //Save the time at which the packet was sent
748  context->timestamp = time;
749  //Delay until repeated probe
750  context->timeout = DHCP_CLIENT_PROBE_DELAY;
751  //Increment retransmission counter
752  context->retransmitCount++;
753  }
754  //Probing is complete?
755  else
756  {
757  //The use of the IPv4 address is now unrestricted
758  interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
759 
760 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
761  //Restart mDNS probing process
762  mdnsResponderStartProbing(interface->mdnsResponderContext);
763 #endif
764  //Dump current DHCP configuration for debugging purpose
765  dhcpClientDumpConfig(context);
766 
767  //The client transitions to the BOUND state
769  }
770  }
771 }
772 
773 
774 /**
775  * @brief BOUND state
776  *
777  * Client has a valid lease and is in its normal operating state
778  *
779  * @param[in] context Pointer to the DHCP client context
780  **/
781 
783 {
784  systime_t t1;
785  systime_t time;
786 
787  //Get current time
788  time = osGetSystemTime();
789 
790  //A client will never attempt to extend the lifetime
791  //of the address when T1 set to 0xFFFFFFFF
792  if(context->t1 != DHCP_INFINITE_TIME)
793  {
794  //Convert T1 to milliseconds
795  if(context->t1 < (MAX_DELAY / 1000))
796  t1 = context->t1 * 1000;
797  else
798  t1 = MAX_DELAY;
799 
800  //Check the time elapsed since the lease was obtained
801  if(timeCompare(time, context->leaseStartTime + t1) >= 0)
802  {
803  //Record the time at which the client started the address renewal process
804  context->configStartTime = time;
805 
806  //Enter the RENEWING state
808  }
809  }
810 }
811 
812 
813 /**
814  * @brief RENEWING state
815  *
816  * Client is trying to renew its lease. It regularly sends
817  * DHCPREQUEST messages with the server that gave it its current
818  * lease specified, and waits for a reply
819  *
820  * @param[in] context Pointer to the DHCP client context
821  **/
822 
824 {
825  systime_t t2;
826  systime_t time;
827 
828  //Get current time
829  time = osGetSystemTime();
830 
831  //Check current time
832  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
833  {
834  //Convert T2 to milliseconds
835  if(context->t2 < (MAX_DELAY / 1000))
836  t2 = context->t2 * 1000;
837  else
838  t2 = MAX_DELAY;
839 
840  //Check whether T2 timer has expired
841  if(timeCompare(time, context->leaseStartTime + t2) < 0)
842  {
843  //First DHCPREQUEST message?
844  if(context->retransmitCount == 0)
845  {
846  //A transaction identifier is used by the client to
847  //match incoming DHCP messages with pending requests
848  context->transactionId = netGetRand();
849  }
850 
851  //Send a DHCPREQUEST message
852  dhcpClientSendRequest(context);
853 
854  //Save the time at which the message was sent
855  context->timestamp = time;
856 
857  //Compute the remaining time until T2 expires
858  context->timeout = context->leaseStartTime + t2 - time;
859 
860  //The client should wait one-half of the remaining time until T2, down to
861  //a minimum of 60 seconds, before retransmitting the DHCPREQUEST message
862  if(context->timeout > (2 * DHCP_CLIENT_REQUEST_MIN_DELAY))
863  context->timeout /= 2;
864 
865  //Increment retransmission counter
866  context->retransmitCount++;
867  }
868  else
869  {
870  //If no DHCPACK arrives before time T2, the client moves to REBINDING
872  }
873  }
874 }
875 
876 
877 /**
878  * @brief REBINDING state
879  *
880  * The client has failed to renew its lease with the server that originally
881  * granted it, and now seeks a lease extension with any server that can
882  * hear it. It periodically sends DHCPREQUEST messages with no server specified
883  * until it gets a reply or the lease ends
884  *
885  * @param[in] context Pointer to the DHCP client context
886  **/
887 
889 {
890  systime_t time;
891  systime_t leaseTime;
892  NetInterface *interface;
893 
894  //Point to the underlying network interface
895  interface = context->settings.interface;
896 
897  //Get current time
898  time = osGetSystemTime();
899 
900  //Check current time
901  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
902  {
903  //Convert the lease time to milliseconds
904  if(context->leaseTime < (MAX_DELAY / 1000))
905  leaseTime = context->leaseTime * 1000;
906  else
907  leaseTime = MAX_DELAY;
908 
909  //Check whether the lease has expired
910  if(timeCompare(time, context->leaseStartTime + leaseTime) < 0)
911  {
912  //First DHCPREQUEST message?
913  if(context->retransmitCount == 0)
914  {
915  //A transaction identifier is used by the client to
916  //match incoming DHCP messages with pending requests
917  context->transactionId = netGetRand();
918  }
919 
920  //Send a DHCPREQUEST message
921  dhcpClientSendRequest(context);
922 
923  //Save the time at which the message was sent
924  context->timestamp = time;
925 
926  //Compute the remaining time until the lease expires
927  context->timeout = context->leaseStartTime + leaseTime - time;
928 
929  //The client should wait one-half of the remaining lease time, down to a
930  //minimum of 60 seconds, before retransmitting the DHCPREQUEST message
931  if(context->timeout > (2 * DHCP_CLIENT_REQUEST_MIN_DELAY))
932  context->timeout /= 2;
933 
934  //Increment retransmission counter
935  context->retransmitCount++;
936  }
937  else
938  {
939  //The host address is no longer valid...
940  interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
941  interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
942 
943  //Clear subnet mask
944  interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
945 
946 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
947  //Restart mDNS probing process
948  mdnsResponderStartProbing(interface->mdnsResponderContext);
949 #endif
950 
951  //If the lease expires before the client receives
952  //a DHCPACK, the client moves to INIT state
954  }
955  }
956 }
957 
958 
959 /**
960  * @brief Send DHCPDISCOVER message
961  * @param[in] context Pointer to the DHCP client context
962  * @return Error code
963  **/
964 
966 {
967  error_t error;
968  size_t offset;
969  NetBuffer *buffer;
970  NetInterface *interface;
971  NetInterface *logicalInterface;
974 #if (DHCP_CLIENT_HOSTNAME_OPTION_SUPPORT == ENABLED)
975  size_t length;
976 #endif
977 
978  //DHCP message type
979  const uint8_t messageType = DHCP_MESSAGE_TYPE_DISCOVER;
980 
981  //Point to the underlying network interface
982  interface = context->settings.interface;
983 
984  //Point to the logical interface
985  logicalInterface = nicGetLogicalInterface(interface);
986 
987  //Allocate a memory buffer to hold the DHCP message
988  buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
989  //Failed to allocate buffer?
990  if(buffer == NULL)
991  return ERROR_OUT_OF_MEMORY;
992 
993  //Point to the beginning of the DHCP message
994  message = netBufferAt(buffer, offset);
995  //Clear memory buffer contents
996  memset(message, 0, DHCP_MIN_MSG_SIZE);
997 
998  //Format DHCPDISCOVER message
1001  message->hlen = sizeof(MacAddr);
1002  message->xid = htonl(context->transactionId);
1003  message->secs = dhcpClientComputeElapsedTime(context);
1004  message->flags = HTONS(DHCP_FLAG_BROADCAST);
1005  message->ciaddr = IPV4_UNSPECIFIED_ADDR;
1006  message->chaddr = logicalInterface->macAddr;
1007 
1008  //Write magic cookie before setting any option
1009  message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
1010  //Properly terminate options field
1011  message->options[0] = DHCP_OPT_END;
1012 
1013  //DHCP Message Type option
1015  &messageType, sizeof(messageType));
1016 
1017 #if (DHCP_CLIENT_HOSTNAME_OPTION_SUPPORT == ENABLED)
1018  //Retrieve the length of the host name
1019  length = strlen(context->settings.hostname);
1020 
1021  //Any host name defined?
1022  if(length > 0)
1023  {
1024  //The Host Name option specifies the name of the client
1026  context->settings.hostname, length);
1027  }
1028 #endif
1029 
1030 #if (DHCP_CLIENT_ID_OPTION_SUPPORT == ENABLED)
1031  //Any client identifier defined?
1032  if(context->settings.clientIdLength > 0)
1033  {
1034  //DHCP servers use this value to index their database of address bindings
1036  context->settings.clientId, context->settings.clientIdLength);
1037  }
1038 #endif
1039 
1040  //Check whether rapid commit is enabled
1041  if(context->settings.rapidCommit)
1042  {
1043  //Include the Rapid Commit option if the client is prepared
1044  //to perform the DHCPDISCOVER-DHCPACK message exchange
1046  }
1047 
1048  //Set destination IP address
1049  destIpAddr.length = sizeof(Ipv4Addr);
1050  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
1051 
1052  //Debug message
1053  TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
1055 
1056  //Dump the contents of the message for debugging purpose
1058 
1059  //Broadcast DHCPDISCOVER message
1060  error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
1061  DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
1062 
1063  //Free previously allocated memory
1064  netBufferFree(buffer);
1065  //Return status code
1066  return error;
1067 }
1068 
1069 
1070 /**
1071  * @brief Send DHCPREQUEST message
1072  * @param[in] context Pointer to the DHCP client context
1073  * @return Error code
1074  **/
1075 
1077 {
1078  error_t error;
1079  size_t offset;
1080  NetBuffer *buffer;
1081  NetInterface *interface;
1082  NetInterface *logicalInterface;
1085 #if (DHCP_CLIENT_HOSTNAME_OPTION_SUPPORT == ENABLED)
1086  size_t length;
1087 #endif
1088 
1089  //DHCP message type
1090  const uint8_t messageType = DHCP_MESSAGE_TYPE_REQUEST;
1091 
1092  //Point to the underlying network interface
1093  interface = context->settings.interface;
1094 
1095  //Point to the logical interface
1096  logicalInterface = nicGetLogicalInterface(interface);
1097 
1098  //Allocate a memory buffer to hold the DHCP message
1099  buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
1100  //Failed to allocate buffer?
1101  if(buffer == NULL)
1102  return ERROR_OUT_OF_MEMORY;
1103 
1104  //Point to the beginning of the DHCP message
1105  message = netBufferAt(buffer, offset);
1106  //Clear memory buffer contents
1107  memset(message, 0, DHCP_MIN_MSG_SIZE);
1108 
1109  //Format DHCPREQUEST message
1112  message->hlen = sizeof(MacAddr);
1113  message->xid = htonl(context->transactionId);
1114  message->secs = dhcpClientComputeElapsedTime(context);
1115 
1116  //The client IP address must be included if the client
1117  //is fully configured and can respond to ARP requests
1118  if(context->state == DHCP_STATE_RENEWING ||
1119  context->state == DHCP_STATE_REBINDING)
1120  {
1121  message->flags = 0;
1122  message->ciaddr = interface->ipv4Context.addr;
1123  }
1124  else
1125  {
1126  message->flags = HTONS(DHCP_FLAG_BROADCAST);
1127  message->ciaddr = IPV4_UNSPECIFIED_ADDR;
1128  }
1129 
1130  //Client hardware address
1131  message->chaddr = logicalInterface->macAddr;
1132  //Write magic cookie before setting any option
1133  message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
1134  //Properly terminate options field
1135  message->options[0] = DHCP_OPT_END;
1136 
1137  //DHCP Message Type option
1139  &messageType, sizeof(messageType));
1140 
1141 #if (DHCP_CLIENT_HOSTNAME_OPTION_SUPPORT == ENABLED)
1142  //Retrieve the length of the host name
1143  length = strlen(context->settings.hostname);
1144 
1145  //Any host name defined?
1146  if(length > 0)
1147  {
1148  //The Host Name option specifies the name of the client
1150  context->settings.hostname, length);
1151  }
1152 #endif
1153 
1154 #if (DHCP_CLIENT_ID_OPTION_SUPPORT == ENABLED)
1155  //Any client identifier defined?
1156  if(context->settings.clientIdLength > 0)
1157  {
1158  //DHCP servers use this value to index their database of address bindings
1160  context->settings.clientId, context->settings.clientIdLength);
1161  }
1162 #endif
1163 
1164  //Server Identifier option
1165  if(context->state == DHCP_STATE_REQUESTING)
1166  {
1168  &context->serverIpAddr, sizeof(Ipv4Addr));
1169  }
1170 
1171  //Requested IP Address option
1172  if(context->state == DHCP_STATE_REQUESTING ||
1173  context->state == DHCP_STATE_REBOOTING)
1174  {
1176  &context->requestedIpAddr, sizeof(Ipv4Addr));
1177  }
1178 
1179  //Parameter Request List option
1181  dhcpOptionList, sizeof(dhcpOptionList));
1182 
1183  //IP address is being renewed?
1184  if(context->state == DHCP_STATE_RENEWING)
1185  {
1186  //The client transmits the message directly to the
1187  //server that initially granted the lease
1188  destIpAddr.length = sizeof(Ipv4Addr);
1189  destIpAddr.ipv4Addr = context->serverIpAddr;
1190  }
1191  else
1192  {
1193  //Broadcast the message
1194  destIpAddr.length = sizeof(Ipv4Addr);
1195  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
1196  }
1197 
1198  //Debug message
1199  TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
1201 
1202  //Dump the contents of the message for debugging purpose
1204 
1205  //Send DHCPREQUEST message
1206  error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
1207  DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
1208 
1209  //Free previously allocated memory
1210  netBufferFree(buffer);
1211  //Return status code
1212  return error;
1213 }
1214 
1215 
1216 /**
1217  * @brief Send DHCPDECLINE message
1218  * @param[in] context Pointer to the DHCP client context
1219  * @return Error code
1220  **/
1221 
1223 {
1224  error_t error;
1225  size_t offset;
1226  NetBuffer *buffer;
1227  NetInterface *interface;
1228  NetInterface *logicalInterface;
1231 
1232  //DHCP message type
1233  const uint8_t messageType = DHCP_MESSAGE_TYPE_DECLINE;
1234 
1235  //Point to the underlying network interface
1236  interface = context->settings.interface;
1237 
1238  //Point to the logical interface
1239  logicalInterface = nicGetLogicalInterface(interface);
1240 
1241  //Allocate a memory buffer to hold the DHCP message
1242  buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
1243  //Failed to allocate buffer?
1244  if(buffer == NULL)
1245  return ERROR_OUT_OF_MEMORY;
1246 
1247  //Point to the beginning of the DHCP message
1248  message = netBufferAt(buffer, offset);
1249  //Clear memory buffer contents
1250  memset(message, 0, DHCP_MIN_MSG_SIZE);
1251 
1252  //Format DHCPDECLINE message
1255  message->hlen = sizeof(MacAddr);
1256  message->xid = htonl(context->transactionId);
1257  message->secs = 0;
1258  message->flags = 0;
1259  message->ciaddr = IPV4_UNSPECIFIED_ADDR;
1260  message->chaddr = logicalInterface->macAddr;
1261 
1262  //Write magic cookie before setting any option
1263  message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
1264  //Properly terminate options field
1265  message->options[0] = DHCP_OPT_END;
1266 
1267  //DHCP Message Type option
1269  &messageType, sizeof(messageType));
1270  //Server Identifier option
1272  &context->serverIpAddr, sizeof(Ipv4Addr));
1273  //Requested IP Address option
1275  &context->requestedIpAddr, sizeof(Ipv4Addr));
1276 
1277  //Set destination IP address
1278  destIpAddr.length = sizeof(Ipv4Addr);
1279  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
1280 
1281  //Debug message
1282  TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
1284 
1285  //Dump the contents of the message for debugging purpose
1287 
1288  //Broadcast DHCPDECLINE message
1289  error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
1290  DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
1291 
1292  //Free previously allocated memory
1293  netBufferFree(buffer);
1294  //Return status code
1295  return error;
1296 }
1297 
1298 
1299 /**
1300  * @brief Process incoming DHCP message
1301  * @param[in] interface Underlying network interface
1302  * @param[in] pseudoHeader UDP pseudo header
1303  * @param[in] udpHeader UDP header
1304  * @param[in] buffer Multi-part buffer containing the incoming DHCP message
1305  * @param[in] offset Offset to the first byte of the DHCP message
1306  * @param[in] param Pointer to the DHCP client context
1307  **/
1308 
1310  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
1311  const NetBuffer *buffer, size_t offset, void *param)
1312 {
1313  size_t length;
1314  DhcpClientContext *context;
1316  DhcpOption *option;
1317 
1318  //Point to the DHCP client context
1319  context = (DhcpClientContext *) param;
1320 
1321  //Retrieve the length of the DHCP message
1322  length = netBufferGetLength(buffer) - offset;
1323 
1324  //Make sure the DHCP message is valid
1325  if(length < sizeof(DhcpMessage))
1326  return;
1328  return;
1329 
1330  //Point to the beginning of the DHCP message
1331  message = netBufferAt(buffer, offset);
1332  //Sanity check
1333  if(message == NULL)
1334  return;
1335 
1336  //Debug message
1337  TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
1339 
1340  //Dump the contents of the message for debugging purpose
1342 
1343  //The DHCP server shall respond with a BOOTREPLY opcode
1344  if(message->op != DHCP_OPCODE_BOOTREPLY)
1345  return;
1346  //Enforce hardware type
1347  if(message->htype != DHCP_HARDWARE_TYPE_ETH)
1348  return;
1349  //Check the length of the hardware address
1350  if(message->hlen != sizeof(MacAddr))
1351  return;
1352  //Check magic cookie
1353  if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
1354  return;
1355 
1356  //The DHCP Message Type option must be included in every DHCP message
1358 
1359  //Failed to retrieve the Message Type option?
1360  if(option == NULL || option->length != 1)
1361  return;
1362 
1363  //Check message type
1364  switch(option->value[0])
1365  {
1367  //Parse DHCPOFFER message
1368  dhcpClientParseOffer(context, message, length);
1369  break;
1370  case DHCP_MESSAGE_TYPE_ACK:
1371  //Parse DHCPACK message
1372  dhcpClientParseAck(context, message, length);
1373  break;
1374  case DHCP_MESSAGE_TYPE_NAK:
1375  //Parse DHCPNAK message
1376  dhcpClientParseNak(context, message, length);
1377  break;
1378  default:
1379  //Silently drop incoming message
1380  break;
1381  }
1382 }
1383 
1384 
1385 /**
1386  * @brief Parse DHCPOFFER message
1387  * @param[in] context Pointer to the DHCP client context
1388  * @param[in] message Pointer to the incoming DHCP message
1389  * @param[in] length Length of the incoming message to parse
1390  **/
1391 
1393  const DhcpMessage *message, size_t length)
1394 {
1395  DhcpOption *serverIdOption;
1396  NetInterface *interface;
1397  NetInterface *logicalInterface;
1398 
1399  //Point to the underlying network interface
1400  interface = context->settings.interface;
1401 
1402  //Point to the logical interface
1403  logicalInterface = nicGetLogicalInterface(interface);
1404 
1405  //Discard any received packet that does not match the transaction ID
1406  if(ntohl(message->xid) != context->transactionId)
1407  return;
1408  //Make sure the IP address offered to the client is valid
1409  if(message->yiaddr == IPV4_UNSPECIFIED_ADDR)
1410  return;
1411  //Check MAC address
1412  if(!macCompAddr(&message->chaddr, &logicalInterface->macAddr))
1413  return;
1414 
1415  //Make sure that the DHCPOFFER message is received in response to
1416  //a DHCPDISCOVER message
1417  if(context->state != DHCP_STATE_SELECTING)
1418  return;
1419 
1420  //A DHCP server always returns its own address in the Server Identifier option
1422 
1423  //Failed to retrieve the Server Identifier option?
1424  if(serverIdOption == NULL || serverIdOption->length != 4)
1425  return;
1426 
1427  //Record the IP address of the DHCP server
1428  ipv4CopyAddr(&context->serverIpAddr, serverIdOption->value);
1429  //Record the IP address offered to the client
1430  context->requestedIpAddr = message->yiaddr;
1431 
1432  //Switch to the REQUESTING state
1434 }
1435 
1436 
1437 /**
1438  * @brief Parse DHCPACK message
1439  * @param[in] context Pointer to the DHCP client context
1440  * @param[in] message Pointer to the incoming DHCP message
1441  * @param[in] length Length of the incoming message to parse
1442  * @return Error code
1443  **/
1444 
1446  const DhcpMessage *message, size_t length)
1447 {
1448  uint_t i;
1449  uint_t n;
1450  DhcpOption *option;
1451  DhcpOption *serverIdOption;
1452  NetInterface *interface;
1453  NetInterface *logicalInterface;
1454  NetInterface *physicalInterface;
1455 
1456  //Point to the underlying network interface
1457  interface = context->settings.interface;
1458 
1459  //Point to the logical interface
1460  logicalInterface = nicGetLogicalInterface(interface);
1461  //Point to the physical interface
1462  physicalInterface = nicGetPhysicalInterface(interface);
1463 
1464  //Discard any received packet that does not match the transaction ID
1465  if(ntohl(message->xid) != context->transactionId)
1466  return;
1467  //Make sure the IP address assigned to the client is valid
1468  if(message->yiaddr == IPV4_UNSPECIFIED_ADDR)
1469  return;
1470  //Check MAC address
1471  if(!macCompAddr(&message->chaddr, &logicalInterface->macAddr))
1472  return;
1473 
1474  //A DHCP server always returns its own address in the Server Identifier option
1476 
1477  //Failed to retrieve the Server Identifier option?
1478  if(serverIdOption == NULL || serverIdOption->length != 4)
1479  return;
1480 
1481  //Check current state
1482  if(context->state == DHCP_STATE_SELECTING)
1483  {
1484  //A DHCPACK message is not acceptable when rapid commit is disallowed
1485  if(!context->settings.rapidCommit)
1486  return;
1487 
1488  //Search for the Rapid Commit option
1490 
1491  //A server must include this option in a DHCPACK message sent
1492  //in a response to a DHCPDISCOVER message when completing the
1493  //DHCPDISCOVER-DHCPACK message exchange
1494  if(option == NULL || option->length != 0)
1495  return;
1496  }
1497  else if(context->state == DHCP_STATE_REQUESTING ||
1498  context->state == DHCP_STATE_RENEWING)
1499  {
1500  //Check the server identifier
1501  if(!ipv4CompAddr(serverIdOption->value, &context->serverIpAddr))
1502  return;
1503  }
1504  else if(context->state == DHCP_STATE_REBOOTING ||
1505  context->state == DHCP_STATE_REBINDING)
1506  {
1507  //Do not check the server identifier
1508  }
1509  else
1510  {
1511  //Silently discard the DHCPACK message
1512  return;
1513  }
1514 
1515  //Retrieve IP Address Lease Time option
1517 
1518  //Failed to retrieve specified option?
1519  if(option == NULL || option->length != 4)
1520  return;
1521 
1522  //Record the lease time
1523  context->leaseTime = LOAD32BE(option->value);
1524 
1525  //Retrieve Renewal Time Value option
1527 
1528  //Specified option found?
1529  if(option != NULL && option->length == 4)
1530  {
1531  //This option specifies the time interval from address assignment
1532  //until the client transitions to the RENEWING state
1533  context->t1 = LOAD32BE(option->value);
1534  }
1535  else if(context->leaseTime != DHCP_INFINITE_TIME)
1536  {
1537  //By default, T1 is set to 50% of the lease time
1538  context->t1 = context->leaseTime / 2;
1539  }
1540  else
1541  {
1542  //Infinite lease
1543  context->t1 = DHCP_INFINITE_TIME;
1544  }
1545 
1546  //Retrieve Rebinding Time value option
1548 
1549  //Specified option found?
1550  if(option != NULL && option->length == 4)
1551  {
1552  //This option specifies the time interval from address assignment
1553  //until the client transitions to the REBINDING state
1554  context->t2 = LOAD32BE(option->value);
1555  }
1556  else if(context->leaseTime != DHCP_INFINITE_TIME)
1557  {
1558  //By default, T2 is set to 87.5% of the lease time
1559  context->t2 = context->leaseTime * 7 / 8;
1560  }
1561  else
1562  {
1563  //Infinite lease
1564  context->t2 = DHCP_INFINITE_TIME;
1565  }
1566 
1567  //Retrieve Subnet Mask option
1569 
1570  //The specified option has been found?
1571  if(option != NULL && option->length == sizeof(Ipv4Addr))
1572  {
1573  //Record subnet mask
1574  ipv4CopyAddr(&interface->ipv4Context.subnetMask, option->value);
1575  }
1576 
1577  //Retrieve Router option
1579 
1580  //The specified option has been found?
1581  if(option != NULL && !(option->length % sizeof(Ipv4Addr)))
1582  {
1583  //Save default gateway
1584  if(option->length >= sizeof(Ipv4Addr))
1585  ipv4CopyAddr(&interface->ipv4Context.defaultGateway, option->value);
1586  }
1587 
1588  //Use the DNS servers provided by the DHCP server?
1589  if(!context->settings.manualDnsConfig)
1590  {
1591  //Retrieve DNS Server option
1593 
1594  //The specified option has been found?
1595  if(option != NULL && !(option->length % sizeof(Ipv4Addr)))
1596  {
1597  //Get the number of addresses provided in the response
1598  n = option->length / sizeof(Ipv4Addr);
1599 
1600  //Loop through the list of addresses
1601  for(i = 0; i < n && i < IPV4_DNS_SERVER_LIST_SIZE; i++)
1602  {
1603  //Record DNS server address
1604  ipv4CopyAddr(&interface->ipv4Context.dnsServerList[i],
1605  option->value + i * sizeof(Ipv4Addr));
1606  }
1607  }
1608  }
1609 
1610  //Retrieve MTU option
1612 
1613  //The specified option has been found?
1614  if(option != NULL && option->length == 2)
1615  {
1616  //This option specifies the MTU to use on this interface
1617  n = LOAD16BE(option->value);
1618 
1619  //Make sure that the option's value is acceptable
1620  if(n >= IPV4_MINIMUM_MTU && n <= physicalInterface->nicDriver->mtu)
1621  {
1622  //Set the MTU to be used on the interface
1623  interface->ipv4Context.linkMtu = n;
1624  }
1625  }
1626 
1627  //Record the IP address of the DHCP server
1628  ipv4CopyAddr(&context->serverIpAddr, serverIdOption->value);
1629  //Record the IP address assigned to the client
1630  context->requestedIpAddr = message->yiaddr;
1631 
1632  //Save the time a which the lease was obtained
1633  context->leaseStartTime = osGetSystemTime();
1634 
1635  //Check current state
1636  if(context->state == DHCP_STATE_REQUESTING ||
1637  context->state == DHCP_STATE_REBOOTING)
1638  {
1639  //Use the IP address as a tentative address
1640  interface->ipv4Context.addr = message->yiaddr;
1641  interface->ipv4Context.addrState = IPV4_ADDR_STATE_TENTATIVE;
1642 
1643  //Clear conflict flag
1644  interface->ipv4Context.addrConflict = FALSE;
1645 
1646  //The client should probe the newly received address
1648  }
1649  else
1650  {
1651  //Assign the IP address to the client
1652  interface->ipv4Context.addr = message->yiaddr;
1653  interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
1654 
1655 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
1656  //Restart mDNS probing process
1657  mdnsResponderStartProbing(interface->mdnsResponderContext);
1658 #endif
1659  //The client transitions to the BOUND state
1661  }
1662 }
1663 
1664 
1665 /**
1666  * @brief Parse DHCPNAK message
1667  * @param[in] context Pointer to the DHCP client context
1668  * @param[in] message Pointer to the incoming DHCP message
1669  * @param[in] length Length of the incoming message to parse
1670  * @return Error code
1671  **/
1672 
1674  const DhcpMessage *message, size_t length)
1675 {
1676  DhcpOption *serverIdOption;
1677  NetInterface *interface;
1678  NetInterface *logicalInterface;
1679 
1680  //Point to the underlying network interface
1681  interface = context->settings.interface;
1682 
1683  //Point to the logical interface
1684  logicalInterface = nicGetLogicalInterface(interface);
1685 
1686  //Discard any received packet that does not match the transaction ID
1687  if(ntohl(message->xid) != context->transactionId)
1688  return;
1689  //Check MAC address
1690  if(!macCompAddr(&message->chaddr, &logicalInterface->macAddr))
1691  return;
1692 
1693  //A DHCP server always returns its own address in the Server Identifier option
1695 
1696  //Failed to retrieve the Server Identifier option?
1697  if(serverIdOption == NULL || serverIdOption->length != 4)
1698  return;
1699 
1700  //Check current state
1701  if(context->state == DHCP_STATE_REQUESTING ||
1702  context->state == DHCP_STATE_RENEWING)
1703  {
1704  //Check the server identifier
1705  if(!ipv4CompAddr(serverIdOption->value, &context->serverIpAddr))
1706  return;
1707  }
1708  else if(context->state == DHCP_STATE_REBOOTING ||
1709  context->state == DHCP_STATE_REBINDING)
1710  {
1711  //Do not check the server identifier
1712  }
1713  else
1714  {
1715  //Silently discard the DHCPNAK message
1716  return;
1717  }
1718 
1719  //The host address is no longer appropriate for the link
1720  interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
1721  interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
1722 
1723  //Clear subnet mask
1724  interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
1725 
1726 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
1727  //Restart mDNS probing process
1728  mdnsResponderStartProbing(interface->mdnsResponderContext);
1729 #endif
1730 
1731  //Restart DHCP configuration
1733 }
1734 
1735 
1736 /**
1737  * @brief Manage DHCP configuration timeout
1738  * @param[in] context Pointer to the DHCP client context
1739  **/
1740 
1742 {
1743  systime_t time;
1744  NetInterface *interface;
1745 
1746  //Point to the underlying network interface
1747  interface = context->settings.interface;
1748 
1749  //Get current time
1750  time = osGetSystemTime();
1751 
1752  //Any registered callback?
1753  if(context->settings.timeoutEvent != NULL)
1754  {
1755  //DHCP configuration timeout?
1756  if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
1757  {
1758  //Ensure the callback function is only called once
1759  if(!context->timeoutEventDone)
1760  {
1761  //Release exclusive access
1763  //Invoke user callback function
1764  context->settings.timeoutEvent(context, interface);
1765  //Get exclusive access
1767 
1768  //Set flag
1769  context->timeoutEventDone = TRUE;
1770  }
1771  }
1772  }
1773 }
1774 
1775 
1776 /**
1777  * @brief Compute the appropriate secs field
1778  *
1779  * Compute the number of seconds elapsed since the client began
1780  * address acquisition or renewal process
1781  *
1782  * @param[in] context Pointer to the DHCP client context
1783  * @return The elapsed time expressed in seconds
1784  **/
1785 
1787 {
1788  systime_t time;
1789 
1790  //Compute the time elapsed since the DHCP configuration process started
1791  time = (osGetSystemTime() - context->configStartTime) / 1000;
1792 
1793  //The value 0xFFFF is used to represent any elapsed time values
1794  //greater than the largest time value that can be represented
1795  time = MIN(time, 0xFFFF);
1796 
1797  //Convert the 16-bit value to network byte order
1798  return htons(time);
1799 }
1800 
1801 
1802 /**
1803  * @brief Update DHCP FSM state
1804  * @param[in] context Pointer to the DHCP client context
1805  * @param[in] newState New DHCP state to switch to
1806  * @param[in] delay Initial delay
1807  **/
1808 
1810  DhcpState newState, systime_t delay)
1811 {
1812  systime_t time;
1813 
1814  //Get current time
1815  time = osGetSystemTime();
1816 
1817 #if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
1818  //Sanity check
1819  if(newState <= DHCP_STATE_REBINDING)
1820  {
1821  //DHCP FSM states
1822  static const char_t *stateLabel[] =
1823  {
1824  "INIT",
1825  "SELECTING",
1826  "REQUESTING",
1827  "INIT-REBOOT",
1828  "REBOOTING",
1829  "PROBING",
1830  "BOUND",
1831  "RENEWING",
1832  "REBINDING"
1833  };
1834 
1835  //Debug message
1836  TRACE_INFO("%s: DHCP client %s state\r\n",
1837  formatSystemTime(time, NULL), stateLabel[newState]);
1838  }
1839 #endif
1840 
1841  //Set time stamp
1842  context->timestamp = time;
1843  //Set initial delay
1844  context->timeout = delay;
1845  //Reset retransmission counter
1846  context->retransmitCount = 0;
1847  //Switch to the new state
1848  context->state = newState;
1849 
1850  //Any registered callback?
1851  if(context->settings.stateChangeEvent != NULL)
1852  {
1853  NetInterface *interface;
1854 
1855  //Point to the underlying network interface
1856  interface = context->settings.interface;
1857 
1858  //Release exclusive access
1860  //Invoke user callback function
1861  context->settings.stateChangeEvent(context, interface, newState);
1862  //Get exclusive access
1864  }
1865 }
1866 
1867 
1868 /**
1869  * @brief Dump DHCP configuration for debugging purpose
1870  * @param[in] context Pointer to the DHCP client context
1871  **/
1872 
1874 {
1875 #if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
1876  uint_t i;
1877  NetInterface *interface;
1878  Ipv4Context *ipv4Context;
1879 
1880  //Point to the underlying network interface
1881  interface = context->settings.interface;
1882  //Point to the IPv4 context
1883  ipv4Context = &interface->ipv4Context;
1884 
1885  //Debug message
1886  TRACE_INFO("\r\n");
1887  TRACE_INFO("DHCP configuration:\r\n");
1888 
1889  //Lease start time
1890  TRACE_INFO(" Lease Start Time = %s\r\n",
1891  formatSystemTime(context->leaseStartTime, NULL));
1892 
1893  //Lease time
1894  TRACE_INFO(" Lease Time = %" PRIu32 "s\r\n", context->leaseTime);
1895  //Renewal time
1896  TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->t1);
1897  //Rebinding time
1898  TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->t2);
1899 
1900  //Host address
1901  TRACE_INFO(" IPv4 Address = %s\r\n",
1902  ipv4AddrToString(ipv4Context->addr, NULL));
1903 
1904  //Subnet mask
1905  TRACE_INFO(" Subnet Mask = %s\r\n",
1906  ipv4AddrToString(ipv4Context->subnetMask, NULL));
1907 
1908  //Default gateway
1909  TRACE_INFO(" Default Gateway = %s\r\n",
1910  ipv4AddrToString(ipv4Context->defaultGateway, NULL));
1911 
1912  //DNS servers
1913  for(i = 0; i < IPV4_DNS_SERVER_LIST_SIZE; i++)
1914  {
1915  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
1916  ipv4AddrToString(ipv4Context->dnsServerList[i], NULL));
1917  }
1918 
1919  //Maximum transmit unit
1920  TRACE_INFO(" MTU = %" PRIuSIZE "\r\n", interface->ipv4Context.linkMtu);
1921  TRACE_INFO("\r\n");
1922 #endif
1923 }
1924 
1925 #endif
An address assigned to an interface whose use is unrestricted.
Definition: ipv4.h:184
DhcpLinkChangeCallback linkChangeEvent
Link state change event.
Definition: dhcp_client.h:220
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:232
#define DHCP_CLIENT_INIT_DELAY
Definition: dhcp_client.h:82
NetInterface * interface
Definition: tcp.h:373
uint32_t systime_t
Definition: compiler_port.h:44
error_t dhcpClientStart(DhcpClientContext *context)
Start DHCP client.
Definition: dhcp_client.c:177
#define DhcpClientContext
Definition: dhcp_client.h:152
void dhcpClientStateRebooting(DhcpClientContext *context)
REBOOTING state.
Definition: dhcp_client.c:637
#define DHCP_CLIENT_DISCOVER_MAX_RT
Definition: dhcp_client.h:96
mDNS responder (Multicast DNS)
#define timeCompare(t1, t2)
Definition: os_port.h:40
char char_t
Definition: compiler_port.h:41
char_t * ipv4AddrToString(Ipv4Addr ipAddr, char_t *str)
Convert a binary IPv4 address to dot-decimal notation.
Definition: ipv4.c:1785
Definitions common to DHCP client and server.
#define DHCP_MIN_MSG_SIZE
Definition: dhcp_common.h:42
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t time
TCP/IP stack core.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:280
Debugging facilities.
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:295
#define DHCP_HARDWARE_TYPE_ETH
Definition: dhcp_common.h:47
#define DHCP_CLIENT_MAX_HOSTNAME_LEN
Definition: dhcp_client.h:61
void dhcpClientGetDefaultSettings(DhcpClientSettings *settings)
Initialize settings with default values.
Definition: dhcp_client.c:73
uint8_t message[]
Definition: chap.h:150
Invalid parameter.
Definition: error.h:45
void dhcpClientStateInit(DhcpClientContext *context)
INIT state.
Definition: dhcp_client.c:416
#define IPV4_DNS_SERVER_LIST_SIZE
Definition: ipv4.h:61
#define IPV4_MINIMUM_MTU
Definition: ipv4.h:76
void dhcpClientTick(DhcpClientContext *context)
DHCP client timer handler.
Definition: dhcp_client.c:277
bool_t rapidCommit
Quick configuration using rapid commit.
Definition: dhcp_client.h:216
__start_packed struct @128 DhcpOption
DHCP option.
IP network address.
Definition: ip.h:57
#define DHCP_CLIENT_REQUEST_MAX_RT
Definition: dhcp_client.h:117
#define DHCP_CLIENT_RAND_FACTOR
Definition: dhcp_client.h:145
#define DHCP_CLIENT_PORT
Definition: dhcp_common.h:39
char_t hostname[DHCP_CLIENT_MAX_HOSTNAME_LEN+1]
Host name.
Definition: dhcp_client.h:210
#define LOAD32BE(p)
Definition: cpu_endian.h:192
#define htons(value)
Definition: cpu_endian.h:390
#define HTONL(value)
Definition: cpu_endian.h:389
#define DHCP_CLIENT_DISCOVER_INIT_RT
Definition: dhcp_client.h:89
#define ipv4CompAddr(ipAddr1, ipAddr2)
Definition: ipv4.h:137
#define DHCP_CLIENT_REQUEST_MAX_RC
Definition: dhcp_client.h:103
void dhcpClientStateRequesting(DhcpClientContext *context)
REQUESTING state.
Definition: dhcp_client.c:518
#define HTONS(value)
Definition: cpu_endian.h:388
DhcpOption * dhcpGetOption(const DhcpMessage *message, size_t length, uint8_t optionCode)
Find the specified option in a DHCP message.
Definition: dhcp_common.c:106
#define TRUE
Definition: os_port.h:48
DhcpState
DHCP FSM states.
Definition: dhcp_client.h:164
size_t clientIdLength
Length of the client identifier.
Definition: dhcp_client.h:214
void dhcpClientParseOffer(DhcpClientContext *context, const DhcpMessage *message, size_t length)
Parse DHCPOFFER message.
Definition: dhcp_client.c:1392
#define IPV4_DEFAULT_TTL
Definition: ipv4.h:54
void dhcpClientChangeState(DhcpClientContext *context, DhcpState newState, systime_t delay)
Update DHCP FSM state.
Definition: dhcp_client.c:1809
#define DHCP_CLIENT_REQUEST_INIT_RT
Definition: dhcp_client.h:110
DHCP client (Dynamic Host Configuration Protocol)
#define ntohl(value)
Definition: cpu_endian.h:397
NetInterface * interface
Network interface to configure.
Definition: dhcp_client.h:208
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:75
error_t mdnsResponderStartProbing(MdnsResponderContext *context)
Restart probing process.
uint32_t netGetRand(void)
Get a random value.
Definition: net.c:1523
#define htonl(value)
Definition: cpu_endian.h:391
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:411
#define DHCP_SERVER_PORT
Definition: dhcp_common.h:38
An address that is not assigned to any interface.
Definition: ipv4.h:182
Date and time management.
error_t dhcpClientStop(DhcpClientContext *context)
Stop DHCP client.
Definition: dhcp_client.c:220
void dhcpClientStateBound(DhcpClientContext *context)
BOUND state.
Definition: dhcp_client.c:782
DhcpStateChangeCallback stateChangeEvent
FSM state change event.
Definition: dhcp_client.h:221
__start_packed struct @126 UdpHeader
UDP header.
error_t dhcpDumpMessage(const DhcpMessage *message, size_t length)
Dump DHCP message for debugging purpose.
Definition: dhcp_debug.c:156
error_t dhcpClientSendDecline(DhcpClientContext *context)
Send DHCPDECLINE message.
Definition: dhcp_client.c:1222
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:723
systime_t dhcpClientTickCounter
Definition: dhcp_client.c:53
An address whose uniqueness on a link is being verified.
Definition: ipv4.h:183
#define DHCP_MAGIC_COOKIE
Definition: dhcp_common.h:49
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1495
#define LOAD16BE(p)
Definition: cpu_endian.h:168
#define DHCP_CLIENT_PROBE_NUM
Definition: dhcp_client.h:131
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:86
#define MIN(a, b)
Definition: os_port.h:60
error_t dhcpClientSendDiscover(DhcpClientContext *context)
Send DHCPDISCOVER message.
Definition: dhcp_client.c:965
void dhcpClientProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *param)
Process incoming DHCP message.
Definition: dhcp_client.c:1309
void dhcpClientStateSelecting(DhcpClientContext *context)
SELECTING state.
Definition: dhcp_client.c:456
void dhcpClientCheckTimeout(DhcpClientContext *context)
Manage DHCP configuration timeout.
Definition: dhcp_client.c:1741
systime_t timeout
DHCP configuration timeout.
Definition: dhcp_client.h:218
void dhcpClientParseAck(DhcpClientContext *context, const DhcpMessage *message, size_t length)
Parse DHCPACK message.
Definition: dhcp_client.c:1445
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:656
#define TRACE_INFO(...)
Definition: debug.h:86
#define DHCP_MAX_MSG_SIZE
Definition: dhcp_common.h:44
Success.
Definition: error.h:42
int32_t netGetRandRange(int32_t min, int32_t max)
Get a random value in the specified range.
Definition: net.c:1554
error_t
Error codes.
Definition: error.h:40
void dhcpAddOption(DhcpMessage *message, uint8_t optionCode, const void *optionValue, size_t optionLen)
Append an option to a DHCP message.
Definition: dhcp_common.c:49
NetInterface * nicGetPhysicalInterface(NetInterface *interface)
Retrieve physical interface.
Definition: nic.c:85
Ipv4Addr destIpAddr
Definition: ipcp.h:76
void dhcpClientStateRenewing(DhcpClientContext *context)
RENEWING state.
Definition: dhcp_client.c:823
unsigned int uint_t
Definition: compiler_port.h:43
uint16_t dhcpClientComputeElapsedTime(DhcpClientContext *context)
Compute the appropriate secs field.
Definition: dhcp_client.c:1786
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
__start_packed struct @112 MacAddr
MAC address.
void dhcpClientStateRebinding(DhcpClientContext *context)
REBINDING state.
Definition: dhcp_client.c:888
#define PRIuSIZE
Definition: compiler_port.h:72
error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, uint8_t ttl)
Send a UDP datagram (raw interface)
Definition: udp.c:448
#define NetInterface
Definition: net.h:34
#define MAX_DELAY
Definition: os_port.h:74
uint32_t t2
void dhcpClientParseNak(DhcpClientContext *context, const DhcpMessage *message, size_t length)
Parse DHCPNAK message.
Definition: dhcp_client.c:1673
void dhcpClientDumpConfig(DhcpClientContext *context)
Dump DHCP configuration for debugging purpose.
Definition: dhcp_client.c:1873
const uint8_t dhcpOptionList[]
Definition: dhcp_client.c:56
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:95
#define DHCP_CLIENT_PROBE_DELAY
Definition: dhcp_client.h:138
DHCP client settings.
Definition: dhcp_client.h:206
error_t dhcpClientSendRequest(DhcpClientContext *context)
Send DHCPREQUEST message.
Definition: dhcp_client.c:1076
IP pseudo header.
Definition: ip.h:76
__start_packed struct @127 DhcpMessage
DHCP message.
void dhcpClientLinkChangeEvent(DhcpClientContext *context)
Callback function for link change event.
Definition: dhcp_client.c:354
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:95
uint32_t t1
OsMutex netMutex
Definition: net.c:70
NetInterface * nicGetLogicalInterface(NetInterface *interface)
Retrieve logical interface.
Definition: nic.c:60
IPv4 context.
Definition: ipv4.h:317
void dhcpClientStateProbing(DhcpClientContext *context)
PROBING state.
Definition: dhcp_client.c:715
#define DHCP_CLIENT_REQUEST_MIN_DELAY
Definition: dhcp_client.h:124
Data logging functions for debugging purpose (DHCP)
DhcpState dhcpClientGetState(DhcpClientContext *context)
Retrieve current state.
Definition: dhcp_client.c:251
error_t dhcpClientInit(DhcpClientContext *context, const DhcpClientSettings *settings)
DHCP client initialization.
Definition: dhcp_client.c:110
bool_t manualDnsConfig
Force manual DNS configuration.
Definition: dhcp_client.h:217
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
void dhcpClientStateInitReboot(DhcpClientContext *context)
INIT-REBOOT state.
Definition: dhcp_client.c:597
#define DHCP_INFINITE_TIME
Definition: dhcp_common.h:51
#define FALSE
Definition: os_port.h:44
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
error_t arpSendProbe(NetInterface *interface, Ipv4Addr targetIpAddr)
Send ARP probe.
Definition: arp.c:757
DhcpTimeoutCallback timeoutEvent
DHCP configuration timeout event.
Definition: dhcp_client.h:219
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:133
#define IPV4_BROADCAST_ADDR
Definition: ipv4.h:97
#define TRACE_DEBUG(...)
Definition: debug.h:98