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