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-2020 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.8
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  //The DHCPv6 client must be bound to a valid interface
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  osMemset(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  //Make sure the DHCPv6 client context is valid
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  //Make sure the DHCPv6 client context is valid
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  NetTxAncillary ancillary;
1262 
1263  //Point to the underlying network interface
1264  interface = context->settings.interface;
1265 
1266  //Allocate a memory buffer to hold the DHCPv6 message
1267  buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset);
1268  //Failed to allocate buffer?
1269  if(buffer == NULL)
1270  return ERROR_OUT_OF_MEMORY;
1271 
1272  //Point to the beginning of the DHCPv6 message
1273  message = netBufferAt(buffer, offset);
1274 
1275  //Set DHCPv6 message type
1276  message->msgType = type;
1277 
1278  //The transaction ID is chosen by the client
1279  STORE24BE(context->transactionId, message->transactionId);
1280 
1281  //Size of the DHCPv6 message
1282  length = sizeof(Dhcpv6Message);
1283 
1284  //The client must include a Client Identifier option
1285  //to identify itself to the server
1287  context->clientId, context->clientIdLength);
1288 
1289  //Request, Renew, Release or Decline message?
1290  if(type == DHCPV6_MSG_TYPE_REQUEST ||
1294  {
1295  //The client places the identifier of the destination
1296  //server in a Server Identifier option
1298  context->serverId, context->serverIdLength);
1299  }
1300 
1301  //Solicit message?
1303  {
1304  //Check whether rapid commit is enabled
1305  if(context->settings.rapidCommit)
1306  {
1307  //Include the Rapid Commit option if the client is prepared
1308  //to perform the Solicit/Reply message exchange
1310  }
1311  }
1312 
1313  //Solicit, Request, Confirm, Renew or Rebind message?
1314  if(type == DHCPV6_MSG_TYPE_SOLICIT ||
1319  {
1320  //Point to the client's fully qualified domain name
1321  fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
1322 
1323  //The FQDN option can be used by the client to convey its
1324  //fully qualified domain name to the server
1326  fqdnOption, sizeof(Dhcpv6FqdnOption) + context->clientFqdnLength);
1327 
1328  //The client should include an Option Request option to indicate
1329  //the options the client is interested in receiving
1331  &dhcpv6OptionList, sizeof(dhcpv6OptionList));
1332  }
1333 
1334  //Prepare an IA_NA option for a the current interface
1335  iaNaOption.iaId = htonl(interface->id);
1336 
1337  //Solicit, Request or Confirm message?
1338  if(type == DHCPV6_MSG_TYPE_SOLICIT ||
1341  {
1342  //The client should set the T1 and T2 fields in any IA_NA options to 0
1343  iaNaOption.t1 = 0;
1344  iaNaOption.t2 = 0;
1345  }
1346  else
1347  {
1348  //T1 and T2 are provided as a hint
1349  iaNaOption.t1 = htonl(context->ia.t1);
1350  iaNaOption.t2 = htonl(context->ia.t2);
1351  }
1352 
1353  //The client includes IA options for any IAs to which
1354  //it wants the server to assign addresses
1356  &iaNaOption, sizeof(Dhcpv6IaNaOption));
1357 
1358  //Request, Confirm, Renew, Rebind, Release or Decline message?
1359  if(type == DHCPV6_MSG_TYPE_REQUEST ||
1365  {
1366  //Loop through the IPv6 addresses recorded by the client
1367  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1368  {
1369  //Point to the current entry
1370  entry = &context->ia.addrList[i];
1371 
1372  //Valid IPv6 address?
1373  if(entry->validLifetime > 0)
1374  {
1375  //Prepare an IA Address option
1376  iaAddrOption.address = entry->addr;
1377 
1378  //Confirm message?
1380  {
1381  //The client should set the preferred and valid lifetimes to 0
1382  iaAddrOption.preferredLifetime = 0;
1383  iaAddrOption.validLifetime = 0;
1384  }
1385  else
1386  {
1387  //Preferred and valid lifetimes are provided as a hint
1388  iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime);
1389  iaAddrOption.validLifetime = htonl(entry->validLifetime);
1390  }
1391 
1392  //Add the IA Address option
1394  &iaAddrOption, sizeof(iaAddrOption));
1395  }
1396  }
1397  }
1398 
1399  //Compute the time elapsed since the client sent the first message
1400  elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context);
1401 
1402  //The client must include an Elapsed Time option in messages to indicate
1403  //how long the client has been trying to complete a DHCP message exchange
1405  &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption));
1406 
1407  //Adjust the length of the multi-part buffer
1408  netBufferSetLength(buffer, offset + length);
1409 
1410  //Destination address
1411  destIpAddr.length = sizeof(Ipv6Addr);
1413 
1414  //Debug message
1415  TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n",
1417 
1418  //Dump the contents of the message for debugging purpose
1420 
1421  //Additional options can be passed to the stack along with the packet
1422  ancillary = NET_DEFAULT_TX_ANCILLARY;
1423 
1424  //Send DHCPv6 message
1425  error = udpSendBuffer(interface, NULL, DHCPV6_CLIENT_PORT, &destIpAddr,
1426  DHCPV6_SERVER_PORT, buffer, offset, &ancillary);
1427 
1428  //Free previously allocated memory
1429  netBufferFree(buffer);
1430  //Return status code
1431  return error;
1432 }
1433 
1434 
1435 /**
1436  * @brief Process incoming DHCPv6 message
1437  * @param[in] interface Underlying network interface
1438  * @param[in] pseudoHeader UDP pseudo header
1439  * @param[in] udpHeader UDP header
1440  * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message
1441  * @param[in] offset Offset to the first byte of the DHCPv6 message
1442  * @param[in] ancillary Additional options passed to the stack along with
1443  * the packet
1444  * @param[in] param Pointer to the DHCPv6 client context
1445  **/
1446 
1448  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
1449  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
1450  void *param)
1451 {
1452  size_t length;
1453  Dhcpv6ClientContext *context;
1455 
1456  //Point to the DHCPv6 client context
1457  context = (Dhcpv6ClientContext *) param;
1458 
1459  //Retrieve the length of the DHCPv6 message
1460  length = netBufferGetLength(buffer) - offset;
1461 
1462  //Make sure the DHCPv6 message is valid
1463  if(length < sizeof(Dhcpv6Message))
1464  return;
1465 
1466  //Point to the beginning of the DHCPv6 message
1467  message = netBufferAt(buffer, offset);
1468  //Sanity check
1469  if(message == NULL)
1470  return;
1471 
1472  //Debug message
1473  TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n",
1475 
1476  //Dump the contents of the message for debugging purpose
1478 
1479  //Check message type
1480  switch(message->msgType)
1481  {
1483  //Parse Advertise message
1485  break;
1486  case DHCPV6_MSG_TYPE_REPLY:
1487  //Parse Reply message
1489  break;
1490  default:
1491  //Silently drop incoming message
1492  break;
1493  }
1494 }
1495 
1496 
1497 /**
1498  * @brief Parse Advertise message
1499  * @param[in] context Pointer to the DHCPv6 client context
1500  * @param[in] message Pointer to the incoming message to parse
1501  * @param[in] length Length of the incoming message
1502  **/
1503 
1505  const Dhcpv6Message *message, size_t length)
1506 {
1507  uint_t i;
1508  int_t serverPreference;
1509  NetInterface *interface;
1510  Dhcpv6StatusCode status;
1511  Dhcpv6Option *option;
1512  Dhcpv6Option *serverIdOption;
1513  Dhcpv6IaNaOption *iaNaOption;
1514 
1515  //Point to the underlying network interface
1516  interface = context->settings.interface;
1517 
1518  //Make sure that the Advertise message is received in response to
1519  //a Solicit message
1520  if(context->state != DHCPV6_STATE_SOLICIT)
1521  return;
1522 
1523  //Discard any received packet that does not match the transaction ID
1524  if(LOAD24BE(message->transactionId) != context->transactionId)
1525  return;
1526 
1527  //Get the length of the Options field
1528  length -= sizeof(Dhcpv6Message);
1529 
1530  //Search for the Client Identifier option
1532 
1533  //Discard any received packet that does not include a Client Identifier option
1534  if(option == NULL)
1535  return;
1536  //Check the length of the option
1537  if(ntohs(option->length) != context->clientIdLength)
1538  return;
1539  //Check whether the Client Identifier matches our identifier
1540  if(osMemcmp(option->value, context->clientId, context->clientIdLength))
1541  return;
1542 
1543  //Search for the Server Identifier option
1544  serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
1545 
1546  //Discard any received packet that does not include a Server Identifier option
1547  if(serverIdOption == NULL)
1548  return;
1549  //Check the length of the server DUID
1550  if(ntohs(serverIdOption->length) == 0)
1551  return;
1552  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
1553  return;
1554 
1555  //Get the status code returned by the server
1556  status = dhcpv6GetStatusCode(message->options, length);
1557 
1558  //If the message contains a Status Code option indicating a failure,
1559  //then the Advertise message is discarded by the client
1560  if(status != DHCPV6_STATUS_SUCCESS)
1561  return;
1562 
1563  //Search for the Preference option
1565 
1566  //Check whether the option has been found
1567  if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption))
1568  {
1569  //Server server preference value
1570  serverPreference = option->value[0];
1571  }
1572  else
1573  {
1574  //Any Advertise that does not include a Preference option
1575  //is considered to have a preference value of 0
1576  serverPreference = 0;
1577  }
1578 
1579  //Select the Advertise message that offers the highest server preference value
1580  if(serverPreference > context->serverPreference)
1581  {
1582  //Save the length of the DUID
1583  context->serverIdLength = ntohs(serverIdOption->length);
1584  //Record the server DUID
1585  osMemcpy(context->serverId, serverIdOption->value, context->serverIdLength);
1586 
1587  //Flush the list of IPv6 addresses from the client's IA
1588  dhcpv6ClientFlushAddrList(context);
1589  }
1590 
1591  //Point to the first option
1592  i = 0;
1593 
1594  //Loop through DHCPv6 options
1595  while(i < length)
1596  {
1597  //Search for an IA_NA option
1598  option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
1599 
1600  //Unable to find the specified option?
1601  if(option == NULL)
1602  break;
1603 
1604  //Make sure the IA_NA option is valid
1605  if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption))
1606  {
1607  //Get the parameters associated with the IA_NA
1608  iaNaOption = (Dhcpv6IaNaOption *) option->value;
1609 
1610  //Check the IA identifier
1611  if(ntohl(iaNaOption->iaId) == interface->id)
1612  {
1613  //The client examines the status code in each IA individually
1614  status = dhcpv6GetStatusCode(iaNaOption->options,
1615  ntohs(option->length) - sizeof(Dhcpv6IaNaOption));
1616 
1617  //The client must ignore any Advertise message that includes a Status
1618  //Code option containing the value NoAddrsAvail
1619  if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
1620  return;
1621  }
1622 
1623  //Check the server preference value
1624  if(serverPreference > context->serverPreference)
1625  {
1626  //Parse the contents of the IA_NA option
1627  dhcpv6ClientParseIaNaOption(context, option);
1628  }
1629  }
1630 
1631  //Jump to the next option
1632  i += sizeof(Dhcpv6Option) + ntohs(option->length);
1633  }
1634 
1635  //Record the highest server preference value
1636  if(serverPreference > context->serverPreference)
1637  context->serverPreference = serverPreference;
1638 
1639  //If the client receives an Advertise message that includes a
1640  //Preference option with a preference value of 255, the client
1641  //immediately completes the message exchange
1642  if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE)
1643  {
1644  //Continue configuration procedure
1646  }
1647  //The message exchange is not terminated before the first RT has elapsed
1648  else if(context->retransmitCount > 1)
1649  {
1650  //Continue configuration procedure
1652  }
1653 }
1654 
1655 
1656 /**
1657  * @brief Parse Reply message
1658  * @param[in] context Pointer to the DHCPv6 client context
1659  * @param[in] message Pointer to the incoming message to parse
1660  * @param[in] length Length of the incoming message
1661  **/
1662 
1664  const Dhcpv6Message *message, size_t length)
1665 {
1666  error_t error;
1667  uint_t i;
1668  uint_t k;
1669  uint_t n;
1670  bool_t iaNaOptionFound;
1671  systime_t minPreferredLifetime;
1672  NetInterface *interface;
1673  Dhcpv6StatusCode status;
1674  Dhcpv6Option *option;
1675  Dhcpv6Option *serverIdOption;
1676  Dhcpv6ClientAddrEntry *entry;
1677 
1678  //Point to the underlying network interface
1679  interface = context->settings.interface;
1680 
1681  //Discard any received packet that does not match the transaction ID
1682  if(LOAD24BE(message->transactionId) != context->transactionId)
1683  return;
1684 
1685  //Get the length of the Options field
1686  length -= sizeof(Dhcpv6Message);
1687 
1688  //Search for the Client Identifier option
1690 
1691  //Discard any received packet that does not include a Client Identifier option
1692  if(option == NULL)
1693  return;
1694  //Check the length of the option
1695  if(ntohs(option->length) != context->clientIdLength)
1696  return;
1697  //Check whether the Client Identifier matches our identifier
1698  if(osMemcmp(option->value, context->clientId, context->clientIdLength))
1699  return;
1700 
1701  //Search for the Server Identifier option
1702  serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
1703 
1704  //Discard any received packet that does not include a Server Identifier option
1705  if(serverIdOption == NULL)
1706  return;
1707  //Check the length of the server DUID
1708  if(ntohs(serverIdOption->length) == 0)
1709  return;
1710  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
1711  return;
1712 
1713  //Get the status code returned by the server
1714  status = dhcpv6GetStatusCode(message->options, length);
1715 
1716  //Check current state
1717  if(context->state == DHCPV6_STATE_SOLICIT)
1718  {
1719  //A Reply message is not acceptable when rapid commit is disallowed
1720  if(!context->settings.rapidCommit)
1721  return;
1722 
1723  //Search for the Rapid Commit option
1725 
1726  //The client discards any message that does not include a Rapid Commit option
1727  if(option == NULL || ntohs(option->length) != 0)
1728  return;
1729  }
1730  else if(context->state == DHCPV6_STATE_REQUEST)
1731  {
1732  //The client must discard the Reply message if the contents of the
1733  //Server Identifier option do not match the server's DUID
1734  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1735  return;
1736  }
1737  else if(context->state == DHCPV6_STATE_CONFIRM)
1738  {
1739  //When the client receives a NotOnLink status from the server in response
1740  //to a Confirm message, the client performs DHCP server solicitation
1741  if(status == DHCPV6_STATUS_NOT_ON_LINK)
1742  {
1743  //Restart the DHCP server discovery process
1745 
1746  //Exit immediately
1747  return;
1748  }
1749  }
1750  else if(context->state == DHCPV6_STATE_RENEW)
1751  {
1752  //The client must discard the Reply message if the contents of the
1753  //Server Identifier option do not match the server's DUID
1754  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1755  return;
1756  }
1757  else if(context->state == DHCPV6_STATE_REBIND)
1758  {
1759  //Do not check the server's DUID when the Reply message is
1760  //received in response to a Rebind message
1761  }
1762  else if(context->state == DHCPV6_STATE_RELEASE)
1763  {
1764  //The client must discard the Reply message if the contents of the
1765  //Server Identifier option do not match the server's DUID
1766  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1767  return;
1768 
1769  //When the client receives a valid Reply message in response to a
1770  //Release message, the client considers the Release event completed,
1771  //regardless of the Status Code option(s) returned by the server
1772  context->running = FALSE;
1773 
1774  //Reinitialize state machine
1776 
1777  //Exit immediately
1778  return;
1779  }
1780  else if(context->state == DHCPV6_STATE_DECLINE)
1781  {
1782  //The client must discard the Reply message if the contents of the
1783  //Server Identifier option do not match the server's DUID
1784  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
1785  return;
1786 
1787  //When the client receives a valid Reply message in response to a
1788  //Decline message, the client considers the Decline event completed,
1789  //regardless of the Status Code option returned by the server
1791 
1792  //Exit immediately
1793  return;
1794  }
1795  else
1796  {
1797  //Silently discard the Reply message
1798  return;
1799  }
1800 
1801  //Check status code
1802  if(status == DHCPV6_STATUS_USE_MULTICAST)
1803  {
1804  //When the client receives a Reply message with a Status Code option
1805  //with the value UseMulticast, the client records the receipt of the
1806  //message and sends subsequent messages to the server through the
1807  //interface on which the message was received using multicast
1808  return;
1809  }
1810  else if(status == DHCPV6_STATUS_UNSPEC_FAILURE)
1811  {
1812  //If the client receives a Reply message with a Status Code containing
1813  //UnspecFail, the server is indicating that it was unable to process
1814  //the message due to an unspecified failure condition
1815  return;
1816  }
1817 
1818  //This flag will be set if a valid IA_NA option is found
1819  iaNaOptionFound = FALSE;
1820  //Point to the first option
1821  i = 0;
1822 
1823  //Loop through DHCPv6 options
1824  while(i < length)
1825  {
1826  //Search for an IA_NA option
1827  option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
1828 
1829  //Unable to find the specified option?
1830  if(option == NULL)
1831  break;
1832 
1833  //Parse the contents of the IA_NA option
1834  error = dhcpv6ClientParseIaNaOption(context, option);
1835 
1836  //Check error code
1837  if(error == NO_ERROR)
1838  {
1839  //A valid IA_NA option has been found
1840  iaNaOptionFound = TRUE;
1841  }
1842  else if(error == ERROR_NOT_ON_LINK)
1843  {
1844  //When the client receives a NotOnLink status from the server
1845  //in response to a Request, the client can either re-issue the
1846  //Request without specifying any addresses or restart the DHCP
1847  //server discovery process
1849 
1850  //Exit immediately
1851  return;
1852  }
1853  else if(error == ERROR_NO_BINDING)
1854  {
1855  //When the client receives a Reply message in response to a Renew
1856  //or Rebind message, the client sends a Request message if any of
1857  //the IAs in the Reply message contains the NoBinding status code
1859 
1860  //Exit immediately
1861  return;
1862  }
1863  else
1864  {
1865  //If an invalid option is received, the client discards
1866  //the option and process the rest of the message...
1867  }
1868 
1869  //Jump to the next option
1870  i += sizeof(Dhcpv6Option) + ntohs(option->length);
1871  }
1872 
1873  //No usable addresses in any of the IAs?
1874  if(!iaNaOptionFound)
1875  {
1876  //Check whether the client receives a Reply message in response
1877  //to a Renew or Rebind message
1878  if(context->state == DHCPV6_STATE_RENEW ||
1879  context->state == DHCPV6_STATE_REBIND)
1880  {
1881  //The client sends a Renew/Rebind if the IA is not in the Reply message
1882  }
1883  else
1884  {
1885  //If the client finds no usable addresses in any of the IAs, it may try
1886  //another server (perhaps restarting the DHCP server discovery process)
1888  }
1889 
1890  //Exit immediately
1891  return;
1892  }
1893 
1894  //Total number of valid IPv6 in the IA
1895  n = 0;
1896  //Number of new IPv6 addresses in the IA
1897  k = 0;
1898  //Minimum preferred lifetime observed in the IA
1899  minPreferredLifetime = 0;
1900 
1901  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1902  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1903  {
1904  //Point to the current entry
1905  entry = &context->ia.addrList[i];
1906 
1907  //Valid IPv6 address?
1908  if(entry->validLifetime > 0)
1909  {
1910  //Total number of valid IPv6 in the IA
1911  n++;
1912 
1913  //Save the minimum preferred lifetime that has been observed so far
1914  if(minPreferredLifetime < entry->preferredLifetime)
1915  minPreferredLifetime = entry->preferredLifetime;
1916 
1917  //Update lifetimes of the current IPv6 address
1918  ipv6AddAddr(interface, &entry->addr, entry->validLifetime,
1919  entry->preferredLifetime);
1920 
1921  //New IPv6 address added?
1922  if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE)
1923  k++;
1924  }
1925  }
1926 
1927  //Make sure that the IA contains at least one IPv6 address
1928  if(n > 0)
1929  {
1930  //Save the length of the DUID
1931  context->serverIdLength = ntohs(serverIdOption->length);
1932  //Record the server DUID
1933  osMemcpy(context->serverId, serverIdOption->value, context->serverIdLength);
1934 
1935  //Save the time a which the lease was obtained
1936  context->leaseStartTime = osGetSystemTime();
1937 
1938  //Check the value of T1
1939  if(context->ia.t1 == 0)
1940  {
1941  //If T1 is set to 0 by the server, the client may send
1942  //a Renew message at the client's discretion
1943  if(minPreferredLifetime == DHCPV6_INFINITE_TIME)
1944  context->ia.t1 = DHCPV6_INFINITE_TIME;
1945  else
1946  context->ia.t1 = minPreferredLifetime / 2;
1947  }
1948 
1949  //Check the value of T2
1950  if(context->ia.t2 == 0)
1951  {
1952  //If T2 is set to 0 by the server, the client may send
1953  //a Rebind message at the client's discretion
1954  if(context->ia.t1 == DHCPV6_INFINITE_TIME)
1955  context->ia.t2 = DHCPV6_INFINITE_TIME;
1956  else
1957  context->ia.t2 = context->ia.t1 + context->ia.t1 / 2;
1958  }
1959 
1960  //Any addresses added in the IA?
1961  if(k > 0)
1962  {
1963  //Perform Duplicate Address Detection for the new IPv6 addresses
1965  }
1966  else
1967  {
1968  //Switch to the BOUND state
1970  }
1971  }
1972  else
1973  {
1974  //If the client finds no usable addresses in any of the IAs, it may try
1975  //another server (perhaps restarting the DHCP server discovery process)
1977  }
1978 }
1979 
1980 
1981 /**
1982  * @brief Parse IA_NA option
1983  * @param[in] context Pointer to the DHCPv6 client context
1984  * @param[in] option Pointer to the IA_NA option to parse
1985  * @return Error code
1986  **/
1987 
1989  const Dhcpv6Option *option)
1990 {
1991  error_t error;
1992  uint_t n;
1993  size_t i;
1994  size_t length;
1995  NetInterface *interface;
1996  Dhcpv6StatusCode status;
1997  Dhcpv6IaNaOption *iaNaOption;
1998 
1999  //Point to the underlying network interface
2000  interface = context->settings.interface;
2001 
2002  //Number of addresses found in the IA_NA option
2003  n = 0;
2004 
2005  //Make sure the IA_NA option is valid
2006  if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
2007  return ERROR_INVALID_LENGTH;
2008 
2009  //Get the parameters associated with the IA_NA
2010  iaNaOption = (Dhcpv6IaNaOption *) option->value;
2011  //Compute the length of IA_NA Options field
2012  length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption);
2013 
2014  //Check the IA identifier
2015  if(ntohl(iaNaOption->iaId) != interface->id)
2016  return ERROR_WRONG_IDENTIFIER;
2017 
2018  //If a client receives an IA_NA with T1 greater than T2, and both T1
2019  //and T2 are greater than 0, the client discards the IA_NA option and
2020  //processes the remainder of the message as though the server had not
2021  //included the invalid IA_NA option
2022  if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0)
2023  return ERROR_INVALID_PARAMETER;
2024 
2025  //The client examines the status code in each IA individually
2026  status = dhcpv6GetStatusCode(iaNaOption->options, length);
2027 
2028  //Check error code
2029  if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
2030  {
2031  //The client has received no usable address in the IA
2032  return ERROR_NO_ADDRESS;
2033  }
2034  else if(status == DHCPV6_STATUS_NO_BINDING)
2035  {
2036  //Client record (binding) unavailable
2037  return ERROR_NO_BINDING;
2038  }
2039  else if(status == DHCPV6_STATUS_NOT_ON_LINK)
2040  {
2041  //The prefix for the address is not appropriate for the link to which the
2042  //client is attached
2043  return ERROR_NOT_ON_LINK;
2044  }
2045  else if(status != DHCPV6_STATUS_SUCCESS)
2046  {
2047  //Failure, reason unspecified
2048  return ERROR_FAILURE;
2049  }
2050 
2051  //Record T1 and T2 times
2052  context->ia.t1 = ntohl(iaNaOption->t1);
2053  context->ia.t2 = ntohl(iaNaOption->t2);
2054 
2055  //Point to the first option
2056  i = 0;
2057 
2058  //Loop through IA_NA options
2059  while(i < length)
2060  {
2061  //Search for an IA Address option
2062  option = dhcpv6GetOption(iaNaOption->options + i, length - i, DHCPV6_OPTION_IAADDR);
2063 
2064  //Unable to find the specified option?
2065  if(option == NULL)
2066  break;
2067 
2068  //Parse the contents of the IA Address option
2069  error = dhcpv6ClientParseIaAddrOption(context, option);
2070 
2071  //Check status code
2072  if(!error)
2073  {
2074  //Increment the number of addresses found in the IA_NA option
2075  n++;
2076  }
2077 
2078  //Jump to the next option
2079  i += sizeof(Dhcpv6Option) + ntohs(option->length);
2080  }
2081 
2082  //No usable addresses in the IA_NA option?
2083  if(n == 0)
2084  {
2085  //Report an error
2086  return ERROR_NO_ADDRESS;
2087  }
2088 
2089  //Successful processing
2090  return NO_ERROR;
2091 }
2092 
2093 
2094 /**
2095  * @brief Parse IA Address option
2096  * @param[in] context Pointer to the DHCPv6 client context
2097  * @param[in] option Pointer to the IA Address option to parse
2098  * @return Error code
2099  **/
2100 
2102  const Dhcpv6Option *option)
2103 {
2104  size_t length;
2105  uint32_t validLifetime;
2106  uint32_t preferredLifetime;
2107  Dhcpv6StatusCode status;
2108  Dhcpv6IaAddrOption *iaAddrOption;
2109 
2110  //Make sure the IA Address option is valid
2111  if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
2112  return ERROR_INVALID_LENGTH;
2113 
2114  //Point to the contents of the IA Address option
2115  iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
2116  //Compute the length of IA Address Options field
2117  length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption);
2118 
2119  //Convert lifetimes to host byte order
2120  validLifetime = ntohl(iaAddrOption->validLifetime);
2121  preferredLifetime = ntohl(iaAddrOption->preferredLifetime);
2122 
2123  //A client discards any addresses for which the preferred lifetime
2124  //is greater than the valid lifetime
2126  return ERROR_INVALID_PARAMETER;
2127 
2128  //The client examines the status code in each IA Address
2129  status = dhcpv6GetStatusCode(iaAddrOption->options, length);
2130 
2131  //Any error to report?
2132  if(status != DHCPV6_STATUS_SUCCESS)
2133  return ERROR_FAILURE;
2134 
2135  //Check the value of the Valid Lifetime
2136  if(iaAddrOption->validLifetime > 0)
2137  {
2138  //Add any new addresses in the IA option to the IA as recorded
2139  //by the client
2140  dhcpv6ClientAddAddr(context, &iaAddrOption->address,
2142  }
2143  else
2144  {
2145  //Discard any addresses from the IA, as recorded by the client,
2146  //that have a valid lifetime of 0 in the IA Address option
2147  dhcpv6ClientRemoveAddr(context, &iaAddrOption->address);
2148  }
2149 
2150  //Successful processing
2151  return NO_ERROR;
2152 }
2153 
2154 
2155 /**
2156  * @brief Add an IPv6 address to the IA
2157  * @param[in] context Pointer to the DHCPv6 client context
2158  * @param[in] addr IPv6 address to be added
2159  * @param[in] validLifetime Valid lifetime, in seconds
2160  * @param[in] preferredLifetime Preferred lifetime, in seconds
2161  **/
2162 
2164  uint32_t validLifetime, uint32_t preferredLifetime)
2165 {
2166  uint_t i;
2167  Dhcpv6ClientAddrEntry *entry;
2168  Dhcpv6ClientAddrEntry *firstFreeEntry;
2169 
2170  //Keep track of the first free entry
2171  firstFreeEntry = NULL;
2172 
2173  //Loop through the IPv6 addresses recorded by the DHCPv6 client
2174  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
2175  {
2176  //Point to the current entry
2177  entry = &context->ia.addrList[i];
2178 
2179  //Valid IPv6 address?
2180  if(entry->validLifetime > 0)
2181  {
2182  //Check whether the current entry matches the specified address
2183  if(ipv6CompAddr(&entry->addr, addr))
2184  break;
2185  }
2186  else
2187  {
2188  //Keep track of the first free entry
2189  if(firstFreeEntry == NULL)
2190  firstFreeEntry = entry;
2191  }
2192  }
2193 
2194  //No matching entry found?
2195  if(i >= IPV6_PREFIX_LIST_SIZE)
2196  entry = firstFreeEntry;
2197 
2198  //Update the entry if necessary
2199  if(entry != NULL)
2200  {
2201  //Save IPv6 address
2202  entry->addr = *addr;
2203 
2204  //Save lifetimes
2205  entry->validLifetime = validLifetime;
2207  }
2208 }
2209 
2210 
2211 /**
2212  * @brief Remove an IPv6 address from the IA
2213  * @param[in] context Pointer to the DHCPv6 client context
2214  * @param[in] addr IPv6 address to be removed
2215  **/
2216 
2218 {
2219  uint_t i;
2220  NetInterface *interface;
2221  Dhcpv6ClientAddrEntry *entry;
2222 
2223  //Point to the underlying network interface
2224  interface = context->settings.interface;
2225 
2226  //Loop through the IPv6 addresses recorded by the DHCPv6 client
2227  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
2228  {
2229  //Point to the current entry
2230  entry = &context->ia.addrList[i];
2231 
2232  //Valid IPv6 address?
2233  if(entry->validLifetime > 0)
2234  {
2235  //Check whether the current entry matches the specified address
2236  if(ipv6CompAddr(&entry->addr, addr))
2237  {
2238  //The IPv6 address is no more valid and should be removed from
2239  //the list of IPv6 addresses assigned to the interface
2240  ipv6RemoveAddr(interface, addr);
2241 
2242  //Remove the IPv6 address from the IA
2243  entry->validLifetime = 0;
2244  }
2245  }
2246  }
2247 }
2248 
2249 
2250 /**
2251  * @brief Flush the list of IPv6 addresses from the IA
2252  * @param[in] context Pointer to the DHCPv6 client context
2253  **/
2254 
2256 {
2257  uint_t i;
2258  NetInterface *interface;
2259  Dhcpv6ClientAddrEntry *entry;
2260 
2261  //Point to the underlying network interface
2262  interface = context->settings.interface;
2263 
2264  //Loop through the IPv6 addresses recorded by the DHCPv6 client
2265  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
2266  {
2267  //Point to the current entry
2268  entry = &context->ia.addrList[i];
2269 
2270  //Valid IPv6 address?
2271  if(entry->validLifetime > 0)
2272  {
2273  //The IPv6 address is no more valid and should be removed from
2274  //the list of IPv6 addresses assigned to the interface
2275  ipv6RemoveAddr(interface, &entry->addr);
2276 
2277  //Remove the IPv6 address from the IA
2278  entry->validLifetime = 0;
2279  }
2280  }
2281 }
2282 
2283 
2284 /**
2285  * @brief Generate client's DUID
2286  * @param[in] context Pointer to the DHCPv6 client context
2287  * @return Error code
2288  **/
2289 
2291 {
2292  NetInterface *interface;
2293  Dhcpv6DuidLl *duid;
2294 #if (ETH_SUPPORT == ENABLED)
2295  NetInterface *logicalInterface;
2296 #endif
2297 
2298  //Point to the underlying network interface
2299  interface = context->settings.interface;
2300 
2301  //Point to the buffer where to format the client's DUID
2302  duid = (Dhcpv6DuidLl *) context->clientId;
2303 
2304 #if (ETH_SUPPORT == ENABLED)
2305  //Point to the logical interface
2306  logicalInterface = nicGetLogicalInterface(interface);
2307 
2308  //Generate a DUID-LL from the MAC address
2309  duid->type = HTONS(DHCPV6_DUID_LL);
2310  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH);
2311  duid->linkLayerAddr = logicalInterface->macAddr;
2312 #else
2313  //Generate a DUID-LL from the EUI-64 identifier
2314  duid->type = HTONS(DHCPV6_DUID_LL);
2315  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64);
2316  duid->linkLayerAddr = interface->eui64;
2317 #endif
2318 
2319  //Length of the newly generated DUID
2320  context->clientIdLength = sizeof(Dhcpv6DuidLl);
2321 
2322  //Successful processing
2323  return NO_ERROR;
2324 }
2325 
2326 
2327 /**
2328  * @brief Generate client's fully qualified domain name
2329  * @param[in] context Pointer to the DHCPv6 client context
2330  * @return Error code
2331  **/
2332 
2334 {
2335  NetInterface *interface;
2336  Dhcpv6FqdnOption *fqdnOption;
2337 
2338  //Point to the underlying network interface
2339  interface = context->settings.interface;
2340 
2341  //Point to the buffer where to format the client's FQDN
2342  fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
2343 
2344  //Set flags
2345  fqdnOption->mbz = 0;
2346  fqdnOption->n = FALSE;
2347  fqdnOption->o = FALSE;
2348  fqdnOption->s = FALSE;
2349 
2350  //Encode client's FQDN
2351  context->clientFqdnLength = dnsEncodeName(interface->hostname,
2352  fqdnOption->domainName);
2353 
2354  //Successful processing
2355  return NO_ERROR;
2356 }
2357 
2358 
2359 /**
2360  * @brief Generate a link-local address
2361  * @param[in] context Pointer to the DHCPv6 client context
2362  * @return Error code
2363  **/
2364 
2366 {
2367  error_t error;
2368  NetInterface *interface;
2369  Ipv6Addr addr;
2370 
2371  //Point to the underlying network interface
2372  interface = context->settings.interface;
2373 
2374  //Check whether a link-local address has been manually assigned
2375  if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
2376  interface->ipv6Context.addrList[0].permanent)
2377  {
2378  //Keep using the current link-local address
2379  error = NO_ERROR;
2380  }
2381  else
2382  {
2383  //A link-local address is formed by combining the well-known
2384  //link-local prefix fe80::/10 with the interface identifier
2385  ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
2386 
2387 #if (NDP_SUPPORT == ENABLED)
2388  //Check whether Duplicate Address Detection should be performed
2389  if(interface->ndpContext.dupAddrDetectTransmits > 0)
2390  {
2391  //Use the link-local address as a tentative address
2392  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
2394  }
2395  else
2396 #endif
2397  {
2398  //The use of the link-local address is now unrestricted
2399  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
2401  }
2402  }
2403 
2404  //Return status code
2405  return error;
2406 }
2407 
2408 
2409 /**
2410  * @brief Check the Server Identifier option
2411  * @param[in] context Pointer to the DHCPv6 client context
2412  * @param[in] serverIdOption Pointer to the Server Identifier option
2413  * @return TRUE if the option matches the server's DUID, else FALSE
2414  **/
2415 
2417  Dhcpv6Option *serverIdOption)
2418 {
2419  bool_t valid = FALSE;
2420 
2421  //Check the length of the Server Identifier option
2422  if(ntohs(serverIdOption->length) == context->serverIdLength)
2423  {
2424  //Check whether the Server Identifier option matches the server's DUID
2425  if(!osMemcmp(serverIdOption->value, context->serverId, context->serverIdLength))
2426  valid = TRUE;
2427  }
2428 
2429  //Return TRUE if the option matches the server's DUID
2430  return valid;
2431 }
2432 
2433 
2434 /**
2435  * @brief Manage DHCPv6 configuration timeout
2436  * @param[in] context Pointer to the DHCPv6 client context
2437  **/
2438 
2440 {
2441  systime_t time;
2442  NetInterface *interface;
2443 
2444  //Point to the underlying network interface
2445  interface = context->settings.interface;
2446 
2447  //Get current time
2448  time = osGetSystemTime();
2449 
2450  //Any registered callback?
2451  if(context->settings.timeoutEvent != NULL)
2452  {
2453  //DHCPv6 configuration timeout?
2454  if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
2455  {
2456  //Ensure the callback function is only called once
2457  if(!context->timeoutEventDone)
2458  {
2459  //Release exclusive access
2461  //Invoke user callback function
2462  context->settings.timeoutEvent(context, interface);
2463  //Get exclusive access
2465 
2466  //Set flag
2467  context->timeoutEventDone = TRUE;
2468  }
2469  }
2470  }
2471 }
2472 
2473 
2474 /**
2475  * @brief Compute the time elapsed since the client sent the first message
2476  * @param[in] context Pointer to the DHCPv6 client context
2477  * @return The elapsed time expressed in hundredths of a second
2478  **/
2479 
2481 {
2482  systime_t time;
2483 
2484  //Check retransmission counter
2485  if(context->retransmitCount == 0)
2486  {
2487  //The elapsed time must be 0 for the first message
2488  time = 0;
2489  }
2490  else
2491  {
2492  //Compute the time elapsed since the client sent the
2493  //first message (in hundredths of a second)
2494  time = (osGetSystemTime() - context->exchangeStartTime) / 10;
2495 
2496  //The value 0xFFFF is used to represent any elapsed time values
2497  //greater than the largest time value that can be represented
2498  time = MIN(time, 0xFFFF);
2499  }
2500 
2501  //Convert the 16-bit value to network byte order
2502  return htons(time);
2503 }
2504 
2505 
2506 /**
2507  * @brief Update DHCPv6 FSM state
2508  * @param[in] context Pointer to the DHCPv6 client context
2509  * @param[in] newState New DHCPv6 state to switch to
2510  * @param[in] delay Initial delay
2511  **/
2512 
2514  Dhcpv6State newState, systime_t delay)
2515 {
2516  systime_t time;
2517 
2518  //Get current time
2519  time = osGetSystemTime();
2520 
2521 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
2522  //Sanity check
2523  if(newState <= DHCPV6_STATE_DECLINE)
2524  {
2525  //DHCPv6 FSM states
2526  static const char_t *stateLabel[] =
2527  {
2528  "INIT",
2529  "SOLICIT",
2530  "REQUEST",
2531  "INIT-CONFIRM",
2532  "CONFIRM",
2533  "DAD",
2534  "BOUND",
2535  "RENEW",
2536  "REBIND",
2537  "RELEASE",
2538  "DECLINE"
2539  };
2540 
2541  //Debug message
2542  TRACE_INFO("%s: DHCPv6 client %s state\r\n",
2543  formatSystemTime(time, NULL), stateLabel[newState]);
2544  }
2545 #endif
2546 
2547  //Set time stamp
2548  context->timestamp = time;
2549  //Set initial delay
2550  context->timeout = delay;
2551  //Reset retransmission counter
2552  context->retransmitCount = 0;
2553  //Switch to the new state
2554  context->state = newState;
2555 
2556  //Any registered callback?
2557  if(context->settings.stateChangeEvent != NULL)
2558  {
2559  NetInterface *interface;
2560 
2561  //Point to the underlying network interface
2562  interface = context->settings.interface;
2563 
2564  //Release exclusive access
2566  //Invoke user callback function
2567  context->settings.stateChangeEvent(context, interface, newState);
2568  //Get exclusive access
2570  }
2571 }
2572 
2573 
2574 /**
2575  * @brief Dump DHCPv6 configuration for debugging purpose
2576  * @param[in] context Pointer to the DHCPv6 client context
2577  **/
2578 
2580 {
2581 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
2582  uint_t i;
2583  NetInterface *interface;
2584  Ipv6Context *ipv6Context;
2585 
2586  //Point to the underlying network interface
2587  interface = context->settings.interface;
2588  //Point to the IPv6 context
2589  ipv6Context = &interface->ipv6Context;
2590 
2591  //Debug message
2592  TRACE_INFO("\r\n");
2593  TRACE_INFO("DHCPv6 configuration:\r\n");
2594 
2595  //Lease start time
2596  TRACE_INFO(" Lease Start Time = %s\r\n",
2597  formatSystemTime(context->leaseStartTime, NULL));
2598 
2599  //T1 parameter
2600  TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->ia.t1);
2601  //T2 parameter
2602  TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->ia.t2);
2603 
2604  //Global addresses
2605  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
2606  {
2607  TRACE_INFO(" Global Address %u = %s\r\n", i,
2608  ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
2609  }
2610 
2611  //DNS servers
2612  for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
2613  {
2614  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
2615  ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
2616  }
2617 
2618  //Debug message
2619  TRACE_INFO("\r\n");
2620 #endif
2621 }
2622 
2623 #endif
An address whose uniqueness on a link is being verified.
Definition: ipv6.h:172
uint8_t length
Definition: coap_common.h:190
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2281
#define htons(value)
Definition: cpu_endian.h:413
IPv6 (Internet Protocol Version 6)
Date and time management.
bool_t rapidCommit
Quick configuration using rapid commit.
int bool_t
Definition: compiler_port.h:49
__start_packed struct @31 Dhcpv6IaAddrOption
IA Address option.
void dhcpv6ClientTick(Dhcpv6ClientContext *context)
DHCPv6 client timer handler.
#define DHCPV6_CLIENT_REN_MAX_RT
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:68
signed int int_t
Definition: compiler_port.h:44
#define netMutex
Definition: net_legacy.h:266
void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context)
REBIND state.
Dhcpv6StateChangeCallback stateChangeEvent
FSM state change event.
IP network address.
Definition: ip.h:78
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
#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.
void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr)
Remove an IPv6 address from the IA.
#define DHCPV6_INFINITE_TIME
Definition: dhcpv6_common.h:53
error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context, const Dhcpv6Option *option)
Parse IA_NA option.
#define osMemcmp(p1, p2, length)
Definition: os_port.h:146
const Ipv6Addr DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR
Definition: dhcpv6_common.c:51
#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.
#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
#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
#define timeCompare(t1, t2)
Definition: os_port.h:42
IPv6 context.
Definition: ipv6.h:462
An address that is not assigned to any interface.
Definition: ipv6.h:171
Ipv6Addr addr
IPv6 address.
void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context)
Flush the list of IPv6 addresses from the IA.
IP pseudo header.
Definition: ip.h:97
__start_packed struct @43 Dhcpv6FqdnOption
Fully Qualified Domain Name option.
bool_t manualDnsConfig
Force manual DNS configuration.
__start_packed struct @34 Dhcpv6ElapsedTimeOption
Elapsed Time option.
NetInterface * interface
Network interface to configure.
#define DHCPV6_MAX_DUID_SIZE
Definition: dhcpv6_common.h:46
#define DHCPV6_CLIENT_DEC_TIMEOUT
__start_packed struct @29 Dhcpv6IaNaOption
Identity Association for Non-temporary Addresses option.
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
Invalid parameter.
Definition: error.h:47
#define htonl(value)
Definition: cpu_endian.h:414
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
#define osMemcpy(dest, src, length)
Definition: os_port.h:134
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.
uint32_t validLifetime
Valid lifetime.
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.
void dhcpv6ClientProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary, void *param)
Process incoming DHCPv6 message.
Generic error code.
Definition: error.h:45
error_t dhcpv6ClientStop(Dhcpv6ClientContext *context)
Stop DHCPv6 client.
IA address entry.
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:840
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1373
Dhcpv6TimeoutCallback timeoutEvent
DHCPv6 configuration timeout event.
Helper functions for IPv6.
error_t udpSendBuffer(NetInterface *interface, const IpAddr *srcIpAddr, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send a UDP datagram.
Definition: udp.c:512
Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context)
Retrieve current state.
#define Dhcpv6ClientContext
DHCPv6 client settings.
#define NetTxAncillary
Definition: net_misc.h:36
int32_t dhcpv6Rand(int32_t value)
Multiplication by a randomization factor.
systime_t timeout
DHCPv6 configuration timeout.
error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context, const Dhcpv6Option *option)
Parse IA Address option.
Dhcpv6StatusCode
Status code.
#define IPV6_DNS_SERVER_LIST_SIZE
Definition: ipv6.h:94
uint32_t t2
#define TRACE_INFO(...)
Definition: debug.h:95
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.
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:773
error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context, Dhcpv6MessageType type)
Send Solicit message.
#define ENABLED
Definition: os_port.h:37
#define DHCPV6_CLIENT_CNF_TIMEOUT
uint32_t netGetRand(void)
Get a random value.
Definition: net.c:1401
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:421
__start_packed struct @26 Dhcpv6Message
DHCPv6 message.
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:107
char char_t
Definition: compiler_port.h:43
#define ETH_SUPPORT
Definition: ethernet.h:39
#define DHCPV6_SERVER_PORT
Definition: dhcpv6_common.h:41
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
__start_packed struct @25 Dhcpv6DuidLl
DUID-LL structure.
Dhcpv6MessageType
DHCPv6 message types.
Definition: dhcpv6_common.h:88
#define HTONS(value)
Definition: cpu_endian.h:410
void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context)
SOLICIT state.
uint8_t n
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.
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
void dhcpv6ClientParseReply(Dhcpv6ClientContext *context, const Dhcpv6Message *message, size_t length)
Parse Reply message.
#define LOAD24BE(p)
Definition: cpu_endian.h:197
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
__start_packed struct @77 Ipv6Addr
IPv6 network address.
__start_packed struct @20 UdpHeader
UDP header.
#define STORE24BE(a, p)
Definition: cpu_endian.h:273
void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings)
Initialize settings with default values.
Definition: dhcpv6_client.c:75
#define DHCPV6_CLIENT_REB_TIMEOUT
__start_packed struct @33 Dhcpv6PreferenceOption
Preference option.
__start_packed struct @28 Dhcpv6Option
DHCPv6 option.
void dhcpv6ClientChangeState(Dhcpv6ClientContext *context, Dhcpv6State newState, systime_t delay)
Update DHCPv6 FSM state.
uint32_t preferredLifetime
Preferred lifetime.
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
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.
#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
#define osMemset(p, value, length)
Definition: os_port.h:128
TCP/IP stack core.
NetInterface * nicGetLogicalInterface(NetInterface *interface)
Retrieve logical interface.
Definition: nic.c:52
#define DHCPV6_CLIENT_CNF_MAX_RT
Common DNS routines.
#define ipv6GetLinkLocalAddrState(interface)
Definition: ipv6.h:141
uint32_t systime_t
Definition: compiler_port.h:46
#define ntohl(value)
Definition: cpu_endian.h:422
Success.
Definition: error.h:44
Debugging facilities.
Data logging functions for debugging purpose (DHCPv6)
#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