add initial support for stateless DHCPv6

Signed-off-by: goldsimon <goldsimon@gmx.de>
This commit is contained in:
goldsimon 2018-02-22 22:33:16 +01:00
parent 37b4494921
commit 76a13054ee
7 changed files with 909 additions and 28 deletions

View File

@ -1,11 +1,28 @@
/** /**
* @file * @file
* DHCPv6 client: IPv6 address autoconfiguration as per
* RFC 3315 (stateful DHCPv6) and
* RFC 3736 (stateless DHCPv6).
* *
* DHCPv6. * For now, only stateless DHCPv6 is implemented!
*
* @todo:
* - enable/disable API to not always start when RA is received
* - stateful DHCPv6 (for now, only stateless DHCPv6 for DNS and NTP servers works)
* - create Client Identifier?
* - only start requests if a valid local address is available on the netif
* - only start information requests if required (not for every RA)
*
* dhcp6_enable_stateful() enables stateless DHCPv6 for a netif (stateless disabled)
* dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)
* dhcp6_disable() disable DHCPv6 for a netif
*
* When enabled, requests are only issued after receipt of RA with the
* corresponding bits set.
*/ */
/* /*
* Copyright (c) 2010 Inico Technologies Ltd. * Copyright (c) 2018 Simon Goldschmidt
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -32,19 +49,751 @@
* *
* This file is part of the lwIP TCP/IP stack. * This file is part of the lwIP TCP/IP stack.
* *
* Author: Ivan Delamer <delamer@inicotech.com> * Author: Simon Goldschmidt <goldsimon@gmx.de>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/ */
#include "lwip/opt.h" #include "lwip/opt.h"
#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ #if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
#include "lwip/ip6_addr.h" #include "lwip/dhcp6.h"
#include "lwip/prot/dhcp6.h"
#include "lwip/def.h" #include "lwip/def.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include <string.h>
#ifdef LWIP_HOOK_FILENAME
#include LWIP_HOOK_FILENAME
#endif
#ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
#endif
#ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
#define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
#endif
#if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS
#if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS
#define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS
#else
#define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
#endif
#else
#define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0
#endif
/** Option handling: options are parsed in dhcp_parse_reply
* and saved in an array where other functions can load them from.
* This might be moved into the struct dhcp (not necessarily since
* lwIP is single-threaded and the array is only used while in recv
* callback). */
enum dhcp6_option_idx {
DHCP6_OPTION_IDX_CLI_ID = 0,
DHCP6_OPTION_IDX_SERVER_ID,
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
DHCP6_OPTION_IDX_DNS_SERVER,
DHCP6_OPTION_IDX_DOMAIN_LIST,
#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
#if LWIP_DHCP6_GET_NTP_SRV
DHCP6_OPTION_IDX_NTP_SERVER,
#endif /* LWIP_DHCP_GET_NTP_SRV */
DHCP6_OPTION_IDX_MAX
};
struct dhcp6_option_info {
u8_t option_given;
u16_t val_start;
u16_t val_length;
};
/** Holds the decoded option info, only valid while in dhcp6_recv. */
struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX];
#define dhcp6_option_given(dhcp6, idx) (dhcp6_rx_options[idx].option_given != 0)
#define dhcp6_got_option(dhcp6, idx) (dhcp6_rx_options[idx].option_given = 1)
#define dhcp6_clear_option(dhcp6, idx) (dhcp6_rx_options[idx].option_given = 0)
#define dhcp6_clear_all_options(dhcp6) (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options)))
#define dhcp6_get_option_start(dhcp6, idx) (dhcp6_rx_options[idx].val_start)
#define dhcp6_get_option_length(dhcp6, idx) (dhcp6_rx_options[idx].val_length)
#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)
const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
static struct udp_pcb *dhcp6_pcb;
static u8_t dhcp6_pcb_refcount;
/* receive, unfold, parse and free incoming messages */
static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
/** Ensure DHCP PCB is allocated and bound */
static err_t
dhcp6_inc_pcb_refcount(void)
{
if (dhcp6_pcb_refcount == 0) {
LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL);
/* allocate UDP PCB */
dhcp6_pcb = udp_new_ip6();
if (dhcp6_pcb == NULL) {
return ERR_MEM;
}
ip_set_option(dhcp6_pcb, SOF_BROADCAST);
/* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
udp_recv(dhcp6_pcb, dhcp6_recv, NULL);
}
dhcp6_pcb_refcount++;
return ERR_OK;
}
/** Free DHCP PCB if the last netif stops using it */
static void
dhcp6_dec_pcb_refcount(void)
{
LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0));
dhcp6_pcb_refcount--;
if (dhcp6_pcb_refcount == 0) {
udp_remove(dhcp6_pcb);
dhcp6_pcb = NULL;
}
}
/**
* @ingroup dhcp6
* Set a statically allocated struct dhcp6 to work with.
* Using this prevents dhcp6_start to allocate it using mem_malloc.
*
* @param netif the netif for which to set the struct dhcp
* @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
*/
void
dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
{
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
/* clear data structure */
memset(dhcp6, 0, sizeof(struct dhcp6));
/* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
}
/**
* @ingroup dhcp6
* Removes a struct dhcp6 from a netif.
*
* ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
* struct dhcp6 since the memory is passed back to the heap.
*
* @param netif the netif from which to remove the struct dhcp
*/
void dhcp6_cleanup(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", netif != NULL);
if (netif_dhcp6_data(netif) != NULL) {
mem_free(netif_dhcp6_data(netif));
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
}
}
static struct dhcp6*
dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
{
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
if (dhcp6 == NULL) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
if (dhcp6 == NULL) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
return NULL;
}
/* clear data structure, this implies DHCP6_STATE_OFF */
memset(dhcp6, 0, sizeof(struct dhcp6));
/* store this dhcp6 client in the netif */
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
} else {
/* already has DHCP6 client attached */
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
}
if (!dhcp6->pcb_allocated) {
if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */
mem_free(dhcp6);
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
return NULL;
}
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6", dbg_requester));
dhcp6->pcb_allocated = 1;
}
return dhcp6;
}
/*
* Set the DHCPv6 state
* If the state changed, reset the number of tries.
*/
static void
dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller)
{
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n",
dhcp6->state, new_state, dbg_caller));
if (new_state != dhcp6->state) {
dhcp6->state = new_state;
dhcp6->tries = 0;
dhcp6->request_timeout = 0;
}
}
static int
dhcp6_stateless_enabled(struct dhcp6 *dhcp6)
{
if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) ||
(dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) {
return 1;
}
return 0;
}
static int
dhcp6_stateful_enabled(struct dhcp6 *dhcp6)
{
if (dhcp6->state == DHCP6_STATE_OFF) {
return 0;
}
if (dhcp6_stateless_enabled(dhcp6)) {
return 0;
}
return 1;
}
/** Enable stateful DHCPv6 on this netif
* Requests are sent on receipt of an RA message with the
* ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
*
* A struct dhcp6 will be allocated for this netif if not
* set via @ref dhcp6_set_struct before.
*
* @todo: stateful DHCPv6 not supported, yet
*/
err_t
dhcp6_enable_stateful(struct netif *netif)
{
LWIP_UNUSED_ARG(netif);
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("stateful dhcp6 not implemented yet"));
return ERR_VAL;
}
/** Enable stateless DHCPv6 on this netif
* Requests are sent on receipt of an RA message with the
* ND6_RA_FLAG_OTHER_CONFIG flag set.
*
* A struct dhcp6 will be allocated for this netif if not
* set via @ref dhcp6_set_struct before.
*/
err_t
dhcp6_enable_stateless(struct netif *netif)
{
struct dhcp6 *dhcp6;
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));
dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()");
if (dhcp6 == NULL) {
return ERR_MEM;
}
if (dhcp6_stateless_enabled(dhcp6)) {
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled"));
return ERR_OK;
} else if (dhcp6->state != DHCP6_STATE_OFF) {
/* stateful running */
/* @todo: stop stateful once it is implemented */
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6"));
}
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n"));
dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless");
return ERR_OK;
}
/** Disable stateful or stateless DHCPv6 on this netif
* Requests are sent on receipt of an RA message with the
* ND6_RA_FLAG_OTHER_CONFIG flag set.
*/
void
dhcp6_disable(struct netif *netif)
{
struct dhcp6 *dhcp6;
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));
dhcp6 = netif_dhcp6_data(netif);
if (dhcp6 != NULL) {
if (dhcp6->state != DHCP6_STATE_OFF) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n",
(dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful")));
dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable");
}
}
}
/**
* Create a DHCPv6 request, fill in common headers
*
* @param netif the netif under DHCPv6 control
* @param dhcp dhcp control struct
* @param message_type message type of the request
* @param opt_len_alloc option length to allocate
* @param options_out_len option length on exit
* @return a pbuf for the message
*/
static struct pbuf *
dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type,
u16_t opt_len_alloc, u16_t *options_out_len)
{
struct pbuf *p_out;
struct dhcp6_msg *msg_out;
LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;);
LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;);
p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM);
if (p_out == NULL) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
("dhcp6_create_msg(): could not allocate pbuf\n"));
return NULL;
}
LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg",
(p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc));
/* @todo: limit new xid for certain message types? */
/* reuse transaction identifier in retransmissions */
if (dhcp6->tries == 0) {
dhcp6->xid = LWIP_RAND() & 0xFFFFFF;
}
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
("transaction id xid(%"X32_F")\n", dhcp6->xid));
msg_out = (struct dhcp6_msg *)p_out->payload;
memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc);
msg_out->msgtype = message_type;
msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16);
msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8);
msg_out->transaction_id[2] = (u8_t)dhcp6->xid;
*options_out_len = 0;
return p_out;
}
static u16_t
dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value)
{
options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
options[options_out_len++] = (u8_t) (value & 0x00ffU);
return options_out_len;
}
static u16_t
dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options,
u16_t num_req_options, u16_t max_len)
{
size_t i;
u16_t ret;
LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len",
sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len);
LWIP_UNUSED_ARG(max_len);
ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO);
ret = dhcp6_option_short(ret, options, 2 * num_req_options);
for (i = 0; i < num_req_options; i++) {
ret = dhcp6_option_short(ret, options, req_options[i]);
}
return ret;
}
/* All options are added, shrink the pbuf to the required size */
static void
dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out)
{
/* shrink the pbuf to the actual content length */
pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len));
}
#if LWIP_IPV6_DHCP6_STATELESS
static void
dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
{
const u16_t requested_options[] = {DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_SNTP_SERVERS};
u16_t msecs;
struct pbuf *p_out;
u16_t options_out_len;
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
/* create and initialize the DHCP message header */
p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len);
if (p_out != NULL) {
err_t err;
struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
u8_t *options = (u8_t *)(msg_out + 1);
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
LWIP_ARRAYSIZE(requested_options), p_out->len);
LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
DHCP6_INFOREQUEST, options_out_len, p_out->len);
dhcp6_msg_finalize(options_out_len, p_out);
err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
pbuf_free(p_out);
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
LWIP_UNUSED_ARG(err);
} else {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
}
dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
if (dhcp6->tries < 255) {
dhcp6->tries++;
}
msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
}
static err_t
dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
{
/* stateless mode enabled and no request running? */
if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
/* send Information-request and wait for answer; setup receive timeout */
dhcp6_information_request(netif, dhcp6);
}
return ERR_OK;
}
static void
dhcp6_abort_config_request(struct dhcp6 *dhcp6)
{
if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
/* abort running request */
dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
}
}
/* Handle a REPLY to INFOREQUEST
* This parses DNS and NTP server addresses from the reply.
*/
static void
dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
{
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
LWIP_UNUSED_ARG(dhcp6);
LWIP_UNUSED_ARG(p_msg_in);
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
ip_addr_t dns_addr;
ip6_addr_t *dns_addr6;
u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
u16_t idx;
u8_t n;
ip_addr_set_zero_ip6(&dns_addr);
dns_addr6 = ip_2_ip6(&dns_addr);
for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
n++, idx += sizeof(struct ip6_addr_packed)) {
u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
if (copied != sizeof(struct ip6_addr_packed)) {
/* pbuf length mismatch */
return;
}
ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
/* @todo: do we need a different offset than DHCP(v4)? */
dns_setserver(n, &dns_addr);
}
}
/* @ todo: parse and set Domain Search List */
#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
#if LWIP_DHCP6_GET_NTP_SRV
if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
u16_t idx;
u8_t n;
for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
n++, idx += sizeof(struct ip6_addr_packed)) {
u16_t copied;
ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
if (copied != sizeof(struct ip6_addr_packed)) {
/* pbuf length mismatch */
return;
}
ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
}
dhcp6_set_ntp_servers(n, ntp_server_addrs);
}
#endif /* LWIP_DHCP6_GET_NTP_SRV */
}
#endif /* LWIP_IPV6_DHCP6_STATELESS */
/** This function is called from nd6 module when an RA messsage is received
* It triggers DHCPv6 requests (if enabled).
*/
void
dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
{
struct dhcp6 *dhcp6;
LWIP_ASSERT("netif != NULL", netif != NULL);
dhcp6 = netif_dhcp6_data(netif);
LWIP_UNUSED_ARG(managed_addr_config);
LWIP_UNUSED_ARG(other_config);
LWIP_UNUSED_ARG(dhcp6);
#if LWIP_IPV6_DHCP6_STATELESS
if (dhcp6 != NULL) {
if (dhcp6_stateless_enabled(dhcp6)) {
if (other_config) {
dhcp6_request_config(netif, dhcp6);
} else {
dhcp6_abort_config_request(dhcp6);
}
}
}
#endif /* LWIP_IPV6_DHCP6_STATELESS */
}
/**
* Parse the DHCPv6 message and extract the DHCPv6 options.
*
* Extract the DHCPv6 options (offset + length) so that we can later easily
* check for them or extract the contents.
*/
static err_t
dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
{
u16_t offset;
u16_t offset_max;
u16_t options_idx;
struct dhcp6_msg *msg_in;
LWIP_UNUSED_ARG(dhcp6);
/* clear received options */
dhcp6_clear_all_options(dhcp6);
msg_in = (struct dhcp6_msg *)p->payload;
/* parse options */
options_idx = sizeof(struct dhcp6_msg);
/* parse options to the end of the received packet */
offset_max = p->tot_len;
offset = options_idx;
/* at least 4 byte to read? */
while ((offset + 4 <= offset_max)) {
u8_t op_len_buf[4];
u8_t *op_len;
u16_t op;
u16_t len;
u16_t val_offset = (u16_t)(offset + 4);
if (val_offset < offset) {
/* overflow */
return ERR_BUF;
}
/* copy option + length, might be split accross pbufs */
op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset);
if (op_len == NULL) {
/* failed to get option and length */
return ERR_VAL;
}
op = (op_len[0] << 8) | op_len[1];
len = (op_len[2] << 8) | op_len[3];
offset = val_offset + len;
if (offset < val_offset) {
/* overflow */
return ERR_BUF;
}
switch (op) {
case (DHCP6_OPTION_CLIENTID):
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
break;
case (DHCP6_OPTION_SERVERID):
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
break;
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
case (DHCP6_OPTION_DNS_SERVERS):
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
break;
case (DHCP6_OPTION_DOMAIN_LIST):
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
break;
#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
#if LWIP_DHCP6_GET_NTP_SRV
case (DHCP6_OPTION_SNTP_SERVERS):
dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
break;
#endif /* LWIP_DHCP6_GET_NTP_SRV*/
default:
LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
msg_in->msgtype, op, len, q, val_offset);
break;
}
}
return ERR_OK;
}
static void
dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
struct netif *netif = ip_current_input_netif();
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
u8_t msg_type;
u32_t xid;
LWIP_UNUSED_ARG(arg);
/* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) {
goto free_pbuf_and_return;
}
LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
ipaddr_ntoa(addr), port));
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
/* prevent warnings about unused arguments */
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
if (p->len < sizeof(struct dhcp6_msg)) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
goto free_pbuf_and_return;
}
/* match transaction ID against what we expected */
xid = reply_msg->transaction_id[0] << 16;
xid |= reply_msg->transaction_id[1] << 8;
xid |= reply_msg->transaction_id[2];
if (xid != dhcp6->xid) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
goto free_pbuf_and_return;
}
/* option fields could be unfold? */
if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
("problem unfolding DHCPv6 message - too short on memory?\n"));
goto free_pbuf_and_return;
}
/* read DHCP message type */
msg_type = reply_msg->msgtype;
/* message type is DHCP6 REPLY? */
if (msg_type == DHCP6_REPLY) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
#if LWIP_IPV6_DHCP6_STATELESS
/* in info-requesting state? */
if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
dhcp6_handle_config_reply(netif, p);
} else
#endif /* LWIP_IPV6_DHCP6_STATELESS */
{
/* @todo: handle reply in other states? */
}
} else {
/* @todo: handle other message types */
}
free_pbuf_and_return:
pbuf_free(p);
}
/**
* A DHCPv6 request has timed out.
*
* The timer that was started with the DHCPv6 request has
* timed out, indicating no response was received in time.
*/
static void
dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
{
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(dhcp6);
#if LWIP_IPV6_DHCP6_STATELESS
/* back-off period has passed, or server selection timed out */
if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
dhcp6_information_request(netif, dhcp6);
}
#endif /* LWIP_IPV6_DHCP6_STATELESS */
}
/**
* DHCPv6 timeout handling (this function must be called every 500ms,
* see @ref DHCP6_TIMER_MSECS).
*
* A DHCPv6 server is expected to respond within a short period of time.
* This timer checks whether an outstanding DHCPv6 request is timed out.
*/
void
dhcp6_tmr(void)
{
struct netif *netif;
/* loop through netif's */
NETIF_FOREACH(netif) {
struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
/* only act on DHCPv6 configured interfaces */
if (dhcp6 != NULL) {
/* timer is active (non zero), and is about to trigger now */
if (dhcp6->request_timeout > 1) {
dhcp6->request_timeout--;
} else if (dhcp6->request_timeout == 1) {
dhcp6->request_timeout--;
/* { dhcp6->request_timeout == 0 } */
LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
/* this client's request timeout triggered */
dhcp6_timeout(netif, dhcp6);
}
}
}
}
#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */ #endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */

