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