mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #16228 from JKRhb/dhcp-ia-na
sys/net/dhcpv6: Add IA_NA support to the DHCPv6 client
This commit is contained in:
commit
f2f6700865
@ -26,6 +26,7 @@ PSEUDOMODULES += devfs_%
|
||||
PSEUDOMODULES += dhcpv6_%
|
||||
PSEUDOMODULES += dhcpv6_client_dns
|
||||
PSEUDOMODULES += dhcpv6_client_ia_pd
|
||||
PSEUDOMODULES += dhcpv6_client_ia_na
|
||||
PSEUDOMODULES += dhcpv6_client_mud_url
|
||||
PSEUDOMODULES += dhcpv6_relay
|
||||
PSEUDOMODULES += dns_msg
|
||||
|
@ -67,6 +67,9 @@ extern "C" {
|
||||
*/
|
||||
#define DHCPV6_OPT_CID (1U) /**< client identifier option */
|
||||
#define DHCPV6_OPT_SID (2U) /**< server identifier option */
|
||||
#define DHCPV6_OPT_IA_NA (3U) /**< identity association for
|
||||
non-temporary addresses option */
|
||||
#define DHCPV6_OPT_IAADDR (5U) /**< IA address option */
|
||||
#define DHCPV6_OPT_ORO (6U) /**< option request option */
|
||||
#define DHCPV6_OPT_PREF (7U) /**< preference option */
|
||||
#define DHCPV6_OPT_ELAPSED_TIME (8U) /**< elapsed time option */
|
||||
|
@ -59,6 +59,25 @@ extern "C" {
|
||||
#define CONFIG_DHCPV6_CLIENT_PFX_LEASE_MAX (1U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of address leases to be stored
|
||||
*/
|
||||
#ifndef CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX
|
||||
#define CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX (1U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of addresses needed for using DHCPv6 IA_NA.
|
||||
*
|
||||
* @note Used for calculation of @ref CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF.
|
||||
* Set to 0 if `dhcpv6_client_ia_na` is not included.
|
||||
*/
|
||||
#if defined(MODULE_DHCPV6_CLIENT_IA_NA) || defined(DOXYGEN)
|
||||
#define DHCPV6_CLIENT_ADDRS_NUMOF ((int)(CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX))
|
||||
#else
|
||||
#define DHCPV6_CLIENT_ADDRS_NUMOF (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MUD URL (must use the https:// scheme)
|
||||
* For more info, see the [definitions](@ref net_dhcpv6_mud_url_option) below
|
||||
@ -139,6 +158,21 @@ void dhcpv6_client_start(void);
|
||||
void dhcpv6_client_req_ia_pd(unsigned netif, unsigned pfx_len);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Configures the client to request non-temporary addresses for a network
|
||||
* interface from a server
|
||||
* @note For multi-hop WPAN meshes a DHCPv6 relay (which is not implemented in
|
||||
* RIOT yet) is required, as DHCPv6 only acts in link scope.
|
||||
*
|
||||
* @param[in] netif The interface to request non-temporaty addresses for.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -ENOMEM when there is no lease entry available anymore
|
||||
* @retval -ENOTSUP when module `dhcpv6_client_ia_na` is not being used
|
||||
*/
|
||||
int dhcpv6_client_req_ia_na(unsigned netif);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Stack-specific functions
|
||||
*
|
||||
@ -170,6 +204,43 @@ void dhcpv6_client_conf_prefix(unsigned netif, const ipv6_addr_t *pfx,
|
||||
unsigned pfx_len, uint32_t valid,
|
||||
uint32_t pref);
|
||||
|
||||
/**
|
||||
* @brief Checks if the given network interface is configured
|
||||
* to use DHCPv6 IA_NA
|
||||
*
|
||||
* @param[in] netif Network interface to check.
|
||||
*
|
||||
* @return true, if the network interface is set up for IA_NA.
|
||||
*/
|
||||
bool dhcpv6_client_check_ia_na(unsigned netif);
|
||||
|
||||
/**
|
||||
* @brief Configures a address lease that is provided by the server.
|
||||
*
|
||||
* @param[in] netif Network interface the address was for.
|
||||
* @param[in] addr The assigned address.
|
||||
*
|
||||
* @return sizeof(ipv6_addr_t) on success.
|
||||
* @return <0 on error.
|
||||
*/
|
||||
int dhcpv6_client_add_addr(unsigned netif, ipv6_addr_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Deprecates an existing address from an address lease.
|
||||
*
|
||||
* @param[in] netif Network interface the address was for.
|
||||
* @param[in] addr The address to deprecate.
|
||||
*/
|
||||
void dhcpv6_client_deprecate_addr(unsigned netif, const ipv6_addr_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Removes an existing address that originated from an address lease.
|
||||
*
|
||||
* @param[in] netif Network interface the address was for.
|
||||
* @param[in] addr The address to remove.
|
||||
*/
|
||||
void dhcpv6_client_remove_addr(unsigned netif, ipv6_addr_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Determines how long the prefix delegation lease is still valid.
|
||||
*
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <kernel_defines.h>
|
||||
|
||||
#include "net/dhcpv6/client.h"
|
||||
#include "net/ieee802154.h"
|
||||
#include "net/ethernet/hdr.h"
|
||||
#include "net/gnrc/ipv6/nib/conf.h"
|
||||
@ -108,10 +109,12 @@ extern "C" {
|
||||
* @ref GNRC_NETIF_IPV6_GROUPS_NUMOF is also large enough to fit the
|
||||
* addresses' solicited nodes multicast addresses.
|
||||
*
|
||||
* Default: 2 (1 link-local + 1 global address)
|
||||
* Default: 2 (1 link-local + 1 global address) + any additional address via
|
||||
* configuration protocol (e.g. DHCPv6 leases).
|
||||
*/
|
||||
#ifndef CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF
|
||||
#define CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF (2)
|
||||
#define CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF (2 + \
|
||||
DHCPV6_CLIENT_ADDRS_NUMOF)
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,10 @@ menuconfig KCONFIG_USEMODULE_DHCPV6_CLIENT
|
||||
|
||||
if KCONFIG_USEMODULE_DHCPV6_CLIENT
|
||||
|
||||
config DHCPV6_CLIENT_ADDR_LEASE_MAX
|
||||
int "Maximum number of leases to be stored"
|
||||
default 1
|
||||
|
||||
config DHCPV6_CLIENT_PFX_LEASE_MAX
|
||||
int "Maximum number of prefix leases to be stored"
|
||||
default 1
|
||||
|
@ -143,6 +143,35 @@ typedef struct __attribute__((packed)) {
|
||||
uint8_t duid[]; /**< the DUID of the client or server */
|
||||
} dhcpv6_opt_duid_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 identity association for non-temporary addresses (IA_NA) option
|
||||
* format
|
||||
* @see [RFC 8415, section 21.4]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.4)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_IA_NA */
|
||||
network_uint16_t len; /**< 12 + length of dhcpv6_opt_ia_na_t::opts in byte */
|
||||
network_uint32_t ia_id; /**< Unique ID for this IA_NA */
|
||||
network_uint32_t t1; /**< DHCPv6 T1 time (in sec) */
|
||||
network_uint32_t t2; /**< DHCPv6 T2 time (in sec) */
|
||||
uint8_t opts[]; /**< IA_NA options */
|
||||
} dhcpv6_opt_ia_na_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 IA address option format
|
||||
* @see [RFC 8415, section 21.6]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.6)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_IAADDR */
|
||||
network_uint16_t len; /**< 25 + length of dhcpv6_opt_iapfx_t::opts in byte */
|
||||
ipv6_addr_t addr; /**< the address */
|
||||
network_uint32_t pref; /**< preferred lifetime (in sec) */
|
||||
network_uint32_t valid; /**< valid lifetime (in sec) */
|
||||
uint8_t opts[]; /**< IAprefix options */
|
||||
} dhcpv6_opt_iaaddr_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 option request option format
|
||||
* @see [RFC 8415, section 21.7]
|
||||
|
@ -61,6 +61,18 @@ typedef struct {
|
||||
uint8_t leased;
|
||||
} pfx_lease_t;
|
||||
|
||||
/**
|
||||
* @brief Representation of a DHCPv6 address lease
|
||||
* @extends lease_t
|
||||
*/
|
||||
typedef struct {
|
||||
lease_t parent;
|
||||
ipv6_addr_t addr;
|
||||
uint8_t leased;
|
||||
uint32_t valid_until;
|
||||
uint32_t pref_until;
|
||||
} addr_lease_t;
|
||||
|
||||
/**
|
||||
* @brief Client representation of a DHCPv6 server
|
||||
*/
|
||||
@ -75,9 +87,11 @@ static uint8_t send_buf[DHCPV6_CLIENT_SEND_BUFLEN];
|
||||
static uint8_t recv_buf[DHCPV6_CLIENT_BUFLEN];
|
||||
static uint8_t best_adv[DHCPV6_CLIENT_BUFLEN];
|
||||
static uint8_t duid[DHCPV6_CLIENT_DUID_LEN];
|
||||
static addr_lease_t addr_leases[CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX];
|
||||
static pfx_lease_t pfx_leases[CONFIG_DHCPV6_CLIENT_PFX_LEASE_MAX];
|
||||
static server_t server;
|
||||
static event_timeout_t solicit_renew_timeout, rebind_timeout;
|
||||
static event_timeout_t solicit_renew_timeout, rebind_timeout,
|
||||
deprecate_remove_timeout;
|
||||
static event_queue_t *event_queue;
|
||||
static sock_udp_t sock;
|
||||
static sock_udp_ep_t local = { .family = AF_INET6, .port = DHCPV6_CLIENT_PORT };
|
||||
@ -98,6 +112,7 @@ static void _solicit_servers(event_t *event);
|
||||
static void _request(event_t *event);
|
||||
static void _renew(event_t *event);
|
||||
static void _rebind(event_t *event);
|
||||
static void _deprecate_remove_addrs(event_t *event);
|
||||
|
||||
static void _set_event_timeout_ms(event_timeout_t *timeout, event_t *event,
|
||||
uint32_t delay_ms);
|
||||
@ -109,6 +124,7 @@ static event_t solicit_servers = { .handler = _solicit_servers };
|
||||
static event_t request = { .handler = _request };
|
||||
static event_t renew = { .handler = _renew };
|
||||
static event_t rebind = { .handler = _rebind };
|
||||
static event_t deprecate_remove_addrs = { .handler = _deprecate_remove_addrs };
|
||||
|
||||
#ifdef MODULE_AUTO_INIT_DHCPV6_CLIENT
|
||||
static char _thread_stack[DHCPV6_CLIENT_STACK_SIZE];
|
||||
@ -131,13 +147,51 @@ static void *_thread(void *args)
|
||||
event_queue_t event_queue;
|
||||
event_queue_init(&event_queue);
|
||||
dhcpv6_client_init(&event_queue, SOCK_ADDR_ANY_NETIF);
|
||||
/* TODO: add configuration for IA_NA here, when implemented */
|
||||
dhcpv6_client_start();
|
||||
event_loop(&event_queue); /* never returns */
|
||||
return NULL;
|
||||
}
|
||||
#endif /* MODULE_AUTO_INIT_DHCPV6_CLIENT */
|
||||
|
||||
void _print_ia_na_debug_info(uint16_t netif, int result_code)
|
||||
{
|
||||
if (result_code == 0) {
|
||||
return;
|
||||
} else {
|
||||
DEBUG("DHCPv6 client: No free address lease available to configure "
|
||||
"IA_NA for network interface %i\n", netif);
|
||||
}
|
||||
}
|
||||
|
||||
void _initialize_ia_na(uint16_t netif)
|
||||
{
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int res;
|
||||
|
||||
/* If no specific interface ID is given, check all
|
||||
interfaces if DHCP IA_NA is enabled. Otherwise
|
||||
use the specific interface ID. */
|
||||
if (netif == SOCK_ADDR_ANY_NETIF) {
|
||||
netif_t* netif = NULL;
|
||||
while ((netif = netif_iter(netif))) {
|
||||
int16_t netif_id = netif_get_id(netif);
|
||||
if (netif_id < 0) {
|
||||
continue;
|
||||
}
|
||||
if (dhcpv6_client_check_ia_na(netif_id)) {
|
||||
res = dhcpv6_client_req_ia_na(netif_id);
|
||||
_print_ia_na_debug_info(netif_id, res);
|
||||
}
|
||||
}
|
||||
} else if (dhcpv6_client_check_ia_na(netif)) {
|
||||
res = dhcpv6_client_req_ia_na(netif);
|
||||
_print_ia_na_debug_info(netif, res);
|
||||
}
|
||||
}
|
||||
|
||||
void dhcpv6_client_init(event_queue_t *eq, uint16_t netif)
|
||||
{
|
||||
assert(eq->waiter != NULL);
|
||||
@ -145,6 +199,9 @@ void dhcpv6_client_init(event_queue_t *eq, uint16_t netif)
|
||||
assert(strlen(mud_url) <= MAX_MUD_URL_LENGTH);
|
||||
assert(strncmp(mud_url, "https://", 8) == 0);
|
||||
}
|
||||
|
||||
_initialize_ia_na(netif);
|
||||
|
||||
event_queue = eq;
|
||||
local.netif = netif;
|
||||
remote.netif = netif;
|
||||
@ -185,6 +242,29 @@ void dhcpv6_client_req_ia_pd(unsigned netif, unsigned pfx_len)
|
||||
}
|
||||
}
|
||||
|
||||
int dhcpv6_client_req_ia_na(unsigned netif)
|
||||
{
|
||||
assert(IS_USED(MODULE_DHCPV6_CLIENT_IA_NA));
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
LOG_WARNING("DHCPv6 client: Unable to request IA_NA as module "
|
||||
"`dhcpv6_client_ia_na` is not used\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
addr_lease_t *lease = NULL;
|
||||
|
||||
for (unsigned i = 0; i < CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX; i++) {
|
||||
if (addr_leases[i].parent.ia_id.id == 0) {
|
||||
lease = &addr_leases[i];
|
||||
lease->parent.ia_id.info.netif = netif;
|
||||
lease->parent.ia_id.info.type = DHCPV6_OPT_IA_NA;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void _post_solicit_servers(void)
|
||||
{
|
||||
event_post(event_queue, &solicit_servers);
|
||||
@ -316,6 +396,48 @@ static inline size_t _compose_ia_pd_opt(dhcpv6_opt_ia_pd_t *ia_pd,
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline size_t _compose_ia_na_opt(dhcpv6_opt_ia_na_t *ia_na,
|
||||
uint32_t ia_id, uint16_t opts_len)
|
||||
{
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t len = 12U + opts_len;
|
||||
|
||||
ia_na->type = byteorder_htons(DHCPV6_OPT_IA_NA);
|
||||
ia_na->len = byteorder_htons(len);
|
||||
ia_na->ia_id = byteorder_htonl(ia_id);
|
||||
ia_na->t1.u32 = 0;
|
||||
ia_na->t2.u32 = 0;
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline size_t _add_ia_na(uint8_t *buf, size_t len_max)
|
||||
{
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t msg_len = 0;
|
||||
|
||||
for (unsigned i = 0; i < CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX; i++) {
|
||||
uint32_t ia_id = addr_leases[i].parent.ia_id.id;
|
||||
if (ia_id != 0) {
|
||||
dhcpv6_opt_ia_na_t *ia_na = (dhcpv6_opt_ia_na_t *)(&buf[msg_len]);
|
||||
|
||||
msg_len += _compose_ia_na_opt(ia_na, ia_id, 0U);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_len > len_max) {
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
static inline size_t _add_ia_pd_from_config(uint8_t *buf, size_t len_max)
|
||||
{
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_PD)) {
|
||||
@ -441,6 +563,7 @@ static int _preparse_advertise(uint8_t *adv, size_t len, uint8_t **buf)
|
||||
dhcpv6_opt_pref_t *pref = NULL;
|
||||
dhcpv6_opt_status_t *status = NULL;
|
||||
dhcpv6_opt_ia_pd_t *ia_pd = NULL;
|
||||
dhcpv6_opt_ia_na_t *ia_na = NULL;
|
||||
size_t orig_len = len;
|
||||
uint8_t pref_val = 0;
|
||||
|
||||
@ -471,6 +594,11 @@ static int _preparse_advertise(uint8_t *adv, size_t len, uint8_t **buf)
|
||||
ia_pd = (dhcpv6_opt_ia_pd_t *)opt;
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_IA_NA:
|
||||
if (IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
ia_na = (dhcpv6_opt_ia_na_t *)opt;
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_PREF:
|
||||
pref = (dhcpv6_opt_pref_t *)opt;
|
||||
break;
|
||||
@ -479,9 +607,10 @@ static int _preparse_advertise(uint8_t *adv, size_t len, uint8_t **buf)
|
||||
}
|
||||
}
|
||||
if ((cid == NULL) || (sid == NULL) ||
|
||||
(IS_USED(MODULE_DHCPV6_CLIENT_IA_PD) && (ia_pd == NULL))) {
|
||||
(IS_USED(MODULE_DHCPV6_CLIENT_IA_PD) && (ia_pd == NULL)) ||
|
||||
(IS_USED(MODULE_DHCPV6_CLIENT_IA_NA) && (ia_na == NULL))) {
|
||||
DEBUG("DHCPv6 client: ADVERTISE does not contain either server ID, "
|
||||
"client ID or IA_PD option\n");
|
||||
"client ID, IA_PD or IA_NA option\n");
|
||||
return -1;
|
||||
}
|
||||
if (!_check_status_opt(status) || !_check_cid_opt(cid)) {
|
||||
@ -524,6 +653,46 @@ static void _schedule_t1_t2(void)
|
||||
_schedule_t2();
|
||||
}
|
||||
|
||||
static void _update_t1_t2(unsigned lease_t1, unsigned lease_t2)
|
||||
{
|
||||
if ((lease_t1 != 0) && (lease_t2 != 0) &&
|
||||
((server.t1 == 0) || (server.t1 >= lease_t1)) &&
|
||||
((t2 == 0) || (t2 >= lease_t2))) {
|
||||
server.t1 = lease_t1;
|
||||
t2 = lease_t2;
|
||||
_schedule_t1_t2();
|
||||
}
|
||||
}
|
||||
|
||||
static void _update_t2(unsigned lease_t1, unsigned lease_t2)
|
||||
{
|
||||
if ((lease_t1 != 0) && (lease_t2 != 0) &&
|
||||
(server.t1 > lease_t1) && (t2 > lease_t2)) {
|
||||
server.t1 = lease_t1;
|
||||
t2 = lease_t2;
|
||||
_schedule_t2();
|
||||
}
|
||||
}
|
||||
|
||||
static void _update_addr_lease(const dhcpv6_opt_iaaddr_t *iaaddr, addr_lease_t *lease) {
|
||||
if (iaaddr != NULL) {
|
||||
uint32_t valid = byteorder_ntohl(iaaddr->valid);
|
||||
uint32_t pref = byteorder_ntohl(iaaddr->pref);
|
||||
|
||||
lease->leased = 1U;
|
||||
memcpy(&lease->addr, &iaaddr->addr, sizeof(ipv6_addr_t));
|
||||
if (dhcpv6_client_add_addr(lease->parent.ia_id.info.netif,
|
||||
&lease->addr) == sizeof(ipv6_addr_t)) {
|
||||
DEBUG("IP ADDRESS successfully added!\n");
|
||||
lease->pref_until = pref;
|
||||
lease->valid_until = valid;
|
||||
|
||||
_set_event_timeout_sec(&deprecate_remove_timeout, &deprecate_remove_addrs,
|
||||
pref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _parse_advertise(uint8_t *adv, size_t len)
|
||||
{
|
||||
dhcpv6_opt_smr_t *smr = NULL;
|
||||
@ -579,12 +748,49 @@ static void _parse_advertise(uint8_t *adv, size_t len)
|
||||
}
|
||||
pd_t1 = byteorder_ntohl(ia_pd->t1);
|
||||
pd_t2 = byteorder_ntohl(ia_pd->t2);
|
||||
if ((pd_t1 != 0) && (pd_t2 != 0) &&
|
||||
(server.t1 > pd_t1) && (t2 > pd_t2)) {
|
||||
server.t1 = pd_t1;
|
||||
t2 = pd_t2;
|
||||
_schedule_t2();
|
||||
_update_t2(pd_t1, pd_t2);
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_IA_NA:
|
||||
for (unsigned i = 0;
|
||||
IS_USED(MODULE_DHCPV6_CLIENT_IA_NA) &&
|
||||
i < CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX;
|
||||
i++) {
|
||||
dhcpv6_opt_ia_na_t *ia_na = (dhcpv6_opt_ia_na_t *)opt;
|
||||
unsigned na_t1, na_t2;
|
||||
uint32_t ia_id = byteorder_ntohl(ia_na->ia_id);
|
||||
size_t ia_na_len = byteorder_ntohs(ia_na->len) -
|
||||
(sizeof(dhcpv6_opt_ia_na_t) - sizeof(dhcpv6_opt_t));
|
||||
size_t ia_na_orig_len = ia_na_len;
|
||||
|
||||
if (addr_leases[i].parent.ia_id.id != ia_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check for status */
|
||||
for (dhcpv6_opt_t *ia_na_opt = (dhcpv6_opt_t *)(ia_na + 1);
|
||||
ia_na_len > 0;
|
||||
ia_na_len -= _opt_len(ia_na_opt),
|
||||
ia_na_opt = _opt_next(ia_na_opt)) {
|
||||
if (ia_na_len > ia_na_orig_len) {
|
||||
DEBUG("DHCPv6 client: IA_NA options overflow option "
|
||||
"boundaries\n");
|
||||
return;
|
||||
}
|
||||
switch (byteorder_ntohs(ia_na_opt->type)) {
|
||||
case DHCPV6_OPT_STATUS: {
|
||||
if (!_check_status_opt((dhcpv6_opt_status_t *)ia_na_opt)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
na_t1 = byteorder_ntohl(ia_na->t1);
|
||||
na_t2 = byteorder_ntohl(ia_na->t2);
|
||||
_update_t2(na_t1, na_t2);
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_SMR:
|
||||
@ -600,10 +806,68 @@ static void _parse_advertise(uint8_t *adv, size_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
static bool _parse_ia_na_option(dhcpv6_opt_ia_na_t *ia_na)
|
||||
{
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; (i < CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX); i++) {
|
||||
dhcpv6_opt_iaaddr_t *iaaddr = NULL;
|
||||
addr_lease_t *lease = &addr_leases[i];
|
||||
unsigned na_t1, na_t2;
|
||||
uint32_t ia_id = byteorder_ntohl(ia_na->ia_id);
|
||||
size_t ia_na_len = byteorder_ntohs(ia_na->len) -
|
||||
(sizeof(dhcpv6_opt_ia_na_t) - sizeof(dhcpv6_opt_t));
|
||||
size_t ia_na_orig_len = ia_na_len;
|
||||
|
||||
if (lease->parent.ia_id.id != ia_id) {
|
||||
continue;
|
||||
}
|
||||
/* check for status */
|
||||
for (dhcpv6_opt_t *ia_na_opt = (dhcpv6_opt_t *)(ia_na + 1);
|
||||
ia_na_len > 0;
|
||||
ia_na_len -= _opt_len(ia_na_opt),
|
||||
ia_na_opt = _opt_next(ia_na_opt)) {
|
||||
if (ia_na_len > ia_na_orig_len) {
|
||||
DEBUG("DHCPv6 client: IA_NA options overflow option "
|
||||
"boundaries\n");
|
||||
return false;
|
||||
}
|
||||
switch (byteorder_ntohs(ia_na_opt->type)) {
|
||||
case DHCPV6_OPT_STATUS: {
|
||||
if (!_check_status_opt((dhcpv6_opt_status_t *)ia_na_opt)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DHCPV6_OPT_IAADDR: {
|
||||
dhcpv6_opt_iaaddr_t *this_iaaddr = (dhcpv6_opt_iaaddr_t *)ia_na_opt;
|
||||
if ((!lease->leased) ||
|
||||
(iaaddr == NULL)) {
|
||||
/* only take first address for now */
|
||||
iaaddr = this_iaaddr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
na_t1 = byteorder_ntohl(ia_na->t1);
|
||||
na_t2 = byteorder_ntohl(ia_na->t2);
|
||||
_update_t1_t2(na_t1, na_t2);
|
||||
_update_addr_lease(iaaddr, lease);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _parse_reply(uint8_t *rep, size_t len)
|
||||
{
|
||||
dhcpv6_opt_duid_t *cid = NULL, *sid = NULL;
|
||||
dhcpv6_opt_ia_pd_t *ia_pd = NULL;
|
||||
dhcpv6_opt_ia_na_t *ia_na = NULL;
|
||||
dhcpv6_opt_status_t *status = NULL;
|
||||
dhcpv6_opt_smr_t *smr = NULL;
|
||||
size_t orig_len = len;
|
||||
@ -634,6 +898,9 @@ static bool _parse_reply(uint8_t *rep, size_t len)
|
||||
ia_pd = (dhcpv6_opt_ia_pd_t *)opt;
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_IA_NA:
|
||||
ia_na = (dhcpv6_opt_ia_na_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_SMR:
|
||||
smr = (dhcpv6_opt_smr_t *)opt;
|
||||
break;
|
||||
@ -642,9 +909,10 @@ static bool _parse_reply(uint8_t *rep, size_t len)
|
||||
}
|
||||
}
|
||||
if ((cid == NULL) || (sid == NULL) ||
|
||||
(IS_USED(MODULE_DHCPV6_CLIENT_IA_PD) && (ia_pd == NULL))) {
|
||||
(IS_USED(MODULE_DHCPV6_CLIENT_IA_PD) && (ia_pd == NULL)) ||
|
||||
(IS_USED(MODULE_DHCPV6_CLIENT_IA_NA) && (ia_na == NULL))) {
|
||||
DEBUG("DHCPv6 client: ADVERTISE does not contain either server ID, "
|
||||
"client ID or IA_PD option\n");
|
||||
"client ID, IA_PD or IA_NA option\n");
|
||||
return false;
|
||||
}
|
||||
if (!_check_cid_opt(cid) || !_check_sid_opt(sid)) {
|
||||
@ -718,13 +986,7 @@ static bool _parse_reply(uint8_t *rep, size_t len)
|
||||
}
|
||||
pd_t1 = byteorder_ntohl(ia_pd->t1);
|
||||
pd_t2 = byteorder_ntohl(ia_pd->t2);
|
||||
if ((pd_t1 != 0) && (pd_t2 != 0) &&
|
||||
((server.t1 == 0) || (server.t1 >= pd_t1)) &&
|
||||
((t2 == 0) || (t2 >= pd_t2))) {
|
||||
server.t1 = pd_t1;
|
||||
t2 = pd_t2;
|
||||
_schedule_t1_t2();
|
||||
}
|
||||
_update_t1_t2(pd_t1, pd_t2);
|
||||
if ((iapfx != NULL)) {
|
||||
uint32_t valid = byteorder_ntohl(iapfx->valid);
|
||||
uint32_t pref = byteorder_ntohl(iapfx->pref);
|
||||
@ -743,6 +1005,14 @@ static bool _parse_reply(uint8_t *rep, size_t len)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_IA_NA:
|
||||
if (_parse_ia_na_option((dhcpv6_opt_ia_na_t *)opt)) {
|
||||
/* No error occurred */
|
||||
break;
|
||||
} else {
|
||||
/* Something went wrong */
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -771,6 +1041,7 @@ static void _solicit_servers(event_t *event)
|
||||
msg_len += _compose_elapsed_time_opt(time);
|
||||
msg_len += _compose_oro_opt((dhcpv6_opt_oro_t *)&send_buf[msg_len], oro_opts,
|
||||
ARRAY_SIZE(oro_opts));
|
||||
msg_len += _add_ia_na(&send_buf[msg_len], sizeof(send_buf) - msg_len);
|
||||
msg_len += _add_ia_pd_from_config(&send_buf[msg_len], sizeof(send_buf) - msg_len);
|
||||
DEBUG("DHCPv6 client: send SOLICIT\n");
|
||||
_flush_stale_replies(&sock);
|
||||
@ -858,6 +1129,17 @@ static void _request_renew_rebind(uint8_t type)
|
||||
mrd = valid_until;
|
||||
}
|
||||
}
|
||||
/* calculate MRD from addr_leases */
|
||||
for (unsigned i = 0;
|
||||
IS_USED(MODULE_DHCPV6_CLIENT_IA_NA) &&
|
||||
(i < CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX);
|
||||
i++) {
|
||||
const addr_lease_t *lease = &addr_leases[i];
|
||||
uint32_t valid_until = lease->valid_until;
|
||||
if (valid_until > mrd) {
|
||||
mrd = valid_until;
|
||||
}
|
||||
}
|
||||
if (mrd == 0) {
|
||||
/* all leases already expired, don't try to rebind and
|
||||
* solicit immediately */
|
||||
@ -884,6 +1166,7 @@ static void _request_renew_rebind(uint8_t type)
|
||||
msg_len += _compose_elapsed_time_opt(time);
|
||||
msg_len += _compose_oro_opt((dhcpv6_opt_oro_t *)&send_buf[msg_len], oro_opts,
|
||||
ARRAY_SIZE(oro_opts));
|
||||
msg_len += _add_ia_na(&send_buf[msg_len], sizeof(send_buf) - msg_len);
|
||||
msg_len += _add_ia_pd_from_config(&send_buf[msg_len], sizeof(send_buf) - msg_len);
|
||||
_flush_stale_replies(&sock);
|
||||
while (sock_udp_send(&sock, send_buf, msg_len, &remote) <= 0) {}
|
||||
@ -935,6 +1218,33 @@ static void _rebind(event_t *event)
|
||||
_request_renew_rebind(DHCPV6_REBIND);
|
||||
}
|
||||
|
||||
static void _deprecate_remove_addrs(event_t *event)
|
||||
{
|
||||
if (!IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)event;
|
||||
|
||||
for (unsigned i = 0; (i < CONFIG_DHCPV6_CLIENT_ADDR_LEASE_MAX); i++) {
|
||||
uint32_t now = _now_sec();
|
||||
addr_lease_t *lease = &addr_leases[i];
|
||||
if (now >= lease->valid_until) {
|
||||
DEBUG("DHCPv6 client: removing address\n");
|
||||
dhcpv6_client_remove_addr(lease->parent.ia_id.info.netif,
|
||||
&lease->addr);
|
||||
lease->leased = 0U;
|
||||
} else if (now >= lease->pref_until) {
|
||||
DEBUG("DHCPv6 client: deprecating address\n");
|
||||
dhcpv6_client_deprecate_addr(lease->parent.ia_id.info.netif,
|
||||
&lease->addr);
|
||||
_set_event_timeout_sec(&deprecate_remove_timeout,
|
||||
&deprecate_remove_addrs,
|
||||
lease->valid_until);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _set_event_timeout_ms(event_timeout_t *timeout, event_t *event,
|
||||
uint32_t delay_ms)
|
||||
{
|
||||
|
@ -89,6 +89,43 @@ void dhcpv6_client_conf_prefix(unsigned iface, const ipv6_addr_t *pfx,
|
||||
}
|
||||
}
|
||||
|
||||
bool dhcpv6_client_check_ia_na(unsigned iface)
|
||||
{
|
||||
gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface);
|
||||
|
||||
return netif->ipv6.aac_mode & GNRC_NETIF_AAC_DHCP;
|
||||
}
|
||||
|
||||
int dhcpv6_client_add_addr(unsigned iface, ipv6_addr_t *addr)
|
||||
{
|
||||
gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface);
|
||||
|
||||
DEBUG("DHCPv6 client: ADD IP ADDRESS\n");
|
||||
|
||||
return gnrc_netif_ipv6_addr_add(netif, addr, 64, 0);
|
||||
}
|
||||
|
||||
void dhcpv6_client_deprecate_addr(unsigned iface, const ipv6_addr_t *addr)
|
||||
{
|
||||
gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface);
|
||||
int i;
|
||||
|
||||
gnrc_netif_acquire(netif);
|
||||
i = gnrc_netif_ipv6_addr_idx(netif, addr);
|
||||
if (i >= 0) {
|
||||
netif->ipv6.addrs_flags[i] &= ~GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_MASK;
|
||||
netif->ipv6.addrs_flags[i] |= GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_DEPRECATED;
|
||||
}
|
||||
gnrc_netif_release(netif);
|
||||
}
|
||||
|
||||
void dhcpv6_client_remove_addr(unsigned iface, ipv6_addr_t *addr)
|
||||
{
|
||||
gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface);
|
||||
|
||||
gnrc_netif_ipv6_addr_remove(netif, addr);
|
||||
}
|
||||
|
||||
uint32_t dhcpv6_client_prefix_valid_until(unsigned netif,
|
||||
const ipv6_addr_t *pfx,
|
||||
unsigned pfx_len)
|
||||
|
@ -22,6 +22,7 @@ config GNRC_NETIF_MSG_QUEUE_SIZE_EXP
|
||||
|
||||
config GNRC_NETIF_IPV6_ADDRS_NUMOF
|
||||
int "Maximum number of unicast and anycast addresses per interface"
|
||||
default 3 if DHCPV6_CLIENT_ADDR_LEASE_MAX != 0
|
||||
default 2
|
||||
help
|
||||
If you change this, please make sure that
|
||||
|
@ -162,10 +162,15 @@ void _remove_tentative_addr(gnrc_netif_t *netif, const ipv6_addr_t *addr)
|
||||
/* Cannot use target address as personal address and can
|
||||
* not change hardware address to retry SLAAC => use purely
|
||||
* DHCPv6 instead */
|
||||
/* TODO: implement IA_NA for DHCPv6 */
|
||||
/* then => tgt_netif->aac_mode |= GNRC_NETIF_AAC_DHCP; */
|
||||
DEBUG("nib: would set interface %i to DHCPv6, "
|
||||
"but is not implemented yet", netif->pid);
|
||||
if (IS_USED(MODULE_DHCPV6_CLIENT_IA_NA)) {
|
||||
netif->ipv6.aac_mode &= ~GNRC_NETIF_AAC_AUTO;
|
||||
netif->ipv6.aac_mode |= GNRC_NETIF_AAC_DHCP;
|
||||
dhcpv6_client_req_ia_na(netif->pid);
|
||||
}
|
||||
else {
|
||||
DEBUG("nib: would set interface %i to DHCPv6, "
|
||||
"but DHCPv6 is not provided", netif->pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ RIOTBASE ?= $(CURDIR)/../..
|
||||
BOARD_BLACKLIST += mips-malta pic32-wifire pic32-clicker ruuvitag thingy52
|
||||
|
||||
USEMODULE += dhcpv6_client_ia_pd
|
||||
USEMODULE += dhcpv6_client_ia_na
|
||||
USEMODULE += gnrc_dhcpv6_client
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
USEMODULE += xtimer
|
||||
|
@ -22,8 +22,9 @@
|
||||
"subnet6": [
|
||||
{ "interface": "tapbr0",
|
||||
"subnet": "2001:db8::/32",
|
||||
"pd-pools": [ { "prefix": "2001:db8::",
|
||||
"prefix-len": 32,
|
||||
"pools": [ { "pool": "2001:db8:1::/64" } ],
|
||||
"pd-pools": [ { "prefix": "2001:db8:8000::",
|
||||
"prefix-len": 33,
|
||||
"delegated-len": 64 } ] }
|
||||
]
|
||||
},
|
||||
|
@ -37,6 +37,8 @@ void *_dhcpv6_client_thread(void *args)
|
||||
(void)args;
|
||||
/* initialize client event queue */
|
||||
event_queue_init(&event_queue);
|
||||
/* Configure client to use DHCPv6 IA_NA */
|
||||
netif->ipv6.aac_mode |= GNRC_NETIF_AAC_DHCP;
|
||||
/* initialize DHCPv6 client on any interface */
|
||||
dhcpv6_client_init(&event_queue, SOCK_ADDR_ANY_NETIF);
|
||||
/* configure client to request prefix delegation of /64 subnet
|
||||
|
@ -8,6 +8,13 @@
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
from ipaddress import (
|
||||
IPv6Address,
|
||||
IPv6Network,
|
||||
)
|
||||
|
||||
|
||||
IA_NA_ADDRESS_POOL_PREFIX = "2001:db8:1::"
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
@ -15,17 +22,64 @@ def testfunc(child):
|
||||
child.expect(r"inet6 addr:\sfe80:[0-9a-f:]+\s+scope: link")
|
||||
child.expect(r"Iface\s+\d+")
|
||||
child.expect(r"inet6 addr:\s+fe80:[0-9a-f:]+\s+scope: link")
|
||||
child.expect(r"inet6 addr:\s+(?P<global_addr>[0-9a-f:]+)\s+scope: global")
|
||||
global_addr = child.match.group("global_addr")
|
||||
|
||||
global_addr_1, global_addr_2 = extract_global_addresses(child)
|
||||
|
||||
global_pfx = extract_global_prefix(child)
|
||||
|
||||
test_global_addrs(global_addr_1, global_addr_2, global_pfx)
|
||||
|
||||
|
||||
def extract_global_prefix(child):
|
||||
child.expect(r"(?P<global_pfx>[0-9a-f:]+)/64\s+dev #\d\s+"
|
||||
r"expires \d+ sec\s+"
|
||||
r"deprecates \d+ sec")
|
||||
global_pfx = child.match.group("global_pfx")
|
||||
|
||||
if global_pfx.endswith("::"):
|
||||
# remove one trailing : in case there are no 0s between prefix and
|
||||
# suffix
|
||||
global_pfx = global_pfx[0:-1]
|
||||
assert global_addr.startswith(global_pfx)
|
||||
|
||||
return global_pfx
|
||||
|
||||
|
||||
def extract_global_address(child):
|
||||
"""Expect and extract a global address from the command line."""
|
||||
child.expect(r"inet6 addr:\s+(?P<global_addr>[0-9a-f:]+)\s+scope: global")
|
||||
return child.match.group("global_addr")
|
||||
|
||||
|
||||
def extract_global_addresses(child):
|
||||
"""Extract two global addresses and return them as a tuple."""
|
||||
return extract_global_address(child), extract_global_address(child)
|
||||
|
||||
|
||||
def check_ia_na_addr(ia_na_addr):
|
||||
"""Check if the expected IA_NA address has been assigned"""
|
||||
return IPv6Address(ia_na_addr) in IPv6Network("{}/64".format(IA_NA_ADDRESS_POOL_PREFIX))
|
||||
|
||||
|
||||
def check_ia_pd_addr(ia_pd_addr, global_pfx):
|
||||
"""Check if the expected IA_PD address has been assigned"""
|
||||
return ia_pd_addr.startswith(global_pfx)
|
||||
|
||||
|
||||
def check_global_addrs(ia_na_addr, ia_pd_addr, global_pfx):
|
||||
"""Perform IA_NA check for the first and IA_PD for the second address"""
|
||||
return {
|
||||
"ia_na_check": check_ia_na_addr(ia_na_addr),
|
||||
"ia_pd_check": check_ia_pd_addr(ia_pd_addr, global_pfx),
|
||||
}
|
||||
|
||||
|
||||
def test_global_addrs(global_addr_1, global_addr_2, global_pfx):
|
||||
"""Assert that one global address is the IA_NA and the other one is the IA_PD address"""
|
||||
result_1 = check_global_addrs(global_addr_1, global_addr_2, global_pfx)
|
||||
result_2 = check_global_addrs(global_addr_2, global_addr_1, global_pfx)
|
||||
assert result_1 != result_2
|
||||
assert result_1["ia_na_check"] != result_2["ia_na_check"]
|
||||
assert result_1["ia_pd_check"] != result_2["ia_pd_check"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
Reference in New Issue
Block a user