[457] | 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
|
---|
| 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
|
---|
| 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
|
---|
| 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 | */
|
---|
| 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*
|
---|
| 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
|
---|
| 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
|
---|
| 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
|
---|
| 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
|
---|
| 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
|
---|
| 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 *
|
---|
| 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
|
---|
| 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
|
---|
| 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
|
---|
| 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
|
---|
| 452 | dhcp6_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 |
|
---|
| 489 | static err_t
|
---|
| 490 | dhcp6_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 |
|
---|
| 501 | static void
|
---|
| 502 | dhcp6_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 | */
|
---|
| 513 | static void
|
---|
| 514 | dhcp6_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 | */
|
---|
| 576 | void
|
---|
| 577 | dhcp6_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 | */
|
---|
| 607 | static err_t
|
---|
| 608 | dhcp6_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 |
|
---|
| 688 | static void
|
---|
| 689 | dhcp6_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 |
|
---|
| 755 | free_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 | */
|
---|
| 765 | static void
|
---|
| 766 | dhcp6_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 | */
|
---|
| 789 | void
|
---|
| 790 | dhcp6_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 */
|
---|