source: azure_iot_hub_f767zi/trunk/asp_baseplatform/lwip/lwip-2.1.2/src/core/ipv6/dhcp6.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 27.4 KB
Line 
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). */
96enum 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
109struct 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. */
116struct 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
127const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
128const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
129
130static struct udp_pcb *dhcp6_pcb;
131static u8_t dhcp6_pcb_refcount;
132
133
134/* receive, unfold, parse and free incoming messages */
135static 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 */
138static err_t
139dhcp6_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 */
164static void
165dhcp6_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 */
184void
185dhcp6_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 */
206void 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
216static struct dhcp6*
217dhcp6_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 */
253static void
254dhcp6_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
265static int
266dhcp6_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
276dhcp6_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 */
298err_t
299dhcp6_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 */
315err_t
316dhcp6_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 */
345void
346dhcp6_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 */
376static struct pbuf *
377dhcp6_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
414static u16_t
415dhcp6_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
422static u16_t
423dhcp6_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 */
442static void
443dhcp6_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
451static void
452dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
453{
454 const u16_t requested_options[] = {DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_SNTP_SERVERS};
455 u16_t msecs;
456 struct pbuf *p_out;
457 u16_t options_out_len;
458 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
459 /* create and initialize the DHCP message header */
460 p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len);
461 if (p_out != NULL) {
462 err_t err;
463 struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
464 u8_t *options = (u8_t *)(msg_out + 1);
465 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
466
467 options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
468 LWIP_ARRAYSIZE(requested_options), p_out->len);
469 LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
470 DHCP6_INFOREQUEST, options_out_len, p_out->len);
471 dhcp6_msg_finalize(options_out_len, p_out);
472
473 err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
474 pbuf_free(p_out);
475 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
476 LWIP_UNUSED_ARG(err);
477 } else {
478 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
479 }
480 dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
481 if (dhcp6->tries < 255) {
482 dhcp6->tries++;
483 }
484 msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
485 dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
486 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
487}
488
489static err_t
490dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
491{
492 /* stateless mode enabled and no request running? */
493 if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
494 /* send Information-request and wait for answer; setup receive timeout */
495 dhcp6_information_request(netif, dhcp6);
496 }
497
498 return ERR_OK;
499}
500
501static void
502dhcp6_abort_config_request(struct dhcp6 *dhcp6)
503{
504 if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
505 /* abort running request */
506 dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
507 }
508}
509
510/* Handle a REPLY to INFOREQUEST
511 * This parses DNS and NTP server addresses from the reply.
512 */
513static void
514dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
515{
516 struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
517
518 LWIP_UNUSED_ARG(dhcp6);
519 LWIP_UNUSED_ARG(p_msg_in);
520
521#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
522 if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
523 ip_addr_t dns_addr;
524 ip6_addr_t *dns_addr6;
525 u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
526 u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
527 u16_t idx;
528 u8_t n;
529
530 memset(&dns_addr, 0, sizeof(dns_addr));
531 dns_addr6 = ip_2_ip6(&dns_addr);
532 for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
533 n++, idx += sizeof(struct ip6_addr_packed)) {
534 u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
535 if (copied != sizeof(struct ip6_addr_packed)) {
536 /* pbuf length mismatch */
537 return;
538 }
539 ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
540 /* @todo: do we need a different offset than DHCP(v4)? */
541 dns_setserver(n, &dns_addr);
542 }
543 }
544 /* @ todo: parse and set Domain Search List */
545#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
546
547#if LWIP_DHCP6_GET_NTP_SRV
548 if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
549 ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
550 u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
551 u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
552 u16_t idx;
553 u8_t n;
554
555 for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
556 n++, idx += sizeof(struct ip6_addr_packed)) {
557 u16_t copied;
558 ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
559 ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
560 copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
561 if (copied != sizeof(struct ip6_addr_packed)) {
562 /* pbuf length mismatch */
563 return;
564 }
565 ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
566 }
567 dhcp6_set_ntp_servers(n, ntp_server_addrs);
568 }
569#endif /* LWIP_DHCP6_GET_NTP_SRV */
570}
571#endif /* LWIP_IPV6_DHCP6_STATELESS */
572
573/** This function is called from nd6 module when an RA messsage is received
574 * It triggers DHCPv6 requests (if enabled).
575 */
576void
577dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
578{
579 struct dhcp6 *dhcp6;
580
581 LWIP_ASSERT("netif != NULL", netif != NULL);
582 dhcp6 = netif_dhcp6_data(netif);
583
584 LWIP_UNUSED_ARG(managed_addr_config);
585 LWIP_UNUSED_ARG(other_config);
586 LWIP_UNUSED_ARG(dhcp6);
587
588#if LWIP_IPV6_DHCP6_STATELESS
589 if (dhcp6 != NULL) {
590 if (dhcp6_stateless_enabled(dhcp6)) {
591 if (other_config) {
592 dhcp6_request_config(netif, dhcp6);
593 } else {
594 dhcp6_abort_config_request(dhcp6);
595 }
596 }
597 }
598#endif /* LWIP_IPV6_DHCP6_STATELESS */
599}
600
601/**
602 * Parse the DHCPv6 message and extract the DHCPv6 options.
603 *
604 * Extract the DHCPv6 options (offset + length) so that we can later easily
605 * check for them or extract the contents.
606 */
607static err_t
608dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
609{
610 u16_t offset;
611 u16_t offset_max;
612 u16_t options_idx;
613 struct dhcp6_msg *msg_in;
614
615 LWIP_UNUSED_ARG(dhcp6);
616
617 /* clear received options */
618 dhcp6_clear_all_options(dhcp6);
619 msg_in = (struct dhcp6_msg *)p->payload;
620
621 /* parse options */
622
623 options_idx = sizeof(struct dhcp6_msg);
624 /* parse options to the end of the received packet */
625 offset_max = p->tot_len;
626
627 offset = options_idx;
628 /* at least 4 byte to read? */
629 while ((offset + 4 <= offset_max)) {
630 u8_t op_len_buf[4];
631 u8_t *op_len;
632 u16_t op;
633 u16_t len;
634 u16_t val_offset = (u16_t)(offset + 4);
635 if (val_offset < offset) {
636 /* overflow */
637 return ERR_BUF;
638 }
639 /* copy option + length, might be split accross pbufs */
640 op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset);
641 if (op_len == NULL) {
642 /* failed to get option and length */
643 return ERR_VAL;
644 }
645 op = (op_len[0] << 8) | op_len[1];
646 len = (op_len[2] << 8) | op_len[3];
647 offset = val_offset + len;
648 if (offset < val_offset) {
649 /* overflow */
650 return ERR_BUF;
651 }
652
653 switch (op) {
654 case (DHCP6_OPTION_CLIENTID):
655 dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
656 dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
657 break;
658 case (DHCP6_OPTION_SERVERID):
659 dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
660 dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
661 break;
662#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
663 case (DHCP6_OPTION_DNS_SERVERS):
664 dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
665 dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
666 break;
667 case (DHCP6_OPTION_DOMAIN_LIST):
668 dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
669 dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
670 break;
671#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
672#if LWIP_DHCP6_GET_NTP_SRV
673 case (DHCP6_OPTION_SNTP_SERVERS):
674 dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
675 dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
676 break;
677#endif /* LWIP_DHCP6_GET_NTP_SRV*/
678 default:
679 LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
680 LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
681 msg_in->msgtype, op, len, q, val_offset);
682 break;
683 }
684 }
685 return ERR_OK;
686}
687
688static void
689dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
690{
691 struct netif *netif = ip_current_input_netif();
692 struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
693 struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
694 u8_t msg_type;
695 u32_t xid;
696
697 LWIP_UNUSED_ARG(arg);
698
699 /* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
700 if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) {
701 goto free_pbuf_and_return;
702 }
703
704 LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
705
706 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
707 ipaddr_ntoa(addr), port));
708 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
709 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
710 /* prevent warnings about unused arguments */
711 LWIP_UNUSED_ARG(pcb);
712 LWIP_UNUSED_ARG(addr);
713 LWIP_UNUSED_ARG(port);
714
715 if (p->len < sizeof(struct dhcp6_msg)) {
716 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
717 goto free_pbuf_and_return;
718 }
719
720 /* match transaction ID against what we expected */
721 xid = reply_msg->transaction_id[0] << 16;
722 xid |= reply_msg->transaction_id[1] << 8;
723 xid |= reply_msg->transaction_id[2];
724 if (xid != dhcp6->xid) {
725 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
726 ("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
727 goto free_pbuf_and_return;
728 }
729 /* option fields could be unfold? */
730 if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
731 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
732 ("problem unfolding DHCPv6 message - too short on memory?\n"));
733 goto free_pbuf_and_return;
734 }
735
736 /* read DHCP message type */
737 msg_type = reply_msg->msgtype;
738 /* message type is DHCP6 REPLY? */
739 if (msg_type == DHCP6_REPLY) {
740 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
741#if LWIP_IPV6_DHCP6_STATELESS
742 /* in info-requesting state? */
743 if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
744 dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
745 dhcp6_handle_config_reply(netif, p);
746 } else
747#endif /* LWIP_IPV6_DHCP6_STATELESS */
748 {
749 /* @todo: handle reply in other states? */
750 }
751 } else {
752 /* @todo: handle other message types */
753 }
754
755free_pbuf_and_return:
756 pbuf_free(p);
757}
758
759/**
760 * A DHCPv6 request has timed out.
761 *
762 * The timer that was started with the DHCPv6 request has
763 * timed out, indicating no response was received in time.
764 */
765static void
766dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
767{
768 LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
769
770 LWIP_UNUSED_ARG(netif);
771 LWIP_UNUSED_ARG(dhcp6);
772
773#if LWIP_IPV6_DHCP6_STATELESS
774 /* back-off period has passed, or server selection timed out */
775 if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
776 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
777 dhcp6_information_request(netif, dhcp6);
778 }
779#endif /* LWIP_IPV6_DHCP6_STATELESS */
780}
781
782/**
783 * DHCPv6 timeout handling (this function must be called every 500ms,
784 * see @ref DHCP6_TIMER_MSECS).
785 *
786 * A DHCPv6 server is expected to respond within a short period of time.
787 * This timer checks whether an outstanding DHCPv6 request is timed out.
788 */
789void
790dhcp6_tmr(void)
791{
792 struct netif *netif;
793 /* loop through netif's */
794 NETIF_FOREACH(netif) {
795 struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
796 /* only act on DHCPv6 configured interfaces */
797 if (dhcp6 != NULL) {
798 /* timer is active (non zero), and is about to trigger now */
799 if (dhcp6->request_timeout > 1) {
800 dhcp6->request_timeout--;
801 } else if (dhcp6->request_timeout == 1) {
802 dhcp6->request_timeout--;
803 /* { dhcp6->request_timeout == 0 } */
804 LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
805 /* this client's request timeout triggered */
806 dhcp6_timeout(netif, dhcp6);
807 }
808 }
809 }
810}
811
812#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
Note: See TracBrowser for help on using the repository browser.