View File

@ -58,6 +58,7 @@
#include "lwip/netif.h" #include "lwip/netif.h"
#include "lwip/icmp6.h" #include "lwip/icmp6.h"
#include "lwip/mld6.h" #include "lwip/mld6.h"
#include "lwip/dhcp6.h"
#include "lwip/ip.h" #include "lwip/ip.h"
#include "lwip/stats.h" #include "lwip/stats.h"
#include "lwip/dns.h" #include "lwip/dns.h"
@ -621,6 +622,12 @@ nd6_input(struct pbuf *p, struct netif *inp)
/* Update flags in local entry (incl. preference). */ /* Update flags in local entry (incl. preference). */
default_router_list[i].flags = ra_hdr->flags; default_router_list[i].flags = ra_hdr->flags;
#if LWIP_IPV6_DHCP6
/* Trigger DHCPv6 if enabled */
dhcp6_nd6_ra_trigger(inp, ra_hdr->flags & ND6_RA_FLAG_MANAGED_ADDR_CONFIG,
ra_hdr->flags & ND6_RA_FLAG_OTHER_CONFIG);
#endif
/* Offset to options. */ /* Offset to options. */
offset = sizeof(struct ra_header); offset = sizeof(struct ra_header);

View File

@ -57,6 +57,7 @@
#include "lwip/nd6.h" #include "lwip/nd6.h"
#include "lwip/ip6_frag.h" #include "lwip/ip6_frag.h"
#include "lwip/mld6.h" #include "lwip/mld6.h"
#include "lwip/dhcp6.h"
#include "lwip/sys.h" #include "lwip/sys.h"
#include "lwip/pbuf.h" #include "lwip/pbuf.h"
@ -108,6 +109,9 @@ const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
#if LWIP_IPV6_MLD #if LWIP_IPV6_MLD
{MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)}, {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
#endif /* LWIP_IPV6_MLD */ #endif /* LWIP_IPV6_MLD */
#if LWIP_IPV6_DHCP6
{DHCP6_TIMER_MSECS, HANDLER(dhcp6_tmr)},
#endif /* LWIP_IPV6_DHCP6 */
#endif /* LWIP_IPV6 */ #endif /* LWIP_IPV6 */
}; };
const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers); const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers);

