• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  *
4  * @defgroup dhcp6 DHCPv6
5  * @ingroup ip6
6  * DHCPv6 client: IPv6 address autoconfiguration as per
7  * RFC 3315 (stateful DHCPv6) and
8  * RFC 3736 (stateless DHCPv6).
9  *
10  * For now, only stateless DHCPv6 is implemented!
11  *
12  * TODO:
13  * - enable/disable API to not always start when RA is received
14  * - stateful DHCPv6 (for now, only stateless DHCPv6 for DNS and NTP servers works)
15  * - create Client Identifier?
16  * - only start requests if a valid local address is available on the netif
17  * - only start information requests if required (not for every RA)
18  *
19  * dhcp6_enable_stateful() enables stateful DHCPv6 for a netif (stateless disabled)\n
20  * dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)\n
21  * dhcp6_disable() disable DHCPv6 for a netif
22  *
23  * When enabled, requests are only issued after receipt of RA with the
24  * corresponding bits set.
25  */
26 
27 /*
28  * Copyright (c) 2018 Simon Goldschmidt
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without modification,
32  * are permitted provided that the following conditions are met:
33  *
34  * 1. Redistributions of source code must retain the above copyright notice,
35  *    this list of conditions and the following disclaimer.
36  * 2. Redistributions in binary form must reproduce the above copyright notice,
37  *    this list of conditions and the following disclaimer in the documentation
38  *    and/or other materials provided with the distribution.
39  * 3. The name of the author may not be used to endorse or promote products
40  *    derived from this software without specific prior written permission.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
43  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
44  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
45  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
46  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
47  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
50  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
51  * OF SUCH DAMAGE.
52  *
53  * This file is part of the lwIP TCP/IP stack.
54  *
55  * Author: Simon Goldschmidt <goldsimon@gmx.de>
56  */
57 
58 #include "lwip/opt.h"
59 
60 #if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
61 
62 #include "lwip/dhcp6.h"
63 #include "lwip/prot/dhcp6.h"
64 #include "lwip/def.h"
65 #include "lwip/udp.h"
66 #include "lwip/dns.h"
67 
68 #include <string.h>
69 
70 #ifdef LWIP_HOOK_FILENAME
71 #include LWIP_HOOK_FILENAME
72 #endif
73 #ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
74 #define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
75 #endif
76 #ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
77 #define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
78 #endif
79 
80 #if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS
81 #if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS
82 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS
83 #else
84 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
85 #endif
86 #else
87 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0
88 #endif
89 
90 
91 /** Option handling: options are parsed in dhcp6_parse_reply
92  * and saved in an array where other functions can load them from.
93  * This might be moved into the struct dhcp6 (not necessarily since
94  * lwIP is single-threaded and the array is only used while in recv
95  * callback). */
96 enum dhcp6_option_idx {
97   DHCP6_OPTION_IDX_CLI_ID = 0,
98   DHCP6_OPTION_IDX_SERVER_ID,
99 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
100   DHCP6_OPTION_IDX_DNS_SERVER,
101   DHCP6_OPTION_IDX_DOMAIN_LIST,
102 #endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
103 #if LWIP_DHCP6_GET_NTP_SRV
104   DHCP6_OPTION_IDX_NTP_SERVER,
105 #endif /* LWIP_DHCP_GET_NTP_SRV */
106   DHCP6_OPTION_IDX_MAX
107 };
108 
109 struct dhcp6_option_info {
110   u8_t option_given;
111   u16_t val_start;
112   u16_t val_length;
113 };
114 
115 /** Holds the decoded option info, only valid while in dhcp6_recv. */
116 struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX];
117 
118 #define dhcp6_option_given(dhcp6, idx)           (dhcp6_rx_options[idx].option_given != 0)
119 #define dhcp6_got_option(dhcp6, idx)             (dhcp6_rx_options[idx].option_given = 1)
120 #define dhcp6_clear_option(dhcp6, idx)           (dhcp6_rx_options[idx].option_given = 0)
121 #define dhcp6_clear_all_options(dhcp6)           (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options)))
122 #define dhcp6_get_option_start(dhcp6, idx)       (dhcp6_rx_options[idx].val_start)
123 #define dhcp6_get_option_length(dhcp6, idx)      (dhcp6_rx_options[idx].val_length)
124 #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)
125 
126 
127 const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
128 const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
129 
130 static struct udp_pcb *dhcp6_pcb;
131 static u8_t dhcp6_pcb_refcount;
132 
133 
134 /* receive, unfold, parse and free incoming messages */
135 static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
136 
137 /** Ensure DHCP PCB is allocated and bound */
138 static err_t
dhcp6_inc_pcb_refcount(void)139 dhcp6_inc_pcb_refcount(void)
140 {
141   if (dhcp6_pcb_refcount == 0) {
142     LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL);
143 
144     /* allocate UDP PCB */
145     dhcp6_pcb = udp_new_ip6();
146 
147     if (dhcp6_pcb == NULL) {
148       return ERR_MEM;
149     }
150 
151     ip_set_option(dhcp6_pcb, SOF_BROADCAST);
152 
153     /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
154     udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
155     udp_recv(dhcp6_pcb, dhcp6_recv, NULL);
156   }
157 
158   dhcp6_pcb_refcount++;
159 
160   return ERR_OK;
161 }
162 
163 /** Free DHCP PCB if the last netif stops using it */
164 static void
dhcp6_dec_pcb_refcount(void)165 dhcp6_dec_pcb_refcount(void)
166 {
167   LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0));
168   dhcp6_pcb_refcount--;
169 
170   if (dhcp6_pcb_refcount == 0) {
171     udp_remove(dhcp6_pcb);
172     dhcp6_pcb = NULL;
173   }
174 }
175 
176 /**
177  * @ingroup dhcp6
178  * Set a statically allocated struct dhcp6 to work with.
179  * Using this prevents dhcp6_start to allocate it using mem_malloc.
180  *
181  * @param netif the netif for which to set the struct dhcp
182  * @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
183  */
184 void
dhcp6_set_struct(struct netif * netif,struct dhcp6 * dhcp6)185 dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
186 {
187   LWIP_ASSERT("netif != NULL", netif != NULL);
188   LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
189   LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
190 
191   /* clear data structure */
192   memset(dhcp6, 0, sizeof(struct dhcp6));
193   /* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */
194   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
195 }
196 
197 /**
198  * @ingroup dhcp6
199  * Removes a struct dhcp6 from a netif.
200  *
201  * ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
202  *            struct dhcp6 since the memory is passed back to the heap.
203  *
204  * @param netif the netif from which to remove the struct dhcp
205  */
dhcp6_cleanup(struct netif * netif)206 void dhcp6_cleanup(struct netif *netif)
207 {
208   LWIP_ASSERT("netif != NULL", netif != NULL);
209 
210   if (netif_dhcp6_data(netif) != NULL) {
211     mem_free(netif_dhcp6_data(netif));
212     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
213   }
214 }
215 
216 static struct dhcp6*
dhcp6_get_struct(struct netif * netif,const char * dbg_requester)217 dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
218 {
219   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
220   if (dhcp6 == NULL) {
221     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
222     dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
223     if (dhcp6 == NULL) {
224       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
225       return NULL;
226     }
227 
228     /* clear data structure, this implies DHCP6_STATE_OFF */
229     memset(dhcp6, 0, sizeof(struct dhcp6));
230     /* store this dhcp6 client in the netif */
231     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
232   } else {
233     /* already has DHCP6 client attached */
234     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
235   }
236 
237   if (!dhcp6->pcb_allocated) {
238     if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */
239       mem_free(dhcp6);
240       netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
241       return NULL;
242     }
243     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6", dbg_requester));
244     dhcp6->pcb_allocated = 1;
245   }
246   return dhcp6;
247 }
248 
249 /*
250  * Set the DHCPv6 state
251  * If the state changed, reset the number of tries.
252  */
253 static void
dhcp6_set_state(struct dhcp6 * dhcp6,u8_t new_state,const char * dbg_caller)254 dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller)
255 {
256   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n",
257     dhcp6->state, new_state, dbg_caller));
258   if (new_state != dhcp6->state) {
259     dhcp6->state = new_state;
260     dhcp6->tries = 0;
261     dhcp6->request_timeout = 0;
262   }
263 }
264 
265 static int
dhcp6_stateless_enabled(struct dhcp6 * dhcp6)266 dhcp6_stateless_enabled(struct dhcp6 *dhcp6)
267 {
268   if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) ||
269       (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) {
270     return 1;
271   }
272   return 0;
273 }
274 
275 /*static int
276 dhcp6_stateful_enabled(struct dhcp6 *dhcp6)
277 {
278   if (dhcp6->state == DHCP6_STATE_OFF) {
279     return 0;
280   }
281   if (dhcp6_stateless_enabled(dhcp6)) {
282     return 0;
283   }
284   return 1;
285 }*/
286 
287 /**
288  * @ingroup dhcp6
289  * Enable stateful DHCPv6 on this netif
290  * Requests are sent on receipt of an RA message with the
291  * ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
292  *
293  * A struct dhcp6 will be allocated for this netif if not
294  * set via @ref dhcp6_set_struct before.
295  *
296  * @todo: stateful DHCPv6 not supported, yet
297  */
298 err_t
dhcp6_enable_stateful(struct netif * netif)299 dhcp6_enable_stateful(struct netif *netif)
300 {
301   LWIP_UNUSED_ARG(netif);
302   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("stateful dhcp6 not implemented yet"));
303   return ERR_VAL;
304 }
305 
306 /**
307  * @ingroup dhcp6
308  * Enable stateless DHCPv6 on this netif
309  * Requests are sent on receipt of an RA message with the
310  * ND6_RA_FLAG_OTHER_CONFIG flag set.
311  *
312  * A struct dhcp6 will be allocated for this netif if not
313  * set via @ref dhcp6_set_struct before.
314  */
315 err_t
dhcp6_enable_stateless(struct netif * netif)316 dhcp6_enable_stateless(struct netif *netif)
317 {
318   struct dhcp6 *dhcp6;
319 
320   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_enable_stateless(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
321 
322   dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()");
323   if (dhcp6 == NULL) {
324     return ERR_MEM;
325   }
326   if (dhcp6_stateless_enabled(dhcp6)) {
327     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled"));
328     return ERR_OK;
329   } else if (dhcp6->state != DHCP6_STATE_OFF) {
330     /* stateful running */
331     /* @todo: stop stateful once it is implemented */
332     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6"));
333   }
334   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n"));
335   dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless");
336   return ERR_OK;
337 }
338 
339 /**
340  * @ingroup dhcp6
341  * Disable stateful or stateless DHCPv6 on this netif
342  * Requests are sent on receipt of an RA message with the
343  * ND6_RA_FLAG_OTHER_CONFIG flag set.
344  */
345 void
dhcp6_disable(struct netif * netif)346 dhcp6_disable(struct netif *netif)
347 {
348   struct dhcp6 *dhcp6;
349 
350   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_disable(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
351 
352   dhcp6 = netif_dhcp6_data(netif);
353   if (dhcp6 != NULL) {
354     if (dhcp6->state != DHCP6_STATE_OFF) {
355       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n",
356         (dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful")));
357       dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable");
358       if (dhcp6->pcb_allocated != 0) {
359         dhcp6_dec_pcb_refcount(); /* free DHCPv6 PCB if not needed any more */
360         dhcp6->pcb_allocated = 0;
361       }
362     }
363   }
364 }
365 
366 /**
367  * Create a DHCPv6 request, fill in common headers
368  *
369  * @param netif the netif under DHCPv6 control
370  * @param dhcp6 dhcp6 control struct
371  * @param message_type message type of the request
372  * @param opt_len_alloc option length to allocate
373  * @param options_out_len option length on exit
374  * @return a pbuf for the message
375  */
376 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)377 dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type,
378                  u16_t opt_len_alloc, u16_t *options_out_len)
379 {
380   struct pbuf *p_out;
381   struct dhcp6_msg *msg_out;
382 
383   LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;);
384   LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;);
385   p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM);
386   if (p_out == NULL) {
387     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
388                 ("dhcp6_create_msg(): could not allocate pbuf\n"));
389     return NULL;
390   }
391   LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg",
392               (p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc));
393 
394   /* @todo: limit new xid for certain message types? */
395   /* reuse transaction identifier in retransmissions */
396   if (dhcp6->tries == 0) {
397     dhcp6->xid = LWIP_RAND() & 0xFFFFFF;
398   }
399 
400   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
401               ("transaction id xid(%"X32_F")\n", dhcp6->xid));
402 
403   msg_out = (struct dhcp6_msg *)p_out->payload;
404   memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc);
405 
406   msg_out->msgtype = message_type;
407   msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16);
408   msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8);
409   msg_out->transaction_id[2] = (u8_t)dhcp6->xid;
410   *options_out_len = 0;
411   return p_out;
412 }
413 
414 static u16_t
dhcp6_option_short(u16_t options_out_len,u8_t * options,u16_t value)415 dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value)
416 {
417   options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
418   options[options_out_len++] = (u8_t) (value & 0x00ffU);
419   return options_out_len;
420 }
421 
422 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)423 dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options,
424                            u16_t num_req_options, u16_t max_len)
425 {
426   size_t i;
427   u16_t ret;
428 
429   LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len",
430     sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len);
431   LWIP_UNUSED_ARG(max_len);
432 
433   ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO);
434   ret = dhcp6_option_short(ret, options, 2 * num_req_options);
435   for (i = 0; i < num_req_options; i++) {
436     ret = dhcp6_option_short(ret, options, req_options[i]);
437   }
438   return ret;
439 }
440 
441 /* All options are added, shrink the pbuf to the required size */
442 static void
dhcp6_msg_finalize(u16_t options_out_len,struct pbuf * p_out)443 dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out)
444 {
445   /* shrink the pbuf to the actual content length */
446   pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len));
447 }
448 
449 
450 #if LWIP_IPV6_DHCP6_STATELESS
451 static void
dhcp6_information_request(struct netif * netif,struct dhcp6 * dhcp6)452 dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
453 {
454   const u16_t requested_options[] = {
455 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
456     DHCP6_OPTION_DNS_SERVERS,
457     DHCP6_OPTION_DOMAIN_LIST
458 #endif
459 #if LWIP_DHCP6_GET_NTP_SRV
460     , DHCP6_OPTION_SNTP_SERVERS
461 #endif
462   };
463 
464   u16_t msecs;
465   struct pbuf *p_out;
466   u16_t options_out_len;
467   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
468   /* create and initialize the DHCP message header */
469   p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len);
470   if (p_out != NULL) {
471     err_t err;
472     struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
473     u8_t *options = (u8_t *)(msg_out + 1);
474     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
475 
476     options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
477       LWIP_ARRAYSIZE(requested_options), p_out->len);
478     LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
479       DHCP6_INFOREQUEST, options_out_len, p_out->len);
480     dhcp6_msg_finalize(options_out_len, p_out);
481 
482     err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
483     pbuf_free(p_out);
484     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
485     LWIP_UNUSED_ARG(err);
486   } else {
487     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
488   }
489   dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
490   if (dhcp6->tries < 255) {
491     dhcp6->tries++;
492   }
493   msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
494   dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
495   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
496 }
497 
498 static err_t
dhcp6_request_config(struct netif * netif,struct dhcp6 * dhcp6)499 dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
500 {
501   /* stateless mode enabled and no request running? */
502   if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
503     /* send Information-request and wait for answer; setup receive timeout */
504     dhcp6_information_request(netif, dhcp6);
505   }
506 
507   return ERR_OK;
508 }
509 
510 static void
dhcp6_abort_config_request(struct dhcp6 * dhcp6)511 dhcp6_abort_config_request(struct dhcp6 *dhcp6)
512 {
513   if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
514     /* abort running request */
515     dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
516   }
517 }
518 
519 /* Handle a REPLY to INFOREQUEST
520  * This parses DNS and NTP server addresses from the reply.
521  */
522 static void
dhcp6_handle_config_reply(struct netif * netif,struct pbuf * p_msg_in)523 dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
524 {
525   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
526 
527   LWIP_UNUSED_ARG(dhcp6);
528   LWIP_UNUSED_ARG(p_msg_in);
529 
530 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
531   if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
532     ip_addr_t dns_addr;
533     ip6_addr_t *dns_addr6;
534     u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
535     u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
536     u16_t idx;
537     u8_t n;
538 
539     ip_addr_set_zero_ip6(&dns_addr);
540     dns_addr6 = ip_2_ip6(&dns_addr);
541     for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
542          n++, idx += sizeof(struct ip6_addr_packed)) {
543       u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
544       if (copied != sizeof(struct ip6_addr_packed)) {
545         /* pbuf length mismatch */
546         return;
547       }
548       ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
549       /* @todo: do we need a different offset than DHCP(v4)? */
550       dns_setserver(n, &dns_addr);
551     }
552   }
553   /* @ todo: parse and set Domain Search List */
554 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
555 
556 #if LWIP_DHCP6_GET_NTP_SRV
557   if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
558     ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
559     u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
560     u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
561     u16_t idx;
562     u8_t n;
563 
564     for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
565          n++, idx += sizeof(struct ip6_addr_packed)) {
566       u16_t copied;
567       ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
568       ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
569       copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
570       if (copied != sizeof(struct ip6_addr_packed)) {
571         /* pbuf length mismatch */
572         return;
573       }
574       ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
575     }
576     dhcp6_set_ntp_servers(n, ntp_server_addrs);
577   }
578 #endif /* LWIP_DHCP6_GET_NTP_SRV */
579 }
580 #endif /* LWIP_IPV6_DHCP6_STATELESS */
581 
582 /** This function is called from nd6 module when an RA messsage is received
583  * It triggers DHCPv6 requests (if enabled).
584  */
585 void
dhcp6_nd6_ra_trigger(struct netif * netif,u8_t managed_addr_config,u8_t other_config)586 dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
587 {
588   struct dhcp6 *dhcp6;
589 
590   LWIP_ASSERT("netif != NULL", netif != NULL);
591   dhcp6 = netif_dhcp6_data(netif);
592 
593   LWIP_UNUSED_ARG(managed_addr_config);
594   LWIP_UNUSED_ARG(other_config);
595   LWIP_UNUSED_ARG(dhcp6);
596 
597 #if LWIP_IPV6_DHCP6_STATELESS
598   if (dhcp6 != NULL) {
599     if (dhcp6_stateless_enabled(dhcp6)) {
600       if (other_config) {
601         dhcp6_request_config(netif, dhcp6);
602       } else {
603         dhcp6_abort_config_request(dhcp6);
604       }
605     }
606   }
607 #endif /* LWIP_IPV6_DHCP6_STATELESS */
608 }
609 
610 /**
611  * Parse the DHCPv6 message and extract the DHCPv6 options.
612  *
613  * Extract the DHCPv6 options (offset + length) so that we can later easily
614  * check for them or extract the contents.
615  */
616 static err_t
dhcp6_parse_reply(struct pbuf * p,struct dhcp6 * dhcp6)617 dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
618 {
619   u16_t offset;
620   u16_t offset_max;
621   u16_t options_idx;
622   struct dhcp6_msg *msg_in;
623 
624   LWIP_UNUSED_ARG(dhcp6);
625 
626   /* clear received options */
627   dhcp6_clear_all_options(dhcp6);
628   msg_in = (struct dhcp6_msg *)p->payload;
629 
630   /* parse options */
631 
632   options_idx = sizeof(struct dhcp6_msg);
633   /* parse options to the end of the received packet */
634   offset_max = p->tot_len;
635 
636   offset = options_idx;
637   /* at least 4 byte to read? */
638   while ((offset + 4 <= offset_max)) {
639     u8_t op_len_buf[4];
640     u8_t *op_len;
641     u16_t op;
642     u16_t len;
643     u16_t val_offset = (u16_t)(offset + 4);
644     if (val_offset < offset) {
645       /* overflow */
646       return ERR_BUF;
647     }
648     /* copy option + length, might be split accross pbufs */
649     op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset);
650     if (op_len == NULL) {
651       /* failed to get option and length */
652       return ERR_VAL;
653     }
654     op = (op_len[0] << 8) | op_len[1];
655     len = (op_len[2] << 8) | op_len[3];
656     offset = val_offset + len;
657     if (offset < val_offset) {
658       /* overflow */
659       return ERR_BUF;
660     }
661 
662     switch (op) {
663       case (DHCP6_OPTION_CLIENTID):
664         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
665         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
666         break;
667       case (DHCP6_OPTION_SERVERID):
668         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
669         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
670         break;
671 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
672       case (DHCP6_OPTION_DNS_SERVERS):
673         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
674         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
675         break;
676       case (DHCP6_OPTION_DOMAIN_LIST):
677         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
678         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
679         break;
680 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
681 #if LWIP_DHCP6_GET_NTP_SRV
682       case (DHCP6_OPTION_SNTP_SERVERS):
683         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
684         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
685         break;
686 #endif /* LWIP_DHCP6_GET_NTP_SRV*/
687       default:
688         LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
689         LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
690           msg_in->msgtype, op, len, q, val_offset);
691         break;
692     }
693   }
694   return ERR_OK;
695 }
696 
697 static void
dhcp6_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)698 dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
699 {
700   struct netif *netif = ip_current_input_netif();
701   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
702   struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
703   u8_t msg_type;
704   u32_t xid;
705 
706   LWIP_UNUSED_ARG(arg);
707 
708   /* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
709   if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) {
710     goto free_pbuf_and_return;
711   }
712 
713   LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
714 
715   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
716     ipaddr_ntoa(addr), port));
717   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
718   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
719   /* prevent warnings about unused arguments */
720   LWIP_UNUSED_ARG(pcb);
721   LWIP_UNUSED_ARG(addr);
722   LWIP_UNUSED_ARG(port);
723 
724   if (p->len < sizeof(struct dhcp6_msg)) {
725     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
726     goto free_pbuf_and_return;
727   }
728 
729   /* match transaction ID against what we expected */
730   xid = reply_msg->transaction_id[0] << 16;
731   xid |= reply_msg->transaction_id[1] << 8;
732   xid |= reply_msg->transaction_id[2];
733   if (xid != dhcp6->xid) {
734     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
735                 ("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
736     goto free_pbuf_and_return;
737   }
738   /* option fields could be unfold? */
739   if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
740     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
741                 ("problem unfolding DHCPv6 message - too short on memory?\n"));
742     goto free_pbuf_and_return;
743   }
744 
745   /* read DHCP message type */
746   msg_type = reply_msg->msgtype;
747   /* message type is DHCP6 REPLY? */
748   if (msg_type == DHCP6_REPLY) {
749     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
750 #if LWIP_IPV6_DHCP6_STATELESS
751     /* in info-requesting state? */
752     if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
753       dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
754       dhcp6_handle_config_reply(netif, p);
755     } else
756 #endif /* LWIP_IPV6_DHCP6_STATELESS */
757     {
758       /* @todo: handle reply in other states? */
759     }
760   } else {
761     /* @todo: handle other message types */
762   }
763 
764 free_pbuf_and_return:
765   pbuf_free(p);
766 }
767 
768 /**
769  * A DHCPv6 request has timed out.
770  *
771  * The timer that was started with the DHCPv6 request has
772  * timed out, indicating no response was received in time.
773  */
774 static void
dhcp6_timeout(struct netif * netif,struct dhcp6 * dhcp6)775 dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
776 {
777   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
778 
779   LWIP_UNUSED_ARG(netif);
780   LWIP_UNUSED_ARG(dhcp6);
781 
782 #if LWIP_IPV6_DHCP6_STATELESS
783   /* back-off period has passed, or server selection timed out */
784   if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
785     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
786     dhcp6_information_request(netif, dhcp6);
787   }
788 #endif /* LWIP_IPV6_DHCP6_STATELESS */
789 }
790 
791 /**
792  * DHCPv6 timeout handling (this function must be called every 500ms,
793  * see @ref DHCP6_TIMER_MSECS).
794  *
795  * A DHCPv6 server is expected to respond within a short period of time.
796  * This timer checks whether an outstanding DHCPv6 request is timed out.
797  */
798 void
dhcp6_tmr(void)799 dhcp6_tmr(void)
800 {
801   struct netif *netif;
802   /* loop through netif's */
803 #ifdef LOSCFG_NET_CONTAINER
804   NETIF_FOREACH(netif, get_root_net_group()) {
805 #else
806   NETIF_FOREACH(netif) {
807 #endif
808     struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
809     /* only act on DHCPv6 configured interfaces */
810     if (dhcp6 != NULL) {
811       /* timer is active (non zero), and is about to trigger now */
812       if (dhcp6->request_timeout > 1) {
813         dhcp6->request_timeout--;
814       } else if (dhcp6->request_timeout == 1) {
815         dhcp6->request_timeout--;
816         /* { dhcp6->request_timeout == 0 } */
817         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
818         /* this client's request timeout triggered */
819         dhcp6_timeout(netif, dhcp6);
820       }
821     }
822   }
823 }
824 
825 #if LWIP_LOWPOWER
826 #include "lwip/lowpower.h"
827 u32_t
828 dhcp6_tmr_tick()
829 {
830   struct netif *netif = NULL;
831   u32_t tick = 0;
832   /* loop through netif's */
833   #ifdef LOSCFG_NET_CONTAINER
834   NETIF_FOREACH(netif, get_root_net_group())
835 #else
836   NETIF_FOREACH(netif)
837 #endif
838   {
839     struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
840     /* only act on DHCPv6 configured interfaces */
841     if ((dhcp6 != NULL) && (dhcp6->request_timeout > 0)) {
842       SET_TMR_TICK(tick, dhcp6->request_timeout);
843     }
844   }
845   LWIP_DEBUGF(LOWPOWER_DEBUG, ("%s tmr tick: %u\n", "dhcp6_tmr_tick", tick));
846   return tick;
847 }
848 #endif /* LWIP_LOWPOWER */
849 
850 #endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
851