• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  *
4  * @defgroup dhcp6 DHCPv6
5  * @ingroup ip6
6  * DHCPv6 client: IPv6 address autoconfiguration as per
7  * RFC 3315 (stateful DHCPv6) and
8  * RFC 3736 (stateless DHCPv6).
9  *
10  * For now, only stateless DHCPv6 and stateful DHCPv6 client is implemented!
11  *
12  * dhcp6_enable_stateful() enables stateful DHCPv6 for a netif (stateless disabled)\n
13  * dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)\n
14  * dhcp6_disable() disable DHCPv6 for a netif
15  *
16  * When enabled, requests are only issued after receipt of RA with the
17  * corresponding bits set.
18  * When stateful DHCPv6 enabled, requests are sent after state of linklocal address is
19  * IP6_ADDR_PREFERRED, not depend on receipt of an RA message with the
20  * ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
21  */
22 
23 /*
24  * Copyright (c) 2018 Simon Goldschmidt
25  * All rights reserved.
26  *
27  * Redistribution and use in source and binary forms, with or without modification,
28  * are permitted provided that the following conditions are met:
29  *
30  * 1. Redistributions of source code must retain the above copyright notice,
31  *    this list of conditions and the following disclaimer.
32  * 2. Redistributions in binary form must reproduce the above copyright notice,
33  *    this list of conditions and the following disclaimer in the documentation
34  *    and/or other materials provided with the distribution.
35  * 3. The name of the author may not be used to endorse or promote products
36  *    derived from this software without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
39  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
41  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
43  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
46  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
47  * OF SUCH DAMAGE.
48  *
49  * This file is part of the lwIP TCP/IP stack.
50  *
51  * Author: Simon Goldschmidt <goldsimon@gmx.de>
52  */
53 
54 #include "lwip/opt.h"
55 
56 #if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
57 
58 #include "lwip/dhcp6.h"
59 #include "lwip/prot/dhcp6.h"
60 #include "lwip/def.h"
61 #include "lwip/udp.h"
62 #include "lwip/dns.h"
63 #include "lwip/sys.h"
64 
65 #include <string.h>
66 static inline void dhcp6_put_ushort(unsigned char *obuf, u16_t val);
67 #if LWIP_IPV6_DHCP6_STATEFUL
68 static void dhcp6_stateful_struct_free(struct dhcp6 *dhcp6_p);
69 static void dhcp6_linklocal_address_check(struct netif *netif_p, struct dhcp6 *dhcp6_p);
70 static err_t dhcp6_form_duid(const struct netif *netif_p, struct dhcp6 *dhcp6_p, u8_t type);
71 static err_t dhcp6_create_iaid(const struct netif *netif_p, struct dhcp6 *dhcp6_p, u8_t idx);
72 static err_t dhcp6_stateful_init(const struct netif *netif_p, struct dhcp6 *dhcp6_p);
73 static void dhcp6_rebind_failure_cleanup(struct netif *netif_p, struct dhcp6 *dhcp6_p);
74 static void dhcp6_post_release_cleanup(struct dhcp6 *dhcp6_p);
75 static void dhcp6_set_timeout_params(struct dhcp6 *dhcp6_p);
76 static void dhcp6_stateful_timeout(struct netif *netif_p, struct dhcp6 *dhcp6_p);
77 static void dhcp6_solicit_to_request(struct netif *netif_p, struct dhcp6 *dhcp6_p);
78 static void dhcp6_solicit(struct netif *netif_p, struct dhcp6 *dhcp6_p);
79 static void dhcp6_request(struct netif *netif_p, struct dhcp6 *dhcp6_p);
80 static void dhcp6_request_next_server(struct netif *netif_p, struct dhcp6 *dhcp6_p);
81 static void dhcp6_renew(struct netif *netif_p, struct dhcp6 *dhcp6_p);
82 static void dhcp6_rebind(struct netif *netif_p, struct dhcp6 *dhcp6_p);
83 static void dhcp6_release(struct netif *netif_p, struct dhcp6 *dhcp6_p);
84 static void dhcp6_decline(struct netif *netif_p, struct dhcp6 *dhcp6_p);
85 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
86 
87 #ifdef LWIP_HOOK_FILENAME
88 #include LWIP_HOOK_FILENAME
89 #endif
90 #ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
91 #define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
92 #endif
93 #ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
94 #define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
95 #endif
96 
97 #if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS
98 #if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS
99 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS
100 #else
101 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
102 #endif
103 #else
104 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0
105 #endif
106 
107 
108 /** Option handling: options are parsed in dhcp6_parse_reply
109  * and saved in an array where other functions can load them from.
110  * This might be moved into the struct dhcp6 (not necessarily since
111  * lwIP is single-threaded and the array is only used while in recv
112  * callback). */
113 enum dhcp6_option_idx {
114   DHCP6_OPTION_IDX_CLI_ID = 0,
115   DHCP6_OPTION_IDX_SERVER_ID,
116 #if LWIP_IPV6_DHCP6_STATEFUL
117   DHCP6_OPTION_IDX_IA_NA,
118   DHCP6_OPTION_IDX_PREFERENCE,
119 #if DHCP6_ENABLE_UNICAST_SUPPORT
120   DHCP6_OPTION_IDX_UNICAST,
121 #endif /* DHCP6_ENABLE_UNICAST_SUPPORT */
122   DHCP6_OPTION_IDX_STATUS_CODE,
123 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
124 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
125   DHCP6_OPTION_IDX_DNS_SERVER,
126   DHCP6_OPTION_IDX_DOMAIN_LIST,
127 #endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
128 #if LWIP_DHCP6_GET_NTP_SRV
129   DHCP6_OPTION_IDX_NTP_SERVER,
130 #endif /* LWIP_DHCP_GET_NTP_SRV */
131   DHCP6_OPTION_IDX_MAX
132 };
133 
134 struct dhcp6_option_info {
135   u8_t option_given;
136   u16_t val_start;
137   u16_t val_length;
138 };
139 
140 #if LWIP_IPV6_DHCP6_STATEFUL
141 #ifdef LWIP_DEBUG
142 static const char *dhcp6_stateful_message_string[] = {
143   "SOLICIT",              /* 1 */
144   "ADVERTISE",            /* 2 */
145   "REQUEST",              /* 3 */
146   "CONFIRM",              /* 4 */
147   "RENEW",                /* 5 */
148   "REBIND",               /* 6 */
149   "REPLY",                /* 7 */
150   "RELEASE",              /* 8 */
151   "DECLINE",              /* 9 */
152   "RECONFIGURE",          /* 10 */
153   "INFOREQUEST",          /* 11 */
154   "RELAYFORW",            /* 12 */
155   "RELAYREPL"             /* 13 */
156 };
157 #define DHCP6_STATEFUL_MESSAGE_TYPE_TO_STRING(message_type) \
158   (((message_type) > 0 && (message_type) < DHCP6_MESSAGE_END) ? \
159   dhcp6_stateful_message_string[(message_type)-1] : "UNKNOWN MESSAGE")
160 #endif
161 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
162 /** Holds the decoded option info, only valid while in dhcp6_recv. */
163 struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX];
164 
165 #define dhcp6_option_given(dhcp6, idx)           (dhcp6_rx_options[idx].option_given != 0)
166 #define dhcp6_got_option(dhcp6, idx)             (dhcp6_rx_options[idx].option_given = 1)
167 #define dhcp6_clear_option(dhcp6, idx)           (dhcp6_rx_options[idx].option_given = 0)
168 #define dhcp6_clear_all_options(dhcp6)           (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options)))
169 #define dhcp6_get_option_start(dhcp6, idx)       (dhcp6_rx_options[idx].val_start)
170 #define dhcp6_get_option_length(dhcp6, idx)      (dhcp6_rx_options[idx].val_length)
171 #define dhcp6_set_option(dhcp6, idx, start, len) do { dhcp6_rx_options[idx].val_start = (start); dhcp6_rx_options[idx].val_length = (len); }while(0)
172 
173 
174 const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
175 const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF050000, 0, 0, 0x00010003);
176 
177 /* receive, unfold, parse and free incoming messages */
178 static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
179 
180 static void
dhcp6_pcb_del(struct dhcp6 * dhcp6_p)181 dhcp6_pcb_del(struct dhcp6 *dhcp6_p)
182 {
183   udp_remove(dhcp6_p->pcb);
184   dhcp6_p->pcb = NULL;
185 
186   return;
187 }
188 
189 static err_t
dhcp6_pcb_new(struct dhcp6 * dhcp6_p)190 dhcp6_pcb_new(struct dhcp6 *dhcp6_p)
191 {
192   err_t result;
193 
194    /* allocate UDP PCB */
195   dhcp6_p->pcb = udp_new_ip6();
196   if (dhcp6_p->pcb == NULL) {
197     DHCP6_STATS_INC(dhcp6.memerr);
198     return ERR_MEM;
199   }
200 
201 #if LWIP_SO_PRIORITY
202   dhcp6_p->pcb->priority = LWIP_PKT_PRIORITY_DHCP6;
203 #endif /* LWIP_SO_PRIORITY */
204 
205   ip_set_option(dhcp6_p->pcb, SOF_BROADCAST);
206 
207   result = udp_bind(dhcp6_p->pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
208   if (result != ERR_OK) {
209     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6 bind failed"));
210     dhcp6_pcb_del(dhcp6_p);
211     return result;
212   }
213 
214   result = udp_connect(dhcp6_p->pcb, IP6_ADDR_ANY, DHCP6_SERVER_PORT);
215   if (result != ERR_OK) {
216     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6 connect failed"));
217     dhcp6_pcb_del(dhcp6_p);
218     return result;
219   }
220 
221   udp_recv(dhcp6_p->pcb, dhcp6_recv, NULL);
222 
223   return ERR_OK;
224 }
225 
226 /**
227  * @ingroup dhcp6
228  * Set a statically allocated struct dhcp6 to work with.
229  * Using this prevents dhcp6_start to allocate it using mem_malloc.
230  *
231  * @param netif the netif for which to set the struct dhcp
232  * @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
233  */
234 void
dhcp6_set_struct(struct netif * netif,struct dhcp6 * dhcp6)235 dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
236 {
237   LWIP_ASSERT("netif != NULL", netif != NULL);
238   LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
239   LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
240 
241   /* clear data structure */
242   memset(dhcp6, 0, sizeof(struct dhcp6));
243   /* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */
244   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
245 }
246 
247 /**
248  * @ingroup dhcp6
249  * Removes a struct dhcp6 from a netif.
250  *
251  * ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
252  *            struct dhcp6 since the memory is passed back to the heap.
253  *
254  * @param netif the netif from which to remove the struct dhcp
255  */
dhcp6_cleanup(struct netif * netif)256 void dhcp6_cleanup(struct netif *netif)
257 {
258   LWIP_ERROR("invalid netif", netif != NULL, return);
259   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
260 
261   if (dhcp6 != NULL) {
262 #if LWIP_IPV6_DHCP6_STATEFUL
263     dhcp6_stateful_struct_free(dhcp6);
264 #else
265     LWIP_UNUSED_ARG(dhcp6);
266 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
267     mem_free(dhcp6);
268     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
269   }
270 }
271 
272 static struct dhcp6*
dhcp6_get_struct(struct netif * netif,const char * dbg_requester)273 dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
274 {
275 #ifndef LWIP_DEBUG
276   LWIP_UNUSED_ARG(dbg_requester);
277 #endif
278   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
279   if (dhcp6 == NULL) {
280     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
281     dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
282     if (dhcp6 == NULL) {
283       DHCP6_STATS_INC(dhcp6.memerr);
284       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
285       return NULL;
286     }
287 
288     /* clear data structure, this implies DHCP6_STATE_OFF */
289     memset(dhcp6, 0, sizeof(struct dhcp6));
290     /* store this dhcp6 client in the netif */
291     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
292   } else {
293     /* already has DHCP6 client attached */
294     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
295   }
296 
297   if (dhcp6->pcb == NULL) {
298     if (dhcp6_pcb_new(dhcp6) != ERR_OK) {
299       mem_free(dhcp6);
300       netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
301       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6 pcb failed\n", dbg_requester));
302       return NULL;
303     }
304     /* bind dhcp udp_pcb to specific netif, this could make dhcp client start on multiple netif */
305     dhcp6->pcb->netif_idx = netif->ifindex;
306     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6 pcb", dbg_requester));
307   }
308   return dhcp6;
309 }
310 
311 /*
312  * Set the DHCPv6 state
313  * If the state changed, reset the number of tries.
314  */
315 static void
dhcp6_set_state(struct dhcp6 * dhcp6,u8_t new_state,const char * dbg_caller)316 dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller)
317 {
318 #ifndef LWIP_DEBUG
319     LWIP_UNUSED_ARG(dbg_caller);
320 #endif
321   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n",
322     dhcp6->state, new_state, dbg_caller));
323   if (new_state != dhcp6->state) {
324     dhcp6->state = new_state;
325     dhcp6->tries = 0;
326     dhcp6->request_timeout = 0;
327   }
328 }
329 
330 static int
dhcp6_stateless_enabled(struct dhcp6 * dhcp6)331 dhcp6_stateless_enabled(struct dhcp6 *dhcp6)
332 {
333   if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) ||
334       (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) {
335     return 1;
336   }
337   return 0;
338 }
339 
340 int
dhcp6_stateful_enabled(struct dhcp6 * dhcp6)341 dhcp6_stateful_enabled(struct dhcp6 *dhcp6)
342 {
343   if (dhcp6->state == DHCP6_STATE_OFF) {
344     return 0;
345   }
346   if (dhcp6_stateless_enabled(dhcp6)) {
347     return 0;
348   }
349   return 1;
350 }
351 
352 /**
353  * @ingroup dhcp6
354  * Enable stateful DHCPv6 on this netif
355  * Requests are sent on receipt of an RA message with the
356  * ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
357  *
358  * A struct dhcp6 will be allocated for this netif if not
359  * set via @ref dhcp6_set_struct before.
360  *
361  */
362 err_t
dhcp6_enable_stateful(struct netif * netif)363 dhcp6_enable_stateful(struct netif *netif)
364 {
365   struct dhcp6 *dhcp6 = NULL;
366   s8_t addr_idx = LWIP_IPV6_NUM_ADDRESSES;
367   s8_t j;
368 
369   LWIP_UNUSED_ARG(netif);
370 #if LWIP_IPV6_DHCP6_STATEFUL
371   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_enable_stateful(netif=%p) %s%"U16_F"\n",
372     (void *)netif, netif->name, (u16_t)netif->num));
373 
374   if (!ip6_addr_ispreferred(netif_ip6_addr_state(netif, 0))) {
375     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateful(): linklocal address is invalid"));
376     return ERR_VAL;
377   }
378   for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
379     if ((ip6_addr_isinvalid(netif_ip6_addr_state(netif, j))) ||
380         (ip6_addr_isduplicated(netif_ip6_addr_state(netif, j)))) {
381       addr_idx = j;
382       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("ip6 addr addr_idx= %u is free\n", addr_idx));
383       break;
384     }
385   }
386   if (addr_idx == LWIP_IPV6_NUM_ADDRESSES) {
387     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("IA_NA addr assign failed, the netif IP address space is not enough\n"));
388     return ERR_NOADDR;
389   }
390 
391   dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateful()");
392   if (dhcp6 == NULL) {
393     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateful(): get dhcp6 struct failed"));
394     return ERR_MEM;
395   }
396   if (dhcp6_stateful_enabled(dhcp6)) {
397     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateful(): stateful DHCPv6 already enabled"));
398     return ERR_OK;
399   } else if (dhcp6->state != DHCP6_STATE_OFF) {
400     /* stateless running */
401 #if LWIP_IPV6_AUTOCONFIG
402     netif_set_ip6_autoconfig_disabled(netif);
403 #endif /* LWIP_IPV6_AUTOCONFIG */
404     /* not handle message from stateless server */
405     dhcp6->xid = 0;
406     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateful(): switching from stateless to stateful DHCPv6"));
407   }
408 
409   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateful(): stateful DHCPv6 enabled\n"));
410   if (dhcp6->struct_init == 0) {
411     (void)dhcp6_stateful_init(netif, dhcp6);
412   }
413   dhcp6->addr_idx = addr_idx;
414   dhcp6_set_state(dhcp6, DHCP6_STATE_STATEFUL_INIT, "dhcp6_enable_stateful");
415   dhcp6_linklocal_address_check(netif, dhcp6);
416   return ERR_OK;
417 #else
418   LWIP_UNUSED_ARG(j);
419   LWIP_UNUSED_ARG(addr_idx);
420   LWIP_UNUSED_ARG(dhcp6);
421   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateful(): not support stateful DHCPv6"));
422   return ERR_VAL;
423 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
424 }
425 
426 /**
427  * @ingroup dhcp6
428  * Enable stateless DHCPv6 on this netif
429  * Requests are sent on receipt of an RA message with the
430  * ND6_RA_FLAG_OTHER_CONFIG flag set.
431  *
432  * A struct dhcp6 will be allocated for this netif if not
433  * set via @ref dhcp6_set_struct before.
434  */
435 err_t
dhcp6_enable_stateless(struct netif * netif)436 dhcp6_enable_stateless(struct netif *netif)
437 {
438   struct dhcp6 *dhcp6;
439 
440   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
441               ("dhcp6_enable_stateless netif= %s%"U16_F"\n", netif->name, (u16_t)netif->num));
442 
443   dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()");
444   if (dhcp6 == NULL) {
445     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): get dhcp6 struct failed"));
446     return ERR_MEM;
447   }
448   if (dhcp6_stateless_enabled(dhcp6)) {
449     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled"));
450     return ERR_OK;
451   } else if (dhcp6->state != DHCP6_STATE_OFF) {
452     /* stateful running */
453 #if LWIP_IPV6_DHCP6_STATEFUL
454     (void)dhcp6_release_stateful(netif);
455     dhcp6_post_release_cleanup(dhcp6);
456     /* won't handle the REPLY of this RELEASE when timeout */
457     dhcp6->request_timeout = 0;
458     /* not handle message from stateful server */
459     dhcp6->xid = 0;
460     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6"));
461 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
462   }
463   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n"));
464   dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless");
465   return ERR_OK;
466 }
467 
468 /**
469  * @ingroup dhcp6
470  * Disable stateful or stateless DHCPv6 on this netif
471  * Requests are sent on receipt of an RA message with the
472  * ND6_RA_FLAG_OTHER_CONFIG flag set.
473  */
474 void
dhcp6_disable(struct netif * netif)475 dhcp6_disable(struct netif *netif)
476 {
477   struct dhcp6 *dhcp6;
478 
479   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
480               ("dhcp6_disable(netif=%p) %s%"U16_F"\n", (void *)netif, netif->name, (u16_t)netif->num));
481 
482   dhcp6 = netif_dhcp6_data(netif);
483   if (dhcp6 != NULL) {
484     if (dhcp6->state != DHCP6_STATE_OFF) {
485       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n",
486         (dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful")));
487 #if LWIP_IPV6_DHCP6_STATEFUL
488       /* try to send RELEASE if needed */
489       (void)dhcp6_release_stateful(netif);
490       /* the REPLY of this RELEASE won't be handled */
491       dhcp6_post_release_cleanup(dhcp6);
492 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
493       /* not handle message from server if enabled immediately */
494       dhcp6->xid = 0;
495       dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable");
496     }
497     if (dhcp6->pcb != NULL) {
498       dhcp6_pcb_del(dhcp6);
499     }
500   }
501 }
502 
503 /*
504  * @ingroup dhcp6
505  * Stateful DHCPv6 release on this netif
506  */
507 err_t
dhcp6_release_stateful(struct netif * netif)508 dhcp6_release_stateful(struct netif *netif)
509 {
510   struct dhcp6 *dhcp6 = NULL;
511 
512   LWIP_UNUSED_ARG(netif);
513 #if LWIP_IPV6_DHCP6_STATEFUL
514   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_release_stateful(netif=%p) %s%"U16_F"\n",
515               (void *)netif, netif->name, (u16_t)netif->num));
516 
517   dhcp6 = netif_dhcp6_data(netif);
518   if (dhcp6 == NULL) {
519     return ERR_VAL;
520   }
521   if (dhcp6->state == DHCP6_STATE_STATEFUL_RENEW ||
522       dhcp6->state == DHCP6_STATE_STATEFUL_REBIND ||
523       dhcp6->state == DHCP6_STATE_STATEFUL_IDLE) {
524     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_release_stateful(): have address to be released"));
525     /* [RFC3315 Section 18.1.6]: The client MUST stop using all of the addresses being released as
526        soon as the client begins the Release message exchange process */
527     if (dhcp6->addr_idx != LWIP_IPV6_NUM_ADDRESSES) {
528       netif_ip6_addr_set_state(netif, dhcp6->addr_idx, IP6_ADDR_INVALID);
529       netif_ip6_addr_set_parts(netif, dhcp6->addr_idx, 0, 0, 0, 0);
530     }
531 
532     dhcp6_set_state(dhcp6, DHCP6_STATE_STATEFUL_RELEASE, "dhcp6_release_stateful");
533     dhcp6_set_timeout_params(dhcp6);
534     dhcp6_release(netif, dhcp6);
535     return ERR_OK;
536   } else if (dhcp6->state == DHCP6_STATE_STATEFUL_RELEASE) {
537     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_release_stateful(): address has been released"));
538     return ERR_OK;
539   } else {
540     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_release_stateful(): no address to be released"));
541     return ERR_VAL;
542   }
543 #else
544   LWIP_UNUSED_ARG(dhcp6);
545   return ERR_VAL;
546 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
547 }
548 
549 /*
550  * Create a DHCPv6 request, fill in common headers
551  *
552  * @param netif the netif under DHCPv6 control
553  * @param dhcp6 dhcp6 control struct
554  * @param message_type message type of the request
555  * @param opt_len_alloc option length to allocate
556  * @param options_out_len option length on exit
557  * @return a pbuf for the message
558  */
559 static struct pbuf *
dhcp6_create_msg(struct netif * netif,struct dhcp6 * dhcp6,u8_t message_type,u16_t opt_len_alloc,u16_t * options_out_len)560 dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type,
561                  u16_t opt_len_alloc, u16_t *options_out_len)
562 {
563   struct pbuf *p_out;
564   struct dhcp6_msg *msg_out;
565 
566   LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;);
567   LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;);
568   LWIP_ERROR("dhcp6_create_msg: opt_len_alloc to large", (opt_len_alloc <= (0xffff - sizeof(struct dhcp6_msg))),
569              return NULL);
570   p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM);
571   if (p_out == NULL) {
572     DHCP6_STATS_INC(dhcp6.memerr);
573     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
574                 ("dhcp6_create_msg(): could not allocate pbuf\n"));
575     return NULL;
576   }
577   LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg",
578               (p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc));
579 
580   /* @todo: limit new xid for certain message types? */
581   /* reuse transaction identifier in retransmissions */
582   if (dhcp6->tries == 0) {
583 #ifdef LWIP_RAND
584     dhcp6->xid = LWIP_RAND() & 0xFFFFFF;
585 #else
586     dhcp6->xid = (dhcp6->xid + 1) & 0xFFFFFF;
587 #endif
588   }
589 
590   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
591               ("transaction id xid(%"X32_F")\n", dhcp6->xid));
592 
593   msg_out = (struct dhcp6_msg *)p_out->payload;
594   memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc);
595 
596   msg_out->msgtype = message_type;
597   msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16);
598   msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8);
599   msg_out->transaction_id[2] = (u8_t)dhcp6->xid;
600   *options_out_len = 0;
601   return p_out;
602 }
603 
604 static u16_t
dhcp6_option_short(u16_t options_out_len,u8_t * options,u16_t value)605 dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value)
606 {
607   dhcp6_put_ushort(options + options_out_len, value);
608   options_out_len = (u16_t)(options_out_len + sizeof(u16_t));
609   return options_out_len;
610 }
611 
612 static u16_t
dhcp6_option_optionrequest(u16_t options_out_len,u8_t * options,const u16_t * req_options,u16_t num_req_options,u16_t max_len)613 dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options,
614                            u16_t num_req_options, u16_t max_len)
615 {
616   size_t i;
617   u16_t ret;
618 
619   LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len",
620     sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len);
621   LWIP_UNUSED_ARG(max_len);
622 
623   ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO);
624   ret = dhcp6_option_short(ret, options, 2 * num_req_options);
625   for (i = 0; i < num_req_options; i++) {
626     ret = dhcp6_option_short(ret, options, req_options[i]);
627   }
628   return ret;
629 }
630 
631 /* All options are added, shrink the pbuf to the required size */
632 static void
dhcp6_msg_finalize(u16_t options_out_len,struct pbuf * p_out)633 dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out)
634 {
635   /* shrink the pbuf to the actual content length */
636   pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len));
637 }
638 
639 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
640 static err_t
dhcp6_set_dns_servers(const struct netif * netif,const struct dhcp6 * dhcp6_p,const struct pbuf * p_msg_in)641 dhcp6_set_dns_servers(const struct netif *netif, const struct dhcp6 *dhcp6_p, const struct pbuf *p_msg_in)
642 {
643   ip_addr_t dns_addr;
644   ip6_addr_t *dns_addr6 = NULL;
645   u16_t op_start, op_len;
646   u16_t idx;
647   u8_t n;
648 
649   LWIP_UNUSED_ARG(dhcp6_p);
650 
651   op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_DNS_SERVER);
652   op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_DNS_SERVER);
653   ip_addr_set_zero_ip6(&dns_addr);
654   dns_addr6 = ip_2_ip6(&dns_addr);
655   for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
656        n++, idx = (u16_t)(idx + sizeof(struct ip6_addr_packed))) {
657     u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
658     if (copied != sizeof(struct ip6_addr_packed)) {
659       /* pbuf length mismatch */
660       return ERR_VAL;
661     }
662     ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
663     /* @todo: do we need a different offset than DHCP(v4)? */
664     dns_setserver(n, &dns_addr);
665   }
666 
667   return ERR_OK;
668 }
669 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
670 
671 #if LWIP_DHCP6_GET_NTP_SRV
672 static err_t
dhcp6_set_ntp_srv(const struct netif * netif,const struct dhcp6 * dhcp6_p,const struct pbuf * p_msg_in)673 dhcp6_set_ntp_srv(const struct netif *netif, const struct dhcp6 *dhcp6_p, const struct pbuf *p_msg_in)
674 {
675   ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
676   u16_t idx;
677   u16_t copied;
678   u8_t n;
679 
680   LWIP_UNUSED_ARG(dhcp6_p);
681 
682   u16_t op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_NTP_SERVER);
683   u16_t op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_NTP_SERVER);
684 
685   for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
686        n++, idx += sizeof(struct ip6_addr_packed)) {
687     ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
688     ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
689     copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
690     if (copied != sizeof(struct ip6_addr_packed)) {
691       /* pbuf length mismatch */
692       return ERR_VAL;
693     }
694     ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
695   }
696   dhcp6_set_ntp_servers(n, ntp_server_addrs);
697 
698   return ERR_OK;
699 }
700 #endif /* LWIP_DHCP6_GET_NTP_SRV */
701 
702 #if LWIP_IPV6_DHCP6_STATELESS
703 static void
dhcp6_information_request(struct netif * netif,struct dhcp6 * dhcp6)704 dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
705 {
706   const u16_t requested_options[] = {
707 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
708     DHCP6_OPTION_DNS_SERVERS,
709     DHCP6_OPTION_DOMAIN_LIST
710 #endif
711 #if LWIP_DHCP6_GET_NTP_SRV
712     , DHCP6_OPTION_SNTP_SERVERS
713 #endif
714   };
715 
716   u16_t msecs;
717   struct pbuf *p_out;
718   u16_t options_out_len;
719   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
720   /* create and initialize the DHCP message header */
721   p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST,
722                            DHCP6_OPT_HEADER_LEN + sizeof(requested_options), &options_out_len);
723   if (p_out != NULL) {
724     err_t err;
725     struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
726     u8_t *options = (u8_t *)(msg_out + 1);
727     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
728 
729     options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
730       LWIP_ARRAYSIZE(requested_options), p_out->len);
731     LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
732       DHCP6_INFOREQUEST, options_out_len, p_out->len);
733     dhcp6_msg_finalize(options_out_len, p_out);
734 
735     DHCP6_STATS_INC(dhcp6.xmit);
736     err = udp_sendto_if(dhcp6->pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
737     pbuf_free(p_out);
738     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
739     LWIP_UNUSED_ARG(err);
740   } else {
741     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
742   }
743   dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
744   if (dhcp6->tries < 255) {
745     dhcp6->tries++;
746   }
747   msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
748   dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
749   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
750 }
751 
752 static err_t
dhcp6_request_config(struct netif * netif,struct dhcp6 * dhcp6)753 dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
754 {
755   /* stateless mode enabled and no request running? */
756   if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
757     /* send Information-request and wait for answer; setup receive timeout */
758     dhcp6_information_request(netif, dhcp6);
759   }
760 
761   return ERR_OK;
762 }
763 
764 static void
dhcp6_abort_config_request(struct dhcp6 * dhcp6)765 dhcp6_abort_config_request(struct dhcp6 *dhcp6)
766 {
767   if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
768     /* abort running request */
769     dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
770   }
771 }
772 
773 /* Handle a REPLY to INFOREQUEST
774  * This parses DNS and NTP server addresses from the reply.
775  */
776 static void
dhcp6_handle_config_reply(struct netif * netif,struct pbuf * p_msg_in)777 dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
778 {
779   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
780 
781   LWIP_UNUSED_ARG(dhcp6);
782   LWIP_UNUSED_ARG(p_msg_in);
783 
784 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
785   if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
786     if (ERR_OK != dhcp6_set_dns_servers(netif, dhcp6, p_msg_in)) {
787       DHCP6_STATS_INC(dhcp6.err);
788       DHCP6_STATS_INC(dhcp6.drop);
789       return;
790     }
791   }
792   /* @ todo: parse and set Domain Search List */
793 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
794 
795 #if LWIP_DHCP6_GET_NTP_SRV
796   if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
797     if (ERR_OK != dhcp6_set_ntp_srv(netif, dhcp6, p_msg_in)) {
798       DHCP6_STATS_INC(dhcp6.err);
799       DHCP6_STATS_INC(dhcp6.drop);
800       return;
801     }
802   }
803 #endif /* LWIP_DHCP6_GET_NTP_SRV */
804 }
805 #endif /* LWIP_IPV6_DHCP6_STATELESS */
806 
807 static inline void
dhcp6_put_ushort(u8_t * obuf,u16_t val)808 dhcp6_put_ushort(u8_t *obuf, u16_t val)
809 {
810   u16_t tmp = htons(val);
811   (void)memcpy_s(obuf, sizeof(tmp), &tmp, sizeof(tmp));
812 }
813 
814 #if LWIP_IPV6_DHCP6_STATEFUL
815 static void
dhcp6_stateful_struct_free(struct dhcp6 * dhcp6_p)816 dhcp6_stateful_struct_free(struct dhcp6 *dhcp6_p)
817 {
818   if (dhcp6_p == NULL) {
819     return;
820   }
821   if (dhcp6_p->duid.duid_id != NULL) {
822     mem_free(dhcp6_p->duid.duid_id);
823     dhcp6_p->duid.duid_id = NULL;
824     dhcp6_p->duid.duid_len = 0;
825   }
826 
827   dhcp6_p->struct_init = 0;
828 
829   return;
830 }
831 
832 /*
833  * caller : dhcp6_enable_stateful, check netif_p != NULL by netifapi_netif_common
834  * caller : dhcp6_stateful_timeout, check netif_p != NULL by NETIF_FOREACH(netif)
835  */
836 static void
dhcp6_linklocal_address_check(struct netif * netif_p,struct dhcp6 * dhcp6_p)837 dhcp6_linklocal_address_check(struct netif *netif_p, struct dhcp6 *dhcp6_p)
838 {
839   if (ip6_addr_ispreferred(netif_ip6_addr_state(netif_p, 0))) {
840     dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_SOLICIT, "dhcp6_linklocal_address_check");
841     dhcp6_set_timeout_params(dhcp6_p);
842     dhcp6_solicit(netif_p, dhcp6_p);
843   } else {
844     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_linklocal_address_check(): linklocal address not ready"));
845     dhcp6_p->request_timeout = 1;
846   }
847 
848   return;
849 }
850 
851 static err_t
dhcp6_form_duid(const struct netif * netif_p,struct dhcp6 * dhcp6_p,u8_t type)852 dhcp6_form_duid(const struct netif *netif_p, struct dhcp6 *dhcp6_p, u8_t type)
853 {
854   duid_t *duid_p = NULL;
855   u16_t len;
856   err_t ret = ERR_OK;
857 
858   duid_p = &(dhcp6_p->duid);
859   switch (type) {
860     case DHCP6_DUID_LLT:
861       /* to do */
862       ret = ERR_VAL;
863       break;
864     case DHCP6_DUID_EN:
865       /* to do */
866       ret = ERR_VAL;
867       break;
868     case DHCP6_DUID_LL:
869       len = (u16_t)(4U + netif_p->hwaddr_len);
870       duid_p->duid_id = (u8_t *)mem_malloc(len);
871       if (duid_p->duid_id == NULL) {
872         DHCP6_STATS_INC(dhcp6.memerr);
873         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("could not allocate duid_id\n"));
874         return ERR_MEM;
875       }
876       dhcp6_put_ushort(duid_p->duid_id, DHCP6_DUID_LL);
877       dhcp6_put_ushort(duid_p->duid_id + 2U, DHCP6HTYPE_ETHER);
878       if (memcpy_s(duid_p->duid_id + 4U, netif_p->hwaddr_len, netif_p->hwaddr, netif_p->hwaddr_len) != EOK) {
879         DHCP6_STATS_INC(dhcp6.memerr);
880         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("memory copy error\n"));
881         return ERR_MEM;
882       }
883       duid_p->duid_len = len;
884       break;
885     case DHCP6_DUID_UUID:
886       /* to do */
887       ret = ERR_VAL;
888       break;
889     default:
890       DHCP6_STATS_INC(dhcp6.opterr);
891       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("unkonw duid type(%u)\n", type));
892       ret = ERR_VAL;
893       break;
894   }
895 
896   return ret;
897 }
898 
899 static err_t
dhcp6_create_iaid(const struct netif * netif_p,struct dhcp6 * dhcp6_p,u8_t idx)900 dhcp6_create_iaid(const struct netif *netif_p, struct dhcp6 *dhcp6_p, u8_t idx)
901 {
902   iaid_t *iaid_p = NULL;
903   u16_t start_idx, copy_len;
904 
905   iaid_p = &((dhcp6_p->ia[idx]).iaid);
906   iaid_p->iaid_len = sizeof(struct ia_info);
907   (void)memset_s(&(iaid_p->ia), iaid_p->iaid_len, 0x0, iaid_p->iaid_len);
908 
909   if (netif_p->hwaddr_len > DHCP6_IAID_SIZE) {
910     start_idx = (u16_t)(netif_p->hwaddr_len - DHCP6_IAID_SIZE);
911     copy_len = DHCP6_IAID_SIZE;
912   } else {
913     start_idx = 0;
914     copy_len = netif_p->hwaddr_len;
915   }
916 
917   (void)memcpy_s(&(iaid_p->ia.dh6_ia_iaid), copy_len, netif_p->hwaddr + start_idx, copy_len);
918 
919   if (idx) {
920     ((u8_t *)(&(iaid_p->ia.dh6_ia_iaid)))[3] = (u8_t)(((u8_t *)(&(iaid_p->ia.dh6_ia_iaid)))[3] + idx);
921   }
922 
923   return ERR_OK;
924 }
925 
926 static err_t
dhcp6_stateful_init(const struct netif * netif_p,struct dhcp6 * dhcp6_p)927 dhcp6_stateful_init(const struct netif *netif_p, struct dhcp6 *dhcp6_p)
928 {
929   err_t ret;
930   u8_t i;
931 
932   LWIP_ASSERT("dhcp6_p != NULL", dhcp6_p != NULL);
933   LWIP_ASSERT("netif_p != NULL", netif_p != NULL);
934 
935   ret = dhcp6_form_duid(netif_p, dhcp6_p, DHCP6_DUID_LL);
936   if (ret != ERR_OK) {
937     goto dhcp6_stateful_init_failed;
938   }
939 
940   for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
941     ret = dhcp6_create_iaid(netif_p, dhcp6_p, i);
942     if (ret != ERR_OK) {
943       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_create_iaid failed because of memory error.\n"));
944       goto dhcp6_stateful_init_failed;
945     }
946   }
947 
948   dhcp6_p->struct_init = 1;
949 
950   return ERR_OK;
951 dhcp6_stateful_init_failed:
952   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("init stateful DHCPv6 failed\n"));
953   dhcp6_stateful_struct_free(dhcp6_p);
954   return ret;
955 }
956 
957 static u16_t
dhcp6_option_append(u16_t options_out_len,u8_t * options,u8_t option_type,u16_t op_len,const u8_t * val)958 dhcp6_option_append(u16_t options_out_len, u8_t *options, u8_t option_type, u16_t op_len, const u8_t *val)
959 {
960   u16_t len = options_out_len;
961   dhcp6_put_ushort(options + options_out_len, option_type);
962   options_out_len = (u16_t)(options_out_len + sizeof(u16_t));
963   dhcp6_put_ushort(options + options_out_len, op_len);
964   options_out_len = (u16_t)(options_out_len + sizeof(u16_t));
965   if (memcpy_s(options + options_out_len, op_len, val, op_len) != EOK) {
966     return len;
967   }
968   options_out_len = (u16_t)(options_out_len + op_len);
969 
970   return options_out_len;
971 }
972 
973 static void
dhcp6_set_timeout_params(struct dhcp6 * dhcp6_p)974 dhcp6_set_timeout_params(struct dhcp6 *dhcp6_p)
975 {
976   s8_t addr_idx;
977   if (dhcp6_p == NULL) {
978     return;
979   }
980   addr_idx = dhcp6_p->addr_idx;
981 
982   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("set timeout params of state %d\n", dhcp6_p->state));
983 
984   dhcp6_p->tries = 0;
985   dhcp6_p->duration_start = sys_now();
986   dhcp6_p->IRT = 0;
987   dhcp6_p->MRC = 0;
988   dhcp6_p->MRT = 0;
989   dhcp6_p->MRD = 0;
990 
991   switch (dhcp6_p->state) {
992     case DHCP6_STATE_STATEFUL_SOLICIT:
993       dhcp6_p->IRT = SOL_TIMEOUT * MS_PER_SECOND;
994       dhcp6_p->MRT = SOL_MAX_RT * MS_PER_SECOND;
995       break;
996     case DHCP6_STATE_STATEFUL_INFOREQ:
997       dhcp6_p->IRT = INF_TIMEOUT * MS_PER_SECOND;
998       dhcp6_p->MRT = INF_MAX_RT * MS_PER_SECOND;
999       break;
1000     case DHCP6_STATE_STATEFUL_REQUEST:
1001       dhcp6_p->IRT = REQ_TIMEOUT * MS_PER_SECOND;
1002       dhcp6_p->MRC = REQ_MAX_RC;
1003       dhcp6_p->MRT = REQ_MAX_RT * MS_PER_SECOND;
1004       break;
1005     case DHCP6_STATE_STATEFUL_RENEW:
1006       dhcp6_p->IRT = REN_TIMEOUT * MS_PER_SECOND;
1007       dhcp6_p->MRT = REN_MAX_RT * MS_PER_SECOND;
1008       if (addr_idx != LWIP_IPV6_NUM_ADDRESSES) {
1009         if (dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t2 == 0) {
1010           dhcp6_p->MRD = 900000; /* 900000: 15 min */
1011         } else if (dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t2 == DHCP6_DURATION_INFINITE) {
1012           /* Renew forever, never hit Rebind */
1013           dhcp6_p->MRD = 0;
1014         } else {
1015           u32_t max;
1016           max = dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t2 - dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t1;
1017           if (max > (u32_t)(~0) / MS_PER_SECOND) {
1018             max = (u32_t)(~0) / MS_PER_SECOND;
1019           }
1020           /* Remaining util T2 */
1021           dhcp6_p->MRD = max * MS_PER_SECOND;
1022         }
1023       }
1024       break;
1025     case DHCP6_STATE_STATEFUL_REBIND:
1026       dhcp6_p->IRT = REB_TIMEOUT * MS_PER_SECOND;
1027       dhcp6_p->MRT = REB_MAX_RT * MS_PER_SECOND;
1028       if (addr_idx != LWIP_IPV6_NUM_ADDRESSES) {
1029         u32_t max;
1030         max = dhcp6_p->ia[addr_idx].iaaddr.dh6_iaaddr_valid - dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t2;
1031         if (max > (u32_t)(~0) / MS_PER_SECOND) {
1032           max = (u32_t)(~0) / MS_PER_SECOND;
1033         }
1034         dhcp6_p->MRD = max * MS_PER_SECOND;
1035       }
1036       break;
1037     case DHCP6_STATE_STATEFUL_RELEASE:
1038       dhcp6_p->IRT = REL_TIMEOUT * MS_PER_SECOND;
1039       dhcp6_p->MRC = REL_MAX_RC;
1040       break;
1041     case DHCP6_STATE_STATEFUL_DECLINE:
1042       dhcp6_p->IRT = DEC_TIMEOUT * MS_PER_SECOND;
1043       dhcp6_p->MRC = DEC_MAX_RC;
1044       break;
1045     default:
1046       DHCP6_STATS_INC(dhcp6.err);
1047       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("unkonw state(%d)\n", dhcp6_p->state));
1048       break;
1049   }
1050 
1051   return;
1052 }
1053 
1054 static u32_t
dhcp6_retransmission_duration(u32_t duration_start)1055 dhcp6_retransmission_duration(u32_t duration_start)
1056 {
1057   u32_t now_ms = sys_now();
1058   u32_t duration;
1059 
1060   if (now_ms >= duration_start) {
1061     duration = now_ms - duration_start;
1062   } else {
1063     /*
1064      * as (now_ms < duration_start), ((0xffffffff - duration_start) + now_ms)
1065      * must be smaller than 0xffffffff. duration will not be overflow.
1066      */
1067     duration = (0xffffffff - duration_start) + now_ms;
1068   }
1069 
1070   return duration;
1071 }
1072 
1073 static void
dhcp6_stateful_timeout(struct netif * netif_p,struct dhcp6 * dhcp6_p)1074 dhcp6_stateful_timeout(struct netif *netif_p, struct dhcp6 *dhcp6_p)
1075 {
1076   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_stateful_timeout() : %02x\n", dhcp6_p->state));
1077 
1078   switch (dhcp6_p->state) {
1079     case DHCP6_STATE_STATEFUL_INIT:
1080       dhcp6_linklocal_address_check(netif_p, dhcp6_p);
1081       break;
1082     case DHCP6_STATE_STATEFUL_SOLICIT:
1083       if (dhcp6_p->serv_list == NULL) {
1084         dhcp6_solicit(netif_p, dhcp6_p);
1085       } else {
1086         dhcp6_solicit_to_request(netif_p, dhcp6_p);
1087       }
1088       break;
1089     case DHCP6_STATE_STATEFUL_INFOREQ:
1090       break;
1091     case DHCP6_STATE_STATEFUL_REQUEST:
1092       if ((dhcp6_p->MRC != 0) && (dhcp6_p->tries >= dhcp6_p->MRC)) {
1093         dhcp6_request_next_server(netif_p, dhcp6_p);
1094       } else {
1095         dhcp6_request(netif_p, dhcp6_p);
1096       }
1097       break;
1098     case DHCP6_STATE_STATEFUL_IDLE:
1099       dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_RENEW, "dhcp6_stateful_timeout");
1100       dhcp6_set_timeout_params(dhcp6_p);
1101       dhcp6_renew(netif_p, dhcp6_p);
1102       break;
1103     case DHCP6_STATE_STATEFUL_RENEW:
1104       if ((dhcp6_p->MRD && (dhcp6_retransmission_duration(dhcp6_p->duration_start) >= dhcp6_p->MRD)) ||
1105           (dhcp6_p->MRC && dhcp6_p->tries >=  dhcp6_p->MRC)) {
1106         dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_REBIND, "dhcp6_stateful_timeout");
1107         dhcp6_set_timeout_params(dhcp6_p);
1108         dhcp6_rebind(netif_p, dhcp6_p);
1109       } else {
1110         dhcp6_renew(netif_p, dhcp6_p);
1111       }
1112       break;
1113     case DHCP6_STATE_STATEFUL_REBIND:
1114       if ((dhcp6_p->MRD && (dhcp6_retransmission_duration(dhcp6_p->duration_start) >= dhcp6_p->MRD)) ||
1115           (dhcp6_p->MRC && dhcp6_p->tries >=  dhcp6_p->MRC)) {
1116         dhcp6_rebind_failure_cleanup(netif_p, dhcp6_p);
1117         dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_SOLICIT, "dhcp6_stateful_timeout");
1118         dhcp6_set_timeout_params(dhcp6_p);
1119         dhcp6_solicit(netif_p, dhcp6_p);
1120       } else {
1121         dhcp6_rebind(netif_p, dhcp6_p);
1122       }
1123       break;
1124     case DHCP6_STATE_STATEFUL_RELEASE:
1125       if ((dhcp6_p->MRD && (dhcp6_retransmission_duration(dhcp6_p->duration_start) >= dhcp6_p->MRD)) ||
1126           (dhcp6_p->MRC && dhcp6_p->tries >=  dhcp6_p->MRC)) {
1127         dhcp6_post_release_cleanup(dhcp6_p);
1128         /* Don't need timer any more. */
1129         dhcp6_p->request_timeout = 0;
1130         dhcp6_set_state(dhcp6_p, DHCP6_STATE_OFF, "dhcp6_stateful_timeout");
1131       } else {
1132         dhcp6_release(netif_p, dhcp6_p);
1133       }
1134       break;
1135     case DHCP6_STATE_STATEFUL_DECLINE:
1136       if ((dhcp6_p->MRC != 0) && (dhcp6_p->tries >= dhcp6_p->MRC)) {
1137         dhcp6_request_next_server(netif_p, dhcp6_p);
1138       } else {
1139         dhcp6_decline(netif_p, dhcp6_p);
1140       }
1141       break;
1142     default:
1143       DHCP6_STATS_INC(dhcp6.err);
1144       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("unkonw state(%d)\n", dhcp6_p->state));
1145       break;
1146   }
1147 
1148   return;
1149 }
1150 
1151 static opt_comm_t *
dhcp6_server_find_opt(dhcp6_serverinfo_t * dhcp6_serverinfo_p,u16_t opt_type)1152 dhcp6_server_find_opt(dhcp6_serverinfo_t *dhcp6_serverinfo_p, u16_t opt_type)
1153 {
1154   opt_comm_t *opt_comm_p = dhcp6_serverinfo_p->opts;
1155 
1156   while (opt_comm_p) {
1157     if (opt_comm_p->opt_type == opt_type) {
1158       break;
1159     }
1160     opt_comm_p = opt_comm_p->next;
1161   }
1162 
1163   return opt_comm_p;
1164 }
1165 
1166 static dhcp6_serverinfo_t *
dhcp6_find_server(const struct dhcp6 * dhcp6_p,const u8_t * serv_duid,u16_t serv_duid_len)1167 dhcp6_find_server(const struct dhcp6 *dhcp6_p, const u8_t *serv_duid, u16_t serv_duid_len)
1168 {
1169   dhcp6_serverinfo_t *dhcp6_serverinfo_p = dhcp6_p->serv_list;
1170   opt_comm_t *opt_comm_p = NULL;
1171 
1172   while (dhcp6_serverinfo_p) {
1173     opt_comm_p = dhcp6_server_find_opt(dhcp6_serverinfo_p, DHCP6_OPTION_SERVERID);
1174     if ((opt_comm_p != NULL) && (opt_comm_p->opt_start != NULL) && (opt_comm_p->opt_len == serv_duid_len) &&
1175         memcmp(serv_duid, opt_comm_p->opt_start, serv_duid_len) == 0) {
1176         break;
1177     }
1178     dhcp6_serverinfo_p = dhcp6_serverinfo_p->next;
1179   }
1180 
1181   return dhcp6_serverinfo_p;
1182 }
1183 
1184 static void
dhcp6_serverinfo_free(dhcp6_serverinfo_t * dhcp6_serverinfo_p)1185 dhcp6_serverinfo_free(dhcp6_serverinfo_t *dhcp6_serverinfo_p)
1186 {
1187   opt_comm_t *opt_p = NULL;
1188   opt_comm_t *opt_tmp = NULL;
1189 
1190   if (dhcp6_serverinfo_p == NULL) {
1191     return;
1192   }
1193 
1194   if (dhcp6_serverinfo_p->buff) {
1195     mem_free(dhcp6_serverinfo_p->buff);
1196     dhcp6_serverinfo_p->buff = NULL;
1197   }
1198 
1199   opt_p = dhcp6_serverinfo_p->opts;
1200   while (opt_p) {
1201     opt_tmp = opt_p->next;
1202     mem_free(opt_p);
1203     opt_p = opt_tmp;
1204   }
1205   dhcp6_serverinfo_p->opts = NULL;
1206 
1207   mem_free(dhcp6_serverinfo_p);
1208 
1209   return;
1210 }
1211 
1212 static err_t
dhcp6_serverinfo_parse(dhcp6_serverinfo_t * dhcp6_serverinfo_p,u8_t * buff,u16_t buff_len)1213 dhcp6_serverinfo_parse(dhcp6_serverinfo_t *dhcp6_serverinfo_p, u8_t *buff, u16_t buff_len)
1214 {
1215   u16_t idx, op, len, opt_num;
1216   opt_comm_t *opt_p = NULL;
1217 
1218   if ((dhcp6_serverinfo_p == NULL) || (buff == NULL)) {
1219     return ERR_VAL;
1220   }
1221 
1222   idx = 0;
1223   opt_num = 0;
1224   while (idx <= (buff_len - DHCP6_OPT_HEADER_LEN)) {
1225     op = (u16_t)((buff[idx] << 8) | buff[idx + 1]);  // the first two bytes denotes option type
1226     len = (u16_t)((buff[idx + 2] << 8) | buff[idx + 3]);  // the second two bytes denotes option length
1227     if (len > (buff_len - DHCP6_OPT_HEADER_LEN - idx)) {
1228       /* invalid option length */
1229       return ERR_VAL;
1230     }
1231     opt_p = (opt_comm_t *)mem_malloc(sizeof(opt_comm_t));
1232     if (opt_p == NULL) {
1233       DHCP6_STATS_INC(dhcp6.memerr);
1234       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("malloc mem failed\n"));
1235       return ERR_MEM;
1236     }
1237     (void)memset_s(opt_p, sizeof(opt_comm_t), 0x0, sizeof(opt_comm_t));
1238     opt_p->next = NULL;
1239     opt_p->opt_type = op;
1240     opt_p->opt_len = len;
1241     if (len == 0) {
1242       opt_p->opt_start = NULL;
1243     } else {
1244       opt_p->opt_start = buff + idx + DHCP6_OPT_HEADER_LEN;
1245     }
1246 
1247     if (dhcp6_serverinfo_p->opts == NULL) {
1248       dhcp6_serverinfo_p->opts = opt_p;
1249     } else {
1250       opt_p->next = dhcp6_serverinfo_p->opts;
1251       dhcp6_serverinfo_p->opts = opt_p;
1252     }
1253 
1254     /* because (len > (buff_len-4-idx)) is false, so idx will not reverse */
1255     idx = (u16_t)(idx + len + DHCP6_OPT_HEADER_LEN);
1256     if (++opt_num > DHCP6_SERVERINFO_MAX_OPT_NUM) {
1257       DHCP6_STATS_INC(dhcp6.opterr);
1258       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("too many dhcp6 options\n"));
1259       return ERR_VAL;
1260     }
1261   }
1262 
1263   return ERR_OK;
1264 }
1265 
1266 static dhcp6_serverinfo_t *
dhcp6_serverinfo_new(const struct pbuf * p_msg_in)1267 dhcp6_serverinfo_new(const struct pbuf *p_msg_in)
1268 {
1269   dhcp6_serverinfo_t *dhcp6_serverinfo_p = NULL;
1270   u16_t copied, buff_len;
1271 
1272   /*
1273    * tot_len must be at least length of dhcp6 message header plus 8.
1274    * 8 : op and len code length of option SERVER_ID and option CLIENT_ID is 4*2
1275    */
1276   if (p_msg_in->tot_len < (sizeof(struct dhcp6_msg) + 8)) {
1277     DHCP6_STATS_INC(dhcp6.lenerr);
1278     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("invalid dhcp6 message\n"));
1279     return NULL;
1280   }
1281 
1282   buff_len = (u16_t)(p_msg_in->tot_len - sizeof(struct dhcp6_msg));
1283   if (buff_len > DHCP6_SERVERINFO_MAX_LEN) {
1284     DHCP6_STATS_INC(dhcp6.lenerr);
1285     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6 server advertise message is too large\n"));
1286     return NULL;
1287   }
1288 
1289   dhcp6_serverinfo_p = (dhcp6_serverinfo_t *)mem_malloc(sizeof(dhcp6_serverinfo_t));
1290   if (dhcp6_serverinfo_p == NULL) {
1291     DHCP6_STATS_INC(dhcp6.memerr);
1292     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("malloc mem failed\n"));
1293     return NULL;
1294   }
1295   (void)memset_s(dhcp6_serverinfo_p, sizeof(dhcp6_serverinfo_t), 0x0, sizeof(dhcp6_serverinfo_t));
1296 
1297   dhcp6_serverinfo_p->buff = (u8_t *)mem_malloc(buff_len);
1298   if (dhcp6_serverinfo_p->buff == NULL) {
1299     DHCP6_STATS_INC(dhcp6.memerr);
1300     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("malloc mem failed\n"));
1301     goto serverinfo_new_failed;
1302   }
1303 
1304   (void)memset_s(dhcp6_serverinfo_p->buff, buff_len, 0x0, buff_len);
1305   copied = pbuf_copy_partial(p_msg_in, (void *)(dhcp6_serverinfo_p->buff), buff_len, sizeof(struct dhcp6_msg));
1306   if (copied != buff_len) {
1307     DHCP6_STATS_INC(dhcp6.lenerr);
1308     goto serverinfo_new_failed;
1309   }
1310   dhcp6_serverinfo_p->buff_len = buff_len;
1311   if (ERR_OK != dhcp6_serverinfo_parse(dhcp6_serverinfo_p, dhcp6_serverinfo_p->buff, dhcp6_serverinfo_p->buff_len)) {
1312     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("parse dhcp6 server info failed\n"));
1313     goto serverinfo_new_failed;
1314   }
1315 
1316   dhcp6_serverinfo_p->active = 1;
1317   dhcp6_serverinfo_p->unicast = 0;
1318   dhcp6_serverinfo_p->pref = 0;
1319   dhcp6_serverinfo_p->next = NULL;
1320   IP_SET_TYPE_VAL(dhcp6_serverinfo_p->unicast_addr, IPADDR_TYPE_V6);
1321 
1322   return dhcp6_serverinfo_p;
1323 serverinfo_new_failed:
1324   dhcp6_serverinfo_free(dhcp6_serverinfo_p);
1325   return NULL;
1326 }
1327 
1328 static void
dhcp6_serverinfo_insert(dhcp6_serverinfo_t ** head,dhcp6_serverinfo_t * new_p)1329 dhcp6_serverinfo_insert(dhcp6_serverinfo_t **head, dhcp6_serverinfo_t *new_p)
1330 {
1331   dhcp6_serverinfo_t *p = NULL;
1332   dhcp6_serverinfo_t *q = NULL;
1333 
1334   if (new_p == NULL) {
1335     return;
1336   }
1337 
1338   if (*head == NULL) {
1339     *head = new_p;
1340     return;
1341   }
1342 
1343   p = *head;
1344   while (new_p->pref <= p->pref && p->next != NULL) {
1345     q = p;
1346     p = p->next;
1347   }
1348 
1349   if (new_p->pref > p->pref) {
1350     if (p == *head) {
1351       *head = new_p;
1352       new_p->next = p;
1353     } else {
1354       if (q != NULL) {
1355         q->next = new_p;
1356         new_p->next = p;
1357       }
1358     }
1359   } else {
1360     p->next = new_p;
1361   }
1362 
1363   return;
1364 }
1365 
1366 static void
dhcp6_serverinfo_free_list(dhcp6_serverinfo_t ** head)1367 dhcp6_serverinfo_free_list(dhcp6_serverinfo_t **head)
1368 {
1369   dhcp6_serverinfo_t *p = NULL;
1370   dhcp6_serverinfo_t *q = NULL;
1371 
1372   if ((head == NULL) || (*head == NULL)) {
1373     return;
1374   }
1375 
1376   p = *head;
1377   while (p != NULL) {
1378     q = p->next;
1379     dhcp6_serverinfo_free(p);
1380     p = q;
1381   }
1382 
1383   *head = NULL;
1384 }
1385 
1386 static dhcp6_serverinfo_t *
dhcp6_serverinfo_find_active(dhcp6_serverinfo_t * head)1387 dhcp6_serverinfo_find_active(dhcp6_serverinfo_t *head)
1388 {
1389   dhcp6_serverinfo_t *p = NULL;
1390 
1391   if (head == NULL) {
1392     return NULL;
1393   }
1394 
1395   for (p = head; p; p = p->next) {
1396     if (p->active == 1) {
1397       return p;
1398     }
1399   }
1400 
1401   return NULL;
1402 }
1403 
1404 static ia_t*
dhcp6_get_ia_by_iaid(struct dhcp6 * dhcp6_p,const u8_t * iaid,s8_t * pdhcp6_addr_idx)1405 dhcp6_get_ia_by_iaid(struct dhcp6 *dhcp6_p, const u8_t *iaid, s8_t *pdhcp6_addr_idx)
1406 {
1407   s8_t i;
1408   ia_t *ia_p = NULL;
1409 
1410   for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
1411     ia_p = &(dhcp6_p->ia[i]);
1412     if (memcmp((u8_t *)&(ia_p->iaid.ia.dh6_ia_iaid), iaid, DHCP6_IAID_SIZE) == 0) {
1413       *pdhcp6_addr_idx = i;
1414       return ia_p;
1415     }
1416   }
1417 
1418   return NULL;
1419 }
1420 
1421 static void
dhcp6_clear_ia(struct dhcp6 * dhcp6_p)1422 dhcp6_clear_ia(struct dhcp6 *dhcp6_p)
1423 {
1424   s8_t addr_idx = dhcp6_p->addr_idx;
1425 
1426   if (addr_idx == LWIP_IPV6_NUM_ADDRESSES) {
1427     return;
1428   }
1429 
1430   (void)memset_s(&dhcp6_p->ia[addr_idx].iaaddr, sizeof(dhcp6_p->ia[addr_idx].iaaddr),
1431                  0, sizeof(dhcp6_p->ia[addr_idx].iaaddr));
1432   dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t1 = 0;
1433   dhcp6_p->ia[addr_idx].iaid.ia.dh6_ia_t2 = 0;
1434 }
1435 
1436 static void
dhcp6_rebind_failure_cleanup(struct netif * netif_p,struct dhcp6 * dhcp6_p)1437 dhcp6_rebind_failure_cleanup(struct netif *netif_p, struct dhcp6 *dhcp6_p)
1438 {
1439   s8_t addr_idx = dhcp6_p->addr_idx;
1440 
1441   if (addr_idx != LWIP_IPV6_NUM_ADDRESSES) {
1442     netif_ip6_addr_set_state(netif_p, addr_idx, IP6_ADDR_INVALID);
1443     netif_ip6_addr_set_parts(netif_p, addr_idx, 0, 0, 0, 0);
1444   }
1445   dhcp6_clear_ia(dhcp6_p);
1446   dhcp6_clear_all_options(dhcp6_p);
1447   dhcp6_p->current_serv = NULL;
1448   dhcp6_serverinfo_free_list(&dhcp6_p->serv_list);
1449 }
1450 
1451 static void
dhcp6_post_release_cleanup(struct dhcp6 * dhcp6_p)1452 dhcp6_post_release_cleanup(struct dhcp6 *dhcp6_p)
1453 {
1454   dhcp6_clear_ia(dhcp6_p);
1455   dhcp6_clear_all_options(dhcp6_p);
1456   dhcp6_p->current_serv = NULL;
1457   dhcp6_serverinfo_free_list(&dhcp6_p->serv_list);
1458   dhcp6_p->addr_idx = LWIP_IPV6_NUM_ADDRESSES;
1459 }
1460 
1461 static u8_t *
dhcp6_option_extract_content(const struct pbuf * p_msg_in,const struct dhcp6 * dhcp6_p,enum dhcp6_option_idx idx)1462 dhcp6_option_extract_content(const struct pbuf *p_msg_in, const struct dhcp6 *dhcp6_p,
1463                              enum dhcp6_option_idx idx)
1464 {
1465   LWIP_UNUSED_ARG(dhcp6_p);
1466   u16_t op_start = dhcp6_get_option_start(dhcp6_p, idx);
1467   u16_t op_len = dhcp6_get_option_length(dhcp6_p, idx);
1468   u8_t *option_p = mem_malloc((mem_size_t)op_len);
1469   if (option_p == NULL) {
1470     return NULL;
1471   }
1472   if (pbuf_copy_partial(p_msg_in, option_p, op_len, op_start) != op_len) {
1473     mem_free(option_p);
1474     return NULL;
1475   }
1476   return option_p;
1477 }
1478 
1479 static void
dhcp6_option_free_content(u8_t * option_p)1480 dhcp6_option_free_content(u8_t *option_p)
1481 {
1482   if (option_p != NULL) {
1483     mem_free(option_p);
1484   }
1485 }
1486 
1487 static void
dhcp6_handle_advertise(struct netif * netif_p,const struct pbuf * p_msg_in)1488 dhcp6_handle_advertise(struct netif *netif_p, const struct pbuf *p_msg_in)
1489 {
1490   struct dhcp6 *dhcp6_p = netif_dhcp6_data(netif_p);
1491   dhcp6_serverinfo_t *dhcp6_serverinfo_p = NULL;
1492   u16_t op_start, op_len, copied;
1493   u8_t *option_p = NULL;
1494   struct dhcp6opt_iaaddr *iaaddr_p = NULL;
1495   u32_t dh6_iaaddr_preferred;
1496   u32_t dh6_iaaddr_valid;
1497 
1498   LWIP_UNUSED_ARG(dhcp6_p);
1499   LWIP_UNUSED_ARG(p_msg_in);
1500 
1501   if (dhcp6_p->state != DHCP6_STATE_STATEFUL_SOLICIT) {
1502     DHCP6_STATS_INC(dhcp6.drop);
1503     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("discard this Advertise\n"));
1504     goto exit;
1505   }
1506 
1507   if (!dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_IA_NA)) {
1508     /* we should not accept this ADVERTISE if not contain option IA_NA */
1509     DHCP6_STATS_INC(dhcp6.proterr);
1510     DHCP6_STATS_INC(dhcp6.drop);
1511     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no IA_NA of Advertise\n"));
1512     goto exit;
1513   }
1514   u16_t idx, idx_last;
1515   u16_t op, len;
1516   u8_t ia_addr_found = 0;
1517   op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1518   op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1519   option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1520   if (option_p == NULL) {
1521     DHCP6_STATS_INC(dhcp6.lenerr);
1522     DHCP6_STATS_INC(dhcp6.drop);
1523     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("len : %u\n", op_len));
1524     goto exit;
1525   }
1526 
1527   /* IA_NA option-len : 12 + length of IA_NA-options field */
1528   if (op_len <= 12) {
1529     /* there is no sub option of IA_NA */
1530     DHCP6_STATS_INC(dhcp6.proterr);
1531     DHCP6_STATS_INC(dhcp6.drop);
1532     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no sub option of IA_NA\n"));
1533     goto exit;
1534   }
1535   idx = 12;
1536   while (idx <= (op_len - DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN)) {
1537     op = (u16_t)((option_p[idx] << 8) | option_p[idx + 1]);
1538     if (DHCP6_OPTION_IAADDR == op) {
1539       ia_addr_found = 1;
1540       break;
1541     }
1542     len = (u16_t)((option_p[idx + 2] << 8) | option_p[idx + 3]);
1543     /*
1544      * plus 4 to skip the field option code and length code, because (idx <= (op_len- 4)), so there
1545      * is no need to check idx_last overflow
1546      */
1547     idx_last = (u16_t)(idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN);
1548     idx = (u16_t)(idx_last + len);
1549     if (idx < idx_last) {
1550       /* length mismatch */
1551       DHCP6_STATS_INC(dhcp6.lenerr);
1552       DHCP6_STATS_INC(dhcp6.drop);
1553       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("idx_last : %u, len : %u\n", idx_last, len));
1554       goto exit;
1555     }
1556   }
1557   if (ia_addr_found == 0) {
1558     /* there is no option IA Address as a sub option of IA_NA */
1559     DHCP6_STATS_INC(dhcp6.proterr);
1560     DHCP6_STATS_INC(dhcp6.drop);
1561     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no option IA Address of IA_NA\n"));
1562     goto exit;
1563   }
1564 
1565   /*
1566    * the client MUST ignore any Advertise message that includes
1567    * a Status Code option containing the value NoAddrsAvail.
1568    * [RFC3315 Section 17.1.3].
1569    * We only apply this when we are going to request an address or
1570    * a prefix.
1571    */
1572   if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_STATUS_CODE)) {
1573     u16_t status_code;
1574     op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_STATUS_CODE);
1575     op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_STATUS_CODE);
1576     if (op_len < sizeof(u16_t)) {
1577       /* length of option Status Code must be not less than 2 */
1578       DHCP6_STATS_INC(dhcp6.lenerr);
1579       DHCP6_STATS_INC(dhcp6.drop);
1580       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("invalid option STATUS_CODE len %u\n", op_len));
1581       goto exit;
1582     }
1583     copied = pbuf_copy_partial(p_msg_in, (void *)(&status_code), sizeof(status_code), op_start);
1584     if (copied != sizeof(status_code)) {
1585       /* pbuf length mismatch */
1586       DHCP6_STATS_INC(dhcp6.lenerr);
1587       DHCP6_STATS_INC(dhcp6.drop);
1588       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch, copied %u\n", copied));
1589       goto exit;
1590     }
1591     status_code = ntohs(status_code);
1592     if (DHCP6_STATUS_NOADDRSAVAIL == status_code) {
1593       /* NoAddrsAvail */
1594       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : NoAddrsAvail\n"));
1595       goto exit;
1596     }
1597   }
1598 
1599   /* Status Code also can be the sub option of IA_NA/IA_TA/IAADDR */
1600   if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_IA_NA)) {
1601     u16_t status_code;
1602     op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1603     op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1604     dhcp6_option_free_content(option_p);
1605     option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1606     if (option_p == NULL) {
1607       DHCP6_STATS_INC(dhcp6.lenerr);
1608       DHCP6_STATS_INC(dhcp6.drop);
1609       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("len : %u\n", op_len));
1610       goto exit;
1611     }
1612     /* IA_NA option-len : 12 + IA_NA option code length */
1613     if (op_len > 12) {
1614       idx = 12;
1615       while (idx <= (op_len - DHCP6_OPT_HEADER_LEN)) {
1616         op = (u16_t)((option_p[idx] << 8) | option_p[idx + 1]);
1617         len = (u16_t)((option_p[idx + 2] << 8) | option_p[idx + 3]);
1618         if (DHCP6_OPTION_IAADDR == op) {
1619           if (len >= 24) {
1620             if ((idx + len) > (op_len - DHCP6_OPT_HEADER_LEN)) {
1621               /* length mismatch */
1622               DHCP6_STATS_INC(dhcp6.lenerr);
1623               DHCP6_STATS_INC(dhcp6.drop);
1624               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch idx : %u, len : %u, op_len : %u\n",
1625                 idx, len, op_len));
1626               goto exit;
1627             }
1628             iaaddr_p = (struct dhcp6opt_iaaddr *)(&(option_p[idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN]));
1629             dh6_iaaddr_preferred = ntohl(iaaddr_p->dh6_iaaddr_preferred);
1630             dh6_iaaddr_valid = ntohl(iaaddr_p->dh6_iaaddr_valid);
1631             if (dh6_iaaddr_preferred > dh6_iaaddr_valid) {
1632               /* A client discards any addresses for which the preferred lifetime is greater than the valid lifetime. */
1633               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
1634                           ("error: the preferred lifetime is greater than the valid lifetime\n"));
1635               goto exit;
1636             }
1637           }
1638           /* IAADDR option-len : 24 + length of IAaddr-options field */
1639           if (len > 24) {
1640             u16_t sub_idx, sub_op, sub_len;
1641             sub_idx = (u16_t)(idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + 24);
1642             if ((sub_idx < idx) || (sub_idx > (op_len - DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN))) {
1643               /* length mismatch */
1644               DHCP6_STATS_INC(dhcp6.lenerr);
1645               DHCP6_STATS_INC(dhcp6.drop);
1646               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch sub_idx : %u, idx : %u, op_len : %u\n",
1647                 sub_idx, idx, op_len));
1648               goto exit;
1649             }
1650             /* the sub option of IAADDR only can be Status Code */
1651             sub_op = (u16_t)((option_p[sub_idx] << 8) | option_p[sub_idx + 1]);
1652             sub_len = (u16_t)((option_p[sub_idx + 2] << 8) | option_p[sub_idx + 3]);
1653             if ((u16_t)(sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + sub_len) < sub_idx ||
1654                 (u16_t)(sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + sub_len) > op_len) {
1655               /* length mismatch */
1656               DHCP6_STATS_INC(dhcp6.lenerr);
1657               DHCP6_STATS_INC(dhcp6.drop);
1658               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch sub_idx : %u, sub_len : %u, op_len : %u\n",
1659                 sub_idx, sub_len, op_len));
1660               goto exit;
1661             }
1662 
1663             if ((DHCP6_OPTION_STATUS_CODE == sub_op) && (sub_len >= sizeof(u16_t))) {
1664               status_code = (u16_t)((option_p[sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN] << 8) |
1665                 option_p[sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + 1]);
1666               if (DHCP6_STATUS_NOADDRSAVAIL == status_code) {
1667                 /* NoAddrsAvail */
1668                 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : NoAddrsAvail of IAADDR\n"));
1669                 goto exit;
1670               }
1671             } else {
1672               /* as the sub option of IAADDR only can be Status Code and length of option Status Code
1673                * must be not less than 2, if not, this message is invalid.
1674                */
1675               if (DHCP6_OPTION_STATUS_CODE == sub_op) {
1676                 DHCP6_STATS_INC(dhcp6.proterr);
1677               } else {
1678                 DHCP6_STATS_INC(dhcp6.lenerr);
1679               }
1680               DHCP6_STATS_INC(dhcp6.drop);
1681               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
1682                           ("inlvaid sub option %u or invalid length of option STATUS_CODE\n", sub_op));
1683               goto exit;
1684             }
1685           }
1686         } else if (DHCP6_OPTION_STATUS_CODE == op) {
1687           if (len < 2) {
1688             /* length of option Status Code must be not less than 2 */
1689             DHCP6_STATS_INC(dhcp6.lenerr);
1690             DHCP6_STATS_INC(dhcp6.drop);
1691             LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("invalid option STATUS_CODE len %u\n", len));
1692             goto exit;
1693           }
1694           status_code = (u16_t)((option_p[idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN] << 8) |
1695             option_p[idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + 1]);
1696           if (DHCP6_STATUS_NOADDRSAVAIL == status_code) {
1697             /* NoAddrsAvail */
1698             LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : NoAddrsAvail of IA_NA\n"));
1699             goto exit;
1700           }
1701         }
1702         /*
1703          * plus 4 to skip the field option code and length code, because (idx <= (op_len- 4)), so there
1704          * is no need to check idx_last overflow
1705          */
1706         idx_last = (u16_t)(idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN);
1707         idx = (u16_t)(idx_last + len);
1708         if (idx < idx_last) {
1709           /* length mismatch */
1710           DHCP6_STATS_INC(dhcp6.lenerr);
1711           DHCP6_STATS_INC(dhcp6.drop);
1712           LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch idx_last : %u, len : %u\n", idx_last, len));
1713           goto exit;
1714         }
1715       }
1716     }
1717   }
1718 
1719   /* A Advertise message must contain a Server ID option and a Client ID option */
1720   if (!dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID) ||
1721       !dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_CLI_ID)) {
1722     DHCP6_STATS_INC(dhcp6.proterr);
1723     DHCP6_STATS_INC(dhcp6.drop);
1724     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no SERVER_ID or CLI_ID\n"));
1725     goto exit;
1726   }
1727 
1728   /*
1729    * DUID in the Client ID option (which must be contained for our
1730    * client implementation) must match ours.
1731    */
1732   op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_CLI_ID);
1733   op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_CLI_ID);
1734   dhcp6_option_free_content(option_p);
1735   option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_CLI_ID);
1736   if ((op_len != dhcp6_p->duid.duid_len) || (option_p == NULL) ||
1737       (memcmp(option_p, dhcp6_p->duid.duid_id, op_len) != 0)) {
1738     /*
1739      * condition 1 : op_len must be equal to the netif's duid_len
1740      * condition 2 : the content of option CLI_ID must be the same with the netif's duid
1741      */
1742     DHCP6_STATS_INC(dhcp6.err);
1743     DHCP6_STATS_INC(dhcp6.drop);
1744     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DUID not match\n"));
1745     goto exit;
1746   }
1747 
1748   op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID);
1749   op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID);
1750   dhcp6_option_free_content(option_p);
1751   option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID);
1752   if (option_p == NULL) {
1753     DHCP6_STATS_INC(dhcp6.lenerr);
1754     DHCP6_STATS_INC(dhcp6.drop);
1755     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("invalid len(%u) of SERVER_ID or copy failed\n", op_len));
1756     goto exit;
1757   }
1758   dhcp6_serverinfo_p = dhcp6_find_server(dhcp6_p, option_p, op_len);
1759   if (dhcp6_serverinfo_p == NULL) {
1760     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have new server info\n"));
1761     if (dhcp6_p->serv_cnt >= DHCP6_MAX_SERVER_NUM) {
1762       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("too many server info\n"));
1763       DHCP6_STATS_INC(dhcp6.drop);
1764       goto exit;
1765     }
1766     dhcp6_p->serv_cnt++;
1767     dhcp6_serverinfo_p = dhcp6_serverinfo_new(p_msg_in);
1768     if (dhcp6_serverinfo_p == NULL) {
1769       DHCP6_STATS_INC(dhcp6.drop);
1770       goto exit;
1771     }
1772     if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_PREFERENCE)) {
1773       u8_t pref;
1774       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have PREFERENCE\n"));
1775       op_start = dhcp6_get_option_start(dhcp6_p, DHCP6_OPTION_IDX_PREFERENCE);
1776       copied = pbuf_copy_partial(p_msg_in, (void *)(&pref), sizeof(pref), op_start);
1777       if (copied != sizeof(pref)) {
1778         /* pbuf length mismatch */
1779         DHCP6_STATS_INC(dhcp6.lenerr);
1780         DHCP6_STATS_INC(dhcp6.drop);
1781         dhcp6_serverinfo_free(dhcp6_serverinfo_p);
1782         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("copy PREFERENCE failed\n"));
1783         goto exit;
1784       }
1785       dhcp6_serverinfo_p->pref = pref;
1786       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("PREFERENCE %02x\n", pref));
1787     }
1788     dhcp6_serverinfo_insert(&(dhcp6_p->serv_list), dhcp6_serverinfo_p);
1789 
1790     /*
1791      * If the client receives an Advertise message that includes a
1792      * Preference option with a preference value of 255, the client
1793      * immediately begins a client-initiated message exchange.
1794      * [RFC3315 Section 17.1.2]
1795      */
1796     /*
1797      * If the client does not receive any Advertise messages before the
1798      * first RT has elapsed, it begins the retransmission mechanism
1799      * described in section 14. The client terminates the retransmission
1800      * process as soon as it receives any Advertise message, and the client
1801      * acts on the received Advertise message without waiting for any
1802      * additional Advertise messages.
1803      * [RFC3315 Section 17.1.2]
1804      */
1805     if ((dhcp6_serverinfo_p->pref == DHCP6_OPTION_PREFERENCE_MAX) ||
1806         (dhcp6_p->RT > (2 * dhcp6_p->IRT))) {
1807       dhcp6_p->current_serv = dhcp6_serverinfo_p;
1808       dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_REQUEST, "dhcp6_handle_advertise");
1809       dhcp6_set_timeout_params(dhcp6_p);
1810       dhcp6_request(netif_p, dhcp6_p);
1811     }
1812   }
1813 
1814 exit:
1815   dhcp6_option_free_content(option_p);
1816 }
1817 
1818 static void
dhcp6_handle_reply(struct netif * netif_p,const struct pbuf * p_msg_in)1819 dhcp6_handle_reply(struct netif *netif_p, const struct pbuf *p_msg_in)
1820 {
1821   struct dhcp6 *dhcp6_p = netif_dhcp6_data(netif_p);
1822   u16_t op_len;
1823   u8_t state;
1824   u8_t *option_p = NULL;
1825 #if DHCP6_ENABLE_UNICAST_SUPPORT
1826   dhcp6_serverinfo_t *dhcp6_serverinfo_p = NULL;
1827 #endif
1828   s8_t addr_idx = dhcp6_p->addr_idx;
1829 
1830   LWIP_UNUSED_ARG(dhcp6_p);
1831   LWIP_UNUSED_ARG(p_msg_in);
1832 
1833   state = dhcp6_p->state;
1834   if ((state != DHCP6_STATE_STATEFUL_INFOREQ) &&
1835       (state != DHCP6_STATE_STATEFUL_REQUEST) &&
1836       (state != DHCP6_STATE_STATEFUL_RENEW) &&
1837       (state != DHCP6_STATE_STATEFUL_REBIND) &&
1838       (state != DHCP6_STATE_STATEFUL_RELEASE) &&
1839       (state != DHCP6_STATE_STATEFUL_DECLINE)) {
1840     DHCP6_STATS_INC(dhcp6.drop);
1841     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("discard this Reply\n"));
1842     goto exit;
1843   }
1844 
1845   /* A Reply message must contain a Server ID option and a Client ID option */
1846   if (!dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID) ||
1847       !dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_CLI_ID)) {
1848     DHCP6_STATS_INC(dhcp6.proterr);
1849     DHCP6_STATS_INC(dhcp6.drop);
1850     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no SERVER_ID or CLI_ID\n"));
1851     goto exit;
1852   }
1853 
1854   /*
1855    * DUID in the Client ID option (which must be contained for our
1856    * client implementation) must match ours.
1857    */
1858   op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_CLI_ID);
1859   option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_CLI_ID);
1860   if ((op_len != dhcp6_p->duid.duid_len) || (option_p == NULL) ||
1861       (memcmp(option_p, dhcp6_p->duid.duid_id, op_len) != 0)) {
1862     /*
1863      * condition 1 : op_len must be equal to the netif's duid_len
1864      * condition 2 : the content of option CLI_ID must be the same with the netif's duid
1865      */
1866     DHCP6_STATS_INC(dhcp6.err);
1867     DHCP6_STATS_INC(dhcp6.drop);
1868     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DUID not match\n"));
1869     goto exit;
1870   }
1871 
1872 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
1873   if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_DNS_SERVER)) {
1874     if (ERR_OK != dhcp6_set_dns_servers(netif_p, dhcp6_p, p_msg_in)) {
1875       DHCP6_STATS_INC(dhcp6.err);
1876       DHCP6_STATS_INC(dhcp6.drop);
1877       goto exit;
1878     }
1879   }
1880   /* @ Note: parse and set Domain Search List */
1881 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
1882 
1883 #if LWIP_DHCP6_GET_NTP_SRV
1884   if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_NTP_SERVER)) {
1885     if (ERR_OK != dhcp6_set_ntp_srv(netif_p, dhcp6_p, p_msg_in)) {
1886       DHCP6_STATS_INC(dhcp6.err);
1887       DHCP6_STATS_INC(dhcp6.drop);
1888       goto exit;
1889     }
1890   }
1891 #endif /* LWIP_DHCP6_GET_NTP_SRV */
1892 
1893   if (state != DHCP6_STATE_STATEFUL_RELEASE) {
1894     if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_IA_NA)) {
1895       ia_t *ia_p = NULL;
1896       u16_t idx, idx_last, status_code;
1897       u16_t op, len;
1898       s8_t addr_idx_match;
1899       struct netif *netif = NULL;
1900       struct ia_info *ia_info_p = NULL;
1901       struct dhcp6opt_iaaddr *iaaddr_p = NULL;
1902       op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1903       dhcp6_option_free_content(option_p);
1904       option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_IA_NA);
1905       if (option_p == NULL) {
1906         DHCP6_STATS_INC(dhcp6.lenerr);
1907         DHCP6_STATS_INC(dhcp6.drop);
1908         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("len : %u\n", op_len));
1909         goto exit;
1910       }
1911       ia_p = dhcp6_get_ia_by_iaid(dhcp6_p, &(option_p[0]), &addr_idx);
1912       if ((ia_p == NULL) || (addr_idx != dhcp6_p->addr_idx)) {
1913         DHCP6_STATS_INC(dhcp6.proterr);
1914         DHCP6_STATS_INC(dhcp6.drop);
1915         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no such iaid of netif, dhcp6_addr_idx:%d\n", dhcp6_p->addr_idx));
1916         goto exit;
1917       }
1918 
1919       ia_info_p = (struct ia_info *)(&(option_p[0]));
1920       ia_p->iaid.ia.dh6_ia_t1 = ntohl(ia_info_p->dh6_ia_t1);
1921       ia_p->iaid.ia.dh6_ia_t2 = ntohl(ia_info_p->dh6_ia_t2);
1922       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("T1:%u T2:%u\n", ia_p->iaid.ia.dh6_ia_t1, ia_p->iaid.ia.dh6_ia_t2));
1923 
1924       /* IA_NA option-len : 12 + length of IA_NA-options field */
1925       if (op_len > 12) {
1926         idx = 12;
1927         while (idx <= (op_len - DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN)) {
1928           op = (u16_t)((option_p[idx] << 8) | option_p[idx + 1]);
1929           len = (u16_t)((option_p[idx + 2] << 8) | option_p[idx + 3]);
1930 
1931           if (DHCP6_OPTION_IAADDR == op) {
1932             if (len < 24) {
1933               DHCP6_STATS_INC(dhcp6.proterr);
1934               DHCP6_STATS_INC(dhcp6.drop);
1935               /* option-len 24 + length of IAaddr-options field. */
1936               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("invalid len(%u) of IAADDR\n", len));
1937               goto exit;
1938             }
1939             /* IAADDR option-len : 24 + length of IAaddr-options field */
1940             if (len > 24) {
1941               u16_t sub_idx, sub_op, sub_len;
1942               sub_idx = (u16_t)(idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + 24);
1943               if (sub_idx < idx || sub_idx > (op_len - DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN)) {
1944                 /* length mismatch */
1945                 DHCP6_STATS_INC(dhcp6.lenerr);
1946                 DHCP6_STATS_INC(dhcp6.drop);
1947                 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch sub_idx : %u, idx : %u, op_len : %u\n",
1948                   sub_idx, idx, op_len));
1949                 goto exit;
1950               }
1951               /* the sub option of IAADDR only can be Status Code */
1952               sub_op = (u16_t)((option_p[sub_idx] << 8) | option_p[sub_idx + 1]);
1953               sub_len = (u16_t)((option_p[sub_idx + 2] << 8) | option_p[sub_idx + 3]);
1954               /* sub_op filed and sub_len filed length is 4 */
1955               if ((u16_t)(sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + sub_len) < sub_idx ||
1956                   (u16_t)(sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + sub_len) > op_len) {
1957                 /* length mismatch */
1958                 DHCP6_STATS_INC(dhcp6.lenerr);
1959                 DHCP6_STATS_INC(dhcp6.drop);
1960                 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch sub_idx : %u, sub_len : %u, op_len : %u\n",
1961                   sub_idx, sub_len, op_len));
1962                 goto exit;
1963               }
1964               /* Status Code len must >= 2 */
1965               if ((DHCP6_OPTION_STATUS_CODE == sub_op) && (sub_len >= 2)) {
1966                 status_code = (u16_t)((option_p[sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN] << 8) |
1967                   option_p[sub_idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + 1]);
1968                 if (DHCP6_STATUS_NOADDRSAVAIL == status_code) {
1969                   /* NoAddrsAvail */
1970                   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : NoAddrsAvail of IAADDR\n"));
1971                   goto exit;
1972                 } else if (DHCP6_STATUS_UNSPECFAIL == status_code) {
1973                   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : DHCP6_STATUS_UNSPECFAIL of IA_NA\n"));
1974                   goto exit;
1975                 }
1976               }
1977             }
1978             if ((idx + len) > (op_len - DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN)) {
1979               /* length mismatch */
1980               DHCP6_STATS_INC(dhcp6.lenerr);
1981               DHCP6_STATS_INC(dhcp6.drop);
1982               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch idx : %u, len : %u, op_len : %u\n",
1983                 idx, len, op_len));
1984               goto exit;
1985             }
1986 
1987             iaaddr_p = (struct dhcp6opt_iaaddr *)(&(option_p[idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN]));
1988             NETIF_FOREACH(netif) {
1989               /* check if the address is being used by us */
1990               addr_idx_match = netif_get_ip6_addr_match(netif, &iaaddr_p->dh6_iaaddr_addr);
1991               if ((addr_idx_match >= 0) && ((netif != netif_p) || addr_idx_match != addr_idx)) {
1992                 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("error: the addres is already being used by us\n"));
1993                 dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_DECLINE, "dhcp6_handle_reply");
1994                 dhcp6_set_timeout_params(dhcp6_p);
1995                 dhcp6_decline(netif_p, dhcp6_p);
1996                 goto exit;
1997               }
1998             }
1999 
2000             ia_p->iaaddr.dh6_iaaddr_addr = iaaddr_p->dh6_iaaddr_addr;
2001             ia_p->iaaddr.dh6_iaaddr_preferred = ntohl(iaaddr_p->dh6_iaaddr_preferred);
2002             ia_p->iaaddr.dh6_iaaddr_valid = ntohl(iaaddr_p->dh6_iaaddr_valid);
2003             if (ia_p->iaaddr.dh6_iaaddr_preferred > ia_p->iaaddr.dh6_iaaddr_valid) {
2004               /* A client discards any addresses for which the preferred lifetime is greater than the valid lifetime. */
2005               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
2006                           ("error: the preferred lifetime is greater than the valid lifetime\n"));
2007               goto exit;
2008             }
2009 
2010 #if LWIP_IPV6_ADDRESS_LIFETIMES
2011             netif_ip6_addr_set_valid_life(netif_p, addr_idx, ia_p->iaaddr.dh6_iaaddr_valid);
2012             netif_ip6_addr_set_pref_life(netif_p, addr_idx, ia_p->iaaddr.dh6_iaaddr_preferred);
2013 #endif
2014             if (LWIP_IS_DAD_ENABLED(netif_p)) {
2015               netif_ip6_addr_set_state(netif_p, addr_idx, IP6_ADDR_TENTATIVE);
2016               /* as addr state is IP6_ADDR_TENTATIVE, NS will be sent via nd6_tmr->nd6_send_ns */
2017               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("IA_NA addr assigned, will do DAD\n"));
2018             } else {
2019               netif_ip6_addr_set_state(netif_p, addr_idx, IP6_ADDR_PREFERRED);
2020               /* as addr state is IP6_ADDR_PREFERRED, NA will be sent */
2021               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("IA_NA addr assigned, will send NA\n"));
2022             }
2023             netif_ip6_addr_set_parts(netif_p, addr_idx,
2024                                      iaaddr_p->dh6_iaaddr_addr.addr[0], iaaddr_p->dh6_iaaddr_addr.addr[1],
2025                                      iaaddr_p->dh6_iaaddr_addr.addr[2], iaaddr_p->dh6_iaaddr_addr.addr[3]);
2026           } else if (op == DHCP6_OPTION_STATUS_CODE) {
2027             if (len < 2) {
2028               /* length of option Status Code must be not less than 2 */
2029               DHCP6_STATS_INC(dhcp6.lenerr);
2030               DHCP6_STATS_INC(dhcp6.drop);
2031               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("invalid option STATUS_CODE len %u\n", len));
2032               goto exit;
2033             }
2034             /* skip the field option code and length code to get status_code */
2035             status_code = (u16_t)((option_p[idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN] << 8) |
2036                                   option_p[idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN + 1]);
2037             if (status_code == DHCP6_STATUS_NOADDRSAVAIL) {
2038               /* NoAddrsAvail */
2039               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : NoAddrsAvail of IA_NA\n"));
2040               goto exit;
2041             } else if (status_code == DHCP6_STATUS_UNSPECFAIL) {
2042               LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("have status code : DHCP6_STATUS_UNSPECFAIL of IA_NA\n"));
2043               goto exit;
2044             }
2045           }
2046           /*
2047            * plus 4 to skip the field option code and length code, because (idx <= (op_len- 4)), so there
2048            * is no need to check idx_last overflow
2049            */
2050           idx_last = (u16_t)(idx + DHCP6_OPTION_IAADDR_SUBOP_LEN_MIN);
2051           idx = (u16_t)(idx_last + len);
2052           if (idx < idx_last) {
2053             /* length mismatch */
2054             DHCP6_STATS_INC(dhcp6.lenerr);
2055             DHCP6_STATS_INC(dhcp6.drop);
2056             LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("length mismatch idx_last : %u, len : %u\n", idx_last, len));
2057             goto exit;
2058           }
2059         }
2060       }
2061     }
2062   }
2063 
2064 #if DHCP6_ENABLE_UNICAST_SUPPORT
2065   if (dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID) &&
2066       dhcp6_option_given(dhcp6_p, DHCP6_OPTION_IDX_UNICAST)) {
2067     op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID);
2068     dhcp6_option_free_content(option_p);
2069     option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_SERVER_ID);
2070     if (option_p == NULL) {
2071       DHCP6_STATS_INC(dhcp6.lenerr);
2072       DHCP6_STATS_INC(dhcp6.drop);
2073       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("len : %u\n", op_len));
2074       goto exit;
2075     }
2076     dhcp6_serverinfo_p = dhcp6_find_server(dhcp6_p, option_p, op_len);
2077     if (dhcp6_serverinfo_p != NULL) {
2078       op_len = dhcp6_get_option_length(dhcp6_p, DHCP6_OPTION_IDX_UNICAST);
2079       if (op_len == sizeof(ip6_addr_t)) {
2080         dhcp6_option_free_content(option_p);
2081         option_p = dhcp6_option_extract_content(p_msg_in, dhcp6_p, DHCP6_OPTION_IDX_UNICAST);
2082         if (option_p == NULL) {
2083           DHCP6_STATS_INC(dhcp6.lenerr);
2084           DHCP6_STATS_INC(dhcp6.drop);
2085           LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("len : %u\n", op_len));
2086           goto exit;
2087         }
2088         (void)memcpy_s(&dhcp6_serverinfo_p->unicast_addr, op_len, option_p, op_len);
2089         dhcp6_serverinfo_p->unicast = 1;
2090       }
2091     }
2092   }
2093 #endif
2094 
2095   if (state == DHCP6_STATE_STATEFUL_RELEASE) {
2096     dhcp6_post_release_cleanup(dhcp6_p);
2097     /* Don't need timer any more. */
2098     dhcp6_p->request_timeout = 0;
2099     dhcp6_set_state(dhcp6_p, DHCP6_STATE_OFF, "dhcp6_handle_reply");
2100     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("receive Reply for Release\n"));
2101   } else if (state ==  DHCP6_STATE_STATEFUL_INFOREQ) {
2102     /* not supported yet. */
2103   } else if (state ==  DHCP6_STATE_STATEFUL_DECLINE) {
2104     dhcp6_request_next_server(netif_p, dhcp6_p);
2105     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("receive Reply for Decline\n"));
2106   } else if (addr_idx != LWIP_IPV6_NUM_ADDRESSES) {
2107     ia_t *ia_p = NULL;
2108     dhcp6_set_state(dhcp6_p,  DHCP6_STATE_STATEFUL_IDLE, "dhcp6_handle_reply");
2109     ia_p = &(dhcp6_p->ia[addr_idx]);
2110 
2111     dhcp6_p->request_timeout = (u16_t)((ia_p->iaid.ia.dh6_ia_t1 * MS_PER_SECOND + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
2112     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
2113                 ("dhcp6_handle_reply(): set request timeout %"U32_F" msecs\n", ia_p->iaid.ia.dh6_ia_t1*1000));
2114   }
2115 
2116 exit:
2117   dhcp6_option_free_content(option_p);
2118 }
2119 
2120 void
dhcp6_dad_handle(struct netif * netif_p,const ip6_addr_t * target_address)2121 dhcp6_dad_handle(struct netif *netif_p, const ip6_addr_t *target_address)
2122 {
2123   struct dhcp6 *dhcp6_p = netif_dhcp6_data(netif_p);
2124   ia_t *ia_p = NULL;
2125   s8_t addr_idx;
2126 
2127   if ((dhcp6_p == NULL) || (target_address == NULL)) {
2128     return;
2129   }
2130 
2131   if (dhcp6_p->current_serv == NULL) {
2132     return;
2133   }
2134 
2135   addr_idx = dhcp6_p->addr_idx;
2136   if (addr_idx == LWIP_IPV6_NUM_ADDRESSES) {
2137     return;
2138   }
2139   ia_p = &(dhcp6_p->ia[addr_idx]);
2140   if (ip6_addr_cmp(&(ia_p->iaaddr.dh6_iaaddr_addr), target_address)) {
2141     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("receive NA for request address from others\n"));
2142     dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_DECLINE, "dhcp6_dad_handle");
2143     dhcp6_set_timeout_params(dhcp6_p);
2144     dhcp6_decline(netif_p, dhcp6_p);
2145   }
2146 
2147   return;
2148 }
2149 
2150 static void
dhcp6_solicit_to_request(struct netif * netif_p,struct dhcp6 * dhcp6_p)2151 dhcp6_solicit_to_request(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2152 {
2153   dhcp6_serverinfo_t *dhcp6_serverinfo_p = NULL;
2154 
2155   dhcp6_serverinfo_p = dhcp6_serverinfo_find_active(dhcp6_p->serv_list);
2156   if (dhcp6_serverinfo_p == NULL) {
2157     dhcp6_solicit(netif_p, dhcp6_p);
2158   } else {
2159     dhcp6_p->current_serv = dhcp6_serverinfo_p;
2160     dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_REQUEST, "dhcp6_solicit_to_request");
2161     dhcp6_set_timeout_params(dhcp6_p);
2162     dhcp6_request(netif_p, dhcp6_p);
2163   }
2164 
2165   return;
2166 }
2167 
2168 static const u16_t*
dhcp6_get_option_list(u8_t message_type)2169 dhcp6_get_option_list(u8_t message_type)
2170 {
2171   static u16_t dhcp6_solicit_option_list[] = {
2172     DHCP6_OPTION_ELAPSED_TIME, DHCP6_OPTION_CLIENTID, DHCP6_OPTION_IA_NA, DHCP6_OPTION_ORO, 0
2173   };
2174   static u16_t dhcp6_request_option_list[] = {
2175     DHCP6_OPTION_ELAPSED_TIME, DHCP6_OPTION_CLIENTID, DHCP6_OPTION_IA_NA, DHCP6_OPTION_SERVERID, DHCP6_OPTION_ORO, 0
2176   };
2177   static u16_t dhcp6_renew_option_list[] = {
2178     DHCP6_OPTION_ELAPSED_TIME, DHCP6_OPTION_CLIENTID, DHCP6_OPTION_IA_NA, DHCP6_OPTION_SERVERID, DHCP6_OPTION_ORO, 0
2179   };
2180   static u16_t dhcp6_rebind_option_list[] = {
2181     DHCP6_OPTION_ELAPSED_TIME, DHCP6_OPTION_CLIENTID, DHCP6_OPTION_IA_NA, DHCP6_OPTION_ORO, 0
2182   };
2183   static u16_t dhcp6_release_option_list[] = {
2184     DHCP6_OPTION_ELAPSED_TIME, DHCP6_OPTION_CLIENTID, DHCP6_OPTION_IA_NA, DHCP6_OPTION_SERVERID, 0
2185   };
2186   static u16_t dhcp6_decline_option_list[] = {
2187     DHCP6_OPTION_ELAPSED_TIME, DHCP6_OPTION_CLIENTID, DHCP6_OPTION_IA_NA, DHCP6_OPTION_SERVERID, 0
2188   };
2189 
2190   switch (message_type) {
2191     case DHCP6_SOLICIT:
2192       return dhcp6_solicit_option_list;
2193     case DHCP6_REQUEST:
2194       return dhcp6_request_option_list;
2195     case DHCP6_RENEW:
2196       return dhcp6_renew_option_list;
2197     case DHCP6_REBIND:
2198       return dhcp6_rebind_option_list;
2199     case DHCP6_RELEASE:
2200       return dhcp6_release_option_list;
2201     case DHCP6_DECLINE:
2202       return dhcp6_decline_option_list;
2203     default:
2204       return NULL;
2205   }
2206 }
2207 
2208 static u8_t
dhcp6_options_list_check(const u16_t * option_list,u16_t option)2209 dhcp6_options_list_check(const u16_t *option_list, u16_t option)
2210 {
2211   u32_t i = 0;
2212   for (i = 0; option_list[i] && option_list[i] != option; i++) {
2213     ;
2214   }
2215   return (option_list[i] != 0);
2216 }
2217 
2218 static void
dhcp6_stateful_send(struct netif * netif_p,struct dhcp6 * dhcp6_p,u8_t message_type)2219 dhcp6_stateful_send(struct netif *netif_p, struct dhcp6 *dhcp6_p, u8_t message_type)
2220 {
2221   const u16_t requested_options[] = {DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_SNTP_SERVERS};
2222   struct pbuf *p_out = NULL;
2223   u16_t options_out_len;
2224   u16_t opt_len_alloc = 0;
2225   u16_t val_16;
2226   const u16_t *option_list = NULL;
2227   u32_t i = 0;
2228   opt_comm_t *serv_id = NULL;
2229   opt_comm_t *ia_info = NULL;
2230   u8_t *ia_option = NULL;
2231   u16_t ia_option_length = 0;
2232   u32_t elapsed_time;
2233   u32_t remaining_timeout = 0;
2234   s8_t addr_idx = dhcp6_p->addr_idx;
2235 
2236   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
2237               ("dhcp6_stateful_send: %s\n", DHCP6_STATEFUL_MESSAGE_TYPE_TO_STRING(message_type)));
2238 
2239   option_list = dhcp6_get_option_list(message_type);
2240   if (option_list == NULL) {
2241     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("Unsupported message\n"));
2242     return;
2243   }
2244 
2245   if ((dhcp6_p->current_serv == NULL) && dhcp6_options_list_check(option_list, DHCP6_OPTION_SERVERID)) {
2246     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCPv6 Server is not available\n"));
2247     return;
2248   }
2249 
2250   while (option_list[i]) {
2251     switch (option_list[i]) {
2252       case DHCP6_OPTION_ELAPSED_TIME:
2253         opt_len_alloc = (u16_t)(opt_len_alloc + DHCP6_OPT_HEADER_LEN + DHCPv6_ELAPSED_TIME_VALUE_BYTES);
2254         break;
2255       case DHCP6_OPTION_CLIENTID:
2256         opt_len_alloc = (u16_t)(opt_len_alloc + DHCP6_OPT_HEADER_LEN + dhcp6_p->duid.duid_len);
2257         break;
2258       case DHCP6_OPTION_IA_NA:
2259         /* prepare ia_option and length here */
2260         if (dhcp6_p->current_serv) {
2261           ia_info = dhcp6_server_find_opt(dhcp6_p->current_serv, DHCP6_OPTION_IA_NA);
2262           if (ia_info == NULL) {
2263             LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("ia_info not found\n"));
2264             return;
2265           }
2266           ia_option = ia_info->opt_start;
2267           ia_option_length = ia_info->opt_len;
2268         } else {
2269           LWIP_ASSERT("dhcp6_addr_idx is error", addr_idx != LWIP_IPV6_NUM_ADDRESSES);
2270           ia_option = (u8_t *)&((dhcp6_p->ia[addr_idx]).iaid.ia);
2271           ia_option_length = (dhcp6_p->ia[addr_idx]).iaid.iaid_len;
2272         }
2273         opt_len_alloc = (u16_t)(opt_len_alloc + DHCP6_OPT_HEADER_LEN + ia_option_length);
2274         break;
2275       case DHCP6_OPTION_SERVERID:
2276         if (dhcp6_p->current_serv) {
2277           serv_id = dhcp6_server_find_opt(dhcp6_p->current_serv, DHCP6_OPTION_SERVERID);
2278         }
2279 
2280         if (serv_id == NULL) {
2281           LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("serv_id not found\n"));
2282           return;
2283         }
2284         opt_len_alloc = (u16_t)(opt_len_alloc + DHCP6_OPT_HEADER_LEN + serv_id->opt_len);
2285         break;
2286       case DHCP6_OPTION_ORO:
2287         opt_len_alloc = (u16_t)(opt_len_alloc + DHCP6_OPT_HEADER_LEN + sizeof(requested_options));
2288         break;
2289       default:
2290         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("Unsupported DHCPv6 Option (%u)\n", option_list[i]));
2291         return;
2292     }
2293     i++;
2294   }
2295 
2296   /* create and initialize the DHCP message header */
2297   p_out = dhcp6_create_msg(netif_p, dhcp6_p, message_type, opt_len_alloc, &options_out_len);
2298   if (p_out != NULL) {
2299     err_t err;
2300     struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
2301     u8_t *options = (u8_t *)(msg_out + 1);
2302     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
2303                 ("DHCP6_%s: making request\n", DHCP6_STATEFUL_MESSAGE_TYPE_TO_STRING(message_type)));
2304 
2305     // DHCP6_OPTION_ELAPSED_TIME
2306     if (dhcp6_p->tries == 0) {
2307       elapsed_time = 0;
2308       val_16 = htons(0);
2309     } else {
2310       elapsed_time = dhcp6_retransmission_duration(dhcp6_p->duration_start);
2311       /* elapsed-time is expressed in hundredths of a second (10^-2 seconds), max is 0xffff */
2312       /*
2313        * when (dhcp6_p->tries == 255), elapsed_time MUST be greater than 0xffff, but be smaller than (u32_t)(-1).
2314        * because sys_now() may overflow more than 1 time, we use (dhcp6_p->tries == 255) to indicate that value of
2315        * OPTION_ELAPSED_TIME have reach the maximum, instead of that value of OPTION_ELAPSED_TIME reverse while
2316        * sys_now() overflow.
2317        */
2318       if (dhcp6_p->tries == UCHAR_MAX) {
2319         val_16 = htons((u16_t)(0xffff));
2320       } else {
2321         if ((u32_t)(elapsed_time / 100) > (u32_t)(0x0000ffff)) {
2322           val_16 = htons((u16_t)(0xffff));
2323         } else {
2324           val_16 = htons((u16_t)(elapsed_time / 100));
2325         }
2326       }
2327     }
2328     if (dhcp6_p->MRD && (elapsed_time < dhcp6_p->MRD)) {
2329       remaining_timeout = dhcp6_p->MRD - elapsed_time;
2330     } else {
2331       remaining_timeout = 0;
2332     }
2333 
2334     i = 0;
2335     while (option_list[i]) {
2336       switch (option_list[i]) {
2337         case DHCP6_OPTION_ELAPSED_TIME:
2338           /* DHCP6_OPTION_ELAPSED_TIME option len is 2 */
2339           options_out_len = dhcp6_option_append(options_out_len, options, DHCP6_OPTION_ELAPSED_TIME,
2340                                                 2, (u8_t *)(&(val_16)));
2341           break;
2342         case DHCP6_OPTION_CLIENTID:
2343           options_out_len = dhcp6_option_append(options_out_len, options, DHCP6_OPTION_CLIENTID,
2344                                                 dhcp6_p->duid.duid_len, dhcp6_p->duid.duid_id);
2345           break;
2346         case DHCP6_OPTION_IA_NA:
2347           /* ia_option_length and ia_option will be set properly in the aboved 'switch' block */
2348           options_out_len = dhcp6_option_append(options_out_len, options, DHCP6_OPTION_IA_NA,
2349                                                 ia_option_length, ia_option);
2350           break;
2351         case DHCP6_OPTION_SERVERID:
2352           /* serv_id will be set in the above 'switch' block */
2353           if (serv_id) {
2354             options_out_len = dhcp6_option_append(options_out_len, options, DHCP6_OPTION_SERVERID,
2355                                                   serv_id->opt_len, serv_id->opt_start);
2356           }
2357           break;
2358         case DHCP6_OPTION_ORO:
2359           options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
2360                                                        LWIP_ARRAYSIZE(requested_options), p_out->len);
2361           break;
2362         default:
2363           break;
2364       }
2365       i++;
2366     }
2367 
2368     LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif_p, dhcp6_p, dhcp6_p->state, msg_out,
2369       message_type, options_out_len, p_out->len);
2370 
2371     dhcp6_msg_finalize(options_out_len, p_out);
2372 
2373 #if DHCP6_ENABLE_UNICAST_SUPPORT
2374     if (dhcp6_p->current_serv && dhcp6_p->current_serv->unicast) {
2375       ip_addr_t src_ip;
2376       /* Offset to IAID */
2377       IP_ADDR6(&src_ip,
2378                ia_option[4 + 0],
2379                ia_option[4 + 1],
2380                ia_option[4 + 2],
2381                ia_option[4 + 3]);
2382 
2383       DHCP6_STATS_INC(dhcp6.xmit);
2384       err = udp_sendto_if_src(dhcp6_p->pcb, p_out, &dhcp6_p->current_serv->unicast_addr, DHCP6_SERVER_PORT,
2385                               netif_p, &src_ip);
2386     } else {
2387 #endif
2388       DHCP6_STATS_INC(dhcp6.xmit);
2389       err = udp_sendto_if(dhcp6_p->pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif_p);
2390 #if DHCP6_ENABLE_UNICAST_SUPPORT
2391     }
2392 #endif
2393     (void)pbuf_free(p_out);
2394     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
2395                 ("DHCP6_%s -> %d\n", DHCP6_STATEFUL_MESSAGE_TYPE_TO_STRING(message_type), (int)err));
2396     LWIP_UNUSED_ARG(err);
2397   } else {
2398     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
2399                 ("dhcp6_stateful_send: could not allocate DHCP6 msg\n"));
2400   }
2401   if (dhcp6_p->tries < UCHAR_MAX) {
2402     dhcp6_p->tries++;
2403   }
2404 
2405   if (dhcp6_p->tries == 1) {
2406     dhcp6_p->RT = dhcp6_p->IRT + (u32_t)(dhcp6_p->IRT * DHCPV6_RT_RAND);
2407   } else {
2408     dhcp6_p->RT = (dhcp6_p->RT << 1) + (u32_t)(dhcp6_p->RT * DHCPV6_RT_RAND);
2409   }
2410 
2411   if (dhcp6_p->MRT != 0 && dhcp6_p->RT > dhcp6_p->MRT) {
2412     /* [RFC3315 Section 14 ] */
2413     dhcp6_p->RT = dhcp6_p->MRT + (u32_t)(dhcp6_p->MRT * DHCPV6_RT_RAND);
2414   }
2415   if (dhcp6_p->MRD != 0 && remaining_timeout && (dhcp6_p->RT > remaining_timeout)) {
2416     dhcp6_p->RT = remaining_timeout;
2417   }
2418   dhcp6_p->request_timeout = (u16_t)((dhcp6_p->RT + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
2419 
2420   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
2421               ("dhcp6_stateful_request_timeout(): set request timeout %"U32_F" msecs\n", dhcp6_p->RT));
2422 
2423   return;
2424 }
2425 
2426 static void
dhcp6_solicit(struct netif * netif_p,struct dhcp6 * dhcp6_p)2427 dhcp6_solicit(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2428 {
2429   dhcp6_stateful_send(netif_p, dhcp6_p, DHCP6_SOLICIT);
2430 }
2431 
2432 static void
dhcp6_request(struct netif * netif_p,struct dhcp6 * dhcp6_p)2433 dhcp6_request(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2434 {
2435   dhcp6_stateful_send(netif_p, dhcp6_p, DHCP6_REQUEST);
2436 }
2437 
2438 static void
dhcp6_renew(struct netif * netif_p,struct dhcp6 * dhcp6_p)2439 dhcp6_renew(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2440 {
2441   dhcp6_stateful_send(netif_p, dhcp6_p, DHCP6_RENEW);
2442 }
2443 
2444 static void
dhcp6_rebind(struct netif * netif_p,struct dhcp6 * dhcp6_p)2445 dhcp6_rebind(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2446 {
2447   dhcp6_stateful_send(netif_p, dhcp6_p, DHCP6_REBIND);
2448 }
2449 
2450 static void
dhcp6_release(struct netif * netif_p,struct dhcp6 * dhcp6_p)2451 dhcp6_release(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2452 {
2453   dhcp6_stateful_send(netif_p, dhcp6_p, DHCP6_RELEASE);
2454 }
2455 
2456 static void
dhcp6_request_next_server(struct netif * netif_p,struct dhcp6 * dhcp6_p)2457 dhcp6_request_next_server(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2458 {
2459   dhcp6_serverinfo_t *dhcp6_serverinfo_p = NULL;
2460 
2461   dhcp6_serverinfo_p = dhcp6_p->current_serv;
2462   dhcp6_p->current_serv = NULL;
2463   dhcp6_serverinfo_p->active = 0;
2464   dhcp6_clear_ia(dhcp6_p);
2465 
2466   dhcp6_serverinfo_p = dhcp6_serverinfo_find_active(dhcp6_p->serv_list);
2467   if (dhcp6_serverinfo_p == NULL) {
2468     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("no more active server, solicit again\n"));
2469     dhcp6_serverinfo_free_list(&dhcp6_p->serv_list);
2470     dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_SOLICIT, "dhcp6_request_next_server");
2471     dhcp6_set_timeout_params(dhcp6_p);
2472     dhcp6_solicit(netif_p, dhcp6_p);
2473   } else {
2474     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("request another active server\n"));
2475     dhcp6_p->current_serv = dhcp6_serverinfo_p;
2476     dhcp6_set_state(dhcp6_p, DHCP6_STATE_STATEFUL_REQUEST, "dhcp6_request_next_server");
2477     dhcp6_set_timeout_params(dhcp6_p);
2478     dhcp6_request(netif_p, dhcp6_p);
2479   }
2480 
2481   return;
2482 }
2483 
2484 static void
dhcp6_decline(struct netif * netif_p,struct dhcp6 * dhcp6_p)2485 dhcp6_decline(struct netif *netif_p, struct dhcp6 *dhcp6_p)
2486 {
2487   dhcp6_stateful_send(netif_p, dhcp6_p, DHCP6_DECLINE);
2488 }
2489 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
2490 
2491 /** This function is called from nd6 module when an RA messsage is received
2492  * It triggers DHCPv6 requests (if enabled).
2493  */
2494 void
dhcp6_nd6_ra_trigger(struct netif * netif,u8_t managed_addr_config,u8_t other_config)2495 dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
2496 {
2497   struct dhcp6 *dhcp6;
2498 
2499   LWIP_ASSERT("netif != NULL", netif != NULL);
2500   dhcp6 = netif_dhcp6_data(netif);
2501 
2502   LWIP_UNUSED_ARG(managed_addr_config);
2503   LWIP_UNUSED_ARG(other_config);
2504   LWIP_UNUSED_ARG(dhcp6);
2505 
2506 #if LWIP_IPV6_DHCP6_STATELESS
2507   if (dhcp6 != NULL) {
2508     if (dhcp6_stateless_enabled(dhcp6)) {
2509       if (other_config) {
2510         dhcp6_request_config(netif, dhcp6);
2511       } else {
2512         dhcp6_abort_config_request(dhcp6);
2513       }
2514     }
2515   }
2516 #endif /* LWIP_IPV6_DHCP6_STATELESS */
2517 }
2518 
2519 /**
2520  * Parse the DHCPv6 message and extract the DHCPv6 options.
2521  *
2522  * Extract the DHCPv6 options (offset + length) so that we can later easily
2523  * check for them or extract the contents.
2524  */
2525 static err_t
dhcp6_parse_reply(struct pbuf * p,struct dhcp6 * dhcp6)2526 dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
2527 {
2528   u16_t offset;
2529   u16_t offset_max;
2530   u16_t options_idx;
2531   u8_t op_len_buf[DHCP6_OPT_HEADER_LEN];
2532   u8_t *op_len = NULL;
2533   u16_t op;
2534   u16_t len;
2535   u16_t val_offset;
2536   struct dhcp6_msg *msg_in = NULL;
2537 
2538   LWIP_UNUSED_ARG(dhcp6);
2539 
2540   /* clear received options */
2541   dhcp6_clear_all_options(dhcp6);
2542   msg_in = (struct dhcp6_msg *)p->payload;
2543 
2544   /* parse options */
2545 
2546   options_idx = sizeof(struct dhcp6_msg);
2547   /* parse options to the end of the received packet */
2548   offset_max = p->tot_len;
2549 
2550   offset = options_idx;
2551   /* at least 4 byte to read? */
2552   while ((offset + DHCP6_OPT_HEADER_LEN <= offset_max)) {
2553     val_offset = (u16_t)(offset + DHCP6_OPT_HEADER_LEN);
2554     if (val_offset < offset) {
2555       /* overflow */
2556       DHCP6_STATS_INC(dhcp6.lenerr);
2557       DHCP6_STATS_INC(dhcp6.drop);
2558       return ERR_BUF;
2559     }
2560     /* copy option + length, might be split accross pbufs */
2561     op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, DHCP6_OPT_HEADER_LEN, DHCP6_OPT_HEADER_LEN, offset);
2562     if (op_len == NULL) {
2563       /* failed to get option and length */
2564       DHCP6_STATS_INC(dhcp6.lenerr);
2565       DHCP6_STATS_INC(dhcp6.drop);
2566       return ERR_VAL;
2567     }
2568     op = (op_len[0] << 8) | op_len[1];
2569     len = (op_len[2] << 8) | op_len[3];
2570     offset = val_offset + len;
2571     if (offset < val_offset) {
2572       /* overflow */
2573       DHCP6_STATS_INC(dhcp6.lenerr);
2574       DHCP6_STATS_INC(dhcp6.drop);
2575       return ERR_BUF;
2576     }
2577     if (offset > offset_max) {
2578       /* length mismatch */
2579       DHCP6_STATS_INC(dhcp6.lenerr);
2580       DHCP6_STATS_INC(dhcp6.drop);
2581       return ERR_BUF;
2582     }
2583 
2584     switch (op) {
2585       case (DHCP6_OPTION_CLIENTID):
2586         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
2587         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
2588         break;
2589       case (DHCP6_OPTION_SERVERID):
2590         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
2591         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
2592         break;
2593 #if LWIP_IPV6_DHCP6_STATEFUL
2594       case (DHCP6_OPTION_IA_NA):
2595         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_IA_NA);
2596         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_IA_NA, val_offset, len);
2597         break;
2598       case (DHCP6_OPTION_PREFERENCE):
2599         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_PREFERENCE);
2600         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_PREFERENCE, val_offset, len);
2601         break;
2602 #if DHCP6_ENABLE_UNICAST_SUPPORT
2603       case (DHCP6_OPTION_UNICAST):
2604         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_UNICAST);
2605         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_UNICAST, val_offset, len);
2606         break;
2607 #endif
2608       case (DHCP6_OPTION_STATUS_CODE):
2609         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_STATUS_CODE);
2610         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_STATUS_CODE, val_offset, len);
2611         break;
2612 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
2613 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
2614       case (DHCP6_OPTION_DNS_SERVERS):
2615         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
2616         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
2617         break;
2618       case (DHCP6_OPTION_DOMAIN_LIST):
2619         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
2620         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
2621         break;
2622 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
2623 #if LWIP_DHCP6_GET_NTP_SRV
2624       case (DHCP6_OPTION_SNTP_SERVERS):
2625         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
2626         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
2627         break;
2628 #endif /* LWIP_DHCP6_GET_NTP_SRV*/
2629       default:
2630         LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
2631         LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
2632           msg_in->msgtype, op, len, q, val_offset);
2633         break;
2634     }
2635   }
2636   return ERR_OK;
2637 }
2638 
2639 static void
dhcp6_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)2640 dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
2641 {
2642   struct netif *netif = ip_current_input_netif();
2643   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
2644   struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
2645   u8_t msg_type;
2646   u32_t xid;
2647 
2648   LWIP_UNUSED_ARG(arg);
2649 
2650   DHCP6_STATS_INC(dhcp6.recv);
2651 
2652   /* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
2653   if ((dhcp6 == NULL) || (dhcp6->pcb == NULL)) {
2654     DHCP6_STATS_INC(dhcp6.drop);
2655     goto free_pbuf_and_return;
2656   }
2657 
2658   LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
2659 
2660 #ifdef LWIP_DEBUG
2661   char buf[IPADDR_STRLEN_MAX];
2662   (void)ipaddr_ntoa_r(addr, buf, IPADDR_STRLEN_MAX);
2663 #endif
2664   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
2665               buf, port));
2666   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
2667   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
2668   /* prevent warnings about unused arguments */
2669   LWIP_UNUSED_ARG(pcb);
2670   LWIP_UNUSED_ARG(addr);
2671   LWIP_UNUSED_ARG(port);
2672 
2673   if (p->len < sizeof(struct dhcp6_msg)) {
2674     DHCP6_STATS_INC(dhcp6.lenerr);
2675     DHCP6_STATS_INC(dhcp6.drop);
2676     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
2677     goto free_pbuf_and_return;
2678   }
2679 
2680   /* match transaction ID against what we expected */
2681   xid = reply_msg->transaction_id[0] << 16;
2682   xid |= reply_msg->transaction_id[1] << 8;
2683   xid |= reply_msg->transaction_id[2];
2684   if (xid != dhcp6->xid) {
2685     DHCP6_STATS_INC(dhcp6.drop);
2686     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
2687                 ("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
2688     goto free_pbuf_and_return;
2689   }
2690   /* option fields could be unfold? */
2691   if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
2692     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
2693                 ("problem unfolding DHCPv6 message - too short on memory?\n"));
2694     goto free_pbuf_and_return;
2695   }
2696 
2697   /* read DHCP message type */
2698   msg_type = reply_msg->msgtype;
2699   /* message type is DHCP6 REPLY? */
2700   if (msg_type == DHCP6_REPLY) {
2701     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
2702 #if LWIP_IPV6_DHCP6_STATELESS
2703     /* in info-requesting state? */
2704     if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
2705       dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
2706       dhcp6_handle_config_reply(netif, p);
2707     } else
2708 #endif /* LWIP_IPV6_DHCP6_STATELESS */
2709     {
2710 #if LWIP_IPV6_DHCP6_STATEFUL
2711       dhcp6_handle_reply(netif, p);
2712 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
2713     }
2714   }
2715 #if LWIP_IPV6_DHCP6_STATEFUL
2716   else if (msg_type == DHCP6_ADVERTISE) {
2717     dhcp6_handle_advertise(netif, p);
2718   }
2719 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
2720 
2721 free_pbuf_and_return:
2722   pbuf_free(p);
2723 }
2724 
2725 /**
2726  * A DHCPv6 request has timed out.
2727  *
2728  * The timer that was started with the DHCPv6 request has
2729  * timed out, indicating no response was received in time.
2730  */
2731 static void
dhcp6_timeout(struct netif * netif,struct dhcp6 * dhcp6)2732 dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
2733 {
2734   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
2735 
2736   LWIP_UNUSED_ARG(netif);
2737   LWIP_UNUSED_ARG(dhcp6);
2738 
2739 #if LWIP_IPV6_DHCP6_STATELESS
2740   /* back-off period has passed, or server selection timed out */
2741   if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
2742     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
2743     dhcp6_information_request(netif, dhcp6);
2744   } else
2745 #endif /* LWIP_IPV6_DHCP6_STATELESS */
2746   {
2747 #if LWIP_IPV6_DHCP6_STATEFUL
2748     dhcp6_stateful_timeout(netif, dhcp6);
2749 #endif /* LWIP_IPV6_DHCP6_STATEFUL */
2750   }
2751 }
2752 
2753 /**
2754  * DHCPv6 timeout handling (this function must be called every 500ms,
2755  * see @ref DHCP6_TIMER_MSECS).
2756  *
2757  * A DHCPv6 server is expected to respond within a short period of time.
2758  * This timer checks whether an outstanding DHCPv6 request is timed out.
2759  */
2760 void
dhcp6_tmr(void)2761 dhcp6_tmr(void)
2762 {
2763   struct netif *netif;
2764   /* loop through netif's */
2765   NETIF_FOREACH(netif) {
2766     struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
2767     /* only act on DHCPv6 configured interfaces */
2768     if (dhcp6 != NULL) {
2769       /* timer is active (non zero), and is about to trigger now */
2770       if (dhcp6->request_timeout > 1) {
2771         dhcp6->request_timeout--;
2772       } else if (dhcp6->request_timeout == 1) {
2773         dhcp6->request_timeout--;
2774         /* { dhcp6->request_timeout == 0 } */
2775         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
2776         /* this client's request timeout triggered */
2777         dhcp6_timeout(netif, dhcp6);
2778       }
2779     }
2780   }
2781 }
2782 
2783 #if LWIP_LOWPOWER
2784 #include "lwip/lowpower.h"
2785 u32_t
dhcp6_tmr_tick()2786 dhcp6_tmr_tick()
2787 {
2788   struct netif *netif = NULL;
2789   u32_t tick = 0;
2790   /* loop through netif's */
2791   NETIF_FOREACH(netif) {
2792     struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
2793     /* only act on DHCPv6 configured interfaces */
2794     if ((dhcp6 != NULL) && (dhcp6->request_timeout > 0)) {
2795       SET_TMR_TICK(tick, dhcp6->request_timeout);
2796     }
2797   }
2798   LOWPOWER_DEBUG(("%s tmr tick: %u\n", __func__, tick));
2799   return tick;
2800 }
2801 #endif /* LWIP_LOWPOWER */
2802 
2803 #endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
2804