View File

@ -1,11 +1,13 @@
/** /**
* @file * @file
* *
* IPv6 address autoconfiguration as per RFC 4862. * DHCPv6 client: IPv6 address autoconfiguration as per
* RFC 3315 (stateful DHCPv6) and
* RFC 3736 (stateless DHCPv6).
*/ */
/* /*
* Copyright (c) 2010 Inico Technologies Ltd. * Copyright (c) 2018 Simon Goldschmidt
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -32,12 +34,7 @@
* *
* This file is part of the lwIP TCP/IP stack. * This file is part of the lwIP TCP/IP stack.
* *
* Author: Ivan Delamer <delamer@inicotech.com> * Author: Simon Goldschmidt <goldsimon@gmx.de>
*
* IPv6 address autoconfiguration as per RFC 4862.
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/ */
#ifndef LWIP_HDR_IP6_DHCP6_H #ifndef LWIP_HDR_IP6_DHCP6_H
@ -47,12 +44,53 @@
#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ #if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
#include "lwip/err.h"
#include "lwip/netif.h"
/** period (in milliseconds) of the application calling dhcp6_tmr() */
#define DHCP6_TIMER_MSECS 500
struct dhcp6 struct dhcp6
{ {
/*@todo: implement DHCP6*/ /** transaction identifier of last sent request */
u32_t xid;
/** track PCB allocation state */
u8_t pcb_allocated;
/** current DHCPv6 state machine state */
u8_t state;
/** retries of current request */
u8_t tries;
/** if request config is triggered while another action is active, this keeps track of it */
u8_t request_config_pending;
/** #ticks with period DHCP6_TIMER_MSECS for request timeout */
u16_t request_timeout;
#if LWIP_IPV6_DHCP6_STATEFUL
/* @todo: add more members here to keep track of stateful DHCPv6 data, like lease times */
#endif /* LWIP_IPV6_DHCP6_STATEFUL */
}; };
void dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6);
/** Remove a struct dhcp6 previously set to the netif using dhcp6_set_struct() */
#define dhcp6_remove_struct(netif) netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL)
void dhcp6_cleanup(struct netif *netif);
err_t dhcp6_enable_stateful(struct netif *netif);
err_t dhcp6_enable_stateless(struct netif *netif);
void dhcp6_disable(struct netif *netif);
void dhcp6_tmr(void);
void dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config);
#if LWIP_DHCP6_GET_NTP_SRV
/** This function must exist, in other to add offered NTP servers to
* the NTP (or SNTP) engine.
* See LWIP_DHCP6_MAX_NTP_SERVERS */
extern void dhcp6_set_ntp_servers(u8_t num_ntp_servers, const ip_addr_t* ntp_server_addrs);
#endif /* LWIP_DHCP6_GET_NTP_SRV */
#define netif_dhcp6_data(netif) ((struct dhcp6*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6))
#endif /* LWIP_IPV6_DHCP6 */ #endif /* LWIP_IPV6_DHCP6 */
#endif /* LWIP_HDR_IP6_DHCP6_H */ #endif /* LWIP_HDR_IP6_DHCP6_H */

