dhcpv6_client.c
Go to the documentation of this file.
1 /**
2  * @file dhcpv6_client.c
3  * @brief DHCPv6 client (Dynamic Host Configuration Protocol for IPv6)
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 for IPv6 enables DHCP servers to
28  * pass configuration parameters such as IPv6 network addresses to IPv6
29  * nodes. This protocol is a stateful counterpart to IPv6 Stateless Address
30  * Autoconfiguration (RFC 2462), and can be used separately or concurrently
31  * with the latter to obtain configuration parameters. Refer to RFC 3315
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 DHCPV6_TRACE_LEVEL
39 
40 //Dependencies
41 #include <stdlib.h>
42 #include "core/net.h"
43 #include "ipv6/ipv6.h"
44 #include "ipv6/ipv6_misc.h"
45 #include "ipv6/ndp.h"
46 #include "dhcpv6/dhcpv6_client.h"
47 #include "dhcpv6/dhcpv6_common.h"
48 #include "dhcpv6/dhcpv6_debug.h"
49 #include "dns/dns_common.h"
50 #include "date_time.h"
51 #include "debug.h"
52 
53 //Check TCP/IP stack configuration
54 #if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
55 
56 //Tick counter to handle periodic operations
58 
59 //Requested DHCPv6 options
60 static const uint16_t dhcpv6OptionList[] =
61 {
65 };
66 
67 
68 /**
69  * @brief Initialize settings with default values
70  * @param[out] settings Structure that contains DHCPv6 client settings
71  **/
72 
74 {
75  //Use default interface
76  settings->interface = netGetDefaultInterface();
77 
78  //Support for quick configuration using rapid commit
79  settings->rapidCommit = FALSE;
80  //Use the DNS servers provided by the DHCPv6 server
81  settings->manualDnsConfig = FALSE;
82  //DHCPv6 configuration timeout
83  settings->timeout = 0;
84  //DHCPv6 configuration timeout event
85  settings->timeoutEvent = NULL;
86  //Link state change event
87  settings->linkChangeEvent = NULL;
88  //FSM state change event
89  settings->stateChangeEvent = NULL;
90 }
91 
92 
93 /**
94  * @brief DHCPv6 client initialization
95  * @param[in] context Pointer to the DHCPv6 client context
96  * @param[in] settings DHCPv6 client specific settings
97  * @return Error code
98  **/
99 
101 {
102  error_t error;
103  NetInterface *interface;
104 
105  //Debug message
106  TRACE_INFO("Initializing DHCPv6 client...\r\n");
107 
108  //Ensure the parameters are valid
109  if(context == NULL || settings == NULL)
111 
112  //A valid pointer to the interface being configured is required
113  if(settings->interface == NULL)
115 
116  //Point to the underlying network interface
117  interface = settings->interface;
118 
119  //Clear the DHCPv6 client context
120  memset(context, 0, sizeof(Dhcpv6ClientContext));
121  //Save user settings
122  context->settings = *settings;
123 
124  //Generate client's DUID
125  error = dhcpv6ClientGenerateDuid(context);
126  //any error to report?
127  if(error)
128  return error;
129 
130  //Generate client's fully qualified domain name
131  error = dhcpv6ClientGenerateFqdn(context);
132  //any error to report?
133  if(error)
134  return error;
135 
136  //Callback function to be called when a DHCPv6 message is received
137  error = udpAttachRxCallback(interface, DHCPV6_CLIENT_PORT,
138  dhcpv6ClientProcessMessage, context);
139  //Failed to register callback function?
140  if(error)
141  return error;
142 
143  //DHCPv6 client is currently suspended
144  context->running = FALSE;
145  //Initialize state machine
146  context->state = DHCPV6_STATE_INIT;
147 
148  //Attach the DHCPv6 client context to the network interface
149  interface->dhcpv6ClientContext = context;
150 
151  //Successful initialization
152  return NO_ERROR;
153 }
154 
155 
156 /**
157  * @brief Start DHCPv6 client
158  * @param[in] context Pointer to the DHCPv6 client context
159  * @return Error code
160  **/
161 
163 {
164  NetInterface *interface;
165 
166  //Check parameter
167  if(context == NULL)
169 
170  //Debug message
171  TRACE_INFO("Starting DHCPv6 client...\r\n");
172 
173  //Get exclusive access
175 
176  //Point to the underlying network interface
177  interface = context->settings.interface;
178 
179  //Flush the list of IPv6 addresses from the client's IA
180  dhcpv6ClientFlushAddrList(context);
181 
182  //Automatic DNS server configuration?
183  if(!context->settings.manualDnsConfig)
184  {
185  //Clear the list of DNS servers
186  ipv6FlushDnsServerList(interface);
187  }
188 
189  //Check if the link is up?
190  if(interface->linkState)
191  {
192  //A link-local address is formed by combining the well-known
193  //link-local prefix fe80::/10 with the interface identifier
195  }
196 
197  //Start DHCPv6 client
198  context->running = TRUE;
199  //Initialize state machine
200  context->state = DHCPV6_STATE_INIT;
201 
202  //Release exclusive access
204 
205  //Successful processing
206  return NO_ERROR;
207 }
208 
209 
210 /**
211  * @brief Stop DHCPv6 client
212  * @param[in] context Pointer to the DHCPv6 client context
213  * @return Error code
214  **/
215 
217 {
218  //Check parameter
219  if(context == NULL)
221 
222  //Debug message
223  TRACE_INFO("Stopping DHCPv6 client...\r\n");
224 
225  //Get exclusive access
227 
228  //Stop DHCPv6 client
229  context->running = FALSE;
230  //Reinitialize state machine
231  context->state = DHCPV6_STATE_INIT;
232 
233  //Release exclusive access
235 
236  //Successful processing
237  return NO_ERROR;
238 }
239 
240 
241 /**
242  * @brief Release DHCPv6 lease
243  * @param[in] context Pointer to the DHCPv6 client context
244  * @return Error code
245  **/
246 
248 {
249  uint_t i;
250  NetInterface *interface;
251  Dhcpv6ClientAddrEntry *entry;
252 
253  //Check parameter
254  if(context == NULL)
256 
257  //Debug message
258  TRACE_INFO("Releasing DHCPv6 lease...\r\n");
259 
260  //Get exclusive access
262 
263  //Point to the underlying network interface
264  interface = context->settings.interface;
265 
266  //Check whether the DHCPv6 client is running
267  if(context->running)
268  {
269  //BOUND state?
270  if(context->state == DHCPV6_STATE_BOUND)
271  {
272  //Loop through the IPv6 addresses recorded by the DHCPv6 client
273  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
274  {
275  //Point to the current entry
276  entry = &context->ia.addrList[i];
277 
278  //Valid IPv6 address?
279  if(entry->validLifetime > 0)
280  {
281  //The client must stop using the addresses being released as soon
282  //as the client begins the Release message exchange process
283  ipv6RemoveAddr(interface, &entry->addr);
284  }
285  }
286 
287  //Switch to the RELEASE state
289  }
290  else
291  {
292  //Stop DHCPv6 client
293  context->running = FALSE;
294  //Reinitialize state machine
295  context->state = DHCPV6_STATE_INIT;
296  }
297  }
298 
299  //Release exclusive access
301 
302  //Successful processing
303  return NO_ERROR;
304 }
305 
306 
307 /**
308  * @brief Retrieve current state
309  * @param[in] context Pointer to the DHCPv6 client context
310  * @return Current DHCPv6 client state
311  **/
312 
314 {
315  Dhcpv6State state;
316 
317  //Get exclusive access
319  //Get current state
320  state = context->state;
321  //Release exclusive access
323 
324  //Return current state
325  return state;
326 }
327 
328 
329 /**
330  * @brief DHCPv6 client timer handler
331  *
332  * This routine must be periodically called by the TCP/IP stack to
333  * manage DHCPv6 client operation
334  *
335  * @param[in] context Pointer to the DHCPv6 client context
336  **/
337 
338 
340 {
341  //Make sure the DHCPv6 client has been properly instantiated
342  if(context == NULL)
343  return;
344 
345  //DHCPv6 client finite state machine
346  switch(context->state)
347  {
348  //Process INIT state
349  case DHCPV6_STATE_INIT:
350  //This is the initialization state, where a client begins the process of
351  //acquiring a lease. It also returns here when a lease ends, or when a
352  //lease negotiation fails
353  dhcpv6ClientStateInit(context);
354  break;
355  //Process SOLICIT state
357  //The client sends a Solicit message to locate servers
358  dhcpv6ClientStateSolicit(context);
359  break;
360  //Process REQUEST state
362  //The client sends a Request message to request configuration
363  //parameters, including IP addresses, from a specific server
364  dhcpv6ClientStateRequest(context);
365  break;
366  //Process INIT-CONFIRM state
368  //When a client that already has a valid lease starts up after a
369  //power-down or reboot, it starts here instead of the INIT state
371  break;
372  //Process CONFIRM state
374  //The client sends a Confirm message to any available server
375  //to determine whether the addresses it was assigned are still
376  //appropriate to the link to which the client is connected
377  dhcpv6ClientStateConfirm(context);
378  break;
379  //Process DAD state
380  case DHCPV6_STATE_DAD:
381  //The client should perform duplicate address detection on each
382  //of the addresses in any IAs it receives in the Reply message
383  //before using that address for traffic
384  dhcpv6ClientStateDad(context);
385  break;
386  //Process BOUND state
387  case DHCPV6_STATE_BOUND:
388  //The client has a valid lease and is in its normal operating state
389  dhcpv6ClientStateBound(context);
390  break;
391  //Process RENEW state
392  case DHCPV6_STATE_RENEW:
393  //The client sends a Renew message to the server that originally
394  //provided the client's addresses and configuration parameters to
395  //extend the lifetimes on the addresses assigned to the client
396  //and to update other configuration parameters
397  dhcpv6ClientStateRenew(context);
398  break;
399  //Process REBIND state
400  case DHCPV6_STATE_REBIND:
401  //The client sends a Rebind message to any available server to extend
402  //the lifetimes on the addresses assigned to the client and to update
403  //other configuration parameters. This message is sent after a client
404  //receives no response to a Renew message
405  dhcpv6ClientStateRebind(context);
406  break;
407  //Process RELEASE state
409  //To release one or more addresses, a client sends a Release message
410  //to the server
411  dhcpv6ClientStateRelease(context);
412  break;
413  //Process DECLINE state
415  //If a client detects that one or more addresses assigned to it by a
416  //server are already in use by another node, the client sends a Decline
417  //message to the server to inform it that the address is suspect
418  dhcpv6ClientStateDecline(context);
419  break;
420  //Invalid state...
421  default:
422  //Switch to the default state
423  context->state = DHCPV6_STATE_INIT;
424  break;
425  }
426 }
427 
428 
429 /**
430  * @brief Callback function for link change event
431  * @param[in] context Pointer to the DHCPv6 client context
432  **/
433 
435 {
436  NetInterface *interface;
437 
438  //Make sure the DHCPv6 client has been properly instantiated
439  if(context == NULL)
440  return;
441 
442  //Point to the underlying network interface
443  interface = context->settings.interface;
444 
445  //Check whether the DHCPv6 client is running
446  if(context->running)
447  {
448  //Automatic DNS server configuration?
449  if(!context->settings.manualDnsConfig)
450  {
451  //Clear the list of DNS servers
452  ipv6FlushDnsServerList(interface);
453  }
454 
455  //Link-up event?
456  if(interface->linkState)
457  {
458  //A link-local address is formed by combining the well-known
459  //link-local prefix fe80::/10 with the interface identifier
461  }
462  }
463 
464  //Check the state of the DHCPv6 client
465  switch(context->state)
466  {
469  case DHCPV6_STATE_DAD:
470  case DHCPV6_STATE_BOUND:
471  case DHCPV6_STATE_RENEW:
472  case DHCPV6_STATE_REBIND:
473  //The client already has a valid lease
474  context->state = DHCPV6_STATE_INIT_CONFIRM;
475  break;
477  //Stop DHCPv6 client
478  context->running = FALSE;
479  //Reinitialize state machine
480  context->state = DHCPV6_STATE_INIT;
481  break;
482  default:
483  //Switch to the INIT state
484  context->state = DHCPV6_STATE_INIT;
485  break;
486  }
487 
488  //Any registered callback?
489  if(context->settings.linkChangeEvent != NULL)
490  {
491  //Release exclusive access
493  //Invoke user callback function
494  context->settings.linkChangeEvent(context, interface, interface->linkState);
495  //Get exclusive access
497  }
498 }
499 
500 
501 /**
502  * @brief INIT state
503  *
504  * This is the initialization state, where a client begins the process of
505  * acquiring a lease. It also returns here when a lease ends, or when a
506  * lease negotiation fails
507  *
508  * @param[in] context Pointer to the DHCPv6 client context
509  **/
510 
512 {
513  systime_t delay;
514  NetInterface *interface;
515 
516  //Point to the underlying network interface
517  interface = context->settings.interface;
518 
519  //Check whether the DHCPv6 client is running
520  if(context->running)
521  {
522  //Wait for the link to be up before starting DHCPv6 configuration
523  if(interface->linkState)
524  {
525  //Make sure that a valid link-local address has been assigned to the interface
527  {
528  //Flush the list of IPv6 addresses from the client's IA
529  dhcpv6ClientFlushAddrList(context);
530 
531  //The first Solicit message from the client on the interface must be
532  //delayed by a random amount of time between 0 and SOL_MAX_DELAY
534 
535  //Record the time at which the client started
536  //the address acquisition process
537  context->configStartTime = osGetSystemTime();
538  //Clear flag
539  context->timeoutEventDone = FALSE;
540 
541  //Switch to the SOLICIT state
543  }
544  }
545  }
546 }
547 
548 
549 /**
550  * @brief SOLICIT state
551  *
552  * A client uses the Solicit message to discover DHCPv6 servers
553  *
554  * @param[in] context Pointer to the DHCPv6 client context
555  **/
556 
558 {
559  systime_t time;
560 
561  //Get current time
562  time = osGetSystemTime();
563 
564  //Check current time
565  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
566  {
567  //Check retransmission counter
568  if(context->retransmitCount == 0)
569  {
570  //Reset server preference value
571  context->serverPreference = -1;
572  //Generate a 24-bit transaction ID
573  context->transactionId = netGetRand() & 0x00FFFFFF;
574 
575  //Send a Solicit message
577 
578  //Save the time at which the message was sent
579  context->exchangeStartTime = time;
580  context->timestamp = time;
581 
582  //If the client is waiting for an Advertise message, the first RT
583  //must be selected to be strictly greater than IRT
584  context->timeout = DHCPV6_CLIENT_SOL_TIMEOUT +
586 
587  //Increment retransmission counter
588  context->retransmitCount++;
589  }
590  else
591  {
592  //Check whether a valid Advertise message has been received
593  if(context->serverPreference >= 0)
594  {
595  //Continue configuration procedure
597  }
598  else
599  {
600  //Send a Solicit message
602 
603  //Save the time at which the message was sent
604  context->timestamp = time;
605 
606  //The RT is doubled for each subsequent retransmission
607  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
608 
609  //MRT specifies an upper bound on the value of RT
610  if(context->timeout > DHCPV6_CLIENT_SOL_MAX_RT)
611  {
612  //Compute retransmission timeout
613  context->timeout = DHCPV6_CLIENT_SOL_MAX_RT +
615  }
616 
617  //Increment retransmission counter
618  context->retransmitCount++;
619  }
620  }
621  }
622 
623  //Manage DHCPv6 configuration timeout
624  dhcpv6ClientCheckTimeout(context);
625 }
626 
627 
628 /**
629  * @brief REQUEST state
630  *
631  * The client uses a Request message to populate IAs with addresses and obtain
632  * other configuration information. The client includes one or more more IA
633  * options in the Request message. The server then returns addresses and other
634  * information about the IAs to the client in IA options in a Reply message
635  *
636  * @param[in] context Pointer to the DHCPv6 client context
637  **/
638 
640 {
641  systime_t time;
642 
643  //Get current time
644  time = osGetSystemTime();
645 
646  //Check current time
647  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
648  {
649  //Check retransmission counter
650  if(context->retransmitCount == 0)
651  {
652  //Generate a 24-bit transaction ID
653  context->transactionId = netGetRand() & 0x00FFFFFF;
654 
655  //Send a Request message
657 
658  //Save the time at which the message was sent
659  context->exchangeStartTime = time;
660  context->timestamp = time;
661 
662  //Initial retransmission timeout
663  context->timeout = DHCPV6_CLIENT_REQ_TIMEOUT +
665 
666  //Increment retransmission counter
667  context->retransmitCount++;
668  }
669  else if(context->retransmitCount < DHCPV6_CLIENT_REQ_MAX_RC)
670  {
671  //Send a Request message
673 
674  //Save the time at which the message was sent
675  context->timestamp = time;
676 
677  //The RT is doubled for each subsequent retransmission
678  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
679 
680  //MRT specifies an upper bound on the value of RT
681  if(context->timeout > DHCPV6_CLIENT_REQ_MAX_RT)
682  {
683  //Compute retransmission timeout
684  context->timeout = DHCPV6_CLIENT_REQ_MAX_RT +
686  }
687 
688  //Increment retransmission counter
689  context->retransmitCount++;
690  }
691  else
692  {
693  //If the client does not receive a response within a reasonable
694  //period of time, then it restarts the initialization procedure
696  }
697  }
698 
699  //Manage DHCPv6 configuration timeout
700  dhcpv6ClientCheckTimeout(context);
701 }
702 
703 
704 /**
705  * @brief INIT-CONFIRM state
706  *
707  * When a client that already has a valid lease starts up after a
708  * power-down or reboot, it starts here instead of the INIT state
709  *
710  * @param[in] context Pointer to the DHCPv6 client context
711  **/
712 
714 {
715  systime_t delay;
716  NetInterface *interface;
717 
718  //Point to the underlying network interface
719  interface = context->settings.interface;
720 
721  //Check whether the DHCPv6 client is running
722  if(context->running)
723  {
724  //Wait for the link to be up before starting DHCPv6 configuration
725  if(interface->linkState)
726  {
727  //Make sure that a valid link-local address has been assigned to the interface
729  {
730  //The first Confirm message from the client on the interface must be
731  //delayed by a random amount of time between 0 and CNF_MAX_DELAY
733 
734  //Record the time at which the client started
735  //the address acquisition process
736  context->configStartTime = osGetSystemTime();
737  //Clear flag
738  context->timeoutEventDone = FALSE;
739 
740  //Switch to the CONFIRM state
742  }
743  }
744  }
745 }
746 
747 
748 /**
749  * @brief CONFIRM state
750  *
751  * Whenever a client may have moved to a new link, the prefixes from
752  * the addresses assigned to the interfaces on that link may no longer
753  * be appropriate for the link to which the client is attached. In such
754  * the client must initiate a Confirm/Reply message exchange
755  *
756  * @param[in] context Pointer to the DHCPv6 client context
757  **/
758 
760 {
761  systime_t time;
762 
763  //Get current time
764  time = osGetSystemTime();
765 
766  //Check current time
767  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
768  {
769  //Check retransmission counter
770  if(context->retransmitCount == 0)
771  {
772  //Generate a 24-bit transaction ID
773  context->transactionId = netGetRand() & 0x00FFFFFF;
774 
775  //Send a Confirm message
777 
778  //Save the time at which the client sent the first message
779  context->exchangeStartTime = time;
780  context->timestamp = time;
781 
782  //Initial retransmission timeout
783  context->timeout = DHCPV6_CLIENT_CNF_TIMEOUT +
785 
786  //Increment retransmission counter
787  context->retransmitCount++;
788  }
789  else
790  {
791  //Send a Confirm message
793 
794  //Save the time at which the message was sent
795  context->timestamp = time;
796 
797  //The RT is doubled for each subsequent retransmission
798  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
799 
800  //MRT specifies an upper bound on the value of RT
801  if(context->timeout > DHCPV6_CLIENT_CNF_MAX_RT)
802  {
803  //Compute retransmission timeout
804  context->timeout = DHCPV6_CLIENT_CNF_MAX_RT +
806  }
807 
808  //Increment retransmission counter
809  context->retransmitCount++;
810  }
811  }
812  else
813  {
814  //Check retransmission counter
815  if(context->retransmitCount > 0)
816  {
817  //The message exchange fails once MRD seconds have elapsed since
818  //the client first transmitted the message
819  if(timeCompare(time, context->exchangeStartTime + DHCPV6_CLIENT_CNF_MAX_RD) >= 0)
820  {
821  //If the client receives no responses before the message transmission
822  //process terminates, the client should continue to use any IP
823  //addresses using the last known lifetimes for those addresses
825  }
826  }
827  }
828 
829  //Manage DHCPv6 configuration timeout
830  dhcpv6ClientCheckTimeout(context);
831 }
832 
833 
834 /**
835  * @brief DAD state
836  *
837  * The client perform duplicate address detection on each
838  * of the addresses in any IAs it receives in the Reply message
839  * before using that address for traffic
840  *
841  * @param[in] context Pointer to the DHCPv6 client context
842  **/
843 
845 {
846  uint_t i;
847  NetInterface *interface;
848  Ipv6AddrState state;
849  Dhcpv6ClientAddrEntry *entry;
850 
851  //Point to the underlying network interface
852  interface = context->settings.interface;
853 
854  //Loop through the IPv6 addresses recorded by the DHCPv6 client
855  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
856  {
857  //Point to the current entry
858  entry = &context->ia.addrList[i];
859 
860  //Check the IPv6 address is a tentative address?
861  if(entry->validLifetime > 0)
862  {
863  //Get the state of the current IPv6 address
864  state = ipv6GetAddrState(interface, &entry->addr);
865 
866  //Duplicate Address Detection in progress?
867  if(state == IPV6_ADDR_STATE_TENTATIVE)
868  {
869  //Exit immediately
870  return;
871  }
872  //Duplicate Address Detection failed?
873  else if(state == IPV6_ADDR_STATE_INVALID)
874  {
875  //Switch to the DECLINE state
877  //Exit immediately
878  return;
879  }
880  }
881  }
882 
883  //Dump current DHCPv6 configuration for debugging purpose
884  dhcpv6ClientDumpConfig(context);
885  //Switch to the BOUND state
887 }
888 
889 
890 /**
891  * @brief BOUND state
892  *
893  * Client has a valid lease and is in its normal operating state
894  *
895  * @param[in] context Pointer to the DHCPv6 client context
896  **/
897 
899 {
900  systime_t t1;
901  systime_t time;
902 
903  //Get current time
904  time = osGetSystemTime();
905 
906  //A client will never attempt to extend the lifetime of any
907  //address in an IA with T1 set to 0xffffffff
908  if(context->ia.t1 != DHCPV6_INFINITE_TIME)
909  {
910  //Convert T1 to milliseconds
911  if(context->ia.t1 < (MAX_DELAY / 1000))
912  t1 = context->ia.t1 * 1000;
913  else
914  t1 = MAX_DELAY;
915 
916  //Check the time elapsed since the lease was obtained
917  if(timeCompare(time, context->leaseStartTime + t1) >= 0)
918  {
919  //Record the time at which the client started the address renewal process
920  context->configStartTime = time;
921 
922  //Enter the RENEW state
924  }
925  }
926 }
927 
928 
929 /**
930  * @brief RENEW state
931  *
932  * The client sends a Renew message to the server that originally
933  * provided the client's addresses and configuration parameters to
934  * extend the lifetimes on the addresses assigned to the client
935  * and to update other configuration parameters
936  *
937  * @param[in] context Pointer to the DHCPv6 client context
938  **/
939 
941 {
942  systime_t t2;
943  systime_t time;
944 
945  //Get current time
946  time = osGetSystemTime();
947 
948  //Check current time
949  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
950  {
951  //Check retransmission counter
952  if(context->retransmitCount == 0)
953  {
954  //Generate a 24-bit transaction ID
955  context->transactionId = netGetRand() & 0x00FFFFFF;
956 
957  //Send a Renew message
959 
960  //Save the time at which the message was sent
961  context->exchangeStartTime = time;
962  context->timestamp = time;
963 
964  //Initial retransmission timeout
965  context->timeout = DHCPV6_CLIENT_REN_TIMEOUT +
967  }
968  else
969  {
970  //Send a Renew message
972 
973  //Save the time at which the message was sent
974  context->timestamp = time;
975 
976  //The RT is doubled for each subsequent retransmission
977  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
978 
979  //MRT specifies an upper bound on the value of RT
980  if(context->timeout > DHCPV6_CLIENT_REN_MAX_RT)
981  {
982  //Compute retransmission timeout
983  context->timeout = DHCPV6_CLIENT_REN_MAX_RT +
985  }
986  }
987 
988  //Increment retransmission counter
989  context->retransmitCount++;
990  }
991  else
992  {
993  //A client will never attempt to use a Rebind message to locate a
994  //different server to extend the lifetime of any address in an IA
995  //with T2 set to 0xffffffff
996  if(context->ia.t2 != DHCPV6_INFINITE_TIME)
997  {
998  //Convert T2 to milliseconds
999  if(context->ia.t2 < (MAX_DELAY / 1000))
1000  t2 = context->ia.t2 * 1000;
1001  else
1002  t2 = MAX_DELAY;
1003 
1004  //Check whether T2 timer has expired
1005  if(timeCompare(time, context->leaseStartTime + t2) >= 0)
1006  {
1007  //Switch to the REBIND state
1009  }
1010  }
1011  }
1012 }
1013 
1014 
1015 /**
1016  * @brief REBIND state
1017  *
1018  * The client sends a Rebind message to any available server to extend
1019  * the lifetimes on the addresses assigned to the client and to update
1020  * other configuration parameters. This message is sent after a client
1021  * receives no response to a Renew message
1022  *
1023  * @param[in] context Pointer to the DHCPv6 client context
1024  **/
1025 
1027 {
1028  uint_t i;
1029  systime_t time;
1030  NetInterface *interface;
1031  Dhcpv6ClientAddrEntry *entry;
1032 
1033  //Point to the underlying network interface
1034  interface = context->settings.interface;
1035 
1036  //Get current time
1037  time = osGetSystemTime();
1038 
1039  //Check current time
1040  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
1041  {
1042  //Check retransmission counter
1043  if(context->retransmitCount == 0)
1044  {
1045  //Generate a 24-bit transaction ID
1046  context->transactionId = netGetRand() & 0x00FFFFFF;
1047 
1048  //Send a Rebind message
1050 
1051  //Save the time at which the message was sent
1052  context->exchangeStartTime = time;
1053  context->timestamp = time;
1054 
1055  //Initial retransmission timeout
1056  context->timeout = DHCPV6_CLIENT_REB_TIMEOUT +
1058  }
1059  else
1060  {
1061  //Send a Rebind message
1063 
1064  //Save the time at which the message was sent
1065  context->timestamp = time;
1066 
1067  //The RT is doubled for each subsequent retransmission
1068  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
1069 
1070  //MRT specifies an upper bound on the value of RT
1071  if(context->timeout > DHCPV6_CLIENT_REB_MAX_RT)
1072  {
1073  //Compute retransmission timeout
1074  context->timeout = DHCPV6_CLIENT_REB_MAX_RT +
1076  }
1077  }
1078 
1079  //Increment retransmission counter
1080  context->retransmitCount++;
1081  }
1082  else
1083  {
1084  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1085  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1086  {
1087  //Point to the current entry
1088  entry = &context->ia.addrList[i];
1089 
1090  //Valid IPv6 address?
1091  if(entry->validLifetime > 0)
1092  {
1093  //Check whether the valid lifetime has expired
1094  if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_INVALID)
1095  {
1096  //Restart DHCPv6 configuration
1098  }
1099  }
1100  }
1101  }
1102 }
1103 
1104 
1105 /**
1106  * @brief RELEASE state
1107  *
1108  * To release one or more addresses, a client sends a Release message
1109  * to the server
1110  *
1111  * @param[in] context Pointer to the DHCPv6 client context
1112  **/
1113 
1115 {
1116  systime_t time;
1117 
1118  //Get current time
1119  time = osGetSystemTime();
1120 
1121  //Check current time
1122  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
1123  {
1124  //Check retransmission counter
1125  if(context->retransmitCount == 0)
1126  {
1127  //Generate a 24-bit transaction ID
1128  context->transactionId = netGetRand() & 0x00FFFFFF;
1129 
1130  //Send a Release message
1132 
1133  //Save the time at which the message was sent
1134  context->exchangeStartTime = time;
1135  context->timestamp = time;
1136 
1137  //Initial retransmission timeout
1138  context->timeout = DHCPV6_CLIENT_REL_TIMEOUT +
1140 
1141  //Increment retransmission counter
1142  context->retransmitCount++;
1143  }
1144  else if(context->retransmitCount < DHCPV6_CLIENT_REL_MAX_RC)
1145  {
1146  //Send a Release message
1148 
1149  //Save the time at which the message was sent
1150  context->timestamp = time;
1151 
1152  //The RT is doubled for each subsequent retransmission
1153  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
1154 
1155  //Increment retransmission counter
1156  context->retransmitCount++;
1157  }
1158  else
1159  {
1160  //Implementations should retransmit one or more times, but may
1161  //choose to terminate the retransmission procedure early
1162  context->running = FALSE;
1163 
1164  //Reinitialize state machine
1166  }
1167  }
1168 }
1169 
1170 
1171 /**
1172  * @brief DECLINE state
1173  *
1174  * If a client detects that one or more addresses assigned to it by a
1175  * server are already in use by another node, the client sends a Decline
1176  * message to the server to inform it that the address is suspect
1177  *
1178  * @param[in] context Pointer to the DHCPv6 client context
1179  **/
1180 
1182 {
1183  systime_t time;
1184 
1185  //Get current time
1186  time = osGetSystemTime();
1187 
1188  //Check current time
1189  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
1190  {
1191  //Check retransmission counter
1192  if(context->retransmitCount == 0)
1193  {
1194  //Generate a 24-bit transaction ID
1195  context->transactionId = netGetRand() & 0x00FFFFFF;
1196 
1197  //Send a Decline message
1199 
1200  //Save the time at which the message was sent
1201  context->exchangeStartTime = time;
1202  context->timestamp = time;
1203 
1204  //Initial retransmission timeout
1205  context->timeout = DHCPV6_CLIENT_DEC_TIMEOUT +
1207 
1208  //Increment retransmission counter
1209  context->retransmitCount++;
1210  }
1211  else if(context->retransmitCount < DHCPV6_CLIENT_DEC_MAX_RC)
1212  {
1213  //Send a Decline message
1215 
1216  //Save the time at which the message was sent
1217  context->timestamp = time;
1218 
1219  //The RT is doubled for each subsequent retransmission
1220  context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
1221 
1222  //Increment retransmission counter
1223  context->retransmitCount++;
1224  }
1225  else
1226  {
1227  //If the client does not receive a response within a reasonable
1228  //period of time, then it restarts the initialization procedure
1230  }
1231  }
1232 }
1233 
1234 
1235 /**
1236  * @brief Send Solicit message
1237  * @param[in] context Pointer to the DHCPv6 client context
1238  * @param[in] type DHCPv6 message type
1239  * @return Error code
1240  **/
1241 
1244 {
1245  error_t error;
1246  uint_t i;
1247  size_t length;
1248  size_t offset;
1249  NetBuffer *buffer;
1250  NetInterface *interface;
1252  Dhcpv6Option *option;
1253  Dhcpv6IaNaOption iaNaOption;
1254  Dhcpv6IaAddrOption iaAddrOption;
1255  Dhcpv6FqdnOption *fqdnOption;
1256  Dhcpv6ElapsedTimeOption elapsedTimeOption;
1257  Dhcpv6ClientAddrEntry *entry;
1259 
1260  //Point to the underlying network interface
1261  interface = context->settings.interface;
1262 
1263  //Allocate a memory buffer to hold the DHCPv6 message
1264  buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset);
1265  //Failed to allocate buffer?
1266  if(buffer == NULL)
1267  return ERROR_OUT_OF_MEMORY;
1268 
1269  //Point to the beginning of the DHCPv6 message
1270  message = netBufferAt(buffer, offset);
1271 
1272  //Set DHCPv6 message type
1273  message->msgType = type;
1274 
1275  //The transaction ID is chosen by the client
1276  STORE24BE(context->transactionId, message->transactionId);
1277 
1278  //Size of the DHCPv6 message
1279  length = sizeof(Dhcpv6Message);
1280 
1281  //The client must include a Client Identifier option
1282  //to identify itself to the server
1284  context->clientId, context->clientIdLength);
1285 
1286  //Request, Renew, Release or Decline message?
1287  if(type == DHCPV6_MSG_TYPE_REQUEST ||
1291  {
1292  //The client places the identifier of the destination
1293  //server in a Server Identifier option
1295  context->serverId, context->serverIdLength);
1296  }
1297 
1298  //Solicit message?
1300  {
1301  //Check whether rapid commit is enabled
1302  if(context->settings.rapidCommit)
1303  {
1304  //Include the Rapid Commit option if the client is prepared
1305  //to perform the Solicit/Reply message exchange
1307  }
1308  }
1309 
1310  //Solicit, Request, Confirm, Renew or Rebind message?
1311  if(type == DHCPV6_MSG_TYPE_SOLICIT ||
1316  {
1317  //Point to the client's fully qualified domain name
1318  fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
1319 
1320  //The FQDN option can be used by the client to convey its
1321  //fully qualified domain name to the server
1323  fqdnOption, sizeof(Dhcpv6FqdnOption) + context->clientFqdnLength);
1324 
1325  //The client should include an Option Request option to indicate
1326  //the options the client is interested in receiving
1328  &dhcpv6OptionList, sizeof(dhcpv6OptionList));
1329  }
1330 
1331  //Prepare an IA_NA option for a the current interface
1332  iaNaOption.iaId = htonl(interface->id);
1333 
1334  //Solicit, Request or Confirm message?
1335  if(type == DHCPV6_MSG_TYPE_SOLICIT ||
1338  {
1339  //The client should set the T1 and T2 fields in any IA_NA options to 0
1340  iaNaOption.t1 = 0;
1341  iaNaOption.t2 = 0;
1342  }
1343  else
1344  {
1345  //T1 and T2 are provided as a hint
1346  iaNaOption.t1 = htonl(context->ia.t1);
1347  iaNaOption.t2 = htonl(context->ia.t2);
1348  }
1349 
1350  //The client includes IA options for any IAs to which
1351  //it wants the server to assign addresses
1353  &iaNaOption, sizeof(Dhcpv6IaNaOption));
1354 
1355  //Request, Confirm, Renew, Rebind, Release or Decline message?
1356  if(type == DHCPV6_MSG_TYPE_REQUEST ||
1362  {
1363  //Loop through the IPv6 addresses recorded by the client
1364  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1365  {
1366  //Point to the current entry
1367  entry = &context->ia.addrList[i];
1368 
1369  //Valid IPv6 address?
1370  if(entry->validLifetime > 0)
1371  {
1372  //Prepare an IA Address option
1373  iaAddrOption.address = entry->addr;
1374 
1375  //Confirm message?
1377  {
1378  //The client should set the preferred and valid lifetimes to 0
1379  iaAddrOption.preferredLifetime = 0;
1380  iaAddrOption.validLifetime = 0;
1381  }
1382  else
1383  {
1384  //Preferred and valid lifetimes are provided as a hint
1385  iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime);
1386  iaAddrOption.validLifetime = htonl(entry->validLifetime);
1387  }
1388 
1389  //Add the IA Address option
1391  &iaAddrOption, sizeof(iaAddrOption));
1392  }
1393  }
1394  }
1395 
1396  //Compute the time elapsed since the client sent the first message
1397  elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context);
1398 
1399  //The client must include an Elapsed Time option in messages to indicate
1400  //how long the client has been trying to complete a DHCP message exchange
1402  &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption));
1403 
1404  //Adjust the length of the multi-part buffer
1405  netBufferSetLength(buffer, offset + length);
1406 
1407  //Destination address
1408  destIpAddr.length = sizeof(Ipv6Addr);
1410 
1411  //Debug message
1412  TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n",
1414 
1415  //Dump the contents of the message for debugging purpose
1417 
1418  //Send DHCPv6 message
1419  error = udpSendDatagramEx(interface, DHCPV6_CLIENT_PORT,
1420  &destIpAddr, DHCPV6_SERVER_PORT, buffer, offset, 0);
1421 
1422  //Free previously allocated memory
1423  netBufferFree(buffer);
1424  //Return status code
1425  return error;
1426 }
1427 
1428 
1429 /**
1430  * @brief Process incoming DHCPv6 message
1431  * @param[in] interface Underlying network interface
1432  * @param[in] pseudoHeader UDP pseudo header
1433  * @param[in] udpHeader UDP header
1434  * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message
1435  * @param[in] offset Offset to the first byte of the DHCPv6 message
1436  * @param[in] param Pointer to the DHCPv6 client context
1437  **/
1438 
1440  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
1441  const NetBuffer *buffer, size_t offset, void *param)
1442 {
1443  size_t length;
1444  Dhcpv6ClientContext *context;
1446 
1447  //Point to the DHCPv6 client context
1448  context = (Dhcpv6ClientContext *) param;
1449 
1450  //Retrieve the length of the DHCPv6 message
1451  length = netBufferGetLength(buffer) - offset;
1452 
1453  //Make sure the DHCPv6 message is valid
1454  if(length < sizeof(Dhcpv6Message))
1455  return;
1456 
1457  //Point to the beginning of the DHCPv6 message
1458  message = netBufferAt(buffer, offset);
1459  //Sanity check
1460  if(message == NULL)
1461  return;
1462 
1463  //Debug message
1464  TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n",
1466 
1467  //Dump the contents of the message for debugging purpose
1469 
1470  //Check message type
1471  switch(message->msgType)
1472  {
1474  //Parse Advertise message
1476  break;
1477  case DHCPV6_MSG_TYPE_REPLY:
1478  //Parse Reply message
1480  break;
1481  default:
1482  //Silently drop incoming message
1483  break;
1484  }
1485 }
1486 
1487 
1488 /**
1489  * @brief Parse Advertise message
1490  * @param[in] context Pointer to the DHCPv6 client context
1491  * @param[in] message Pointer to the incoming message to parse
1492  * @param[in] length Length of the incoming message
1493  **/
1494 
1496  const Dhcpv6Message *message, size_t length)
1497 {
1498  uint_t i;
1499  int_t serverPreference;
1500  NetInterface *interface;
1501  Dhcpv6StatusCode status;
1502  Dhcpv6Option *option;
1503  Dhcpv6Option *serverIdOption;
1504  Dhcpv6IaNaOption *iaNaOption;
1505 
1506  //Point to the underlying network interface
1507  interface = context->settings.interface;
1508 
1509  //Make sure that the Advertise message is received in response to
1510  //a Solicit message
1511  if(context->state != DHCPV6_STATE_SOLICIT)
1512  return;
1513 
1514  //Discard any received packet that does not match the transaction ID
1515  if(LOAD24BE(message->transactionId) != context->transactionId)
1516  return;
1517 
1518  //Get the length of the Options field
1519  length -= sizeof(Dhcpv6Message);
1520 
1521  //Search for the Client Identifier option
1523 
1524  //Discard any received packet that does not include a Client Identifier option
1525  if(option == NULL)
1526  return;
1527  //Check the length of the option
1528  if(ntohs(option->length) != context->clientIdLength)
1529  return;
1530  //Check whether the Client Identifier matches our identifier
1531  if(memcmp(option->value, context->clientId, context->clientIdLength))
1532  return;
1533 
1534  //Search for the Server Identifier option
1535  serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
1536 
1537  //Discard any received packet that does not include a Server Identifier option
1538  if(serverIdOption == NULL)
1539  return;
1540  //Check the length of the server DUID
1541  if(ntohs(serverIdOption->length) == 0)
1542  return;
1543  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
1544  return;
1545 
1546  //Get the status code returned by the server
1547  status = dhcpv6GetStatusCode(message->options, length);
1548 
1549  //If the message contains a Status Code option indicating a failure,
1550  //then the Advertise message is discarded by the client
1551  if(status != DHCPV6_STATUS_SUCCESS)
1552  return;
1553 
1554  //Search for the Preference option
1556 
1557  //Check whether the option has been found
1558  if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption))
1559  {
1560  //Server server preference value
1561  serverPreference = option->value[0];
1562  }
1563  else
1564  {
1565  //Any Advertise that does not include a Preference option
1566  //is considered to have a preference value of 0
1567  serverPreference = 0;
1568  }
1569 
1570  //Select the Advertise message that offers the highest server preference value
1571  if(serverPreference > context->serverPreference)
1572  {
1573  //Save the length of the DUID
1574  context->serverIdLength = ntohs(serverIdOption->length);
1575  //Record the server DUID
1576  memcpy(context->serverId, serverIdOption->value, context->serverIdLength);
1577 
1578  //Flush the list of IPv6 addresses from the client's IA
1579  dhcpv6ClientFlushAddrList(context);
1580  }
1581 
1582  //Point to the first option
1583  i = 0;
1584 
1585  //Loop through DHCPv6 options
1586  while(i < length)
1587  {
1588  //Search for an IA_NA option
1589  option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
1590 
1591  //Unable to find the specified option?
1592  if(option == NULL)
1593  break;
1594 
1595  //Make sure the IA_NA option is valid
1596  if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption))
1597  {
1598  //Get the parameters associated with the IA_NA
1599  iaNaOption = (Dhcpv6IaNaOption *) option->value;
1600 
1601  //Check the IA identifier
1602  if(ntohl(iaNaOption->iaId) == interface->id)
1603  {
1604  //The client examines the status code in each IA individually
1605  status = dhcpv6GetStatusCode(iaNaOption->options,
1606  ntohs(option->length) - sizeof(Dhcpv6IaNaOption));
1607 
1608  //The client must ignore any Advertise message that includes a Status
1609  //Code option containing the value NoAddrsAvail
1610  if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
1611  return;
1612  }
1613 
1614  //Check the server preference value
1615  if(serverPreference > context->serverPreference)
1616  {
1617  //Parse the contents of the IA_NA option
1618  dhcpv6ClientParseIaNaOption(context, option);
1619  }
1620  }
1621 
1622  //Jump to the next option
1623  i += sizeof(Dhcpv6Option) + ntohs(option->length);
1624  }
1625 
1626  //Record the highest server preference value
1627  if(serverPreference > context->serverPreference)
1628  context->serverPreference = serverPreference;
1629 
1630  //If the client receives an Advertise message that includes a
1631  //Preference option with a preference value of 255, the client
1632  //immediately completes the message exchange
1633  if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE)
1634  {
1635  //Continue configuration procedure
1637  }
1638  //The message exchange is not terminated before the first RT has elapsed
1639  else if(context->retransmitCount > 1)
1640  {
1641  //Continue configuration procedure
1643  }
1644 }
1645 
1646 
1647 /**
1648  * @brief Parse Reply message
1649  * @param[in] context Pointer to the DHCPv6 client context
1650  * @param[in] message Pointer to the incoming message to parse
1651  * @param[in] length Length of the incoming message
1652  **/
1653 
1655  const Dhcpv6Message *message, size_t length)
1656 {
1657  error_t error;
1658  uint_t i;
1659  uint_t k;
1660  uint_t n;
1661  bool_t iaNaOptionFound;
1662  systime_t minPreferredLifetime;
1663  NetInterface *interface;
1664  Dhcpv6StatusCode status;
1665  Dhcpv6Option *option;
1666  Dhcpv6Option *serverIdOption;
1667  Dhcpv6ClientAddrEntry *entry;
1668 
1669  //Point to the underlying network interface
1670  interface = context->settings.interface;
1671 
1672  //Discard any received packet that does not match the transaction ID
1673  if(LOAD24BE(message->transactionId) != context->transactionId)
1674  return;
1675 
1676  //Get the length of the Options field
1677  length -= sizeof(Dhcpv6Message);
1678 
1679  //Search for the Client Identifier option
1681 
1682  //Discard any received packet that does not include a Client Identifier option
1683  if(option == NULL)
1684  return;
1685  //Check the length of the option
1686  if(ntohs(option->length) != context->clientIdLength)
1687  return;
1688  //Check whether the Client Identifier matches our identifier
1689  if(memcmp(option->value, context->clientId, context->clientIdLength))
1690  return;
1691 
1692  //Search for the Server Identifier option
1693  serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
1694 
1695  //Discard any received packet that does not include a Server Identifier option
1696  if(serverIdOption == NULL)
1697  return;
1698  //Check the length of the server DUID
1699  if(ntohs(serverIdOption->length) == 0)
1700  return;
1701  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
1702  return;
1703 
1704  //Get the status code returned by the server
1705  status = dhcpv6GetStatusCode(message->options, length);
1706 
1707  //Check current state
1708  if(context->state == DHCPV6_STATE_SOLICIT)
1709  {
1710  //A Reply message is not acceptable when rapid commit is disallowed
1711  if(!context->settings.rapidCommit)
1712  return;
1713 
1714  //Search for the Rapid Commit option
1716 
1717  //The client discards any message that does not include a Rapid Commit option
1718  if(option == NULL || ntohs(option->length) != 0)
1719  return;
1720  }
1721  else if(context->state == DHCPV6_STATE_REQUEST)
1722  {
1723  //The client must discard the Reply message if the contents of the
1724  //Server Identifier option do not match the server’s DUID
1725  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1726  return;
1727  }
1728  else if(context->state == DHCPV6_STATE_CONFIRM)
1729  {
1730  //When the client receives a NotOnLink status from the server in response
1731  //to a Confirm message, the client performs DHCP server solicitation
1732  if(status == DHCPV6_STATUS_NOT_ON_LINK)
1733  {
1734  //Restart the DHCP server discovery process
1736 
1737  //Exit immediately
1738  return;
1739  }
1740  }
1741  else if(context->state == DHCPV6_STATE_RENEW)
1742  {
1743  //The client must discard the Reply message if the contents of the
1744  //Server Identifier option do not match the server’s DUID
1745  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1746  return;
1747  }
1748  else if(context->state == DHCPV6_STATE_REBIND)
1749  {
1750  //Do not check the server's DUID when the Reply message is
1751  //received in response to a Rebind message
1752  }
1753  else if(context->state == DHCPV6_STATE_RELEASE)
1754  {
1755  //The client must discard the Reply message if the contents of the
1756  //Server Identifier option do not match the server’s DUID
1757  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1758  return;
1759 
1760  //When the client receives a valid Reply message in response to a
1761  //Release message, the client considers the Release event completed,
1762  //regardless of the Status Code option(s) returned by the server
1763  context->running = FALSE;
1764 
1765  //Reinitialize state machine
1767 
1768  //Exit immediately
1769  return;
1770  }
1771  else if(context->state == DHCPV6_STATE_DECLINE)
1772  {
1773  //The client must discard the Reply message if the contents of the
1774  //Server Identifier option do not match the server’s DUID
1775  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1776  return;
1777 
1778  //When the client receives a valid Reply message in response to a
1779  //Decline message, the client considers the Decline event completed,
1780  //regardless of the Status Code option returned by the server
1782 
1783  //Exit immediately
1784  return;
1785  }
1786  else
1787  {
1788  //Silently discard the Reply message
1789  return;
1790  }
1791 
1792  //Check status code
1793  if(status == DHCPV6_STATUS_USE_MULTICAST)
1794  {
1795  //When the client receives a Reply message with a Status Code option
1796  //with the value UseMulticast, the client records the receipt of the
1797  //message and sends subsequent messages to the server through the
1798  //interface on which the message was received using multicast
1799  return;
1800  }
1801  else if(status == DHCPV6_STATUS_UNSPEC_FAILURE)
1802  {
1803  //If the client receives a Reply message with a Status Code containing
1804  //UnspecFail, the server is indicating that it was unable to process
1805  //the message due to an unspecified failure condition
1806  return;
1807  }
1808 
1809  //This flag will be set if a valid IA_NA option is found
1810  iaNaOptionFound = FALSE;
1811  //Point to the first option
1812  i = 0;
1813 
1814  //Loop through DHCPv6 options
1815  while(i < length)
1816  {
1817  //Search for an IA_NA option
1818  option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
1819 
1820  //Unable to find the specified option?
1821  if(option == NULL)
1822  break;
1823 
1824  //Parse the contents of the IA_NA option
1825  error = dhcpv6ClientParseIaNaOption(context, option);
1826 
1827  //Check error code
1828  if(error == NO_ERROR)
1829  {
1830  //A valid IA_NA option has been found
1831  iaNaOptionFound = TRUE;
1832  }
1833  else if(error == ERROR_NOT_ON_LINK)
1834  {
1835  //When the client receives a NotOnLink status from the server
1836  //in response to a Request, the client can either re-issue the
1837  //Request without specifying any addresses or restart the DHCP
1838  //server discovery process
1840 
1841  //Exit immediately
1842  return;
1843  }
1844  else if(error == ERROR_NO_BINDING)
1845  {
1846  //When the client receives a Reply message in response to a Renew
1847  //or Rebind message, the client sends a Request message if any of
1848  //the IAs in the Reply message contains the NoBinding status code
1850 
1851  //Exit immediately
1852  return;
1853  }
1854  else
1855  {
1856  //If an invalid option is received, the client discards
1857  //the option and process the rest of the message...
1858  }
1859 
1860  //Jump to the next option
1861  i += sizeof(Dhcpv6Option) + ntohs(option->length);
1862  }
1863 
1864  //No usable addresses in any of the IAs?
1865  if(!iaNaOptionFound)
1866  {
1867  //Check whether the client receives a Reply message in response
1868  //to a Renew or Rebind message
1869  if(context->state == DHCPV6_STATE_RENEW ||
1870  context->state == DHCPV6_STATE_REBIND)
1871  {
1872  //The client sends a Renew/Rebind if the IA is not in the Reply message
1873  }
1874  else
1875  {
1876  //If the client finds no usable addresses in any of the IAs, it may try
1877  //another server (perhaps restarting the DHCP server discovery process)
1879  }
1880 
1881  //Exit immediately
1882  return;
1883  }
1884 
1885  //Total number of valid IPv6 in the IA
1886  n = 0;
1887  //Number of new IPv6 addresses in the IA
1888  k = 0;
1889  //Minimum preferred lifetime observed in the IA
1890  minPreferredLifetime = 0;
1891 
1892  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1893  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1894  {
1895  //Point to the current entry
1896  entry = &context->ia.addrList[i];
1897 
1898  //Valid IPv6 address?
1899  if(entry->validLifetime > 0)
1900  {
1901  //Total number of valid IPv6 in the IA
1902  n++;
1903 
1904  //Save the minimum preferred lifetime that has been observed so far
1905  if(minPreferredLifetime < entry->preferredLifetime)
1906  minPreferredLifetime = entry->preferredLifetime;
1907 
1908  //Update lifetimes of the current IPv6 address
1909  ipv6AddAddr(interface, &entry->addr, entry->validLifetime,
1910  entry->preferredLifetime);
1911 
1912  //New IPv6 address added?
1913  if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE)
1914  k++;
1915  }
1916  }
1917 
1918  //Make sure that the IA contains at least one IPv6 address
1919  if(n > 0)
1920  {
1921  //Save the length of the DUID
1922  context->serverIdLength = ntohs(serverIdOption->length);
1923  //Record the server DUID
1924  memcpy(context->serverId, serverIdOption->value, context->serverIdLength);
1925 
1926  //Save the time a which the lease was obtained
1927  context->leaseStartTime = osGetSystemTime();
1928 
1929  //Check the value of T1
1930  if(context->ia.t1 == 0)
1931  {
1932  //If T1 is set to 0 by the server, the client may send
1933  //a Renew message at the client's discretion
1934  if(minPreferredLifetime == DHCPV6_INFINITE_TIME)
1935  context->ia.t1 = DHCPV6_INFINITE_TIME;
1936  else
1937  context->ia.t1 = minPreferredLifetime / 2;
1938  }
1939 
1940  //Check the value of T2
1941  if(context->ia.t2 == 0)
1942  {
1943  //If T2 is set to 0 by the server, the client may send
1944  //a Rebind message at the client's discretion
1945  if(context->ia.t1 == DHCPV6_INFINITE_TIME)
1946  context->ia.t2 = DHCPV6_INFINITE_TIME;
1947  else
1948  context->ia.t2 = context->ia.t1 + context->ia.t1 / 2;
1949  }
1950 
1951  //Any addresses added in the IA?
1952  if(k > 0)
1953  {
1954  //Perform Duplicate Address Detection for the new IPv6 addresses
1956  }
1957  else
1958  {
1959  //Switch to the BOUND state
1961  }
1962  }
1963  else
1964  {
1965  //If the client finds no usable addresses in any of the IAs, it may try
1966  //another server (perhaps restarting the DHCP server discovery process)
1968  }
1969 }
1970 
1971 
1972 /**
1973  * @brief Parse IA_NA option
1974  * @param[in] context Pointer to the DHCPv6 client context
1975  * @param[in] option Pointer to the IA_NA option to parse
1976  * @return Error code
1977  **/
1978 
1980  const Dhcpv6Option *option)
1981 {
1982  error_t error;
1983  uint_t n;
1984  size_t i;
1985  size_t length;
1986  NetInterface *interface;
1987  Dhcpv6StatusCode status;
1988  Dhcpv6IaNaOption *iaNaOption;
1989 
1990  //Point to the underlying network interface
1991  interface = context->settings.interface;
1992 
1993  //Number of addresses found in the IA_NA option
1994  n = 0;
1995 
1996  //Make sure the IA_NA option is valid
1997  if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
1998  return ERROR_INVALID_LENGTH;
1999 
2000  //Get the parameters associated with the IA_NA
2001  iaNaOption = (Dhcpv6IaNaOption *) option->value;
2002  //Compute the length of IA_NA Options field
2003  length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption);
2004 
2005  //Check the IA identifier
2006  if(ntohl(iaNaOption->iaId) != interface->id)
2007  return ERROR_WRONG_IDENTIFIER;
2008 
2009  //If a client receives an IA_NA with T1 greater than T2, and both T1
2010  //and T2 are greater than 0, the client discards the IA_NA option and
2011  //processes the remainder of the message as though the server had not
2012  //included the invalid IA_NA option
2013  if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0)
2014  return ERROR_INVALID_PARAMETER;
2015 
2016  //The client examines the status code in each IA individually
2017  status = dhcpv6GetStatusCode(iaNaOption->options, length);
2018 
2019  //Check error code
2020  if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
2021  {
2022  //The client has received no usable address in the IA
2023  return ERROR_NO_ADDRESS;
2024  }
2025  else if(status == DHCPV6_STATUS_NO_BINDING)
2026  {
2027  //Client record (binding) unavailable
2028  return ERROR_NO_BINDING;
2029  }
2030  else if(status == DHCPV6_STATUS_NOT_ON_LINK)
2031  {
2032  //The prefix for the address is not appropriate for the link to which the
2033  //client is attached
2034  return ERROR_NOT_ON_LINK;
2035  }
2036  else if(status != DHCPV6_STATUS_SUCCESS)
2037  {
2038  //Failure, reason unspecified
2039  return ERROR_FAILURE;
2040  }
2041 
2042  //Record T1 and T2 times
2043  context->ia.t1 = ntohl(iaNaOption->t1);
2044  context->ia.t2 = ntohl(iaNaOption->t2);
2045 
2046  //Point to the first option
2047  i = 0;
2048 
2049  //Loop through IA_NA options
2050  while(i < length)
2051  {
2052  //Search for an IA Address option
2053  option = dhcpv6GetOption(iaNaOption->options + i, length - i, DHCPV6_OPTION_IAADDR);
2054 
2055  //Unable to find the specified option?
2056  if(option == NULL)
2057  break;
2058 
2059  //Parse the contents of the IA Address option
2060  error = dhcpv6ClientParseIaAddrOption(context, option);
2061 
2062  //Check status code
2063  if(!error)
2064  {
2065  //Increment the number of addresses found in the IA_NA option
2066  n++;
2067  }
2068 
2069  //Jump to the next option
2070  i += sizeof(Dhcpv6Option) + ntohs(option->length);
2071  }
2072 
2073  //No usable addresses in the IA_NA option?
2074  if(n == 0)
2075  {
2076  //Report an error
2077  return ERROR_NO_ADDRESS;
2078  }
2079 
2080  //Successful processing
2081  return NO_ERROR;
2082 }
2083 
2084 
2085 /**
2086  * @brief Parse IA Address option
2087  * @param[in] context Pointer to the DHCPv6 client context
2088  * @param[in] option Pointer to the IA Address option to parse
2089  * @return Error code
2090  **/
2091 
2093  const Dhcpv6Option *option)
2094 {
2095  size_t length;
2096  uint32_t validLifetime;
2097  uint32_t preferredLifetime;
2098  Dhcpv6StatusCode status;
2099  Dhcpv6IaAddrOption *iaAddrOption;
2100 
2101  //Make sure the IA Address option is valid
2102  if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
2103  return ERROR_INVALID_LENGTH;
2104 
2105  //Point to the contents of the IA Address option
2106  iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
2107  //Compute the length of IA Address Options field
2108  length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption);
2109 
2110  //Convert lifetimes to host byte order
2111  validLifetime = ntohl(iaAddrOption->validLifetime);
2112  preferredLifetime = ntohl(iaAddrOption->preferredLifetime);
2113 
2114  //A client discards any addresses for which the preferred lifetime
2115  //is greater than the valid lifetime
2117  return ERROR_INVALID_PARAMETER;
2118 
2119  //The client examines the status code in each IA Address
2120  status = dhcpv6GetStatusCode(iaAddrOption->options, length);
2121 
2122  //Any error to report?
2123  if(status != DHCPV6_STATUS_SUCCESS)
2124  return ERROR_FAILURE;
2125 
2126  //Check the value of the Valid Lifetime
2127  if(iaAddrOption->validLifetime > 0)
2128  {
2129  //Add any new addresses in the IA option to the IA as recorded
2130  //by the client
2131  dhcpv6ClientAddAddr(context, &iaAddrOption->address,
2133  }
2134  else
2135  {
2136  //Discard any addresses from the IA, as recorded by the client,
2137  //that have a valid lifetime of 0 in the IA Address option
2138  dhcpv6ClientRemoveAddr(context, &iaAddrOption->address);
2139  }
2140 
2141  //Successful processing
2142  return NO_ERROR;
2143 }
2144 
2145 
2146 /**
2147  * @brief Add an IPv6 address to the IA
2148  * @param[in] context Pointer to the DHCPv6 client context
2149  * @param[in] addr IPv6 address to be added
2150  * @param[in] validLifetime Valid lifetime, in seconds
2151  * @param[in] preferredLifetime Preferred lifetime, in seconds
2152  **/
2153 
2155  uint32_t validLifetime, uint32_t preferredLifetime)
2156 {
2157  uint_t i;
2158  Dhcpv6ClientAddrEntry *entry;
2159  Dhcpv6ClientAddrEntry *firstFreeEntry;
2160 
2161  //Keep track of the first free entry
2162  firstFreeEntry = NULL;
2163 
2164  //Loop through the IPv6 addresses recorded by the DHCPv6 client
2165  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
2166  {
2167  //Point to the current entry
2168  entry = &context->ia.addrList[i];
2169 
2170  //Valid IPv6 address?
2171  if(entry->validLifetime > 0)
2172  {
2173  //Check whether the current entry matches the specified address
2174  if(ipv6CompAddr(&entry->addr, addr))
2175  break;
2176  }
2177  else
2178  {
2179  //Keep track of the first free entry
2180  if(firstFreeEntry == NULL)
2181  firstFreeEntry = entry;
2182  }
2183  }
2184 
2185  //No matching entry found?
2186  if(i >= IPV6_PREFIX_LIST_SIZE)
2187  entry = firstFreeEntry;
2188 
2189  //Update the entry if necessary
2190  if(entry != NULL)
2191  {
2192  //Save IPv6 address
2193  entry->addr = *addr;
2194 
2195  //Save lifetimes
2196  entry->validLifetime = validLifetime;
2198  }
2199 }
2200 
2201 
2202 /**
2203  * @brief Remove an IPv6 address from the IA
2204  * @param[in] context Pointer to the DHCPv6 client context
2205  * @param[in] addr IPv6 address to be removed
2206  **/
2207 
2209 {
2210  uint_t i;
2211  NetInterface *interface;
2212  Dhcpv6ClientAddrEntry *entry;
2213 
2214  //Point to the underlying network interface
2215  interface = context->settings.interface;
2216 
2217  //Loop through the IPv6 addresses recorded by the DHCPv6 client
2218  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
2219  {
2220  //Point to the current entry
2221  entry = &context->ia.addrList[i];
2222 
2223  //Valid IPv6 address?
2224  if(entry->validLifetime > 0)
2225  {
2226  //Check whether the current entry matches the specified address
2227  if(ipv6CompAddr(&entry->addr, addr))
2228  {
2229  //The IPv6 address is no more valid and should be removed from
2230  //the list of IPv6 addresses assigned to the interface
2231  ipv6RemoveAddr(interface, addr);
2232 
2233  //Remove the IPv6 address from the IA
2234  entry->validLifetime = 0;
2235  }
2236  }
2237  }
2238 }
2239 
2240 
2241 /**
2242  * @brief Flush the list of IPv6 addresses from the IA
2243  * @param[in] context Pointer to the DHCPv6 client context
2244  **/
2245 
2247 {
2248  uint_t i;
2249  NetInterface *interface;
2250  Dhcpv6ClientAddrEntry *entry;
2251 
2252  //Point to the underlying network interface
2253  interface = context->settings.interface;
2254 
2255  //Loop through the IPv6 addresses recorded by the DHCPv6 client
2256  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
2257  {
2258  //Point to the current entry
2259  entry = &context->ia.addrList[i];
2260 
2261  //Valid IPv6 address?
2262  if(entry->validLifetime > 0)
2263  {
2264  //The IPv6 address is no more valid and should be removed from
2265  //the list of IPv6 addresses assigned to the interface
2266  ipv6RemoveAddr(interface, &entry->addr);
2267 
2268  //Remove the IPv6 address from the IA
2269  entry->validLifetime = 0;
2270  }
2271  }
2272 }
2273 
2274 
2275 /**
2276  * @brief Generate client's DUID
2277  * @param[in] context Pointer to the DHCPv6 client context
2278  * @return Error code
2279  **/
2280 
2282 {
2283  NetInterface *interface;
2284  Dhcpv6DuidLl *duid;
2285 #if (ETH_SUPPORT == ENABLED)
2286  NetInterface *logicalInterface;
2287 #endif
2288 
2289  //Point to the underlying network interface
2290  interface = context->settings.interface;
2291 
2292  //Point to the buffer where to format the client's DUID
2293  duid = (Dhcpv6DuidLl *) context->clientId;
2294 
2295 #if (ETH_SUPPORT == ENABLED)
2296  //Point to the logical interface
2297  logicalInterface = nicGetLogicalInterface(interface);
2298 
2299  //Generate a DUID-LL from the MAC address
2300  duid->type = HTONS(DHCPV6_DUID_LL);
2301  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH);
2302  duid->linkLayerAddr = logicalInterface->macAddr;
2303 #else
2304  //Generate a DUID-LL from the EUI-64 identifier
2305  duid->type = HTONS(DHCPV6_DUID_LL);
2306  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64);
2307  duid->linkLayerAddr = interface->eui64;
2308 #endif
2309 
2310  //Length of the newly generated DUID
2311  context->clientIdLength = sizeof(Dhcpv6DuidLl);
2312 
2313  //Successful processing
2314  return NO_ERROR;
2315 }
2316 
2317 
2318 /**
2319  * @brief Generate client's fully qualified domain name
2320  * @param[in] context Pointer to the DHCPv6 client context
2321  * @return Error code
2322  **/
2323 
2325 {
2326  NetInterface *interface;
2327  Dhcpv6FqdnOption *fqdnOption;
2328 
2329  //Point to the underlying network interface
2330  interface = context->settings.interface;
2331 
2332  //Point to the buffer where to format the client's FQDN
2333  fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
2334 
2335  //Set flags
2336  fqdnOption->mbz = 0;
2337  fqdnOption->n = FALSE;
2338  fqdnOption->o = FALSE;
2339  fqdnOption->s = FALSE;
2340 
2341  //Encode client's FQDN
2342  context->clientFqdnLength = dnsEncodeName(interface->hostname,
2343  fqdnOption->domainName);
2344 
2345  //Successful processing
2346  return NO_ERROR;
2347 }
2348 
2349 
2350 /**
2351  * @brief Generate a link-local address
2352  * @param[in] context Pointer to the DHCPv6 client context
2353  * @return Error code
2354  **/
2355 
2357 {
2358  error_t error;
2359  NetInterface *interface;
2360  Ipv6Addr addr;
2361 
2362  //Point to the underlying network interface
2363  interface = context->settings.interface;
2364 
2365  //Check whether a link-local address has been manually assigned
2366  if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
2367  interface->ipv6Context.addrList[0].permanent)
2368  {
2369  //Keep using the current link-local address
2370  error = NO_ERROR;
2371  }
2372  else
2373  {
2374  //A link-local address is formed by combining the well-known
2375  //link-local prefix fe80::/10 with the interface identifier
2376  ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
2377 
2378 #if (NDP_SUPPORT == ENABLED)
2379  //Check whether Duplicate Address Detection should be performed
2380  if(interface->ndpContext.dupAddrDetectTransmits > 0)
2381  {
2382  //Use the link-local address as a tentative address
2383  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
2385  }
2386  else
2387 #endif
2388  {
2389  //The use of the link-local address is now unrestricted
2390  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
2392  }
2393  }
2394 
2395  //Return status code
2396  return error;
2397 }
2398 
2399 
2400 /**
2401  * @brief Check the Server Identifier option
2402  * @param[in] context Pointer to the DHCPv6 client context
2403  * @param[in] serverIdOption Pointer to the Server Identifier option
2404  * @return TRUE if the option matches the server’s DUID, else FALSE
2405  **/
2406 
2408  Dhcpv6Option *serverIdOption)
2409 {
2410  bool_t valid = FALSE;
2411 
2412  //Check the length of the Server Identifier option
2413  if(ntohs(serverIdOption->length) == context->serverIdLength)
2414  {
2415  //Check whether the Server Identifier option matches the server’s DUID
2416  if(!memcmp(serverIdOption->value, context->serverId, context->serverIdLength))
2417  valid = TRUE;
2418  }
2419 
2420  //Return TRUE if the option matches the server’s DUID
2421  return valid;
2422 }
2423 
2424 
2425 /**
2426  * @brief Manage DHCPv6 configuration timeout
2427  * @param[in] context Pointer to the DHCPv6 client context
2428  **/
2429 
2431 {
2432  systime_t time;
2433  NetInterface *interface;
2434 
2435  //Point to the underlying network interface
2436  interface = context->settings.interface;
2437 
2438  //Get current time
2439  time = osGetSystemTime();
2440 
2441  //Any registered callback?
2442  if(context->settings.timeoutEvent != NULL)
2443  {
2444  //DHCPv6 configuration timeout?
2445  if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
2446  {
2447  //Ensure the callback function is only called once
2448  if(!context->timeoutEventDone)
2449  {
2450  //Release exclusive access
2452  //Invoke user callback function
2453  context->settings.timeoutEvent(context, interface);
2454  //Get exclusive access
2456 
2457  //Set flag
2458  context->timeoutEventDone = TRUE;
2459  }
2460  }
2461  }
2462 }
2463 
2464 
2465 /**
2466  * @brief Compute the time elapsed since the client sent the first message
2467  * @param[in] context Pointer to the DHCPv6 client context
2468  * @return The elapsed time expressed in hundredths of a second
2469  **/
2470 
2472 {
2473  systime_t time;
2474 
2475  //Check retransmission counter
2476  if(context->retransmitCount == 0)
2477  {
2478  //The elapsed time must be 0 for the first message
2479  time = 0;
2480  }
2481  else
2482  {
2483  //Compute the time elapsed since the client sent the
2484  //first message (in hundredths of a second)
2485  time = (osGetSystemTime() - context->exchangeStartTime) / 10;
2486 
2487  //The value 0xFFFF is used to represent any elapsed time values
2488  //greater than the largest time value that can be represented
2489  time = MIN(time, 0xFFFF);
2490  }
2491 
2492  //Convert the 16-bit value to network byte order
2493  return htons(time);
2494 }
2495 
2496 
2497 /**
2498  * @brief Update DHCPv6 FSM state
2499  * @param[in] context Pointer to the DHCPv6 client context
2500  * @param[in] newState New DHCPv6 state to switch to
2501  * @param[in] delay Initial delay
2502  **/
2503 
2505  Dhcpv6State newState, systime_t delay)
2506 {
2507  systime_t time;
2508 
2509  //Get current time
2510  time = osGetSystemTime();
2511 
2512 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
2513  //Sanity check
2514  if(newState <= DHCPV6_STATE_DECLINE)
2515  {
2516  //DHCPv6 FSM states
2517  static const char_t *stateLabel[] =
2518  {
2519  "INIT",
2520  "SOLICIT",
2521  "REQUEST",
2522  "INIT-CONFIRM",
2523  "CONFIRM",
2524  "DAD",
2525  "BOUND",
2526  "RENEW",
2527  "REBIND",
2528  "RELEASE",
2529  "DECLINE"
2530  };
2531 
2532  //Debug message
2533  TRACE_INFO("%s: DHCPv6 client %s state\r\n",
2534  formatSystemTime(time, NULL), stateLabel[newState]);
2535  }
2536 #endif
2537 
2538  //Set time stamp
2539  context->timestamp = time;
2540  //Set initial delay
2541  context->timeout = delay;
2542  //Reset retransmission counter
2543  context->retransmitCount = 0;
2544  //Switch to the new state
2545  context->state = newState;
2546 
2547  //Any registered callback?
2548  if(context->settings.stateChangeEvent != NULL)
2549  {
2550  NetInterface *interface;
2551 
2552  //Point to the underlying network interface
2553  interface = context->settings.interface;
2554 
2555  //Release exclusive access
2557  //Invoke user callback function
2558  context->settings.stateChangeEvent(context, interface, newState);
2559  //Get exclusive access
2561  }
2562 }
2563 
2564 
2565 /**
2566  * @brief Dump DHCPv6 configuration for debugging purpose
2567  * @param[in] context Pointer to the DHCPv6 client context
2568  **/
2569 
2571 {
2572 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
2573  uint_t i;
2574  NetInterface *interface;
2575  Ipv6Context *ipv6Context;
2576 
2577  //Point to the underlying network interface
2578  interface = context->settings.interface;
2579  //Point to the IPv6 context
2580  ipv6Context = &interface->ipv6Context;
2581 
2582  //Debug message
2583  TRACE_INFO("\r\n");
2584  TRACE_INFO("DHCPv6 configuration:\r\n");
2585 
2586  //Lease start time
2587  TRACE_INFO(" Lease Start Time = %s\r\n",
2588  formatSystemTime(context->leaseStartTime, NULL));
2589 
2590  //T1 parameter
2591  TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->ia.t1);
2592  //T2 parameter
2593  TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->ia.t2);
2594 
2595  //Global addresses
2596  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
2597  {
2598  TRACE_INFO(" Global Address %u = %s\r\n", i,
2599  ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
2600  }
2601 
2602  //DNS servers
2603  for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
2604  {
2605  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
2606  ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
2607  }
2608 
2609  //Debug message
2610  TRACE_INFO("\r\n");
2611 #endif
2612 }
2613 
2614 #endif
__start_packed struct @134 Dhcpv6Option
DHCPv6 option.
error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context, Dhcpv6MessageType type)
Send Solicit message.
Ipv6AddrState ipv6GetAddrState(NetInterface *interface, const Ipv6Addr *addr)
Get the state of the specified IPv6 address.
Definition: ipv6_misc.c:52
#define DHCPV6_CLIENT_REL_MAX_RC
Dhcpv6Option * dhcpv6GetOption(const uint8_t *options, size_t optionsLength, uint16_t optionCode)
Find the specified option in a DHCPv6 message.
#define DHCPV6_CLIENT_REN_TIMEOUT
uint32_t systime_t
Definition: compiler_port.h:44
uint32_t validLifetime
#define DHCPV6_CLIENT_CNF_MAX_RT
#define timeCompare(t1, t2)
Definition: os_port.h:40
char char_t
Definition: compiler_port.h:41
#define DHCPV6_CLIENT_CNF_MAX_DELAY
#define ipv6GetLinkLocalAddrState(interface)
Definition: ipv6.h:139
#define DHCPV6_CLIENT_ADDR_LIST_SIZE
Definition: dhcpv6_client.h:52
void ipv6FlushDnsServerList(NetInterface *interface)
Flush the list of DNS servers.
Definition: ipv6_misc.c:735
#define DHCPV6_CLIENT_REQ_TIMEOUT
Definition: dhcpv6_client.h:87
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t time
void dhcpv6ClientStateDad(Dhcpv6ClientContext *context)
DAD state.
TCP/IP stack core.
Dhcpv6StatusCode
Status code.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:280
Debugging facilities.
Dhcpv6TimeoutCallback timeoutEvent
DHCPv6 configuration timeout event.
#define DHCPV6_CLIENT_DEC_TIMEOUT
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:295
#define DHCPV6_CLIENT_REQ_MAX_RC
Generic error code.
Definition: error.h:43
uint8_t message[]
Definition: chap.h:150
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:318
void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context)
Dump DHCPv6 configuration for debugging purpose.
#define DHCPV6_MAX_DUID_SIZE
Definition: dhcpv6_common.h:44
Invalid parameter.
Definition: error.h:45
#define DHCPV6_CLIENT_CNF_MAX_RD
Dhcpv6LinkChangeCallback linkChangeEvent
Link state change event.
#define DHCPV6_CLIENT_DEC_MAX_RC
NetInterface * interface
Network interface to configure.
#define DHCPV6_SERVER_PORT
Definition: dhcpv6_common.h:39
IP network address.
Definition: ip.h:57
__start_packed struct @183 Ipv6Addr
IPv6 network address.
error_t dhcpv6ClientStart(Dhcpv6ClientContext *context)
Start DHCPv6 client.
Ipv6Addr addr
IPv6 address.
char_t type
int32_t dhcpv6RandRange(int32_t min, int32_t max)
Get a random value in the specified range.
#define htons(value)
Definition: cpu_endian.h:390
void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context)
SOLICIT state.
#define IPV6_PREFIX_LIST_SIZE
Definition: ipv6.h:78
#define DHCPV6_CLIENT_REN_MAX_RT
#define DHCPV6_CLIENT_REB_MAX_RT
void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context)
Manage DHCPv6 configuration timeout.
void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context)
REBIND state.
#define Dhcpv6ClientContext
error_t ipv6SetAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr, Ipv6AddrState state, systime_t validLifetime, systime_t preferredLifetime, bool_t permanent)
Set IPv6 address and address state.
Definition: ipv6_misc.c:92
void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr, uint32_t validLifetime, uint32_t preferredLifetime)
Add an IPv6 address to the IA.
void dhcpv6ClientParseReply(Dhcpv6ClientContext *context, const Dhcpv6Message *message, size_t length)
Parse Reply message.
int32_t dhcpv6Rand(int32_t value)
Multiplication by a randomization factor.
__start_packed struct @135 Dhcpv6IaNaOption
Identity Association for Non-temporary Addresses option.
void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context)
REQUEST state.
#define DHCPV6_CLIENT_PORT
Definition: dhcpv6_common.h:38
error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context, const Dhcpv6Option *option)
Parse IA Address option.
#define HTONS(value)
Definition: cpu_endian.h:388
#define ENABLED
Definition: os_port.h:35
#define TRUE
Definition: os_port.h:48
void dhcpv6ClientChangeState(Dhcpv6ClientContext *context, Dhcpv6State newState, systime_t delay)
Update DHCPv6 FSM state.
const Ipv6Addr DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR
Definition: dhcpv6_common.c:49
uint32_t preferredLifetime
Preferred lifetime.
Dhcpv6Option * dhcpv6AddSubOption(Dhcpv6Option *baseOption, size_t *messageLen, uint16_t optionCode, const void *optionValue, size_t optionLen)
Add a suboption under an existing base option.
uint32_t validLifetime
Valid lifetime.
IA address entry.
#define DHCPV6_CLIENT_CNF_TIMEOUT
DHCPv6 client (Dynamic Host Configuration Protocol for IPv6)
Definitions common to DHCPv6 client, server and relay agent.
void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context)
RELEASE state.
#define ntohl(value)
Definition: cpu_endian.h:397
void ipv6RemoveAddr(NetInterface *interface, const Ipv6Addr *addr)
Remove an entry from the list of IPv6 addresses.
Definition: ipv6_misc.c:331
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:75
#define ntohs(value)
Definition: cpu_endian.h:396
An address that is not assigned to any interface.
Definition: ipv6.h:169
uint32_t netGetRand(void)
Get a random value.
Definition: net.c:1523
error_t dhcpv6ClientInit(Dhcpv6ClientContext *context, const Dhcpv6ClientSettings *settings)
DHCPv6 client initialization.
signed int int_t
Definition: compiler_port.h:42
#define htonl(value)
Definition: cpu_endian.h:391
void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context)
Flush the list of IPv6 addresses from the IA.
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:411
Data logging functions for debugging purpose (DHCPv6)
Date and time management.
error_t dhcpv6ClientRelease(Dhcpv6ClientContext *context)
Release DHCPv6 lease.
#define LOAD24BE(p)
Definition: cpu_endian.h:179
#define DHCPV6_CLIENT_SOL_MAX_RT
Definition: dhcpv6_client.h:80
error_t dhcpv6ClientGenerateFqdn(Dhcpv6ClientContext *context)
Generate client&#39;s fully qualified domain name.
__start_packed struct @126 UdpHeader
UDP header.
error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context)
Generate client&#39;s DUID.
Ipv6AddrState
IPv6 address state.
Definition: ipv6.h:167
systime_t timeout
DHCPv6 configuration timeout.
#define STORE24BE(a, p)
Definition: cpu_endian.h:255
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:723
error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context)
Generate a link-local address.
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1495
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:86
#define MIN(a, b)
Definition: os_port.h:60
Dhcpv6StateChangeCallback stateChangeEvent
FSM state change event.
bool_t rapidCommit
Quick configuration using rapid commit.
void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context)
CONFIRM state.
Dhcpv6StatusCode dhcpv6GetStatusCode(const uint8_t *options, size_t length)
Retrieve status code.
Definition: dhcpv6_common.c:68
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:171
Helper functions for IPv6.
uint32_t preferredLifetime
void ipv6AddAddr(NetInterface *interface, const Ipv6Addr *addr, uint32_t validLifetime, uint32_t preferredLifetime)
Add a new entry to the list of IPv6 addresses.
Definition: ipv6_misc.c:221
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:656
void dhcpv6ClientTick(Dhcpv6ClientContext *context)
DHCPv6 client timer handler.
#define TRACE_INFO(...)
Definition: debug.h:86
bool_t manualDnsConfig
Force manual DNS configuration.
IPv6 (Internet Protocol Version 6)
Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context)
Retrieve current state.
Success.
Definition: error.h:42
uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context)
Compute the time elapsed since the client sent the first message.
void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context)
RENEW state.
error_t
Error codes.
Definition: error.h:40
void ipv6GenerateLinkLocalAddr(const Eui64 *interfaceId, Ipv6Addr *ipAddr)
Generate a IPv6 link-local address from an interface identifier.
Definition: ipv6_misc.c:1356
__start_packed struct @131 Dhcpv6DuidLl
DUID-LL structure.
void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr)
Remove an IPv6 address from the IA.
#define DHCPV6_INFINITE_TIME
Definition: dhcpv6_common.h:51
Ipv4Addr destIpAddr
Definition: ipcp.h:76
__start_packed struct @140 Dhcpv6ElapsedTimeOption
Elapsed Time option.
bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context, Dhcpv6Option *serverIdOption)
Check the Server Identifier option.
size_t dnsEncodeName(const char_t *src, uint8_t *dest)
Encode a domain name using the DNS name notation.
Definition: dns_common.c:54
unsigned int uint_t
Definition: compiler_port.h:43
#define DHCPV6_MAX_SERVER_PREFERENCE
Definition: dhcpv6_common.h:49
void dhcpv6ClientStateInit(Dhcpv6ClientContext *context)
INIT state.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
#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
error_t dhcpv6DumpMessage(const void *message, size_t length)
Dump DHCPv6 message for debugging purpose.
Definition: dhcpv6_debug.c:121
Dhcpv6State
DHCPv6 client FSM states.
void dhcpv6ClientStateBound(Dhcpv6ClientContext *context)
BOUND state.
IP pseudo header.
Definition: ip.h:76
void dhcpv6ClientProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *param)
Process incoming DHCPv6 message.
#define NDP_INFINITE_LIFETIME
Definition: ndp.h:200
NDP (Neighbor Discovery Protocol)
#define DHCPV6_CLIENT_SOL_TIMEOUT
Definition: dhcpv6_client.h:73
#define IPV6_DNS_SERVER_LIST_SIZE
Definition: ipv6.h:92
uint32_t t1
OsMutex netMutex
Definition: net.c:70
NetInterface * nicGetLogicalInterface(NetInterface *interface)
Retrieve logical interface.
Definition: nic.c:60
IPv6 context.
Definition: ipv6.h:460
#define DHCPV6_MAX_MSG_SIZE
Definition: dhcpv6_common.h:42
An address whose uniqueness on a link is being verified.
Definition: ipv6.h:170
Dhcpv6MessageType
DHCPv6 message types.
Definition: dhcpv6_common.h:86
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2298
__start_packed struct @149 Dhcpv6FqdnOption
Fully Qualified Domain Name option.
#define DHCPV6_CLIENT_REQ_MAX_RT
Definition: dhcpv6_client.h:94
void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context, const Dhcpv6Message *message, size_t length)
Parse Advertise message.
#define DHCPV6_CLIENT_REL_TIMEOUT
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
Dhcpv6Option * dhcpv6AddOption(void *message, size_t *messageLen, uint16_t optionCode, const void *optionValue, size_t optionLen)
Add an option to a DHCPv6 message.
__start_packed struct @139 Dhcpv6PreferenceOption
Preference option.
#define ETH_SUPPORT
Definition: ethernet.h:37
Common DNS routines.
#define FALSE
Definition: os_port.h:44
systime_t dhcpv6ClientTickCounter
Definition: dhcpv6_client.c:57
void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context)
Callback function for link change event.
int bool_t
Definition: compiler_port.h:47
void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context)
INIT-CONFIRM state.
__start_packed struct @132 Dhcpv6Message
DHCPv6 message.
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
error_t dhcpv6ClientStop(Dhcpv6ClientContext *context)
Stop DHCPv6 client.
#define ipv6CompAddr(ipAddr1, ipAddr2)
Definition: ipv6.h:119
void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings)
Initialize settings with default values.
Definition: dhcpv6_client.c:73
Ipv4Addr addr
Definition: nbns_common.h:119
#define IPV6_ADDR_LIST_SIZE
Definition: ipv6.h:64
#define DHCPV6_CLIENT_REB_TIMEOUT
DHCPv6 client settings.
__start_packed struct @137 Dhcpv6IaAddrOption
IA Address option.
void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context)
DECLINE state.
#define TRACE_DEBUG(...)
Definition: debug.h:98
error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context, const Dhcpv6Option *option)
Parse IA_NA option.
#define DHCPV6_CLIENT_SOL_MAX_DELAY
Definition: dhcpv6_client.h:66