Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
lwip_dhcp6.c
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 */
Generated on Tue Jul 12 2022 13:54:28 by
