net: dhcpv6: Add Zephyr DHCPv6 client

Add a DHCPv6 client implementation for Zephyr (RFC 8415).

The implementation allows to request IPv6 address and/or prefix from the
DHCPv6 server, and for now supports only the mandatory set of DHCPv6
options needed to achieve this. Currently the implementation supports
the following scenarios:
 * Requesting new IPv6 address/prefix with Solicit/Request exchange
 * Refreshing existing leases with Confirm, Renew or Rebind (depending
   on the context).

For now, no Information Request (the case where neither IPv6 address or
prefix are requested) is supported. No support for Reconfigure was added
either, as this is optional (the client manifests clearly to the server
that it does not support Reconfigure). Support for these can be added
later if needed.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2023-08-23 13:31:48 +02:00 committed by Carles Cufí
parent 1d29a8c3c2
commit e73e78a550
7 changed files with 2599 additions and 0 deletions

116
include/zephyr/net/dhcpv6.h Normal file
View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief DHCPv6 client
*/
#ifndef ZEPHYR_INCLUDE_NET_DHCPV6_H_
#define ZEPHYR_INCLUDE_NET_DHCPV6_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief DHCPv6
* @defgroup dhcpv6 DHCPv6
* @ingroup networking
* @{
*/
/** @cond INTERNAL_HIDDEN */
/** Current state of DHCPv6 client address/prefix negotiation. */
enum net_dhcpv6_state {
NET_DHCPV6_DISABLED,
NET_DHCPV6_INIT,
NET_DHCPV6_SOLICITING,
NET_DHCPV6_REQUESTING,
NET_DHCPV6_CONFIRMING,
NET_DHCPV6_RENEWING,
NET_DHCPV6_REBINDING,
NET_DHCPV6_INFO_REQUESTING,
NET_DHCPV6_BOUND,
} __packed;
#define DHCPV6_TID_SIZE 3
#define DHCPV6_DUID_MAX_SIZE 20
struct net_dhcpv6_duid_raw {
uint16_t type;
uint8_t buf[DHCPV6_DUID_MAX_SIZE];
} __packed;
struct net_dhcpv6_duid_storage {
struct net_dhcpv6_duid_raw duid;
uint8_t length;
};
struct net_if;
/** @endcond */
/** @brief DHCPv6 client configuration parameters. */
struct net_dhcpv6_params {
bool request_addr : 1; /**< Request IPv6 address. */
bool request_prefix : 1; /**< Request IPv6 prefix. */
};
/**
* @brief Start DHCPv6 client on an iface
*
* @details Start DHCPv6 client on a given interface. DHCPv6 client will start
* negotiation for IPv6 address and/or prefix, depending on the configuration.
* Once the negotiation is complete, IPv6 address/prefix details will be added
* to the interface.
*
* @param iface A valid pointer to a network interface
* @param params DHCPv6 client configuration parameters.
*/
void net_dhcpv6_start(struct net_if *iface, struct net_dhcpv6_params *params);
/**
* @brief Stop DHCPv6 client on an iface
*
* @details Stop DHCPv6 client on a given interface. DHCPv6 client
* will remove all configuration obtained from a DHCP server from the
* interface and stop any further negotiation with the server.
*
* @param iface A valid pointer to a network interface
*/
void net_dhcpv6_stop(struct net_if *iface);
/**
* @brief Restart DHCPv6 client on an iface
*
* @details Restart DHCPv6 client on a given interface. DHCPv6 client
* will restart the state machine without any of the initial delays.
*
* @param iface A valid pointer to a network interface
*/
void net_dhcpv6_restart(struct net_if *iface);
/** @cond INTERNAL_HIDDEN */
/**
* @brief DHCPv6 state name
*
* @internal
*/
const char *net_dhcpv6_state_name(enum net_dhcpv6_state state);
/** @endcond */
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_NET_DHCPV6_H_ */

View file

@ -33,6 +33,9 @@
#if defined(CONFIG_NET_DHCPV4) && defined(CONFIG_NET_NATIVE_IPV4)
#include <zephyr/net/dhcpv4.h>
#endif
#if defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6)
#include <zephyr/net/dhcpv6.h>
#endif
#if defined(CONFIG_NET_IPV4_AUTO) && defined(CONFIG_NET_NATIVE_IPV4)
#include <zephyr/net/ipv4_autoconf.h>
#endif
@ -275,6 +278,69 @@ struct net_if_ipv6 {
uint8_t hop_limit;
};
#if defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6)
struct net_if_dhcpv6 {
/** Used for timer list. */
sys_snode_t node;
/** Generated Client ID. */
struct net_dhcpv6_duid_storage clientid;
/** Server ID of the selected server. */
struct net_dhcpv6_duid_storage serverid;
/** DHCPv6 client state. */
enum net_dhcpv6_state state;
/** DHCPv6 client configuration parameters. */
struct net_dhcpv6_params params;
/** Timeout for the next event, absolute time, milliseconds. */
uint64_t timeout;
/** Time of the current exchange start, absolute time, milliseconds */
uint64_t exchange_start;
/** Renewal time, absolute time, milliseconds. */
uint64_t t1;
/** Rebinding time, absolute time, milliseconds. */
uint64_t t2;
/** The time when the last lease expires (terminates rebinding,
* DHCPv6 RFC8415, ch. 18.2.5). Absolute time, milliseconds.
*/
uint64_t expire;
/** Generated IAID for IA_NA. */
uint32_t addr_iaid;
/** Generated IAID for IA_PD. */
uint32_t prefix_iaid;
/** Retransmit timeout for the current message, milliseconds. */
uint32_t retransmit_timeout;
/** Current best server preference received. */
int16_t server_preference;
/** Retransmission counter. */
uint8_t retransmissions;
/** Transaction ID for current exchange. */
uint8_t tid[DHCPV6_TID_SIZE];
/** Prefix length. */
uint8_t prefix_len;
/** Assigned IPv6 prefix. */
struct in6_addr prefix;
/** Assigned IPv6 address. */
struct in6_addr addr;
};
#endif /* defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6) */
/** @cond INTERNAL_HIDDEN */
#if defined(CONFIG_NET_NATIVE_IPV4)
#define NET_IF_MAX_IPV4_ADDR CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT
@ -413,6 +479,10 @@ struct net_if_config {
struct net_if_dhcpv4 dhcpv4;
#endif /* CONFIG_NET_DHCPV4 */
#if defined(CONFIG_NET_DHCPV6) && defined(CONFIG_NET_NATIVE_IPV6)
struct net_if_dhcpv6 dhcpv6;
#endif /* CONFIG_NET_DHCPV6 */
#if defined(CONFIG_NET_IPV4_AUTO) && defined(CONFIG_NET_NATIVE_IPV4)
struct net_if_ipv4_autoconf ipv4auto;
#endif /* CONFIG_NET_IPV4_AUTO */

View file

@ -31,6 +31,7 @@ zephyr_library_sources(net_tc.c)
zephyr_library_sources_ifdef(CONFIG_NET_IP connection.c)
zephyr_library_sources_ifdef(CONFIG_NET_6LO 6lo.c)
zephyr_library_sources_ifdef(CONFIG_NET_DHCPV4 dhcpv4.c)
zephyr_library_sources_ifdef(CONFIG_NET_DHCPV6 dhcpv6.c)
zephyr_library_sources_ifdef(CONFIG_NET_IPV4_AUTO ipv4_autoconf.c)
zephyr_library_sources_ifdef(CONFIG_NET_IPV4 icmpv4.c ipv4.c)
zephyr_library_sources_ifdef(CONFIG_NET_IPV4_IGMP igmp.c)

View file

@ -166,6 +166,12 @@ config NET_MAX_6LO_CONTEXTS
6lowpan context options table size. The value depends on your
network and memory consumption. More 6CO options uses more memory.
config NET_DHCPV6
bool "DHCPv6 client"
select NET_MGMT
select NET_MGMT_EVENT
depends on NET_UDP
if NET_6LO
module = NET_6LO
module-dep = NET_LOG
@ -192,5 +198,13 @@ module-str = Log level for IPv6 neighbor cache
module-help = Enables IPv6 Neighbor Cache code to output debug messages.
source "subsys/net/Kconfig.template.log_config.net"
if NET_DHCPV6
module = NET_DHCPV6
module-dep = NET_LOG
module-str = Log level for DHCPv6 client
module-help = Enables DHCPv6 client code to output debug messages.
source "subsys/net/Kconfig.template.log_config.net"
endif # NET_DHCPV6
endif # NET_NATIVE_IPV6
endif # NET_IPV6

2196
subsys/net/ip/dhcpv6.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,196 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief DHCPv6 internal header
*
* This header should not be included by the application.
*/
#ifndef DHCPV6_INTERNAL_H_
#define DHCPV6_INTERNAL_H_
#include <zephyr/net/dhcpv6.h>
#define DHCPV6_DUID_TYPE_SIZE 2
#define DHVPV6_DUID_LL_HW_TYPE_SIZE 2
#define DHCPV6_DUID_LL_HEADER_SIZE (DHCPV6_DUID_TYPE_SIZE + \
DHVPV6_DUID_LL_HW_TYPE_SIZE)
#define DHCPV6_MSG_TYPE_SIZE 1
#define DHCPV6_HEADER_SIZE (DHCPV6_MSG_TYPE_SIZE + DHCPV6_TID_SIZE)
#define DHCPV6_OPTION_CODE_SIZE 2
#define DHCPV6_OPTION_LENGTH_SIZE 2
#define DHCPV6_OPTION_HEADER_SIZE (DHCPV6_OPTION_CODE_SIZE + \
DHCPV6_OPTION_LENGTH_SIZE)
#define DHCPV6_OPTION_PREFERENCE_SIZE 1
#define DHCPV6_OPTION_ELAPSED_TIME_SIZE 2
#define DHCPV6_OPTION_ELAPSED_TIME_SIZE 2
#define DHCPV6_OPTION_IA_NA_HEADER_SIZE 12
#define DHCPV6_OPTION_IAADDR_HEADER_SIZE 24
#define DHCPV6_OPTION_IA_PD_HEADER_SIZE 12
#define DHCPV6_OPTION_IAPREFIX_HEADER_SIZE 25
#define DHCPV6_OPTION_IAADDR_HEADER_SIZE 24
#define DHCPV6_OPTION_IAPREFIX_HEADER_SIZE 25
#define DHCPV6_OPTION_STATUS_CODE_HEADER_SIZE 2
#define DHCPV6_INFINITY UINT32_MAX
#define DHCPV6_MAX_SERVER_PREFERENCE 255
#define DHCPV6_HARDWARE_ETHERNET_TYPE 1
#define DHCPV6_CLIENT_PORT 546
#define DHCPV6_SERVER_PORT 547
/* DHCPv6 Transmission/retransmission timeouts */
#define DHCPV6_SOL_MAX_DELAY 1000 /* Max delay of first Solicit, milliseconds */
#define DHCPV6_SOL_TIMEOUT 1000 /* Initial Solicit timeout, milliseconds */
#define DHCPV6_SOL_MAX_RT 3600000 /* Max Solicit timeout value, milliseconds */
#define DHCPV6_REQ_TIMEOUT 1000 /* Initial Request timeout, milliseconds */
#define DHCPV6_REQ_MAX_RT 30000 /* Max Request timeout value, milliseconds */
#define DHCPV6_REQ_MAX_RC 10 /* Max Request retry attempts */
#define DHCPV6_CNF_MAX_DELAY 1000 /* Max delay of first Confirm, milliseconds */
#define DHCPV6_CNF_TIMEOUT 1000 /* Initial Confirm timeout, milliseconds */
#define DHCPV6_CNF_MAX_RT 4000 /* Max Confirm timeout, milliseconds */
#define DHCPV6_CNF_MAX_RD 10000 /* Max Confirm duration, milliseconds */
#define DHCPV6_REN_TIMEOUT 10000 /* Initial Renew timeout, milliseconds */
#define DHCPV6_REN_MAX_RT 600000 /* Max Renew timeout value, milliseconds */
#define DHCPV6_REB_TIMEOUT 10000 /* Initial Rebind timeout, milliseconds */
#define DHCPV6_REB_MAX_RT 600000 /* Max Rebind timeout value, milliseconds */
/* DUID structures */
struct dhcpv6_duid_llt {
uint16_t hw_type;
uint32_t time;
uint8_t ll_addr[];
} __packed;
struct dhcpv6_duid_en {
uint32_t enterprise_number;
uint8_t identifier[];
} __packed;
struct dhcpv6_duid_ll {
uint16_t hw_type;
uint8_t ll_addr[];
} __packed;
struct dhcpv6_duid_uuid {
uint8_t uuid[16];
} __packed;
struct dhcpv6_msg_hdr {
uint8_t type; /* Message type */
uint8_t tid[3]; /* Transaction ID */
} __packed;
struct dhcpv6_iaaddr {
uint32_t preferred_lifetime;
uint32_t valid_lifetime;
struct in6_addr addr;
uint16_t status;
};
struct dhcpv6_ia_na {
uint32_t iaid;
uint32_t t1;
uint32_t t2;
uint16_t status;
struct dhcpv6_iaaddr iaaddr;
};
struct dhcpv6_iaprefix {
uint32_t preferred_lifetime;
uint32_t valid_lifetime;
struct in6_addr prefix;
uint8_t prefix_len;
uint16_t status;
};
struct dhcpv6_ia_pd {
uint32_t iaid;
uint32_t t1;
uint32_t t2;
uint16_t status;
struct dhcpv6_iaprefix iaprefix;
};
/* DHCPv6 message types, RFC8415, ch. 7.3. */
enum dhcpv6_msg_type {
DHCPV6_MSG_TYPE_SOLICIT = 1,
DHCPV6_MSG_TYPE_ADVERTISE = 2,
DHCPV6_MSG_TYPE_REQUEST = 3,
DHCPV6_MSG_TYPE_CONFIRM = 4,
DHCPV6_MSG_TYPE_RENEW = 5,
DHCPV6_MSG_TYPE_REBIND = 6,
DHCPV6_MSG_TYPE_REPLY = 7,
DHCPV6_MSG_TYPE_RELEASE = 8,
DHCPV6_MSG_TYPE_DECLINE = 9,
DHCPV6_MSG_TYPE_RECONFIGURE = 10,
DHCPV6_MSG_TYPE_INFORMATION_REQUEST = 11,
DHCPV6_MSG_TYPE_RELAY_FORW = 12,
DHCPV6_MSG_TYPE_RELAY_REPL = 13,
};
/* DHCPv6 option codes, RFC8415, ch. 21. */
enum dhcpv6_option_code {
DHCPV6_OPTION_CODE_CLIENTID = 1,
DHCPV6_OPTION_CODE_SERVERID = 2,
DHCPV6_OPTION_CODE_IA_NA = 3,
DHCPV6_OPTION_CODE_IA_TA = 4,
DHCPV6_OPTION_CODE_IAADDR = 5,
DHCPV6_OPTION_CODE_ORO = 6,
DHCPV6_OPTION_CODE_PREFERENCE = 7,
DHCPV6_OPTION_CODE_ELAPSED_TIME = 8,
DHCPV6_OPTION_CODE_RELAY_MSG = 9,
DHCPV6_OPTION_CODE_AUTH = 11,
DHCPV6_OPTION_CODE_UNICAST = 12,
DHCPV6_OPTION_CODE_STATUS_CODE = 13,
DHCPV6_OPTION_CODE_RAPID_COMMIT = 14,
DHCPV6_OPTION_CODE_USER_CLASS = 15,
DHCPV6_OPTION_CODE_VENDOR_CLASS = 16,
DHCPV6_OPTION_CODE_VENDOR_OPTS = 17,
DHCPV6_OPTION_CODE_INTERFACE_ID = 18,
DHCPV6_OPTION_CODE_RECONF_MSG = 19,
DHCPV6_OPTION_CODE_RECONF_ACCEPT = 20,
DHCPV6_OPTION_CODE_IA_PD = 25,
DHCPV6_OPTION_CODE_IAPREFIX = 26,
DHCPV6_OPTION_CODE_INFORMATION_REFRESH_TIME = 32,
DHCPV6_OPTION_CODE_SOL_MAX_RT = 82,
DHCPV6_OPTION_CODE_INF_MAX_RT = 83,
};
/* DHCPv6 option codes, RFC8415, ch. 21.13. */
enum dhcpv6_status_code {
DHCPV6_STATUS_SUCCESS = 0,
DHCPV6_STATUS_UNSPEC_FAIL = 1,
DHCPV6_STATUS_NO_ADDR_AVAIL = 2,
DHCPV6_STATUS_NO_BINDING = 3,
DHCPV6_STATUS_NOT_ON_LINK = 4,
DHCPV6_STATUS_USE_MULTICAST = 5,
DHCPV6_STATUS_NO_PREFIX_AVAIL = 6,
};
/* DHCPv6 Unique Identifier types, RFC8415, ch. 11.1. */
enum dhcpv6_duid_type {
DHCPV6_DUID_TYPE_LLT = 1, /* Based on Link-Layer Address Plus Time */
DHCPV6_DUID_TYPE_EN = 2, /* Assigned by Vendor Based on Enterprise Number */
DHCPV6_DUID_TYPE_LL = 3, /* Based on Link-Layer Address */
DHCPV6_DUID_TYPE_UUID = 4, /* Based on Universally Unique Identifier */
};
#if defined(CONFIG_NET_DHCPV6)
int net_dhcpv6_init(void);
#else
static inline int net_dhcpv6_init(void)
{
return 0;
}
#endif /* CONFIG_NET_DHCPV6 */
#endif /* DHCPV6_INTERNAL_H_ */

View file

@ -45,6 +45,7 @@ LOG_MODULE_REGISTER(net_core, CONFIG_NET_CORE_LOG_LEVEL);
#include "ipv4.h"
#include "dhcpv4.h"
#include "dhcpv6_internal.h"
#include "route.h"
@ -475,6 +476,11 @@ static inline int services_init(void)
return status;
}
status = net_dhcpv6_init();
if (status != 0) {
return status;
}
dns_init_resolver();
websocket_init();