dhcpv6_client_misc.c
Go to the documentation of this file.
1 /**
2  * @file dhcpv6_client_misc.c
3  * @brief Helper functions for DHCPv6 client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL DHCPV6_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "ipv6/ipv6.h"
37 #include "ipv6/ipv6_misc.h"
38 #include "ipv6/ndp.h"
39 #include "dhcpv6/dhcpv6_client.h"
42 #include "dhcpv6/dhcpv6_common.h"
43 #include "dhcpv6/dhcpv6_debug.h"
44 #include "dns/dns_common.h"
45 #include "date_time.h"
46 #include "debug.h"
47 
48 //Check TCP/IP stack configuration
49 #if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
50 
51 //Requested DHCPv6 options
52 static const uint16_t dhcpv6OptionList[] =
53 {
56 };
57 
58 
59 /**
60  * @brief DHCPv6 client timer handler
61  *
62  * This routine must be periodically called by the TCP/IP stack to
63  * manage DHCPv6 client operation
64  *
65  * @param[in] context Pointer to the DHCPv6 client context
66  **/
67 
69 {
70  //Make sure the DHCPv6 client has been properly instantiated
71  if(context != NULL)
72  {
73  //DHCPv6 client finite state machine
74  switch(context->state)
75  {
76  //INIT state?
77  case DHCPV6_STATE_INIT:
78  //This is the initialization state, where a client begins the process
79  //of acquiring a lease. It also returns here when a lease ends, or
80  //when a lease negotiation fails
81  dhcpv6ClientStateInit(context);
82  break;
83 
84  //SOLICIT state?
86  //The client sends a Solicit message to locate servers
87  dhcpv6ClientStateSolicit(context);
88  break;
89 
90  //REQUEST state?
92  //The client sends a Request message to request configuration
93  //parameters, including IP addresses, from a specific server
94  dhcpv6ClientStateRequest(context);
95  break;
96 
97  //INIT-CONFIRM state?
99  //When a client that already has a valid lease starts up after a
100  //power-down or reboot, it starts here instead of the INIT state
102  break;
103 
104  //CONFIRM state?
106  //The client sends a Confirm message to any available server to
107  //determine whether the addresses it was assigned are still
108  //appropriate to the link to which the client is connected
109  dhcpv6ClientStateConfirm(context);
110  break;
111 
112  //DAD state?
113  case DHCPV6_STATE_DAD:
114  //The client should perform duplicate address detection on each of
115  //the addresses in any IAs it receives in the Reply message before
116  //using that address for traffic
117  dhcpv6ClientStateDad(context);
118  break;
119 
120  //BOUND state?
121  case DHCPV6_STATE_BOUND:
122  //The client has a valid lease and is in its normal operating state
123  dhcpv6ClientStateBound(context);
124  break;
125 
126  //RENEW state?
127  case DHCPV6_STATE_RENEW:
128  //The client sends a Renew message to the server that originally
129  //provided the client's addresses and configuration parameters to
130  //extend the lifetimes on the addresses assigned to the client
131  //and to update other configuration parameters
132  dhcpv6ClientStateRenew(context);
133  break;
134 
135  //REBIND state?
136  case DHCPV6_STATE_REBIND:
137  //The client sends a Rebind message to any available server to extend
138  //the lifetimes on the addresses assigned to the client and to update
139  //other configuration parameters. This message is sent after a client
140  //receives no response to a Renew message
141  dhcpv6ClientStateRebind(context);
142  break;
143 
144  //RELEASE state?
146  //To release one or more addresses, a client sends a Release message
147  //to the server
148  dhcpv6ClientStateRelease(context);
149  break;
150 
151  //DECLINE state?
153  //If a client detects that one or more addresses assigned to it by a
154  //server are already in use by another node, the client sends a Decline
155  //message to the server to inform it that the address is suspect
156  dhcpv6ClientStateDecline(context);
157  break;
158 
159  //Invalid state?
160  default:
161  //Switch to the default state
162  context->state = DHCPV6_STATE_INIT;
163  break;
164  }
165  }
166 }
167 
168 
169 /**
170  * @brief Callback function for link change event
171  * @param[in] context Pointer to the DHCPv6 client context
172  **/
173 
175 {
176  NetInterface *interface;
177 
178  //Make sure the DHCPv6 client has been properly instantiated
179  if(context == NULL)
180  return;
181 
182  //Point to the underlying network interface
183  interface = context->interface;
184 
185  //Check whether the DHCPv6 client is running
186  if(context->running)
187  {
188  //Automatic DNS server configuration?
189  if(!context->manualDnsConfig)
190  {
191  //Clear the list of DNS servers
192  ipv6FlushDnsServerList(interface);
193  }
194 
195  //Link-up event?
196  if(interface->linkState)
197  {
198  //A link-local address is formed by combining the well-known
199  //link-local prefix fe80::/10 with the interface identifier
201  }
202  }
203 
204  //Check the state of the DHCPv6 client
205  switch(context->state)
206  {
209  case DHCPV6_STATE_DAD:
210  case DHCPV6_STATE_BOUND:
211  case DHCPV6_STATE_RENEW:
212  case DHCPV6_STATE_REBIND:
213  //The client already has a valid lease
214  context->state = DHCPV6_STATE_INIT_CONFIRM;
215  break;
216 
218  //Stop DHCPv6 client
219  context->running = FALSE;
220  //Reinitialize state machine
221  context->state = DHCPV6_STATE_INIT;
222  break;
223 
224  default:
225  //Switch to the INIT state
226  context->state = DHCPV6_STATE_INIT;
227  break;
228  }
229 
230  //Any registered callback?
231  if(context->linkChangeEvent != NULL)
232  {
233  //Release exclusive access
234  netUnlock(context->netContext);
235  //Invoke user callback function
236  context->linkChangeEvent(context, interface, interface->linkState);
237  //Get exclusive access
238  netLock(context->netContext);
239  }
240 }
241 
242 
243 /**
244  * @brief Send Solicit message
245  * @param[in] context Pointer to the DHCPv6 client context
246  * @param[in] type DHCPv6 message type
247  * @return Error code
248  **/
249 
252 {
253  error_t error;
254  uint_t i;
255  size_t length;
256  size_t offset;
257  NetBuffer *buffer;
258  NetInterface *interface;
260  Dhcpv6Option *option;
261  Dhcpv6IaNaOption iaNaOption;
262  Dhcpv6IaAddrOption iaAddrOption;
263  Dhcpv6ElapsedTimeOption elapsedTimeOption;
264  Dhcpv6ClientAddrEntry *entry;
266  NetTxAncillary ancillary;
267 
268  //Point to the underlying network interface
269  interface = context->interface;
270 
271  //Allocate a memory buffer to hold the DHCPv6 message
272  buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset);
273  //Failed to allocate buffer?
274  if(buffer == NULL)
275  return ERROR_OUT_OF_MEMORY;
276 
277  //Point to the beginning of the DHCPv6 message
278  message = netBufferAt(buffer, offset, 0);
279 
280  //Set DHCPv6 message type
281  message->msgType = type;
282 
283  //The transaction ID is chosen by the client
284  STORE24BE(context->transactionId, message->transactionId);
285 
286  //Size of the DHCPv6 message
287  length = sizeof(Dhcpv6Message);
288 
289  //The client must include a Client Identifier option to identify itself
290  //to the server
291  dhcpv6AddOption(message, &length, DHCPV6_OPT_CLIENT_ID, context->clientId,
292  context->clientIdLen);
293 
294  //Request, Renew, Release or Decline message?
299  {
300  //The client places the identifier of the destination server in a
301  //Server Identifier option
302  dhcpv6AddOption(message, &length, DHCPV6_OPT_SERVER_ID, context->serverId,
303  context->serverIdLen);
304  }
305 
306  //Solicit message?
308  {
309  //Check whether rapid commit is enabled
310  if(context->rapidCommit)
311  {
312  //Include the Rapid Commit option if the client is prepared to perform
313  //the Solicit/Reply message exchange
315  }
316  }
317 
318  //Prepare an IA_NA option for a the current interface
319  iaNaOption.iaId = htonl(interface->id);
320 
321  //Solicit, Request or Confirm message?
325  {
326  //The client should set the T1 and T2 fields in any IA_NA options to 0
327  iaNaOption.t1 = 0;
328  iaNaOption.t2 = 0;
329  }
330  else
331  {
332  //T1 and T2 are provided as a hint
333  iaNaOption.t1 = htonl(context->ia.t1);
334  iaNaOption.t2 = htonl(context->ia.t2);
335  }
336 
337  //The client includes IA options for any IAs to which it wants the server
338  //to assign addresses
339  option = dhcpv6AddOption(message, &length, DHCPV6_OPT_IA_NA, &iaNaOption,
340  sizeof(Dhcpv6IaNaOption));
341 
342  //Request, Confirm, Renew, Rebind, Release or Decline message?
349  {
350  //Loop through the IPv6 addresses recorded by the client
351  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
352  {
353  //Point to the current entry
354  entry = &context->ia.addrList[i];
355 
356  //Valid IPv6 address?
357  if(entry->validLifetime > 0)
358  {
359  //Prepare an IA Address option
360  iaAddrOption.address = entry->addr;
361 
362  //Confirm message?
364  {
365  //The client should set the preferred and valid lifetimes to 0
366  iaAddrOption.preferredLifetime = 0;
367  iaAddrOption.validLifetime = 0;
368  }
369  else
370  {
371  //Preferred and valid lifetimes are provided as a hint
372  iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime);
373  iaAddrOption.validLifetime = htonl(entry->validLifetime);
374  }
375 
376  //Add the IA Address option
378  &iaAddrOption, sizeof(iaAddrOption));
379  }
380  }
381  }
382 
383  //Compute the time elapsed since the client sent the first message
384  elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context);
385 
386  //The client must include an Elapsed Time option in messages to indicate
387  //how long the client has been trying to complete a DHCP message exchange
389  &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption));
390 
391  //Any registered callback?
392  if(context->addOptionsCallback != NULL)
393  {
394  //Invoke user callback function
395  context->addOptionsCallback(context, message, &length);
396  }
397 
398  //Solicit, Request, Confirm, Renew or Rebind message?
404  {
405  //The client should include an Option Request option to indicate the
406  //options the client is interested in receiving
407  if(dhcpv6GetOption(message->options, length - sizeof(Dhcpv6Message),
408  DHCPV6_OPT_ORO) == NULL)
409  {
410  dhcpv6AddOption(message, &length, DHCPV6_OPT_ORO, &dhcpv6OptionList,
411  sizeof(dhcpv6OptionList));
412  }
413  }
414 
415  //Adjust the length of the multi-part buffer
416  netBufferSetLength(buffer, offset + length);
417 
418  //Destination address
419  destIpAddr.length = sizeof(Ipv6Addr);
421 
422  //Debug message
423  TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n",
425 
426  //Dump the contents of the message for debugging purpose
428 
429  //Additional options can be passed to the stack along with the packet
430  ancillary = NET_DEFAULT_TX_ANCILLARY;
431 
432  //Send DHCPv6 message
433  error = udpSendBuffer(context->netContext, interface, NULL,
435  &ancillary);
436 
437  //Free previously allocated memory
438  netBufferFree(buffer);
439 
440  //Return status code
441  return error;
442 }
443 
444 
445 /**
446  * @brief Process incoming DHCPv6 message
447  * @param[in] interface Underlying network interface
448  * @param[in] pseudoHeader UDP pseudo header
449  * @param[in] udpHeader UDP header
450  * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message
451  * @param[in] offset Offset to the first byte of the DHCPv6 message
452  * @param[in] ancillary Additional options passed to the stack along with
453  * the packet
454  * @param[in] param Pointer to the DHCPv6 client context
455  **/
456 
458  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
459  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
460  void *param)
461 {
462  size_t length;
463  Dhcpv6ClientContext *context;
465 
466  //Point to the DHCPv6 client context
467  context = (Dhcpv6ClientContext *) param;
468 
469  //Retrieve the length of the DHCPv6 message
470  length = netBufferGetLength(buffer) - offset;
471 
472  //Make sure the DHCPv6 message is valid
473  if(length < sizeof(Dhcpv6Message))
474  return;
475 
476  //Point to the beginning of the DHCPv6 message
477  message = netBufferAt(buffer, offset, length);
478  //Sanity check
479  if(message == NULL)
480  return;
481 
482  //Debug message
483  TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n",
485 
486  //Dump the contents of the message for debugging purpose
488 
489  //Check message type
490  switch(message->msgType)
491  {
493  //Parse Advertise message
495  break;
496 
498  //Parse Reply message
500  break;
501 
502  default:
503  //Silently drop incoming message
504  break;
505  }
506 }
507 
508 
509 /**
510  * @brief Parse Advertise message
511  * @param[in] context Pointer to the DHCPv6 client context
512  * @param[in] message Pointer to the incoming message to parse
513  * @param[in] length Length of the incoming message
514  **/
515 
517  const Dhcpv6Message *message, size_t length)
518 {
519  uint_t i;
520  int_t serverPreference;
521  NetInterface *interface;
522  Dhcpv6StatusCode status;
523  Dhcpv6Option *option;
524  Dhcpv6Option *serverIdOption;
525  Dhcpv6IaNaOption *iaNaOption;
526 
527  //Point to the underlying network interface
528  interface = context->interface;
529 
530  //Make sure that the Advertise message is received in response to
531  //a Solicit message
532  if(context->state != DHCPV6_STATE_SOLICIT)
533  return;
534 
535  //Discard any received packet that does not match the transaction ID
536  if(LOAD24BE(message->transactionId) != context->transactionId)
537  return;
538 
539  //Get the length of the Options field
540  length -= sizeof(Dhcpv6Message);
541 
542  //Search for the Client Identifier option
543  option = dhcpv6GetOption(message->options, length, DHCPV6_OPT_CLIENT_ID);
544 
545  //Discard any received packet that does not include a Client Identifier option
546  if(option == NULL)
547  return;
548 
549  //Check the length of the option
550  if(ntohs(option->length) != context->clientIdLen)
551  return;
552 
553  //Check whether the Client Identifier matches our identifier
554  if(osMemcmp(option->value, context->clientId, context->clientIdLen))
555  return;
556 
557  //Search for the Server Identifier option
558  serverIdOption = dhcpv6GetOption(message->options, length,
560 
561  //Discard any received packet that does not include a Server Identifier
562  //option
563  if(serverIdOption == NULL)
564  return;
565 
566  //Check the length of the server DUID
567  if(ntohs(serverIdOption->length) == 0)
568  return;
569 
570  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
571  return;
572 
573  //Get the status code returned by the server
574  status = dhcpv6GetStatusCode(message->options, length);
575 
576  //If the message contains a Status Code option indicating a failure,
577  //then the Advertise message is discarded by the client
578  if(status != DHCPV6_STATUS_SUCCESS)
579  return;
580 
581  //Any registered callback?
582  if(context->parseOptionsCallback != NULL)
583  {
584  //Invoke user callback function
585  context->parseOptionsCallback(context, message,
586  sizeof(Dhcpv6Message) + length);
587  }
588 
589  //Search for the Preference option
591 
592  //Option found?
593  if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption))
594  {
595  //Server server preference value
596  serverPreference = option->value[0];
597  }
598  else
599  {
600  //Any Advertise that does not include a Preference option is considered
601  //to have a preference value of 0
602  serverPreference = 0;
603  }
604 
605  //Select the Advertise message that offers the highest server preference
606  //value
607  if(serverPreference > context->serverPreference)
608  {
609  //Save the length of the DUID
610  context->serverIdLen = ntohs(serverIdOption->length);
611  //Record the server DUID
612  osMemcpy(context->serverId, serverIdOption->value, context->serverIdLen);
613  //Flush the list of IPv6 addresses from the client's IA
614  dhcpv6ClientFlushAddrList(context);
615  }
616 
617  //Point to the first option
618  i = 0;
619 
620  //Loop through DHCPv6 options
621  while(i < length)
622  {
623  //Search for an IA_NA option
624  option = dhcpv6GetOption(message->options + i, length - i,
626 
627  //Unable to find the specified option?
628  if(option == NULL)
629  break;
630 
631  //Make sure the IA_NA option is valid
632  if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption))
633  {
634  //Get the parameters associated with the IA_NA
635  iaNaOption = (Dhcpv6IaNaOption *) option->value;
636 
637  //Check the IA identifier
638  if(ntohl(iaNaOption->iaId) == interface->id)
639  {
640  //The client examines the status code in each IA individually
641  status = dhcpv6GetStatusCode(iaNaOption->options,
642  ntohs(option->length) - sizeof(Dhcpv6IaNaOption));
643 
644  //The client must ignore any Advertise message that includes a
645  //Status Code option containing the value NoAddrsAvail
647  return;
648  }
649 
650  //Check the server preference value
651  if(serverPreference > context->serverPreference)
652  {
653  //Parse the contents of the IA_NA option
654  dhcpv6ClientParseIaNaOption(context, option);
655  }
656  }
657 
658  //Jump to the next option
659  i += sizeof(Dhcpv6Option) + ntohs(option->length);
660  }
661 
662  //Record the highest server preference value
663  if(serverPreference > context->serverPreference)
664  {
665  context->serverPreference = serverPreference;
666  }
667 
668  //If the client receives an Advertise message that includes a Preference
669  //option with a preference value of 255, the client immediately completes
670  //the message exchange
671  if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE)
672  {
673  //Continue configuration procedure
675  }
676  //The message exchange is not terminated before the first RT has elapsed
677  else if(context->retransmitCount > 1)
678  {
679  //Continue configuration procedure
681  }
682 }
683 
684 
685 /**
686  * @brief Parse Reply message
687  * @param[in] context Pointer to the DHCPv6 client context
688  * @param[in] message Pointer to the incoming message to parse
689  * @param[in] length Length of the incoming message
690  **/
691 
693  const Dhcpv6Message *message, size_t length)
694 {
695  error_t error;
696  uint_t i;
697  uint_t k;
698  uint_t n;
699  bool_t iaNaOptionFound;
700  systime_t minPreferredLifetime;
701  NetInterface *interface;
702  Dhcpv6StatusCode status;
703  Dhcpv6Option *option;
704  Dhcpv6Option *serverIdOption;
705  Dhcpv6ClientAddrEntry *entry;
706 
707  //Point to the underlying network interface
708  interface = context->interface;
709 
710  //Discard any received packet that does not match the transaction ID
711  if(LOAD24BE(message->transactionId) != context->transactionId)
712  return;
713 
714  //Get the length of the Options field
715  length -= sizeof(Dhcpv6Message);
716 
717  //Search for the Client Identifier option
718  option = dhcpv6GetOption(message->options, length, DHCPV6_OPT_CLIENT_ID);
719 
720  //Discard any received packet that does not include a Client Identifier option
721  if(option == NULL)
722  return;
723 
724  //Check the length of the option
725  if(ntohs(option->length) != context->clientIdLen)
726  return;
727 
728  //Check whether the Client Identifier matches our identifier
729  if(osMemcmp(option->value, context->clientId, context->clientIdLen))
730  return;
731 
732  //Search for the Server Identifier option
733  serverIdOption = dhcpv6GetOption(message->options, length,
735 
736  //Discard any received packet that does not include a Server Identifier
737  //option
738  if(serverIdOption == NULL)
739  return;
740 
741  //Check the length of the server DUID
742  if(ntohs(serverIdOption->length) == 0)
743  return;
744 
745  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
746  return;
747 
748  //Get the status code returned by the server
749  status = dhcpv6GetStatusCode(message->options, length);
750 
751  //Check current state
752  if(context->state == DHCPV6_STATE_SOLICIT)
753  {
754  //A Reply message is not acceptable when rapid commit is disallowed
755  if(!context->rapidCommit)
756  return;
757 
758  //Search for the Rapid Commit option
759  option = dhcpv6GetOption(message->options, length,
761 
762  //The client discards any message that does not include a Rapid Commit
763  //option
764  if(option == NULL || ntohs(option->length) != 0)
765  return;
766  }
767  else if(context->state == DHCPV6_STATE_REQUEST)
768  {
769  //The client must discard the Reply message if the contents of the
770  //Server Identifier option do not match the server's DUID
771  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
772  return;
773  }
774  else if(context->state == DHCPV6_STATE_CONFIRM)
775  {
776  //When the client receives a NotOnLink status from the server in response
777  //to a Confirm message, the client performs DHCP server solicitation
778  if(status == DHCPV6_STATUS_NOT_ON_LINK)
779  {
780  //Restart the DHCP server discovery process
782 
783  //Exit immediately
784  return;
785  }
786  }
787  else if(context->state == DHCPV6_STATE_RENEW)
788  {
789  //The client must discard the Reply message if the contents of the
790  //Server Identifier option do not match the server's DUID
791  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
792  return;
793  }
794  else if(context->state == DHCPV6_STATE_REBIND)
795  {
796  //Do not check the server's DUID when the Reply message is received
797  //in response to a Rebind message
798  }
799  else if(context->state == DHCPV6_STATE_RELEASE)
800  {
801  //The client must discard the Reply message if the contents of the
802  //Server Identifier option do not match the server's DUID
803  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
804  return;
805 
806  //When the client receives a valid Reply message in response to a
807  //Release message, the client considers the Release event completed,
808  //regardless of the Status Code option(s) returned by the server
809  context->running = FALSE;
810 
811  //Reinitialize state machine
813 
814  //Exit immediately
815  return;
816  }
817  else if(context->state == DHCPV6_STATE_DECLINE)
818  {
819  //The client must discard the Reply message if the contents of the
820  //Server Identifier option do not match the server's DUID
821  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
822  return;
823 
824  //When the client receives a valid Reply message in response to a
825  //Decline message, the client considers the Decline event completed,
826  //regardless of the Status Code option returned by the server
828 
829  //Exit immediately
830  return;
831  }
832  else
833  {
834  //Silently discard the Reply message
835  return;
836  }
837 
838  //Check status code
839  if(status == DHCPV6_STATUS_USE_MULTICAST)
840  {
841  //When the client receives a Reply message with a Status Code option
842  //with the value UseMulticast, the client records the receipt of the
843  //message and sends subsequent messages to the server through the
844  //interface on which the message was received using multicast
845  return;
846  }
847  else if(status == DHCPV6_STATUS_UNSPEC_FAILURE)
848  {
849  //If the client receives a Reply message with a Status Code containing
850  //UnspecFail, the server is indicating that it was unable to process
851  //the message due to an unspecified failure condition
852  return;
853  }
854 
855  //Any registered callback?
856  if(context->parseOptionsCallback != NULL)
857  {
858  //Invoke user callback function
859  context->parseOptionsCallback(context, message,
860  sizeof(Dhcpv6Message) + length);
861  }
862 
863  //Automatic DNS server configuration?
864  if(!context->manualDnsConfig)
865  {
866  Dhcpv6DnsServersOption *dnsServersOption;
867 
868  //Search for the DNS Recursive Name Server option
869  option = dhcpv6GetOption(message->options, length,
871 
872  //Option found?
873  if(option != NULL && ntohs(option->length) >= sizeof(Dhcpv6DnsServersOption))
874  {
875  //Point to the DNS Recursive Name Server option
876  dnsServersOption = (Dhcpv6DnsServersOption *) option->value;
877 
878  //Retrieve the number of addresses
879  n = ntohs(option->length) / sizeof(Ipv6Addr);
880 
881  //Loop through the list of DNS servers
882  for(i = 0; i < n && i < IPV6_DNS_SERVER_LIST_SIZE; i++)
883  {
884  //Record DNS server address
885  interface->ipv6Context.dnsServerList[i] = dnsServersOption->address[i];
886  }
887  }
888  }
889 
890  //This flag will be set if a valid IA_NA option is found
891  iaNaOptionFound = FALSE;
892  //Point to the first option
893  i = 0;
894 
895  //Loop through DHCPv6 options
896  while(i < length)
897  {
898  //Search for an IA_NA option
899  option = dhcpv6GetOption(message->options + i, length - i,
901 
902  //Unable to find the specified option?
903  if(option == NULL)
904  break;
905 
906  //Parse the contents of the IA_NA option
907  error = dhcpv6ClientParseIaNaOption(context, option);
908 
909  //Check error code
910  if(error == NO_ERROR)
911  {
912  //A valid IA_NA option has been found
913  iaNaOptionFound = TRUE;
914  }
915  else if(error == ERROR_NOT_ON_LINK)
916  {
917  //When the client receives a NotOnLink status from the server in
918  //response to a Request, the client can either re-issue the Request
919  //without specifying any addresses or restart the DHCP server
920  //discovery process
922 
923  //Exit immediately
924  return;
925  }
926  else if(error == ERROR_NO_BINDING)
927  {
928  //When the client receives a Reply message in response to a Renew or
929  //Rebind message, the client sends a Request message if any of the IAs
930  //in the Reply message contains the NoBinding status code
932 
933  //Exit immediately
934  return;
935  }
936  else
937  {
938  //If an invalid option is received, the client discards the option
939  //and process the rest of the message
940  }
941 
942  //Jump to the next option
943  i += sizeof(Dhcpv6Option) + ntohs(option->length);
944  }
945 
946  //No usable addresses in any of the IAs?
947  if(!iaNaOptionFound)
948  {
949  //Check whether the client receives a Reply message in response to a
950  //Renew or Rebind message
951  if(context->state == DHCPV6_STATE_RENEW ||
952  context->state == DHCPV6_STATE_REBIND)
953  {
954  //The client sends a Renew/Rebind if the IA is not in the Reply message
955  }
956  else
957  {
958  //If the client finds no usable addresses in any of the IAs, it may try
959  //another server (perhaps restarting the DHCP server discovery process)
961  }
962 
963  //Exit immediately
964  return;
965  }
966 
967  //Total number of valid IPv6 in the IA
968  n = 0;
969  //Number of new IPv6 addresses in the IA
970  k = 0;
971  //Minimum preferred lifetime observed in the IA
972  minPreferredLifetime = 0;
973 
974  //Loop through the IPv6 addresses recorded by the DHCPv6 client
975  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
976  {
977  //Point to the current entry
978  entry = &context->ia.addrList[i];
979 
980  //Valid IPv6 address?
981  if(entry->validLifetime > 0)
982  {
983  //Total number of valid IPv6 in the IA
984  n++;
985 
986  //Save the minimum preferred lifetime that has been observed so far
987  if(minPreferredLifetime < entry->preferredLifetime)
988  {
989  minPreferredLifetime = entry->preferredLifetime;
990  }
991 
992  //Update lifetimes of the current IPv6 address
993  ipv6AddAddr(interface, &entry->addr, entry->validLifetime,
994  entry->preferredLifetime);
995 
996  //New IPv6 address added?
997  if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE)
998  {
999  k++;
1000  }
1001  }
1002  }
1003 
1004  //Make sure that the IA contains at least one IPv6 address
1005  if(n > 0)
1006  {
1007  //Save the length of the DUID
1008  context->serverIdLen = ntohs(serverIdOption->length);
1009  //Record the server DUID
1010  osMemcpy(context->serverId, serverIdOption->value, context->serverIdLen);
1011  //Save the time a which the lease was obtained
1012  context->leaseStartTime = osGetSystemTime();
1013 
1014  //Check the value of T1
1015  if(context->ia.t1 == 0)
1016  {
1017  //If T1 is set to 0 by the server, the client may send a Renew
1018  //message at the client's discretion
1019  if(minPreferredLifetime == DHCPV6_INFINITE_TIME)
1020  {
1021  context->ia.t1 = DHCPV6_INFINITE_TIME;
1022  }
1023  else
1024  {
1025  context->ia.t1 = minPreferredLifetime / 2;
1026  }
1027  }
1028 
1029  //Check the value of T2
1030  if(context->ia.t2 == 0)
1031  {
1032  //If T2 is set to 0 by the server, the client may send a Rebind
1033  //message at the client's discretion
1034  if(context->ia.t1 == DHCPV6_INFINITE_TIME)
1035  {
1036  context->ia.t2 = DHCPV6_INFINITE_TIME;
1037  }
1038  else
1039  {
1040  context->ia.t2 = context->ia.t1 + context->ia.t1 / 2;
1041  }
1042  }
1043 
1044  //Any addresses added in the IA?
1045  if(k > 0)
1046  {
1047  //Perform Duplicate Address Detection for the new IPv6 addresses
1049  }
1050  else
1051  {
1052  //Switch to the BOUND state
1054  }
1055  }
1056  else
1057  {
1058  //If the client finds no usable addresses in any of the IAs, it may try
1059  //another server (perhaps restarting the DHCP server discovery process)
1061  }
1062 }
1063 
1064 
1065 /**
1066  * @brief Parse IA_NA option
1067  * @param[in] context Pointer to the DHCPv6 client context
1068  * @param[in] option Pointer to the IA_NA option to parse
1069  * @return Error code
1070  **/
1071 
1073  const Dhcpv6Option *option)
1074 {
1075  error_t error;
1076  uint_t n;
1077  size_t i;
1078  size_t length;
1079  NetInterface *interface;
1080  Dhcpv6StatusCode status;
1081  Dhcpv6IaNaOption *iaNaOption;
1082 
1083  //Point to the underlying network interface
1084  interface = context->interface;
1085 
1086  //Number of addresses found in the IA_NA option
1087  n = 0;
1088 
1089  //Make sure the IA_NA option is valid
1090  if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
1091  return ERROR_INVALID_LENGTH;
1092 
1093  //Get the parameters associated with the IA_NA
1094  iaNaOption = (Dhcpv6IaNaOption *) option->value;
1095  //Compute the length of IA_NA Options field
1096  length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption);
1097 
1098  //Check the IA identifier
1099  if(ntohl(iaNaOption->iaId) != interface->id)
1100  return ERROR_WRONG_IDENTIFIER;
1101 
1102  //If a client receives an IA_NA with T1 greater than T2, and both T1 and T2
1103  //are greater than 0, the client discards the IA_NA option and processes the
1104  //remainder of the message as though the server had not included the invalid
1105  //IA_NA option
1106  if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0)
1107  return ERROR_INVALID_PARAMETER;
1108 
1109  //The client examines the status code in each IA individually
1110  status = dhcpv6GetStatusCode(iaNaOption->options, length);
1111 
1112  //Check error code
1113  if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
1114  {
1115  //The client has received no usable address in the IA
1116  return ERROR_NO_ADDRESS;
1117  }
1118  else if(status == DHCPV6_STATUS_NO_BINDING)
1119  {
1120  //Client record (binding) unavailable
1121  return ERROR_NO_BINDING;
1122  }
1123  else if(status == DHCPV6_STATUS_NOT_ON_LINK)
1124  {
1125  //The prefix for the address is not appropriate for the link to which the
1126  //client is attached
1127  return ERROR_NOT_ON_LINK;
1128  }
1129  else if(status != DHCPV6_STATUS_SUCCESS)
1130  {
1131  //Failure, reason unspecified
1132  return ERROR_FAILURE;
1133  }
1134 
1135  //Record T1 and T2 times
1136  context->ia.t1 = ntohl(iaNaOption->t1);
1137  context->ia.t2 = ntohl(iaNaOption->t2);
1138 
1139  //Point to the first option
1140  i = 0;
1141 
1142  //Loop through IA_NA options
1143  while(i < length)
1144  {
1145  //Search for an IA Address option
1146  option = dhcpv6GetOption(iaNaOption->options + i, length - i,
1148 
1149  //Unable to find the specified option?
1150  if(option == NULL)
1151  break;
1152 
1153  //Parse the contents of the IA Address option
1154  error = dhcpv6ClientParseIaAddrOption(context, option);
1155 
1156  //Check status code
1157  if(!error)
1158  {
1159  //Increment the number of addresses found in the IA_NA option
1160  n++;
1161  }
1162 
1163  //Jump to the next option
1164  i += sizeof(Dhcpv6Option) + ntohs(option->length);
1165  }
1166 
1167  //No usable addresses in the IA_NA option?
1168  if(n == 0)
1169  {
1170  //Report an error
1171  return ERROR_NO_ADDRESS;
1172  }
1173 
1174  //Successful processing
1175  return NO_ERROR;
1176 }
1177 
1178 
1179 /**
1180  * @brief Parse IA Address option
1181  * @param[in] context Pointer to the DHCPv6 client context
1182  * @param[in] option Pointer to the IA Address option to parse
1183  * @return Error code
1184  **/
1185 
1187  const Dhcpv6Option *option)
1188 {
1189  size_t length;
1190  uint32_t validLifetime;
1191  uint32_t preferredLifetime;
1192  Dhcpv6StatusCode status;
1193  Dhcpv6IaAddrOption *iaAddrOption;
1194 
1195  //Make sure the IA Address option is valid
1196  if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
1197  return ERROR_INVALID_LENGTH;
1198 
1199  //Point to the contents of the IA Address option
1200  iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
1201  //Compute the length of IA Address Options field
1202  length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption);
1203 
1204  //Convert lifetimes to host byte order
1205  validLifetime = ntohl(iaAddrOption->validLifetime);
1206  preferredLifetime = ntohl(iaAddrOption->preferredLifetime);
1207 
1208  //A client discards any addresses for which the preferred lifetime is
1209  //greater than the valid lifetime
1211  return ERROR_INVALID_PARAMETER;
1212 
1213  //The client examines the status code in each IA Address
1214  status = dhcpv6GetStatusCode(iaAddrOption->options, length);
1215 
1216  //Any error to report?
1217  if(status != DHCPV6_STATUS_SUCCESS)
1218  return ERROR_FAILURE;
1219 
1220  //Check the value of the Valid Lifetime
1221  if(iaAddrOption->validLifetime > 0)
1222  {
1223  //Add any new addresses in the IA option to the IA as recorded by the
1224  //client
1225  dhcpv6ClientAddAddr(context, &iaAddrOption->address,
1227  }
1228  else
1229  {
1230  //Discard any addresses from the IA, as recorded by the client, that
1231  //have a valid lifetime of 0 in the IA Address option
1232  dhcpv6ClientRemoveAddr(context, &iaAddrOption->address);
1233  }
1234 
1235  //Successful processing
1236  return NO_ERROR;
1237 }
1238 
1239 
1240 /**
1241  * @brief Add an IPv6 address to the IA
1242  * @param[in] context Pointer to the DHCPv6 client context
1243  * @param[in] addr IPv6 address to be added
1244  * @param[in] validLifetime Valid lifetime, in seconds
1245  * @param[in] preferredLifetime Preferred lifetime, in seconds
1246  **/
1247 
1249  uint32_t validLifetime, uint32_t preferredLifetime)
1250 {
1251  uint_t i;
1252  Dhcpv6ClientAddrEntry *entry;
1253  Dhcpv6ClientAddrEntry *firstFreeEntry;
1254 
1255  //Keep track of the first free entry
1256  firstFreeEntry = NULL;
1257 
1258  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1259  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1260  {
1261  //Point to the current entry
1262  entry = &context->ia.addrList[i];
1263 
1264  //Valid IPv6 address?
1265  if(entry->validLifetime > 0)
1266  {
1267  //Check whether the current entry matches the specified address
1268  if(ipv6CompAddr(&entry->addr, addr))
1269  break;
1270  }
1271  else
1272  {
1273  //Keep track of the first free entry
1274  if(firstFreeEntry == NULL)
1275  {
1276  firstFreeEntry = entry;
1277  }
1278  }
1279  }
1280 
1281  //No matching entry found?
1283  {
1284  entry = firstFreeEntry;
1285  }
1286 
1287  //Update the entry if necessary
1288  if(entry != NULL)
1289  {
1290  //Save IPv6 address
1291  entry->addr = *addr;
1292 
1293  //Save lifetimes
1294  entry->validLifetime = validLifetime;
1296  }
1297 }
1298 
1299 
1300 /**
1301  * @brief Remove an IPv6 address from the IA
1302  * @param[in] context Pointer to the DHCPv6 client context
1303  * @param[in] addr IPv6 address to be removed
1304  **/
1305 
1307 {
1308  uint_t i;
1309  Dhcpv6ClientAddrEntry *entry;
1310 
1311  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1312  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1313  {
1314  //Point to the current entry
1315  entry = &context->ia.addrList[i];
1316 
1317  //Valid IPv6 address?
1318  if(entry->validLifetime > 0)
1319  {
1320  //Check whether the current entry matches the specified address
1321  if(ipv6CompAddr(&entry->addr, addr))
1322  {
1323  //The IPv6 address is no more valid and should be removed from the
1324  //list of IPv6 addresses assigned to the interface
1325  ipv6RemoveAddr(context->interface, addr);
1326 
1327  //Remove the IPv6 address from the IA
1328  entry->validLifetime = 0;
1329  }
1330  }
1331  }
1332 }
1333 
1334 
1335 /**
1336  * @brief Flush the list of IPv6 addresses from the IA
1337  * @param[in] context Pointer to the DHCPv6 client context
1338  **/
1339 
1341 {
1342  uint_t i;
1343  Dhcpv6ClientAddrEntry *entry;
1344 
1345  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1346  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1347  {
1348  //Point to the current entry
1349  entry = &context->ia.addrList[i];
1350 
1351  //Valid IPv6 address?
1352  if(entry->validLifetime > 0)
1353  {
1354  //The IPv6 address is no more valid and should be removed from the
1355  //list of IPv6 addresses assigned to the interface
1356  ipv6RemoveAddr(context->interface, &entry->addr);
1357 
1358  //Remove the IPv6 address from the IA
1359  entry->validLifetime = 0;
1360  }
1361  }
1362 }
1363 
1364 
1365 /**
1366  * @brief Generate client's DUID
1367  * @param[in] context Pointer to the DHCPv6 client context
1368  * @return Error code
1369  **/
1370 
1372 {
1373  Dhcpv6DuidLl *duid;
1374 #if (ETH_SUPPORT == ENABLED)
1375  NetInterface *logicalInterface;
1376 #endif
1377 
1378  //Point to the buffer where to format the client's DUID
1379  duid = (Dhcpv6DuidLl *) context->clientId;
1380 
1381 #if (ETH_SUPPORT == ENABLED)
1382  //Point to the logical interface
1383  logicalInterface = nicGetLogicalInterface(context->interface);
1384 
1385  //Generate a DUID-LL from the MAC address
1386  duid->type = HTONS(DHCPV6_DUID_LL);
1387  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH);
1388  duid->linkLayerAddr = logicalInterface->macAddr;
1389 #else
1390  //Generate a DUID-LL from the EUI-64 identifier
1391  duid->type = HTONS(DHCPV6_DUID_LL);
1392  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64);
1393  duid->linkLayerAddr = interface->eui64;
1394 #endif
1395 
1396  //Length of the newly generated DUID
1397  context->clientIdLen = sizeof(Dhcpv6DuidLl);
1398 
1399  //Successful processing
1400  return NO_ERROR;
1401 }
1402 
1403 
1404 /**
1405  * @brief Generate a link-local address
1406  * @param[in] context Pointer to the DHCPv6 client context
1407  * @return Error code
1408  **/
1409 
1411 {
1412  error_t error;
1413  NetInterface *interface;
1414  Ipv6Addr addr;
1415 
1416  //Point to the underlying network interface
1417  interface = context->interface;
1418 
1419  //Check whether a link-local address has been manually assigned
1420  if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
1421  interface->ipv6Context.addrList[0].permanent)
1422  {
1423  //Keep using the current link-local address
1424  error = NO_ERROR;
1425  }
1426  else
1427  {
1428  //A link-local address is formed by combining the well-known link-local
1429  //prefix fe80::/10 with the interface identifier
1430  ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
1431 
1432 #if (NDP_SUPPORT == ENABLED)
1433  //Check whether Duplicate Address Detection should be performed
1434  if(interface->ndpContext.dupAddrDetectTransmits > 0)
1435  {
1436  //Use the link-local address as a tentative address
1437  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
1439  }
1440  else
1441 #endif
1442  {
1443  //The use of the link-local address is now unrestricted
1444  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
1446  }
1447  }
1448 
1449  //Return status code
1450  return error;
1451 }
1452 
1453 
1454 /**
1455  * @brief Check the Server Identifier option
1456  * @param[in] context Pointer to the DHCPv6 client context
1457  * @param[in] serverIdOption Pointer to the Server Identifier option
1458  * @return TRUE if the option matches the server's DUID, else FALSE
1459  **/
1460 
1462  Dhcpv6Option *serverIdOption)
1463 {
1464  bool_t valid = FALSE;
1465 
1466  //Check the length of the Server Identifier option
1467  if(ntohs(serverIdOption->length) == context->serverIdLen)
1468  {
1469  //Check whether the Server Identifier option matches the server's DUID
1470  if(!osMemcmp(serverIdOption->value, context->serverId,
1471  context->serverIdLen))
1472  {
1473  valid = TRUE;
1474  }
1475  }
1476 
1477  //Return TRUE if the option matches the server's DUID
1478  return valid;
1479 }
1480 
1481 
1482 /**
1483  * @brief Manage DHCPv6 configuration timeout
1484  * @param[in] context Pointer to the DHCPv6 client context
1485  **/
1486 
1488 {
1489  systime_t time;
1490 
1491  //Get current time
1492  time = osGetSystemTime();
1493 
1494  //Any registered callback?
1495  if(context->timeoutEvent != NULL)
1496  {
1497  //DHCPv6 configuration timeout?
1498  if(timeCompare(time, context->configStartTime + context->configTimeout) >= 0)
1499  {
1500  //Ensure the callback function is only called once
1501  if(!context->timeoutEventDone)
1502  {
1503  //Release exclusive access
1504  netUnlock(context->netContext);
1505  //Invoke user callback function
1506  context->timeoutEvent(context, context->interface);
1507  //Get exclusive access
1508  netLock(context->netContext);
1509 
1510  //Set flag
1511  context->timeoutEventDone = TRUE;
1512  }
1513  }
1514  }
1515 }
1516 
1517 
1518 /**
1519  * @brief Compute the time elapsed since the client sent the first message
1520  * @param[in] context Pointer to the DHCPv6 client context
1521  * @return The elapsed time expressed in hundredths of a second
1522  **/
1523 
1525 {
1526  systime_t time;
1527 
1528  //Check retransmission counter
1529  if(context->retransmitCount == 0)
1530  {
1531  //The elapsed time must be 0 for the first message
1532  time = 0;
1533  }
1534  else
1535  {
1536  //Compute the time elapsed since the client sent the first message (in
1537  //hundredths of a second)
1538  time = (osGetSystemTime() - context->exchangeStartTime) / 10;
1539 
1540  //The value 0xFFFF is used to represent any elapsed time values greater
1541  //than the largest time value that can be represented
1542  time = MIN(time, 0xFFFF);
1543  }
1544 
1545  //Convert the 16-bit value to network byte order
1546  return htons(time);
1547 }
1548 
1549 
1550 /**
1551  * @brief Update DHCPv6 FSM state
1552  * @param[in] context Pointer to the DHCPv6 client context
1553  * @param[in] newState New DHCPv6 state to switch to
1554  * @param[in] delay Initial delay
1555  **/
1556 
1558  Dhcpv6State newState, systime_t delay)
1559 {
1560  systime_t time;
1561 
1562  //Get current time
1563  time = osGetSystemTime();
1564 
1565 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
1566  //Sanity check
1567  if(newState <= DHCPV6_STATE_DECLINE)
1568  {
1569  //DHCPv6 FSM states
1570  static const char_t *const stateLabel[] =
1571  {
1572  "INIT",
1573  "SOLICIT",
1574  "REQUEST",
1575  "INIT-CONFIRM",
1576  "CONFIRM",
1577  "DAD",
1578  "BOUND",
1579  "RENEW",
1580  "REBIND",
1581  "RELEASE",
1582  "DECLINE"
1583  };
1584 
1585  //Debug message
1586  TRACE_INFO("%s: DHCPv6 client %s state\r\n",
1587  formatSystemTime(time, NULL), stateLabel[newState]);
1588  }
1589 #endif
1590 
1591  //Set time stamp
1592  context->timestamp = time;
1593  //Set initial delay
1594  context->timeout = delay;
1595  //Reset retransmission counter
1596  context->retransmitCount = 0;
1597  //Switch to the new state
1598  context->state = newState;
1599 
1600  //Any registered callback?
1601  if(context->stateChangeEvent != NULL)
1602  {
1603  //Release exclusive access
1604  netUnlock(context->netContext);
1605  //Invoke user callback function
1606  context->stateChangeEvent(context, context->interface, newState);
1607  //Get exclusive access
1608  netLock(context->netContext);
1609  }
1610 }
1611 
1612 
1613 /**
1614  * @brief Dump DHCPv6 configuration for debugging purpose
1615  * @param[in] context Pointer to the DHCPv6 client context
1616  **/
1617 
1619 {
1620 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
1621  uint_t i;
1622  Ipv6Context *ipv6Context;
1623 
1624  //Point to the IPv6 context
1625  ipv6Context = &context->interface->ipv6Context;
1626 
1627  //Debug message
1628  TRACE_INFO("\r\n");
1629  TRACE_INFO("DHCPv6 configuration:\r\n");
1630 
1631  //Lease start time
1632  TRACE_INFO(" Lease Start Time = %s\r\n",
1633  formatSystemTime(context->leaseStartTime, NULL));
1634 
1635  //T1 parameter
1636  TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->ia.t1);
1637  //T2 parameter
1638  TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->ia.t2);
1639 
1640  //Global addresses
1641  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
1642  {
1643  TRACE_INFO(" Global Address %u = %s\r\n", i,
1644  ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
1645  }
1646 
1647  //DNS servers
1648  for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
1649  {
1650  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
1651  ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
1652  }
1653 
1654  //Debug message
1655  TRACE_INFO("\r\n");
1656 #endif
1657 }
1658 
1659 #endif
@ IPV6_ADDR_STATE_TENTATIVE
An address whose uniqueness on a link is being verified.
Definition: ipv6.h:194
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2344
#define htons(value)
Definition: cpu_endian.h:413
IPv6 (Internet Protocol Version 6)
void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context)
INIT-CONFIRM state.
Date and time management.
void netUnlock(NetContext *context)
Release exclusive access to the core of the TCP/IP stack.
Definition: net.c:319
@ DHCPV6_MSG_TYPE_DECLINE
Definition: dhcpv6_common.h:98
@ ERROR_NO_ADDRESS
Definition: error.h:200
int bool_t
Definition: compiler_port.h:63
@ DHCPV6_STATUS_NOT_ON_LINK
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:70
signed int int_t
Definition: compiler_port.h:56
bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context, Dhcpv6Option *serverIdOption)
Check the Server Identifier option.
@ DHCPV6_OPT_IA_ADDR
@ DHCPV6_MSG_TYPE_SOLICIT
Definition: dhcpv6_common.h:90
IP network address.
Definition: ip.h:90
void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context, const Dhcpv6Message *message, size_t length)
Parse Advertise message.
void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr)
Remove an IPv6 address from the IA.
@ DHCPV6_OPT_IA_NA
#define DHCPV6_CLIENT_PORT
Definition: dhcpv6_common.h:40
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint8_t message[]
Definition: chap.h:154
@ 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:76
Ipv6Addr addr
IPv6 address.
Definition: ipv6.h:441
@ DHCPV6_MSG_TYPE_REBIND
Definition: dhcpv6_common.h:95
void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context)
DECLINE state.
Dhcpv6DnsServersOption
Ipv6Addr
Definition: ipv6.h:280
#define DHCPV6_INFINITE_TIME
Definition: dhcpv6_common.h:53
@ DHCPV6_STATE_REBIND
void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context)
Manage DHCPv6 configuration timeout.
uint8_t type
Definition: coap_common.h:176
#define osMemcmp(p1, p2, length)
Definition: os_port.h:156
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
const Ipv6Addr DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR
Definition: dhcpv6_common.c:54
@ DHCPV6_STATUS_UNSPEC_FAILURE
#define DHCPV6_MAX_SERVER_PREFERENCE
Definition: dhcpv6_common.h:51
void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context)
Flush the list of IPv6 addresses from the IA.
Dhcpv6Option * dhcpv6AddSubOption(Dhcpv6Option *baseOption, size_t *messageLen, uint16_t optionCode, const void *optionValue, size_t optionLen)
Add a suboption under an existing base option.
Dhcpv6Message
@ DHCPV6_STATE_INIT
#define ipv6CompAddr(ipAddr1, ipAddr2)
Definition: ipv6.h:134
Dhcpv6Option * dhcpv6GetOption(const uint8_t *options, size_t optionsLength, uint16_t optionCode)
Search a DHCPv6 message for a given option.
#define DHCPV6_CLIENT_ADDR_LIST_SIZE
Definition: dhcpv6_client.h:54
#define NDP_INFINITE_LIFETIME
Definition: ndp.h:202
Helper functions for DHCPv6 client.
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:77
@ DHCPV6_HARDWARE_TYPE_EUI64
Definition: dhcpv6_common.h:80
@ DHCPV6_OPT_DOMAIN_LIST
#define timeCompare(t1, t2)
Definition: os_port.h:40
IPv6 context.
Definition: ipv6.h:516
@ IPV6_ADDR_STATE_INVALID
An address that is not assigned to any interface.
Definition: ipv6.h:193
Ipv6Addr addr
IPv6 address.
IP pseudo header.
Definition: ip.h:110
error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context, const Dhcpv6Option *option)
Parse IA_NA option.
@ DHCPV6_OPT_CLIENT_ID
@ DHCPV6_STATUS_NO_BINDING
void dhcpv6ClientStateInit(Dhcpv6ClientContext *context)
INIT state.
#define DHCPV6_MAX_DUID_SIZE
Definition: dhcpv6_common.h:46
@ DHCPV6_OPT_ELAPSED_TIME
#define FALSE
Definition: os_port.h:46
void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context)
Dump DHCPv6 configuration for debugging purpose.
@ DHCPV6_STATUS_SUCCESS
@ ERROR_INVALID_PARAMETER
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:776
#define DHCPV6_MAX_MSG_SIZE
Definition: dhcpv6_common.h:44
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
error_t
Error codes.
Definition: error.h:43
void dhcpv6ClientTick(Dhcpv6ClientContext *context)
DHCPv6 client timer handler.
void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context)
Callback function for link change event.
uint32_t validLifetime
@ DHCPV6_STATE_SOLICIT
uint32_t validLifetime
Valid lifetime.
Dhcpv6StatusCode dhcpv6GetStatusCode(const uint8_t *options, size_t length)
Retrieve status code.
Definition: dhcpv6_common.c:73
DHCPv6 client (Dynamic Host Configuration Protocol for IPv6)
Dhcpv6Option
Dhcpv6ElapsedTimeOption
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ DHCPV6_MSG_TYPE_RELEASE
Definition: dhcpv6_common.h:97
Ipv6Addr dnsServerList[IPV6_DNS_SERVER_LIST_SIZE]
DNS servers.
Definition: ipv6.h:527
IA address entry.
#define NetRxAncillary
Definition: net_misc.h:40
Dhcpv6PreferenceOption
Ipv6AddrEntry addrList[IPV6_ADDR_LIST_SIZE]
IPv6 unicast address list.
Definition: ipv6.h:523
#define NetInterface
Definition: net.h:40
void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context)
RENEW state.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
@ ERROR_INVALID_LENGTH
Definition: error.h:111
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.
Helper functions for IPv6.
@ DHCPV6_MSG_TYPE_REPLY
Definition: dhcpv6_common.h:96
#define Dhcpv6ClientContext
#define NetTxAncillary
Definition: net_misc.h:36
error_t udpSendBuffer(NetContext *context, 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:657
@ DHCPV6_MSG_TYPE_RENEW
Definition: dhcpv6_common.h:94
@ ERROR_NOT_ON_LINK
Definition: error.h:202
Dhcpv6StatusCode
Status code.
#define IPV6_DNS_SERVER_LIST_SIZE
Definition: ipv6.h:100
void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr, uint32_t validLifetime, uint32_t preferredLifetime)
Add an IPv6 address to the IA.
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
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:359
void dhcpv6ClientStateDad(Dhcpv6ClientContext *context)
DAD state.
uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context)
Compute the time elapsed since the client sent the first message.
#define MIN(a, b)
Definition: os_port.h:63
Dhcpv6State
DHCPv6 client FSM states.
@ 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:948
void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context)
REQUEST state.
#define ENABLED
Definition: os_port.h:37
@ DHCPV6_STATE_BOUND
void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context)
REBIND state.
NDP (Neighbor Discovery Protocol)
error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context, Dhcpv6MessageType type)
Send Solicit message.
UdpHeader
Definition: udp.h:85
uint32_t systime_t
System time.
#define ntohs(value)
Definition: cpu_endian.h:421
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:116
#define TRACE_DEBUG(...)
Definition: debug.h:119
char char_t
Definition: compiler_port.h:55
#define ETH_SUPPORT
Definition: ethernet.h:39
#define DHCPV6_SERVER_PORT
Definition: dhcpv6_common.h:41
@ DHCPV6_STATE_RENEW
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:241
uint32_t time
void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context)
CONFIRM state.
Dhcpv6MessageType
DHCPv6 message types.
Definition: dhcpv6_common.h:89
DHCPv6 client finite state machine.
@ DHCPV6_STATE_RELEASE
#define HTONS(value)
Definition: cpu_endian.h:410
Dhcpv6IaAddrOption
uint8_t n
@ DHCPV6_STATUS_USE_MULTICAST
error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context)
Generate client's DUID.
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:72
@ DHCPV6_STATUS_NO_ADDRS_AVAILABLE
@ DHCPV6_OPT_PREFERENCE
@ DHCPV6_STATE_DECLINE
#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:1424
void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context)
SOLICIT state.
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:322
@ DHCPV6_STATE_CONFIRM
@ ERROR_WRONG_IDENTIFIER
Definition: error.h:89
@ DHCPV6_OPT_DNS_SERVERS
#define STORE24BE(a, p)
Definition: cpu_endian.h:273
Dhcpv6IaNaOption
@ DHCPV6_STATE_DAD
@ DHCPV6_MSG_TYPE_CONFIRM
Definition: dhcpv6_common.h:93
error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context)
Generate a link-local address.
void netLock(NetContext *context)
Get exclusive access to the core of the TCP/IP stack.
Definition: net.c:307
void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context)
RELEASE state.
void dhcpv6ClientStateBound(Dhcpv6ClientContext *context)
BOUND state.
void * netBufferAt(const NetBuffer *buffer, size_t offset, size_t length)
Returns a pointer to a data segment.
Definition: net_mem.c:418
uint32_t preferredLifetime
Preferred lifetime.
@ DHCPV6_OPT_ORO
@ DHCPV6_HARDWARE_TYPE_ETH
Definition: dhcpv6_common.h:79
Ipv4Addr addr
Definition: nbns_common.h:141
@ DHCPV6_OPT_SERVER_ID
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:195
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context, const Dhcpv6Option *option)
Parse IA Address option.
TCP/IP stack core.
NetInterface * nicGetLogicalInterface(NetInterface *interface)
Retrieve logical interface.
Definition: nic.c:51
@ DHCPV6_STATE_INIT_CONFIRM
Common DNS routines.
@ ERROR_NO_BINDING
Definition: error.h:201
void dhcpv6ClientChangeState(Dhcpv6ClientContext *context, Dhcpv6State newState, systime_t delay)
Update DHCPv6 FSM state.
Dhcpv6DuidLl
@ DHCPV6_DUID_LL
Definition: dhcpv6_common.h:69
#define ntohl(value)
Definition: cpu_endian.h:422
void dhcpv6ClientParseReply(Dhcpv6ClientContext *context, const Dhcpv6Message *message, size_t length)
Parse Reply message.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
Data logging functions for debugging purpose (DHCPv6)
@ DHCPV6_MSG_TYPE_REQUEST
Definition: dhcpv6_common.h:92
@ DHCPV6_OPT_RAPID_COMMIT
systime_t osGetSystemTime(void)
Retrieve system time.
Ipv4Addr destIpAddr
Definition: ipcp.h:80