View File

@ -112,6 +112,7 @@ extern "C" {
enum lwip_internal_netif_client_data_index enum lwip_internal_netif_client_data_index
{ {
#if LWIP_IPV4
#if LWIP_DHCP #if LWIP_DHCP
LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP,
#endif #endif
@ -121,9 +122,15 @@ enum lwip_internal_netif_client_data_index
#if LWIP_IGMP #if LWIP_IGMP
LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP,
#endif #endif
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
#if LWIP_IPV6_DHCP6
LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6,
#endif
#if LWIP_IPV6_MLD #if LWIP_IPV6_MLD
LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6,
#endif #endif
#endif /* LWIP_IPV6 */
LWIP_NETIF_CLIENT_DATA_INDEX_MAX LWIP_NETIF_CLIENT_DATA_INDEX_MAX
}; };

View File

@ -963,7 +963,7 @@
/** /**
* LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select. * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select.
* DHCP servers received in the response are passed to DNS via @ref dns_setserver() * DNS servers received in the response are passed to DNS via @ref dns_setserver()
* (up to the maximum limit defined here). * (up to the maximum limit defined here).
*/ */
#if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__ #if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__
@ -2657,12 +2657,51 @@
*/ */
/** /**
* LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful/stateless address autoconfiguration.
*/ */
#if !defined LWIP_IPV6_DHCP6 || defined __DOXYGEN__ #if !defined LWIP_IPV6_DHCP6 || defined __DOXYGEN__
#define LWIP_IPV6_DHCP6 0 #define LWIP_IPV6_DHCP6 0
#endif #endif
/**
* LWIP_IPV6_DHCP6_STATEFUL==1: enable DHCPv6 stateful address autoconfiguration.
*/
#if !defined LWIP_IPV6_DHCP6_STATEFUL || defined __DOXYGEN__
#define LWIP_IPV6_DHCP6_STATEFUL 0
#endif
/**
* LWIP_IPV6_DHCP6_STATELESS==1: enable DHCPv6 stateless address autoconfiguration.
*/
#if !defined LWIP_IPV6_DHCP6_STATELESS || defined __DOXYGEN__
#define LWIP_IPV6_DHCP6_STATELESS 0
#endif
/**
* LWIP_DHCP6_GETS_NTP==1: Request NTP servers via DHCPv6. For each
* response packet, a callback is called, which has to be provided by the port:
* void dhcp6_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs);
*/
#if !defined LWIP_DHCP6_GET_NTP_SRV || defined __DOXYGEN__
#define LWIP_DHCP6_GET_NTP_SRV 0
#endif
/**
* The maximum of NTP servers requested
*/
#if !defined LWIP_DHCP6_MAX_NTP_SERVERS || defined __DOXYGEN__
#define LWIP_DHCP6_MAX_NTP_SERVERS 1
#endif
/**
* LWIP_DHCP6_MAX_DNS_SERVERS > 0: Request DNS servers via DHCPv6.
* DNS servers received in the response are passed to DNS via @ref dns_setserver()
* (up to the maximum limit defined here).
*/
#if !defined LWIP_DHCP6_MAX_DNS_SERVERS || defined __DOXYGEN__
#define LWIP_DHCP6_MAX_DNS_SERVERS DNS_MAX_SERVERS
#endif
/* /*
--------------------------------------- ---------------------------------------
---------- Hook options --------------- ---------- Hook options ---------------
@ -3047,6 +3086,34 @@
#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) #define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset)
#endif #endif
/**
* LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len):
* Called from various dhcp6 functions when sending a DHCP6 message.
* This hook is called just before the DHCP6 message is sent, so the
* options are at the end of a DHCP6 message.
* Signature:\code{.c}
* void my_hook(struct netif *netif, struct dhcp6 *dhcp, u8_t state, struct dhcp6_msg *msg,
* u8_t msg_type, u16_t *options_len_ptr);
* \endcode
* Arguments:
* - netif: struct netif that the packet will be sent through
* - dhcp6: struct dhcp6 on that netif
* - state: current dhcp6 state (dhcp6_state_enum_t as an u8_t)
* - msg: struct dhcp6_msg that will be sent
* - msg_type: dhcp6 message type to be sent (u8_t)
* - options_len_ptr: pointer to the current length of options in the dhcp6_msg "msg"
* (must be increased when options are added!)
*
* Options need to appended like this:
* u8_t *options = (u8_t *)(msg + 1);
* LWIP_ASSERT("dhcp option overflow", sizeof(struct dhcp6_msg) + *options_len_ptr + newoptlen <= max_len);
* options[(*options_len_ptr)++] = &lt;option_data&gt;;
* [...]
*/
#ifdef __DOXYGEN__
#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
#endif
/** /**
* LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, err) * LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, err)
* Called from socket API to implement setsockopt() for options not provided by lwIP. * Called from socket API to implement setsockopt() for options not provided by lwIP.
@ -3372,6 +3439,13 @@
#if !defined IP6_DEBUG || defined __DOXYGEN__ #if !defined IP6_DEBUG || defined __DOXYGEN__
#define IP6_DEBUG LWIP_DBG_OFF #define IP6_DEBUG LWIP_DBG_OFF
#endif #endif
/**
* DHCP6_DEBUG: Enable debugging in dhcp6.c.
*/
#if !defined DHCP6_DEBUG || defined __DOXYGEN__
#define DHCP6_DEBUG LWIP_DBG_OFF
#endif
/** /**
* @} * @}
*/ */

View File

@ -49,12 +49,6 @@ extern "C" {
/* DHCPv6 message item offsets and length */ /* DHCPv6 message item offsets and length */
#define DHCP6_TRANSACTION_ID_LEN 3 #define DHCP6_TRANSACTION_ID_LEN 3
#define DHCP_SNAME_OFS 44U
#define DHCP_SNAME_LEN 64U
#define DHCP_FILE_OFS 108U
#define DHCP_FILE_LEN 128U
#define DHCP_MSG_LEN 236U
#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4U)*/ /* 4 byte: cookie */
#ifdef PACK_STRUCT_USE_INCLUDES #ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h" # include "arch/bpstruct.h"
@ -76,8 +70,9 @@ PACK_STRUCT_END
/* DHCP6 client states */ /* DHCP6 client states */
typedef enum { typedef enum {
DHCP6_STATE_OFF = 0, DHCP6_STATE_OFF = 0,
DHCP6_STATE_REQUESTING_CONFIG = 1 DHCP6_STATE_STATELESS_IDLE = 1,
} dhcp_state_enum_t; DHCP6_STATE_REQUESTING_CONFIG = 2
} dhcp6_state_enum_t;
/* DHCPv6 message types */ /* DHCPv6 message types */
#define DHCP6_SOLICIT 1 #define DHCP6_SOLICIT 1
@ -93,6 +88,7 @@ typedef enum {
#define DHCP6_INFOREQUEST 11 #define DHCP6_INFOREQUEST 11
#define DHCP6_RELAYFORW 12 #define DHCP6_RELAYFORW 12
#define DHCP6_RELAYREPL 13 #define DHCP6_RELAYREPL 13
/* More message types see https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml */
/** DHCPv6 status codes */ /** DHCPv6 status codes */
#define DHCP6_STATUS_SUCCESS 0 /* Success. */ #define DHCP6_STATUS_SUCCESS 0 /* Success. */
@ -101,11 +97,13 @@ typedef enum {
#define DHCP6_STATUS_NOBINDING 3 /* Client record (binding) unavailable. */ #define DHCP6_STATUS_NOBINDING 3 /* Client record (binding) unavailable. */
#define DHCP6_STATUS_NOTONLINK 4 /* The prefix for the address is not appropriate for the link to which the client is attached. */ #define DHCP6_STATUS_NOTONLINK 4 /* The prefix for the address is not appropriate for the link to which the client is attached. */
#define DHCP6_STATUS_USEMULTICAST 5 /* Sent by a server to a client to force the client to send messages to the server using the All_DHCP_Relay_Agents_and_Servers address. */ #define DHCP6_STATUS_USEMULTICAST 5 /* Sent by a server to a client to force the client to send messages to the server using the All_DHCP_Relay_Agents_and_Servers address. */
/* More status codes see https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml */
/** DHCPv6 DUID types */ /** DHCPv6 DUID types */
#define DHCP6_DUID_LLT 1 /* LLT: Link-layer Address Plus Time */ #define DHCP6_DUID_LLT 1 /* LLT: Link-layer Address Plus Time */
#define DHCP6_DUID_EN 2 /* EN: Enterprise number */ #define DHCP6_DUID_EN 2 /* EN: Enterprise number */
#define DHCP6_DUID_LL 3 /* LL: Link-layer Address */ #define DHCP6_DUID_LL 3 /* LL: Link-layer Address */
#define DHCP6_DUID_UUID 4 /* UUID (RFC 6355) */
/* DHCPv6 options */ /* DHCPv6 options */
#define DHCP6_OPTION_CLIENTID 1 #define DHCP6_OPTION_CLIENTID 1
@ -126,7 +124,11 @@ typedef enum {
#define DHCP6_OPTION_VENDOR_OPTS 17 #define DHCP6_OPTION_VENDOR_OPTS 17
#define DHCP6_OPTION_INTERFACE_ID 18 #define DHCP6_OPTION_INTERFACE_ID 18
#define DHCP6_OPTION_RECONF_MSG 19 #define DHCP6_OPTION_RECONF_MSG 19
#define DHCP6_OPTION_ACCEPT 20 #define DHCP6_OPTION_RECONF_ACCEPT 20
/* More options see https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml */
#define DHCP6_OPTION_DNS_SERVERS 23 /* RFC 3646 */
#define DHCP6_OPTION_DOMAIN_LIST 24 /* RFC 3646 */
#define DHCP6_OPTION_SNTP_SERVERS 31 /* RFC 4075 */
#ifdef __cplusplus #ifdef __cplusplus