Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_dhcp6.c Source File

lwip_dhcp6.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  *
00004  * @defgroup dhcp6 DHCPv6
00005  * @ingroup ip6
00006  * DHCPv6 client: IPv6 address autoconfiguration as per
00007  * RFC 3315 (stateful DHCPv6) and
00008  * RFC 3736 (stateless DHCPv6).
00009  *
00010  * For now, only stateless DHCPv6 is implemented!
00011  *
00012  * TODO:
00013  * - enable/disable API to not always start when RA is received
00014  * - stateful DHCPv6 (for now, only stateless DHCPv6 for DNS and NTP servers works)
00015  * - create Client Identifier?
00016  * - only start requests if a valid local address is available on the netif
00017  * - only start information requests if required (not for every RA)
00018  *
00019  * dhcp6_enable_stateful() enables stateful DHCPv6 for a netif (stateless disabled)\n
00020  * dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)\n
00021  * dhcp6_disable() disable DHCPv6 for a netif
00022  *
00023  * When enabled, requests are only issued after receipt of RA with the
00024  * corresponding bits set.
00025  */
00026 
00027 /*
00028  * Copyright (c) 2018 Simon Goldschmidt
00029  * All rights reserved.
00030  *
00031  * Redistribution and use in source and binary forms, with or without modification,
00032  * are permitted provided that the following conditions are met:
00033  *
00034  * 1. Redistributions of source code must retain the above copyright notice,
00035  *    this list of conditions and the following disclaimer.
00036  * 2. Redistributions in binary form must reproduce the above copyright notice,
00037  *    this list of conditions and the following disclaimer in the documentation
00038  *    and/or other materials provided with the distribution.
00039  * 3. The name of the author may not be used to endorse or promote products
00040  *    derived from this software without specific prior written permission.
00041  *
00042  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00043  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00044  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00045  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00046  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00047  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00048  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00049  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00050  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00051  * OF SUCH DAMAGE.
00052  *
00053  * This file is part of the lwIP TCP/IP stack.
00054  *
00055  * Author: Simon Goldschmidt <goldsimon@gmx.de>
00056  */
00057 
00058 #include "lwip/opt.h"
00059 
00060 #if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
00061 
00062 #include "lwip/dhcp6.h"
00063 #include "lwip/prot/dhcp6.h"
00064 #include "lwip/def.h"
00065 #include "lwip/udp.h"
00066 #include "lwip/dns.h"
00067 
00068 #include <string.h>
00069 
00070 #ifdef LWIP_HOOK_FILENAME
00071 #include LWIP_HOOK_FILENAME
00072 #endif
00073 #ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
00074 #define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
00075 #endif
00076 #ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
00077 #define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
00078 #endif
00079 
00080 #if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS
00081 #if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS
00082 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS
00083 #else
00084 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
00085 #endif
00086 #else
00087 #define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0
00088 #endif
00089 
00090 
00091 /** Option handling: options are parsed in dhcp6_parse_reply
00092  * and saved in an array where other functions can load them from.
00093  * This might be moved into the struct dhcp6 (not necessarily since
00094  * lwIP is single-threaded and the array is only used while in recv
00095  * callback). */
00096 enum dhcp6_option_idx {
00097   DHCP6_OPTION_IDX_CLI_ID = 0,
00098   DHCP6_OPTION_IDX_SERVER_ID,
00099 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
00100   DHCP6_OPTION_IDX_DNS_SERVER,
00101   DHCP6_OPTION_IDX_DOMAIN_LIST,
00102 #endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
00103 #if LWIP_DHCP6_GET_NTP_SRV
00104   DHCP6_OPTION_IDX_NTP_SERVER,
00105 #endif /* LWIP_DHCP_GET_NTP_SRV */
00106   DHCP6_OPTION_IDX_MAX
00107 };
00108 
00109 struct dhcp6_option_info {
00110   u8_t option_given;
00111   u16_t val_start;
00112   u16_t val_length;
00113 };
00114 
00115 /** Holds the decoded option info, only valid while in dhcp6_recv. */
00116 struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX];
00117 
00118 #define dhcp6_option_given(dhcp6, idx)           (dhcp6_rx_options[idx].option_given != 0)
00119 #define dhcp6_got_option(dhcp6, idx)             (dhcp6_rx_options[idx].option_given = 1)
00120 #define dhcp6_clear_option(dhcp6, idx)           (dhcp6_rx_options[idx].option_given = 0)
00121 #define dhcp6_clear_all_options(dhcp6)           (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options)))
00122 #define dhcp6_get_option_start(dhcp6, idx)       (dhcp6_rx_options[idx].val_start)
00123 #define dhcp6_get_option_length(dhcp6, idx)      (dhcp6_rx_options[idx].val_length)
00124 #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)
00125 
00126 
00127 const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
00128 const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
00129 
00130 static struct udp_pcb *dhcp6_pcb;
00131 static u8_t dhcp6_pcb_refcount;
00132 
00133 
00134 /* receive, unfold, parse and free incoming messages */
00135 static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
00136 
00137 /** Ensure DHCP PCB is allocated and bound */
00138 static err_t
00139 dhcp6_inc_pcb_refcount(void)
00140 {
00141   if (dhcp6_pcb_refcount == 0) {
00142     LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL);
00143 
00144     /* allocate UDP PCB */
00145     dhcp6_pcb = udp_new_ip6();
00146 
00147     if (dhcp6_pcb == NULL) {
00148       return ERR_MEM;
00149     }
00150 
00151     ip_set_option(dhcp6_pcb, SOF_BROADCAST);
00152 
00153     /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
00154     udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
00155     udp_recv(dhcp6_pcb, dhcp6_recv, NULL);
00156   }
00157 
00158   dhcp6_pcb_refcount++;
00159 
00160   return ERR_OK;
00161 }
00162 
00163 /** Free DHCP PCB if the last netif stops using it */
00164 static void
00165 dhcp6_dec_pcb_refcount(void)
00166 {
00167   LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0));
00168   dhcp6_pcb_refcount--;
00169 
00170   if (dhcp6_pcb_refcount == 0) {
00171     udp_remove(dhcp6_pcb);
00172     dhcp6_pcb = NULL;
00173   }
00174 }
00175 
00176 /**
00177  * @ingroup dhcp6
00178  * Set a statically allocated struct dhcp6 to work with.
00179  * Using this prevents dhcp6_start to allocate it using mem_malloc.
00180  *
00181  * @param netif the netif for which to set the struct dhcp
00182  * @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
00183  */
00184 void
00185 dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
00186 {
00187   LWIP_ASSERT("netif != NULL", netif != NULL);
00188   LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
00189   LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
00190 
00191   /* clear data structure */
00192   memset(dhcp6, 0, sizeof(struct dhcp6));
00193   /* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */
00194   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
00195 }
00196 
00197 /**
00198  * @ingroup dhcp6
00199  * Removes a struct dhcp6 from a netif.
00200  *
00201  * ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
00202  *            struct dhcp6 since the memory is passed back to the heap.
00203  *
00204  * @param netif the netif from which to remove the struct dhcp
00205  */
00206 void dhcp6_cleanup(struct netif *netif)
00207 {
00208   LWIP_ASSERT("netif != NULL", netif != NULL);
00209 
00210   if (netif_dhcp6_data(netif) != NULL) {
00211     mem_free(netif_dhcp6_data(netif));
00212     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
00213   }
00214 }
00215 
00216 static struct dhcp6*
00217 dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
00218 {
00219   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
00220   if (dhcp6 == NULL) {
00221     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
00222     dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
00223     if (dhcp6 == NULL) {
00224       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
00225       return NULL;
00226     }
00227 
00228     /* clear data structure, this implies DHCP6_STATE_OFF */
00229     memset(dhcp6, 0, sizeof(struct dhcp6));
00230     /* store this dhcp6 client in the netif */
00231     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
00232   } else {
00233     /* already has DHCP6 client attached */
00234     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
00235   }
00236 
00237   if (!dhcp6->pcb_allocated) {
00238     if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */
00239       mem_free(dhcp6);
00240       netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
00241       return NULL;
00242     }
00243     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6", dbg_requester));
00244     dhcp6->pcb_allocated = 1;
00245   }
00246   return dhcp6;
00247 }
00248 
00249 /*
00250  * Set the DHCPv6 state
00251  * If the state changed, reset the number of tries.
00252  */
00253 static void
00254 dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller)
00255 {
00256   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n",
00257     dhcp6->state, new_state, dbg_caller));
00258   if (new_state != dhcp6->state) {
00259     dhcp6->state = new_state;
00260     dhcp6->tries = 0;
00261     dhcp6->request_timeout = 0;
00262   }
00263 }
00264 
00265 static int
00266 dhcp6_stateless_enabled(struct dhcp6 *dhcp6)
00267 {
00268   if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) ||
00269       (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) {
00270     return 1;
00271   }
00272   return 0;
00273 }
00274 
00275 /*static int
00276 dhcp6_stateful_enabled(struct dhcp6 *dhcp6)
00277 {
00278   if (dhcp6->state == DHCP6_STATE_OFF) {
00279     return 0;
00280   }
00281   if (dhcp6_stateless_enabled(dhcp6)) {
00282     return 0;
00283   }
00284   return 1;
00285 }*/
00286 
00287 /**
00288  * @ingroup dhcp6
00289  * Enable stateful DHCPv6 on this netif
00290  * Requests are sent on receipt of an RA message with the
00291  * ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
00292  *
00293  * A struct dhcp6 will be allocated for this netif if not
00294  * set via @ref dhcp6_set_struct before.
00295  *
00296  * @todo: stateful DHCPv6 not supported, yet
00297  */
00298 err_t
00299 dhcp6_enable_stateful(struct netif *netif)
00300 {
00301   LWIP_UNUSED_ARG(netif);
00302   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("stateful dhcp6 not implemented yet"));
00303   return ERR_VAL;
00304 }
00305 
00306 /**
00307  * @ingroup dhcp6
00308  * Enable stateless DHCPv6 on this netif
00309  * Requests are sent on receipt of an RA message with the
00310  * ND6_RA_FLAG_OTHER_CONFIG flag set.
00311  *
00312  * A struct dhcp6 will be allocated for this netif if not
00313  * set via @ref dhcp6_set_struct before.
00314  */
00315 err_t
00316 dhcp6_enable_stateless(struct netif *netif)
00317 {
00318   struct dhcp6 *dhcp6;
00319 
00320   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));
00321 
00322   dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()");
00323   if (dhcp6 == NULL) {
00324     return ERR_MEM;
00325   }
00326   if (dhcp6_stateless_enabled(dhcp6)) {
00327     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled"));
00328     return ERR_OK;
00329   } else if (dhcp6->state != DHCP6_STATE_OFF) {
00330     /* stateful running */
00331     /* @todo: stop stateful once it is implemented */
00332     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6"));
00333   }
00334   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n"));
00335   dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless");
00336   return ERR_OK;
00337 }
00338 
00339 /**
00340  * @ingroup dhcp6
00341  * Disable stateful or stateless DHCPv6 on this netif
00342  * Requests are sent on receipt of an RA message with the
00343  * ND6_RA_FLAG_OTHER_CONFIG flag set.
00344  */
00345 void
00346 dhcp6_disable(struct netif *netif)
00347 {
00348   struct dhcp6 *dhcp6;
00349 
00350   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));
00351 
00352   dhcp6 = netif_dhcp6_data(netif);
00353   if (dhcp6 != NULL) {
00354     if (dhcp6->state != DHCP6_STATE_OFF) {
00355       LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n",
00356         (dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful")));
00357       dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable");
00358       if (dhcp6->pcb_allocated != 0) {
00359         dhcp6_dec_pcb_refcount(); /* free DHCPv6 PCB if not needed any more */
00360         dhcp6->pcb_allocated = 0;
00361       }
00362     }
00363   }
00364 }
00365 
00366 /**
00367  * Create a DHCPv6 request, fill in common headers
00368  *
00369  * @param netif the netif under DHCPv6 control
00370  * @param dhcp6 dhcp6 control struct
00371  * @param message_type message type of the request
00372  * @param opt_len_alloc option length to allocate
00373  * @param options_out_len option length on exit
00374  * @return a pbuf for the message
00375  */
00376 static struct pbuf *
00377 dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type,
00378                  u16_t opt_len_alloc, u16_t *options_out_len)
00379 {
00380   struct pbuf *p_out;
00381   struct dhcp6_msg *msg_out;
00382 
00383   LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;);
00384   LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;);
00385   p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM);
00386   if (p_out == NULL) {
00387     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
00388                 ("dhcp6_create_msg(): could not allocate pbuf\n"));
00389     return NULL;
00390   }
00391   LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg",
00392               (p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc));
00393 
00394   /* @todo: limit new xid for certain message types? */
00395   /* reuse transaction identifier in retransmissions */
00396   if (dhcp6->tries == 0) {
00397     dhcp6->xid = LWIP_RAND() & 0xFFFFFF;
00398   }
00399 
00400   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
00401               ("transaction id xid(%"X32_F")\n", dhcp6->xid));
00402 
00403   msg_out = (struct dhcp6_msg *)p_out->payload;
00404   memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc);
00405 
00406   msg_out->msgtype = message_type;
00407   msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16);
00408   msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8);
00409   msg_out->transaction_id[2] = (u8_t)dhcp6->xid;
00410   *options_out_len = 0;
00411   return p_out;
00412 }
00413 
00414 static u16_t
00415 dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value)
00416 {
00417   options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
00418   options[options_out_len++] = (u8_t) (value & 0x00ffU);
00419   return options_out_len;
00420 }
00421 
00422 static u16_t
00423 dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options,
00424                            u16_t num_req_options, u16_t max_len)
00425 {
00426   size_t i;
00427   u16_t ret;
00428 
00429   LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len",
00430     sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len);
00431   LWIP_UNUSED_ARG(max_len);
00432 
00433   ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO);
00434   ret = dhcp6_option_short(ret, options, 2 * num_req_options);
00435   for (i = 0; i < num_req_options; i++) {
00436     ret = dhcp6_option_short(ret, options, req_options[i]);
00437   }
00438   return ret;
00439 }
00440 
00441 /* All options are added, shrink the pbuf to the required size */
00442 static void
00443 dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out)
00444 {
00445   /* shrink the pbuf to the actual content length */
00446   pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len));
00447 }
00448 
00449 
00450 #if LWIP_IPV6_DHCP6_STATELESS
00451 static void
00452 dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
00453 {
00454   const u16_t requested_options[] = {DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_SNTP_SERVERS};
00455   u16_t msecs;
00456   struct pbuf *p_out;
00457   u16_t options_out_len;
00458   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
00459   /* create and initialize the DHCP message header */
00460   p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len);
00461   if (p_out != NULL) {
00462     err_t err;
00463     struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
00464     u8_t *options = (u8_t *)(msg_out + 1);
00465     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
00466 
00467     options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
00468       LWIP_ARRAYSIZE(requested_options), p_out->len);
00469     LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
00470       DHCP6_INFOREQUEST, options_out_len, p_out->len);
00471     dhcp6_msg_finalize(options_out_len, p_out);
00472 
00473     err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
00474     pbuf_free(p_out);
00475     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
00476     LWIP_UNUSED_ARG(err);
00477   } else {
00478     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
00479   }
00480   dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
00481   if (dhcp6->tries < 255) {
00482     dhcp6->tries++;
00483   }
00484   msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
00485   dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
00486   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
00487 }
00488 
00489 static err_t
00490 dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
00491 {
00492   /* stateless mode enabled and no request running? */
00493   if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
00494     /* send Information-request and wait for answer; setup receive timeout */
00495     dhcp6_information_request(netif, dhcp6);
00496   }
00497 
00498   return ERR_OK;
00499 }
00500 
00501 static void
00502 dhcp6_abort_config_request(struct dhcp6 *dhcp6)
00503 {
00504   if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
00505     /* abort running request */
00506     dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
00507   }
00508 }
00509 
00510 /* Handle a REPLY to INFOREQUEST
00511  * This parses DNS and NTP server addresses from the reply.
00512  */
00513 static void
00514 dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
00515 {
00516   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
00517 
00518   LWIP_UNUSED_ARG(dhcp6);
00519   LWIP_UNUSED_ARG(p_msg_in);
00520 
00521 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
00522   if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
00523     ip_addr_t dns_addr;
00524     ip6_addr_t *dns_addr6;
00525     u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
00526     u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
00527     u16_t idx;
00528     u8_t n;
00529 
00530     memset(&dns_addr, 0, sizeof(dns_addr));
00531     dns_addr6 = ip_2_ip6(&dns_addr);
00532     for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
00533          n++, idx += sizeof(struct ip6_addr_packed)) {
00534       u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
00535       if (copied != sizeof(struct ip6_addr_packed)) {
00536         /* pbuf length mismatch */
00537         return;
00538       }
00539       ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
00540       /* @todo: do we need a different offset than DHCP(v4)? */
00541       dns_setserver(n, &dns_addr);
00542     }
00543   }
00544   /* @ todo: parse and set Domain Search List */
00545 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
00546 
00547 #if LWIP_DHCP6_GET_NTP_SRV
00548   if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
00549     ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
00550     u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
00551     u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
00552     u16_t idx;
00553     u8_t n;
00554 
00555     for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
00556          n++, idx += sizeof(struct ip6_addr_packed)) {
00557       u16_t copied;
00558       ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
00559       ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
00560       copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
00561       if (copied != sizeof(struct ip6_addr_packed)) {
00562         /* pbuf length mismatch */
00563         return;
00564       }
00565       ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
00566     }
00567     dhcp6_set_ntp_servers(n, ntp_server_addrs);
00568   }
00569 #endif /* LWIP_DHCP6_GET_NTP_SRV */
00570 }
00571 #endif /* LWIP_IPV6_DHCP6_STATELESS */
00572 
00573 /** This function is called from nd6 module when an RA messsage is received
00574  * It triggers DHCPv6 requests (if enabled).
00575  */
00576 void
00577 dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
00578 {
00579   struct dhcp6 *dhcp6;
00580 
00581   LWIP_ASSERT("netif != NULL", netif != NULL);
00582   dhcp6 = netif_dhcp6_data(netif);
00583 
00584   LWIP_UNUSED_ARG(managed_addr_config);
00585   LWIP_UNUSED_ARG(other_config);
00586   LWIP_UNUSED_ARG(dhcp6);
00587 
00588 #if LWIP_IPV6_DHCP6_STATELESS
00589   if (dhcp6 != NULL) {
00590     if (dhcp6_stateless_enabled(dhcp6)) {
00591       if (other_config) {
00592         dhcp6_request_config(netif, dhcp6);
00593       } else {
00594         dhcp6_abort_config_request(dhcp6);
00595       }
00596     }
00597   }
00598 #endif /* LWIP_IPV6_DHCP6_STATELESS */
00599 }
00600 
00601 /**
00602  * Parse the DHCPv6 message and extract the DHCPv6 options.
00603  *
00604  * Extract the DHCPv6 options (offset + length) so that we can later easily
00605  * check for them or extract the contents.
00606  */
00607 static err_t
00608 dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
00609 {
00610   u16_t offset;
00611   u16_t offset_max;
00612   u16_t options_idx;
00613   struct dhcp6_msg *msg_in;
00614 
00615   LWIP_UNUSED_ARG(dhcp6);
00616 
00617   /* clear received options */
00618   dhcp6_clear_all_options(dhcp6);
00619   msg_in = (struct dhcp6_msg *)p->payload;
00620 
00621   /* parse options */
00622 
00623   options_idx = sizeof(struct dhcp6_msg);
00624   /* parse options to the end of the received packet */
00625   offset_max = p->tot_len;
00626 
00627   offset = options_idx;
00628   /* at least 4 byte to read? */
00629   while ((offset + 4 <= offset_max)) {
00630     u8_t op_len_buf[4];
00631     u8_t *op_len;
00632     u16_t op;
00633     u16_t len;
00634     u16_t val_offset = (u16_t)(offset + 4);
00635     if (val_offset < offset) {
00636       /* overflow */
00637       return ERR_BUF;
00638     }
00639     /* copy option + length, might be split accross pbufs */
00640     op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset);
00641     if (op_len == NULL) {
00642       /* failed to get option and length */
00643       return ERR_VAL;
00644     }
00645     op = (op_len[0] << 8) | op_len[1];
00646     len = (op_len[2] << 8) | op_len[3];
00647     offset = val_offset + len;
00648     if (offset < val_offset) {
00649       /* overflow */
00650       return ERR_BUF;
00651     }
00652 
00653     switch (op) {
00654       case (DHCP6_OPTION_CLIENTID):
00655         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
00656         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
00657         break;
00658       case (DHCP6_OPTION_SERVERID):
00659         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
00660         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
00661         break;
00662 #if LWIP_DHCP6_PROVIDE_DNS_SERVERS
00663       case (DHCP6_OPTION_DNS_SERVERS):
00664         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
00665         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
00666         break;
00667       case (DHCP6_OPTION_DOMAIN_LIST):
00668         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
00669         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
00670         break;
00671 #endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
00672 #if LWIP_DHCP6_GET_NTP_SRV
00673       case (DHCP6_OPTION_SNTP_SERVERS):
00674         dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
00675         dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
00676         break;
00677 #endif /* LWIP_DHCP6_GET_NTP_SRV*/
00678       default:
00679         LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
00680         LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
00681           msg_in->msgtype, op, len, q, val_offset);
00682         break;
00683     }
00684   }
00685   return ERR_OK;
00686 }
00687 
00688 static void
00689 dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
00690 {
00691   struct netif *netif = ip_current_input_netif();
00692   struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
00693   struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
00694   u8_t msg_type;
00695   u32_t xid;
00696 
00697   LWIP_UNUSED_ARG(arg);
00698 
00699   /* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
00700   if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) {
00701     goto free_pbuf_and_return;
00702   }
00703 
00704   LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
00705 
00706   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
00707     ipaddr_ntoa(addr), port));
00708   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
00709   LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
00710   /* prevent warnings about unused arguments */
00711   LWIP_UNUSED_ARG(pcb);
00712   LWIP_UNUSED_ARG(addr);
00713   LWIP_UNUSED_ARG(port);
00714 
00715   if (p->len < sizeof(struct dhcp6_msg)) {
00716     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
00717     goto free_pbuf_and_return;
00718   }
00719 
00720   /* match transaction ID against what we expected */
00721   xid = reply_msg->transaction_id[0] << 16;
00722   xid |= reply_msg->transaction_id[1] << 8;
00723   xid |= reply_msg->transaction_id[2];
00724   if (xid != dhcp6->xid) {
00725     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
00726                 ("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
00727     goto free_pbuf_and_return;
00728   }
00729   /* option fields could be unfold? */
00730   if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
00731     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
00732                 ("problem unfolding DHCPv6 message - too short on memory?\n"));
00733     goto free_pbuf_and_return;
00734   }
00735 
00736   /* read DHCP message type */
00737   msg_type = reply_msg->msgtype;
00738   /* message type is DHCP6 REPLY? */
00739   if (msg_type == DHCP6_REPLY) {
00740     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
00741 #if LWIP_IPV6_DHCP6_STATELESS
00742     /* in info-requesting state? */
00743     if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
00744       dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
00745       dhcp6_handle_config_reply(netif, p);
00746     } else
00747 #endif /* LWIP_IPV6_DHCP6_STATELESS */
00748     {
00749       /* @todo: handle reply in other states? */
00750     }
00751   } else {
00752     /* @todo: handle other message types */
00753   }
00754 
00755 free_pbuf_and_return:
00756   pbuf_free(p);
00757 }
00758 
00759 /**
00760  * A DHCPv6 request has timed out.
00761  *
00762  * The timer that was started with the DHCPv6 request has
00763  * timed out, indicating no response was received in time.
00764  */
00765 static void
00766 dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
00767 {
00768   LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
00769 
00770   LWIP_UNUSED_ARG(netif);
00771   LWIP_UNUSED_ARG(dhcp6);
00772 
00773 #if LWIP_IPV6_DHCP6_STATELESS
00774   /* back-off period has passed, or server selection timed out */
00775   if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
00776     LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
00777     dhcp6_information_request(netif, dhcp6);
00778   }
00779 #endif /* LWIP_IPV6_DHCP6_STATELESS */
00780 }
00781 
00782 /**
00783  * DHCPv6 timeout handling (this function must be called every 500ms,
00784  * see @ref DHCP6_TIMER_MSECS).
00785  *
00786  * A DHCPv6 server is expected to respond within a short period of time.
00787  * This timer checks whether an outstanding DHCPv6 request is timed out.
00788  */
00789 void
00790 dhcp6_tmr(void)
00791 {
00792   struct netif *netif;
00793   /* loop through netif's */
00794   NETIF_FOREACH(netif) {
00795     struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
00796     /* only act on DHCPv6 configured interfaces */
00797     if (dhcp6 != NULL) {
00798       /* timer is active (non zero), and is about to trigger now */
00799       if (dhcp6->request_timeout > 1) {
00800         dhcp6->request_timeout--;
00801       } else if (dhcp6->request_timeout == 1) {
00802         dhcp6->request_timeout--;
00803         /* { dhcp6->request_timeout == 0 } */
00804         LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
00805         /* this client's request timeout triggered */
00806         dhcp6_timeout(netif, dhcp6);
00807       }
00808     }
00809   }
00810 }
00811 
00812 #endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */