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