1
0
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:
Martine Lenders 2021-08-11 20:50:01 +02:00 committed by GitHub
commit f2f6700865
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 551 additions and 29 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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.
*

View File

@ -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
/**

View File

@ -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

View File

@ -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]

View File

@ -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)
{

View File

@ -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)

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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 } ] }
]
},

View File

@ -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

View File

@ -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__":