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-2024 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.4.4
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, 0);
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 
442  //Return status code
443  return error;
444 }
445 
446 
447 /**
448  * @brief Process incoming DHCPv6 message
449  * @param[in] interface Underlying network interface
450  * @param[in] pseudoHeader UDP pseudo header
451  * @param[in] udpHeader UDP header
452  * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message
453  * @param[in] offset Offset to the first byte of the DHCPv6 message
454  * @param[in] ancillary Additional options passed to the stack along with
455  * the packet
456  * @param[in] param Pointer to the DHCPv6 client context
457  **/
458 
460  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
461  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
462  void *param)
463 {
464  size_t length;
465  Dhcpv6ClientContext *context;
467 
468  //Point to the DHCPv6 client context
469  context = (Dhcpv6ClientContext *) param;
470 
471  //Retrieve the length of the DHCPv6 message
472  length = netBufferGetLength(buffer) - offset;
473 
474  //Make sure the DHCPv6 message is valid
475  if(length < sizeof(Dhcpv6Message))
476  return;
477 
478  //Point to the beginning of the DHCPv6 message
479  message = netBufferAt(buffer, offset, length);
480  //Sanity check
481  if(message == NULL)
482  return;
483 
484  //Debug message
485  TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n",
487 
488  //Dump the contents of the message for debugging purpose
490 
491  //Check message type
492  switch(message->msgType)
493  {
495  //Parse Advertise message
497  break;
498 
500  //Parse Reply message
502  break;
503 
504  default:
505  //Silently drop incoming message
506  break;
507  }
508 }
509 
510 
511 /**
512  * @brief Parse Advertise message
513  * @param[in] context Pointer to the DHCPv6 client context
514  * @param[in] message Pointer to the incoming message to parse
515  * @param[in] length Length of the incoming message
516  **/
517 
519  const Dhcpv6Message *message, size_t length)
520 {
521  uint_t i;
522  int_t serverPreference;
523  NetInterface *interface;
524  Dhcpv6StatusCode status;
525  Dhcpv6Option *option;
526  Dhcpv6Option *serverIdOption;
527  Dhcpv6IaNaOption *iaNaOption;
528 
529  //Point to the underlying network interface
530  interface = context->settings.interface;
531 
532  //Make sure that the Advertise message is received in response to
533  //a Solicit message
534  if(context->state != DHCPV6_STATE_SOLICIT)
535  return;
536 
537  //Discard any received packet that does not match the transaction ID
538  if(LOAD24BE(message->transactionId) != context->transactionId)
539  return;
540 
541  //Get the length of the Options field
542  length -= sizeof(Dhcpv6Message);
543 
544  //Search for the Client Identifier option
545  option = dhcpv6GetOption(message->options, length, DHCPV6_OPT_CLIENT_ID);
546 
547  //Discard any received packet that does not include a Client Identifier option
548  if(option == NULL)
549  return;
550 
551  //Check the length of the option
552  if(ntohs(option->length) != context->clientIdLen)
553  return;
554 
555  //Check whether the Client Identifier matches our identifier
556  if(osMemcmp(option->value, context->clientId, context->clientIdLen))
557  return;
558 
559  //Search for the Server Identifier option
560  serverIdOption = dhcpv6GetOption(message->options, length,
562 
563  //Discard any received packet that does not include a Server Identifier
564  //option
565  if(serverIdOption == NULL)
566  return;
567 
568  //Check the length of the server DUID
569  if(ntohs(serverIdOption->length) == 0)
570  return;
571 
572  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
573  return;
574 
575  //Get the status code returned by the server
576  status = dhcpv6GetStatusCode(message->options, length);
577 
578  //If the message contains a Status Code option indicating a failure,
579  //then the Advertise message is discarded by the client
580  if(status != DHCPV6_STATUS_SUCCESS)
581  return;
582 
583  //Any registered callback?
584  if(context->settings.parseOptionsCallback != NULL)
585  {
586  //Invoke user callback function
587  context->settings.parseOptionsCallback(context, message,
588  sizeof(Dhcpv6Message) + length);
589  }
590 
591  //Search for the Preference option
593 
594  //Option found?
595  if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption))
596  {
597  //Server server preference value
598  serverPreference = option->value[0];
599  }
600  else
601  {
602  //Any Advertise that does not include a Preference option is considered
603  //to have a preference value of 0
604  serverPreference = 0;
605  }
606 
607  //Select the Advertise message that offers the highest server preference
608  //value
609  if(serverPreference > context->serverPreference)
610  {
611  //Save the length of the DUID
612  context->serverIdLen = ntohs(serverIdOption->length);
613  //Record the server DUID
614  osMemcpy(context->serverId, serverIdOption->value, context->serverIdLen);
615  //Flush the list of IPv6 addresses from the client's IA
616  dhcpv6ClientFlushAddrList(context);
617  }
618 
619  //Point to the first option
620  i = 0;
621 
622  //Loop through DHCPv6 options
623  while(i < length)
624  {
625  //Search for an IA_NA option
626  option = dhcpv6GetOption(message->options + i, length - i,
628 
629  //Unable to find the specified option?
630  if(option == NULL)
631  break;
632 
633  //Make sure the IA_NA option is valid
634  if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption))
635  {
636  //Get the parameters associated with the IA_NA
637  iaNaOption = (Dhcpv6IaNaOption *) option->value;
638 
639  //Check the IA identifier
640  if(ntohl(iaNaOption->iaId) == interface->id)
641  {
642  //The client examines the status code in each IA individually
643  status = dhcpv6GetStatusCode(iaNaOption->options,
644  ntohs(option->length) - sizeof(Dhcpv6IaNaOption));
645 
646  //The client must ignore any Advertise message that includes a
647  //Status Code option containing the value NoAddrsAvail
649  return;
650  }
651 
652  //Check the server preference value
653  if(serverPreference > context->serverPreference)
654  {
655  //Parse the contents of the IA_NA option
656  dhcpv6ClientParseIaNaOption(context, option);
657  }
658  }
659 
660  //Jump to the next option
661  i += sizeof(Dhcpv6Option) + ntohs(option->length);
662  }
663 
664  //Record the highest server preference value
665  if(serverPreference > context->serverPreference)
666  {
667  context->serverPreference = serverPreference;
668  }
669 
670  //If the client receives an Advertise message that includes a Preference
671  //option with a preference value of 255, the client immediately completes
672  //the message exchange
673  if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE)
674  {
675  //Continue configuration procedure
677  }
678  //The message exchange is not terminated before the first RT has elapsed
679  else if(context->retransmitCount > 1)
680  {
681  //Continue configuration procedure
683  }
684 }
685 
686 
687 /**
688  * @brief Parse Reply message
689  * @param[in] context Pointer to the DHCPv6 client context
690  * @param[in] message Pointer to the incoming message to parse
691  * @param[in] length Length of the incoming message
692  **/
693 
695  const Dhcpv6Message *message, size_t length)
696 {
697  error_t error;
698  uint_t i;
699  uint_t k;
700  uint_t n;
701  bool_t iaNaOptionFound;
702  systime_t minPreferredLifetime;
703  NetInterface *interface;
704  Dhcpv6StatusCode status;
705  Dhcpv6Option *option;
706  Dhcpv6Option *serverIdOption;
707  Dhcpv6ClientAddrEntry *entry;
708 
709  //Point to the underlying network interface
710  interface = context->settings.interface;
711 
712  //Discard any received packet that does not match the transaction ID
713  if(LOAD24BE(message->transactionId) != context->transactionId)
714  return;
715 
716  //Get the length of the Options field
717  length -= sizeof(Dhcpv6Message);
718 
719  //Search for the Client Identifier option
720  option = dhcpv6GetOption(message->options, length, DHCPV6_OPT_CLIENT_ID);
721 
722  //Discard any received packet that does not include a Client Identifier option
723  if(option == NULL)
724  return;
725 
726  //Check the length of the option
727  if(ntohs(option->length) != context->clientIdLen)
728  return;
729 
730  //Check whether the Client Identifier matches our identifier
731  if(osMemcmp(option->value, context->clientId, context->clientIdLen))
732  return;
733 
734  //Search for the Server Identifier option
735  serverIdOption = dhcpv6GetOption(message->options, length,
737 
738  //Discard any received packet that does not include a Server Identifier
739  //option
740  if(serverIdOption == NULL)
741  return;
742 
743  //Check the length of the server DUID
744  if(ntohs(serverIdOption->length) == 0)
745  return;
746 
747  if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
748  return;
749 
750  //Get the status code returned by the server
751  status = dhcpv6GetStatusCode(message->options, length);
752 
753  //Check current state
754  if(context->state == DHCPV6_STATE_SOLICIT)
755  {
756  //A Reply message is not acceptable when rapid commit is disallowed
757  if(!context->settings.rapidCommit)
758  return;
759 
760  //Search for the Rapid Commit option
761  option = dhcpv6GetOption(message->options, length,
763 
764  //The client discards any message that does not include a Rapid Commit
765  //option
766  if(option == NULL || ntohs(option->length) != 0)
767  return;
768  }
769  else if(context->state == DHCPV6_STATE_REQUEST)
770  {
771  //The client must discard the Reply message if the contents of the
772  //Server Identifier option do not match the server's DUID
773  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
774  return;
775  }
776  else if(context->state == DHCPV6_STATE_CONFIRM)
777  {
778  //When the client receives a NotOnLink status from the server in response
779  //to a Confirm message, the client performs DHCP server solicitation
780  if(status == DHCPV6_STATUS_NOT_ON_LINK)
781  {
782  //Restart the DHCP server discovery process
784 
785  //Exit immediately
786  return;
787  }
788  }
789  else if(context->state == DHCPV6_STATE_RENEW)
790  {
791  //The client must discard the Reply message if the contents of the
792  //Server Identifier option do not match the server's DUID
793  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
794  return;
795  }
796  else if(context->state == DHCPV6_STATE_REBIND)
797  {
798  //Do not check the server's DUID when the Reply message is received
799  //in response to a Rebind message
800  }
801  else if(context->state == DHCPV6_STATE_RELEASE)
802  {
803  //The client must discard the Reply message if the contents of the
804  //Server Identifier option do not match the server's DUID
805  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
806  return;
807 
808  //When the client receives a valid Reply message in response to a
809  //Release message, the client considers the Release event completed,
810  //regardless of the Status Code option(s) returned by the server
811  context->running = FALSE;
812 
813  //Reinitialize state machine
815 
816  //Exit immediately
817  return;
818  }
819  else if(context->state == DHCPV6_STATE_DECLINE)
820  {
821  //The client must discard the Reply message if the contents of the
822  //Server Identifier option do not match the server's DUID
823  if(!dhcpv6ClientCheckServerId(context, serverIdOption))
824  return;
825 
826  //When the client receives a valid Reply message in response to a
827  //Decline message, the client considers the Decline event completed,
828  //regardless of the Status Code option returned by the server
830 
831  //Exit immediately
832  return;
833  }
834  else
835  {
836  //Silently discard the Reply message
837  return;
838  }
839 
840  //Check status code
841  if(status == DHCPV6_STATUS_USE_MULTICAST)
842  {
843  //When the client receives a Reply message with a Status Code option
844  //with the value UseMulticast, the client records the receipt of the
845  //message and sends subsequent messages to the server through the
846  //interface on which the message was received using multicast
847  return;
848  }
849  else if(status == DHCPV6_STATUS_UNSPEC_FAILURE)
850  {
851  //If the client receives a Reply message with a Status Code containing
852  //UnspecFail, the server is indicating that it was unable to process
853  //the message due to an unspecified failure condition
854  return;
855  }
856 
857  //Any registered callback?
858  if(context->settings.parseOptionsCallback != NULL)
859  {
860  //Invoke user callback function
861  context->settings.parseOptionsCallback(context, message,
862  sizeof(Dhcpv6Message) + length);
863  }
864 
865  //Automatic DNS server configuration?
866  if(!context->settings.manualDnsConfig)
867  {
868  Dhcpv6DnsServersOption *dnsServersOption;
869 
870  //Search for the DNS Recursive Name Server option
871  option = dhcpv6GetOption(message->options, length,
873 
874  //Option found?
875  if(option != NULL && ntohs(option->length) >= sizeof(Dhcpv6DnsServersOption))
876  {
877  //Point to the DNS Recursive Name Server option
878  dnsServersOption = (Dhcpv6DnsServersOption *) option->value;
879 
880  //Retrieve the number of addresses
881  n = ntohs(option->length) / sizeof(Ipv6Addr);
882 
883  //Loop through the list of DNS servers
884  for(i = 0; i < n && i < IPV6_DNS_SERVER_LIST_SIZE; i++)
885  {
886  //Record DNS server address
887  interface->ipv6Context.dnsServerList[i] = dnsServersOption->address[i];
888  }
889  }
890  }
891 
892  //This flag will be set if a valid IA_NA option is found
893  iaNaOptionFound = FALSE;
894  //Point to the first option
895  i = 0;
896 
897  //Loop through DHCPv6 options
898  while(i < length)
899  {
900  //Search for an IA_NA option
901  option = dhcpv6GetOption(message->options + i, length - i,
903 
904  //Unable to find the specified option?
905  if(option == NULL)
906  break;
907 
908  //Parse the contents of the IA_NA option
909  error = dhcpv6ClientParseIaNaOption(context, option);
910 
911  //Check error code
912  if(error == NO_ERROR)
913  {
914  //A valid IA_NA option has been found
915  iaNaOptionFound = TRUE;
916  }
917  else if(error == ERROR_NOT_ON_LINK)
918  {
919  //When the client receives a NotOnLink status from the server in
920  //response to a Request, the client can either re-issue the Request
921  //without specifying any addresses or restart the DHCP server
922  //discovery process
924 
925  //Exit immediately
926  return;
927  }
928  else if(error == ERROR_NO_BINDING)
929  {
930  //When the client receives a Reply message in response to a Renew or
931  //Rebind message, the client sends a Request message if any of the IAs
932  //in the Reply message contains the NoBinding status code
934 
935  //Exit immediately
936  return;
937  }
938  else
939  {
940  //If an invalid option is received, the client discards the option
941  //and process the rest of the message
942  }
943 
944  //Jump to the next option
945  i += sizeof(Dhcpv6Option) + ntohs(option->length);
946  }
947 
948  //No usable addresses in any of the IAs?
949  if(!iaNaOptionFound)
950  {
951  //Check whether the client receives a Reply message in response to a
952  //Renew or Rebind message
953  if(context->state == DHCPV6_STATE_RENEW ||
954  context->state == DHCPV6_STATE_REBIND)
955  {
956  //The client sends a Renew/Rebind if the IA is not in the Reply message
957  }
958  else
959  {
960  //If the client finds no usable addresses in any of the IAs, it may try
961  //another server (perhaps restarting the DHCP server discovery process)
963  }
964 
965  //Exit immediately
966  return;
967  }
968 
969  //Total number of valid IPv6 in the IA
970  n = 0;
971  //Number of new IPv6 addresses in the IA
972  k = 0;
973  //Minimum preferred lifetime observed in the IA
974  minPreferredLifetime = 0;
975 
976  //Loop through the IPv6 addresses recorded by the DHCPv6 client
977  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
978  {
979  //Point to the current entry
980  entry = &context->ia.addrList[i];
981 
982  //Valid IPv6 address?
983  if(entry->validLifetime > 0)
984  {
985  //Total number of valid IPv6 in the IA
986  n++;
987 
988  //Save the minimum preferred lifetime that has been observed so far
989  if(minPreferredLifetime < entry->preferredLifetime)
990  {
991  minPreferredLifetime = entry->preferredLifetime;
992  }
993 
994  //Update lifetimes of the current IPv6 address
995  ipv6AddAddr(interface, &entry->addr, entry->validLifetime,
996  entry->preferredLifetime);
997 
998  //New IPv6 address added?
999  if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE)
1000  {
1001  k++;
1002  }
1003  }
1004  }
1005 
1006  //Make sure that the IA contains at least one IPv6 address
1007  if(n > 0)
1008  {
1009  //Save the length of the DUID
1010  context->serverIdLen = ntohs(serverIdOption->length);
1011  //Record the server DUID
1012  osMemcpy(context->serverId, serverIdOption->value, context->serverIdLen);
1013  //Save the time a which the lease was obtained
1014  context->leaseStartTime = osGetSystemTime();
1015 
1016  //Check the value of T1
1017  if(context->ia.t1 == 0)
1018  {
1019  //If T1 is set to 0 by the server, the client may send a Renew
1020  //message at the client's discretion
1021  if(minPreferredLifetime == DHCPV6_INFINITE_TIME)
1022  {
1023  context->ia.t1 = DHCPV6_INFINITE_TIME;
1024  }
1025  else
1026  {
1027  context->ia.t1 = minPreferredLifetime / 2;
1028  }
1029  }
1030 
1031  //Check the value of T2
1032  if(context->ia.t2 == 0)
1033  {
1034  //If T2 is set to 0 by the server, the client may send a Rebind
1035  //message at the client's discretion
1036  if(context->ia.t1 == DHCPV6_INFINITE_TIME)
1037  {
1038  context->ia.t2 = DHCPV6_INFINITE_TIME;
1039  }
1040  else
1041  {
1042  context->ia.t2 = context->ia.t1 + context->ia.t1 / 2;
1043  }
1044  }
1045 
1046  //Any addresses added in the IA?
1047  if(k > 0)
1048  {
1049  //Perform Duplicate Address Detection for the new IPv6 addresses
1051  }
1052  else
1053  {
1054  //Switch to the BOUND state
1056  }
1057  }
1058  else
1059  {
1060  //If the client finds no usable addresses in any of the IAs, it may try
1061  //another server (perhaps restarting the DHCP server discovery process)
1063  }
1064 }
1065 
1066 
1067 /**
1068  * @brief Parse IA_NA option
1069  * @param[in] context Pointer to the DHCPv6 client context
1070  * @param[in] option Pointer to the IA_NA option to parse
1071  * @return Error code
1072  **/
1073 
1075  const Dhcpv6Option *option)
1076 {
1077  error_t error;
1078  uint_t n;
1079  size_t i;
1080  size_t length;
1081  NetInterface *interface;
1082  Dhcpv6StatusCode status;
1083  Dhcpv6IaNaOption *iaNaOption;
1084 
1085  //Point to the underlying network interface
1086  interface = context->settings.interface;
1087 
1088  //Number of addresses found in the IA_NA option
1089  n = 0;
1090 
1091  //Make sure the IA_NA option is valid
1092  if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
1093  return ERROR_INVALID_LENGTH;
1094 
1095  //Get the parameters associated with the IA_NA
1096  iaNaOption = (Dhcpv6IaNaOption *) option->value;
1097  //Compute the length of IA_NA Options field
1098  length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption);
1099 
1100  //Check the IA identifier
1101  if(ntohl(iaNaOption->iaId) != interface->id)
1102  return ERROR_WRONG_IDENTIFIER;
1103 
1104  //If a client receives an IA_NA with T1 greater than T2, and both T1 and T2
1105  //are greater than 0, the client discards the IA_NA option and processes the
1106  //remainder of the message as though the server had not included the invalid
1107  //IA_NA option
1108  if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0)
1109  return ERROR_INVALID_PARAMETER;
1110 
1111  //The client examines the status code in each IA individually
1112  status = dhcpv6GetStatusCode(iaNaOption->options, length);
1113 
1114  //Check error code
1115  if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
1116  {
1117  //The client has received no usable address in the IA
1118  return ERROR_NO_ADDRESS;
1119  }
1120  else if(status == DHCPV6_STATUS_NO_BINDING)
1121  {
1122  //Client record (binding) unavailable
1123  return ERROR_NO_BINDING;
1124  }
1125  else if(status == DHCPV6_STATUS_NOT_ON_LINK)
1126  {
1127  //The prefix for the address is not appropriate for the link to which the
1128  //client is attached
1129  return ERROR_NOT_ON_LINK;
1130  }
1131  else if(status != DHCPV6_STATUS_SUCCESS)
1132  {
1133  //Failure, reason unspecified
1134  return ERROR_FAILURE;
1135  }
1136 
1137  //Record T1 and T2 times
1138  context->ia.t1 = ntohl(iaNaOption->t1);
1139  context->ia.t2 = ntohl(iaNaOption->t2);
1140 
1141  //Point to the first option
1142  i = 0;
1143 
1144  //Loop through IA_NA options
1145  while(i < length)
1146  {
1147  //Search for an IA Address option
1148  option = dhcpv6GetOption(iaNaOption->options + i, length - i,
1150 
1151  //Unable to find the specified option?
1152  if(option == NULL)
1153  break;
1154 
1155  //Parse the contents of the IA Address option
1156  error = dhcpv6ClientParseIaAddrOption(context, option);
1157 
1158  //Check status code
1159  if(!error)
1160  {
1161  //Increment the number of addresses found in the IA_NA option
1162  n++;
1163  }
1164 
1165  //Jump to the next option
1166  i += sizeof(Dhcpv6Option) + ntohs(option->length);
1167  }
1168 
1169  //No usable addresses in the IA_NA option?
1170  if(n == 0)
1171  {
1172  //Report an error
1173  return ERROR_NO_ADDRESS;
1174  }
1175 
1176  //Successful processing
1177  return NO_ERROR;
1178 }
1179 
1180 
1181 /**
1182  * @brief Parse IA Address option
1183  * @param[in] context Pointer to the DHCPv6 client context
1184  * @param[in] option Pointer to the IA Address option to parse
1185  * @return Error code
1186  **/
1187 
1189  const Dhcpv6Option *option)
1190 {
1191  size_t length;
1192  uint32_t validLifetime;
1193  uint32_t preferredLifetime;
1194  Dhcpv6StatusCode status;
1195  Dhcpv6IaAddrOption *iaAddrOption;
1196 
1197  //Make sure the IA Address option is valid
1198  if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
1199  return ERROR_INVALID_LENGTH;
1200 
1201  //Point to the contents of the IA Address option
1202  iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
1203  //Compute the length of IA Address Options field
1204  length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption);
1205 
1206  //Convert lifetimes to host byte order
1207  validLifetime = ntohl(iaAddrOption->validLifetime);
1208  preferredLifetime = ntohl(iaAddrOption->preferredLifetime);
1209 
1210  //A client discards any addresses for which the preferred lifetime is
1211  //greater than the valid lifetime
1213  return ERROR_INVALID_PARAMETER;
1214 
1215  //The client examines the status code in each IA Address
1216  status = dhcpv6GetStatusCode(iaAddrOption->options, length);
1217 
1218  //Any error to report?
1219  if(status != DHCPV6_STATUS_SUCCESS)
1220  return ERROR_FAILURE;
1221 
1222  //Check the value of the Valid Lifetime
1223  if(iaAddrOption->validLifetime > 0)
1224  {
1225  //Add any new addresses in the IA option to the IA as recorded by the
1226  //client
1227  dhcpv6ClientAddAddr(context, &iaAddrOption->address,
1229  }
1230  else
1231  {
1232  //Discard any addresses from the IA, as recorded by the client, that
1233  //have a valid lifetime of 0 in the IA Address option
1234  dhcpv6ClientRemoveAddr(context, &iaAddrOption->address);
1235  }
1236 
1237  //Successful processing
1238  return NO_ERROR;
1239 }
1240 
1241 
1242 /**
1243  * @brief Add an IPv6 address to the IA
1244  * @param[in] context Pointer to the DHCPv6 client context
1245  * @param[in] addr IPv6 address to be added
1246  * @param[in] validLifetime Valid lifetime, in seconds
1247  * @param[in] preferredLifetime Preferred lifetime, in seconds
1248  **/
1249 
1251  uint32_t validLifetime, uint32_t preferredLifetime)
1252 {
1253  uint_t i;
1254  Dhcpv6ClientAddrEntry *entry;
1255  Dhcpv6ClientAddrEntry *firstFreeEntry;
1256 
1257  //Keep track of the first free entry
1258  firstFreeEntry = NULL;
1259 
1260  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1261  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1262  {
1263  //Point to the current entry
1264  entry = &context->ia.addrList[i];
1265 
1266  //Valid IPv6 address?
1267  if(entry->validLifetime > 0)
1268  {
1269  //Check whether the current entry matches the specified address
1270  if(ipv6CompAddr(&entry->addr, addr))
1271  break;
1272  }
1273  else
1274  {
1275  //Keep track of the first free entry
1276  if(firstFreeEntry == NULL)
1277  {
1278  firstFreeEntry = entry;
1279  }
1280  }
1281  }
1282 
1283  //No matching entry found?
1285  {
1286  entry = firstFreeEntry;
1287  }
1288 
1289  //Update the entry if necessary
1290  if(entry != NULL)
1291  {
1292  //Save IPv6 address
1293  entry->addr = *addr;
1294 
1295  //Save lifetimes
1296  entry->validLifetime = validLifetime;
1298  }
1299 }
1300 
1301 
1302 /**
1303  * @brief Remove an IPv6 address from the IA
1304  * @param[in] context Pointer to the DHCPv6 client context
1305  * @param[in] addr IPv6 address to be removed
1306  **/
1307 
1309 {
1310  uint_t i;
1311  NetInterface *interface;
1312  Dhcpv6ClientAddrEntry *entry;
1313 
1314  //Point to the underlying network interface
1315  interface = context->settings.interface;
1316 
1317  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1318  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1319  {
1320  //Point to the current entry
1321  entry = &context->ia.addrList[i];
1322 
1323  //Valid IPv6 address?
1324  if(entry->validLifetime > 0)
1325  {
1326  //Check whether the current entry matches the specified address
1327  if(ipv6CompAddr(&entry->addr, addr))
1328  {
1329  //The IPv6 address is no more valid and should be removed from the
1330  //list of IPv6 addresses assigned to the interface
1331  ipv6RemoveAddr(interface, addr);
1332 
1333  //Remove the IPv6 address from the IA
1334  entry->validLifetime = 0;
1335  }
1336  }
1337  }
1338 }
1339 
1340 
1341 /**
1342  * @brief Flush the list of IPv6 addresses from the IA
1343  * @param[in] context Pointer to the DHCPv6 client context
1344  **/
1345 
1347 {
1348  uint_t i;
1349  NetInterface *interface;
1350  Dhcpv6ClientAddrEntry *entry;
1351 
1352  //Point to the underlying network interface
1353  interface = context->settings.interface;
1354 
1355  //Loop through the IPv6 addresses recorded by the DHCPv6 client
1356  for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
1357  {
1358  //Point to the current entry
1359  entry = &context->ia.addrList[i];
1360 
1361  //Valid IPv6 address?
1362  if(entry->validLifetime > 0)
1363  {
1364  //The IPv6 address is no more valid and should be removed from the
1365  //list of IPv6 addresses assigned to the interface
1366  ipv6RemoveAddr(interface, &entry->addr);
1367 
1368  //Remove the IPv6 address from the IA
1369  entry->validLifetime = 0;
1370  }
1371  }
1372 }
1373 
1374 
1375 /**
1376  * @brief Generate client's DUID
1377  * @param[in] context Pointer to the DHCPv6 client context
1378  * @return Error code
1379  **/
1380 
1382 {
1383  NetInterface *interface;
1384  Dhcpv6DuidLl *duid;
1385 #if (ETH_SUPPORT == ENABLED)
1386  NetInterface *logicalInterface;
1387 #endif
1388 
1389  //Point to the underlying network interface
1390  interface = context->settings.interface;
1391 
1392  //Point to the buffer where to format the client's DUID
1393  duid = (Dhcpv6DuidLl *) context->clientId;
1394 
1395 #if (ETH_SUPPORT == ENABLED)
1396  //Point to the logical interface
1397  logicalInterface = nicGetLogicalInterface(interface);
1398 
1399  //Generate a DUID-LL from the MAC address
1400  duid->type = HTONS(DHCPV6_DUID_LL);
1401  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH);
1402  duid->linkLayerAddr = logicalInterface->macAddr;
1403 #else
1404  //Generate a DUID-LL from the EUI-64 identifier
1405  duid->type = HTONS(DHCPV6_DUID_LL);
1406  duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64);
1407  duid->linkLayerAddr = interface->eui64;
1408 #endif
1409 
1410  //Length of the newly generated DUID
1411  context->clientIdLen = sizeof(Dhcpv6DuidLl);
1412 
1413  //Successful processing
1414  return NO_ERROR;
1415 }
1416 
1417 
1418 /**
1419  * @brief Generate a link-local address
1420  * @param[in] context Pointer to the DHCPv6 client context
1421  * @return Error code
1422  **/
1423 
1425 {
1426  error_t error;
1427  NetInterface *interface;
1428  Ipv6Addr addr;
1429 
1430  //Point to the underlying network interface
1431  interface = context->settings.interface;
1432 
1433  //Check whether a link-local address has been manually assigned
1434  if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
1435  interface->ipv6Context.addrList[0].permanent)
1436  {
1437  //Keep using the current link-local address
1438  error = NO_ERROR;
1439  }
1440  else
1441  {
1442  //A link-local address is formed by combining the well-known link-local
1443  //prefix fe80::/10 with the interface identifier
1444  ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
1445 
1446 #if (NDP_SUPPORT == ENABLED)
1447  //Check whether Duplicate Address Detection should be performed
1448  if(interface->ndpContext.dupAddrDetectTransmits > 0)
1449  {
1450  //Use the link-local address as a tentative address
1451  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
1453  }
1454  else
1455 #endif
1456  {
1457  //The use of the link-local address is now unrestricted
1458  error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
1460  }
1461  }
1462 
1463  //Return status code
1464  return error;
1465 }
1466 
1467 
1468 /**
1469  * @brief Check the Server Identifier option
1470  * @param[in] context Pointer to the DHCPv6 client context
1471  * @param[in] serverIdOption Pointer to the Server Identifier option
1472  * @return TRUE if the option matches the server's DUID, else FALSE
1473  **/
1474 
1476  Dhcpv6Option *serverIdOption)
1477 {
1478  bool_t valid = FALSE;
1479 
1480  //Check the length of the Server Identifier option
1481  if(ntohs(serverIdOption->length) == context->serverIdLen)
1482  {
1483  //Check whether the Server Identifier option matches the server's DUID
1484  if(!osMemcmp(serverIdOption->value, context->serverId,
1485  context->serverIdLen))
1486  {
1487  valid = TRUE;
1488  }
1489  }
1490 
1491  //Return TRUE if the option matches the server's DUID
1492  return valid;
1493 }
1494 
1495 
1496 /**
1497  * @brief Manage DHCPv6 configuration timeout
1498  * @param[in] context Pointer to the DHCPv6 client context
1499  **/
1500 
1502 {
1503  systime_t time;
1504  NetInterface *interface;
1505 
1506  //Point to the underlying network interface
1507  interface = context->settings.interface;
1508 
1509  //Get current time
1510  time = osGetSystemTime();
1511 
1512  //Any registered callback?
1513  if(context->settings.timeoutEvent != NULL)
1514  {
1515  //DHCPv6 configuration timeout?
1516  if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
1517  {
1518  //Ensure the callback function is only called once
1519  if(!context->timeoutEventDone)
1520  {
1521  //Release exclusive access
1523  //Invoke user callback function
1524  context->settings.timeoutEvent(context, interface);
1525  //Get exclusive access
1527 
1528  //Set flag
1529  context->timeoutEventDone = TRUE;
1530  }
1531  }
1532  }
1533 }
1534 
1535 
1536 /**
1537  * @brief Compute the time elapsed since the client sent the first message
1538  * @param[in] context Pointer to the DHCPv6 client context
1539  * @return The elapsed time expressed in hundredths of a second
1540  **/
1541 
1543 {
1544  systime_t time;
1545 
1546  //Check retransmission counter
1547  if(context->retransmitCount == 0)
1548  {
1549  //The elapsed time must be 0 for the first message
1550  time = 0;
1551  }
1552  else
1553  {
1554  //Compute the time elapsed since the client sent the first message (in
1555  //hundredths of a second)
1556  time = (osGetSystemTime() - context->exchangeStartTime) / 10;
1557 
1558  //The value 0xFFFF is used to represent any elapsed time values greater
1559  //than the largest time value that can be represented
1560  time = MIN(time, 0xFFFF);
1561  }
1562 
1563  //Convert the 16-bit value to network byte order
1564  return htons(time);
1565 }
1566 
1567 
1568 /**
1569  * @brief Update DHCPv6 FSM state
1570  * @param[in] context Pointer to the DHCPv6 client context
1571  * @param[in] newState New DHCPv6 state to switch to
1572  * @param[in] delay Initial delay
1573  **/
1574 
1576  Dhcpv6State newState, systime_t delay)
1577 {
1578  systime_t time;
1579 
1580  //Get current time
1581  time = osGetSystemTime();
1582 
1583 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
1584  //Sanity check
1585  if(newState <= DHCPV6_STATE_DECLINE)
1586  {
1587  //DHCPv6 FSM states
1588  static const char_t *const stateLabel[] =
1589  {
1590  "INIT",
1591  "SOLICIT",
1592  "REQUEST",
1593  "INIT-CONFIRM",
1594  "CONFIRM",
1595  "DAD",
1596  "BOUND",
1597  "RENEW",
1598  "REBIND",
1599  "RELEASE",
1600  "DECLINE"
1601  };
1602 
1603  //Debug message
1604  TRACE_INFO("%s: DHCPv6 client %s state\r\n",
1605  formatSystemTime(time, NULL), stateLabel[newState]);
1606  }
1607 #endif
1608 
1609  //Set time stamp
1610  context->timestamp = time;
1611  //Set initial delay
1612  context->timeout = delay;
1613  //Reset retransmission counter
1614  context->retransmitCount = 0;
1615  //Switch to the new state
1616  context->state = newState;
1617 
1618  //Any registered callback?
1619  if(context->settings.stateChangeEvent != NULL)
1620  {
1621  NetInterface *interface;
1622 
1623  //Point to the underlying network interface
1624  interface = context->settings.interface;
1625 
1626  //Release exclusive access
1628  //Invoke user callback function
1629  context->settings.stateChangeEvent(context, interface, newState);
1630  //Get exclusive access
1632  }
1633 }
1634 
1635 
1636 /**
1637  * @brief Dump DHCPv6 configuration for debugging purpose
1638  * @param[in] context Pointer to the DHCPv6 client context
1639  **/
1640 
1642 {
1643 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
1644  uint_t i;
1645  NetInterface *interface;
1646  Ipv6Context *ipv6Context;
1647 
1648  //Point to the underlying network interface
1649  interface = context->settings.interface;
1650  //Point to the IPv6 context
1651  ipv6Context = &interface->ipv6Context;
1652 
1653  //Debug message
1654  TRACE_INFO("\r\n");
1655  TRACE_INFO("DHCPv6 configuration:\r\n");
1656 
1657  //Lease start time
1658  TRACE_INFO(" Lease Start Time = %s\r\n",
1659  formatSystemTime(context->leaseStartTime, NULL));
1660 
1661  //T1 parameter
1662  TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->ia.t1);
1663  //T2 parameter
1664  TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->ia.t2);
1665 
1666  //Global addresses
1667  for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
1668  {
1669  TRACE_INFO(" Global Address %u = %s\r\n", i,
1670  ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
1671  }
1672 
1673  //DNS servers
1674  for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
1675  {
1676  TRACE_INFO(" DNS Server %u = %s\r\n", i + 1,
1677  ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
1678  }
1679 
1680  //Debug message
1681  TRACE_INFO("\r\n");
1682 #endif
1683 }
1684 
1685 #endif
@ IPV6_ADDR_STATE_TENTATIVE
An address whose uniqueness on a link is being verified.
Definition: ipv6.h:174
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2329
#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:199
int bool_t
Definition: compiler_port.h:53
@ DHCPV6_STATUS_NOT_ON_LINK
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
signed int int_t
Definition: compiler_port.h:49
bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context, Dhcpv6Option *serverIdOption)
Check the Server Identifier option.
#define netMutex
Definition: net_legacy.h:195
@ 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:56
@ DHCPV6_MSG_TYPE_REBIND
Definition: dhcpv6_common.h:95
void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context)
DECLINE state.
Dhcpv6DnsServersOption
Ipv6Addr
Definition: ipv6.h:260
#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:153
@ 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:127
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:496
@ IPV6_ADDR_STATE_INVALID
An address that is not assigned to any interface.
Definition: ipv6.h:173
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:756
#define DHCPV6_MAX_MSG_SIZE
Definition: dhcpv6_common.h:44
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
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
IA address entry.
systime_t dhcpv6ClientTickCounter
#define NetRxAncillary
Definition: net_misc.h:40
Dhcpv6PreferenceOption
#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: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.
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:658
@ 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:201
Dhcpv6StatusCode
Status code.
#define IPV6_DNS_SERVER_LIST_SIZE
Definition: ipv6.h:93
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
uint8_t length
Definition: tcp.h:368
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:339
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:96
#define TRACE_DEBUG(...)
Definition: debug.h:107
char char_t
Definition: compiler_port.h:48
#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:221
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:65
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:1402
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 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:123
@ DHCPV6_OPT_SERVER_ID
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:175
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
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:53
@ DHCPV6_STATE_INIT_CONFIRM
Common DNS routines.
@ ERROR_NO_BINDING
Definition: error.h:200
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