Skip to content

Commit

Permalink
gnrc_ipv6_nib: add full RFC4862 DAD support
Browse files Browse the repository at this point in the history
Parts of [RFC4862] were already implemented when NDP via the NIB was
first implemented. This change just includes the DAD portion of
[RFC4862]. This should be enough to make RIOT fully RFC4862 compliant.

[RFC4862]: https://tools.ietf.org/html/rfc4862
  • Loading branch information
miri64 committed May 31, 2018
1 parent 4782877 commit 55adbee
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 76 deletions.
20 changes: 20 additions & 0 deletions sys/include/net/gnrc/ipv6/nib.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@ extern "C" {
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ROUTER != 0
*/
#define GNRC_IPV6_NIB_ROUTE_TIMEOUT (0x4fd0U)

/**
* @brief Perform DAD event.
*
* This message type is for performing DAD for a given address. The expected
* message context is a TENTATIVE IPv6 address.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_SLAAC != 0
*/
#define GNRC_IPV6_NIB_DAD (0x4fd1U)

/**
* @brief Validate a tentative address event.
*
* Moves a TENTATIVE address to VALID state. The expected message context is a
* TENTATIVE IPv6 address.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_SLAAC != 0
*/
#define GNRC_IPV6_NIB_VALID_ADDR (0x4fd2U)
/** @} */

/**
Expand Down
5 changes: 3 additions & 2 deletions sys/include/net/gnrc/netif/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,14 @@ typedef struct {
* and @ref net_gnrc_ipv6_nib "NIB"
*/
evtimer_msg_event_t search_rtr;
#if GNRC_IPV6_NIB_CONF_6LN || DOXYGEN
#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC || DOXYGEN
/**
* @brief Timers for address re-registration
*
* @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6" and
* @ref net_gnrc_ipv6_nib "NIB" and if
* @ref GNRC_IPV6_NIB_CONF_6LN != 0
* @ref GNRC_IPV6_NIB_CONF_6LN != 0 or
* @ref GNRC_IPV6_NIB_CONF_SLAAC != 0
* @note Might also be usable in the later default SLAAC implementation
* for NS retransmission timers.
*/
Expand Down
13 changes: 9 additions & 4 deletions sys/net/gnrc/netif/gnrc_netif.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "net/gnrc.h"
#ifdef MODULE_GNRC_IPV6_NIB
#include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/ipv6.h"
#endif /* MODULE_GNRC_IPV6_NIB */
#ifdef MODULE_NETSTATS_IPV6
#include "net/netstats.h"
Expand Down Expand Up @@ -589,8 +590,13 @@ int gnrc_netif_ipv6_addr_add_internal(gnrc_netif_t *netif,
}
}
#if GNRC_IPV6_NIB_CONF_SLAAC
else {
/* TODO: send out NS to solicited nodes for DAD probing */
else if (!gnrc_netif_is_6ln(netif)) {
/* cast to remove const qualifier (will still be used NIB internally as
* const) */
msg_t msg = { .type = GNRC_IPV6_NIB_DAD,
.content = { .ptr = &netif->ipv6.addrs[idx] } };

msg_send(&msg, gnrc_ipv6_pid);
}
#endif
#else
Expand Down Expand Up @@ -961,8 +967,7 @@ static int _create_candidate_set(const gnrc_netif_t *netif,
* be included in a candidate set."
*/
if ((netif->ipv6.addrs_flags[i] == 0) ||
(gnrc_netif_ipv6_addr_get_state(netif, i) ==
GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE)) {
gnrc_netif_ipv6_addr_dad_trans(netif, i)) {
continue;
}
/* Check if we only want link local addresses */
Expand Down
3 changes: 2 additions & 1 deletion sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@ static void *_event_loop(void *args)
case GNRC_IPV6_NIB_RTR_TIMEOUT:
case GNRC_IPV6_NIB_RECALC_REACH_TIME:
case GNRC_IPV6_NIB_REREG_ADDRESS:
case GNRC_IPV6_NIB_ROUTE_TIMEOUT:
case GNRC_IPV6_NIB_DAD:
case GNRC_IPV6_NIB_VALID_ADDR:
DEBUG("ipv6: NIB timer event received\n");
gnrc_ipv6_nib_handle_timer_event(msg.content.ptr, msg.type);
break;
Expand Down
205 changes: 205 additions & 0 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* Copyright (C) 2018 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/

#include <stdbool.h>

#include "luid.h"
#include "net/gnrc/netif/internal.h"

#include "_nib-6ln.h"
#include "_nib-arsm.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

static char addr_str[IPV6_ADDR_MAX_STR_LEN];

#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC
void _auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx,
uint8_t pfx_len)
{
ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED;
int idx;
uint8_t flags = GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE;

DEBUG("nib: add address based on %s/%u automatically to interface %u\n",
ipv6_addr_to_str(addr_str, pfx, sizeof(addr_str)),
pfx_len, netif->pid);
#if GNRC_IPV6_NIB_CONF_6LN
bool new_address = false;
#endif /* GNRC_IPV6_NIB_CONF_6LN */
gnrc_netif_ipv6_get_iid(netif, (eui64_t *)&addr.u64[1]);
ipv6_addr_init_prefix(&addr, pfx, pfx_len);
if ((idx = gnrc_netif_ipv6_addr_idx(netif, &addr)) < 0) {
if ((idx = gnrc_netif_ipv6_addr_add_internal(netif, &addr, pfx_len,
flags)) < 0) {
DEBUG("nib: Can't add link-local address on interface %u\n",
netif->pid);
return;
}
#if GNRC_IPV6_NIB_CONF_6LN
new_address = true;
#endif /* GNRC_IPV6_NIB_CONF_6LN */
}

#if GNRC_IPV6_NIB_CONF_6LN
/* mark link-local addresses as valid on 6LN */
if (gnrc_netif_is_6ln(netif) && ipv6_addr_is_link_local(pfx)) {
/* don't do this beforehand or risk a deadlock:
* - gnrc_netif_ipv6_addr_add_internal() adds VALID (i.e. manually configured
* addresses to the prefix list locking the NIB's mutex which is already
* locked here) */
netif->ipv6.addrs_flags[idx] &= ~GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_MASK;
netif->ipv6.addrs_flags[idx] |= GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID;
}
#endif /* GNRC_IPV6_NIB_CONF_6LN */
#if GNRC_IPV6_NIB_CONF_6LN
if (new_address && gnrc_netif_is_6ln(netif) &&
!gnrc_netif_is_6lbr(netif)) {
_handle_rereg_address(&netif->ipv6.addrs[idx]);
}
#else /* GNRC_IPV6_NIB_CONF_6LN */
(void)idx;
#endif /* GNRC_IPV6_NIB_CONF_6LN */
}
#endif /* GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC */

#if GNRC_IPV6_NIB_CONF_SLAAC
static bool _try_l2addr_reconfiguration(gnrc_netif_t *netif)
{
uint8_t hwaddr[GNRC_NETIF_L2ADDR_MAXLEN];
uint16_t hwaddr_len;

if (gnrc_netapi_get(netif->pid, NETOPT_SRC_LEN, 0, &hwaddr_len,
sizeof(hwaddr_len)) < 0) {
return false;
}
luid_get(hwaddr, hwaddr_len);
#if GNRC_IPV6_NIB_CONF_6LN
if (hwaddr_len == IEEE802154_LONG_ADDRESS_LEN) {
if (gnrc_netapi_set(netif->pid, NETOPT_ADDRESS_LONG, 0, hwaddr,
hwaddr_len) < 0) {
return false;
}
}
else
#endif
if (gnrc_netapi_set(netif->pid, NETOPT_ADDRESS, 0, hwaddr,
hwaddr_len) < 0) {
return false;
}
return true;
}

static bool _try_addr_reconfiguration(gnrc_netif_t *netif)
{
eui64_t orig_iid;
bool remove_old = false, hwaddr_reconf;

if (gnrc_netif_ipv6_get_iid(netif, &orig_iid) == 0) {
remove_old = true;
}
/* seize netif to netif thread since _try_l2addr_reconfiguration uses
* gnrc_netapi_get()/gnrc_netapi_set(). Since these are synchronous this is
* safe */
gnrc_netif_release(netif);
/* reacquire netif for IPv6 address reconfiguraton */
hwaddr_reconf = _try_l2addr_reconfiguration(netif);
gnrc_netif_acquire(netif);
if (hwaddr_reconf) {
if (remove_old) {
for (unsigned i = 0; i < GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) {
ipv6_addr_t *addr = &netif->ipv6.addrs[i];
if (addr->u64[1].u64 == orig_iid.uint64.u64) {
gnrc_netif_ipv6_addr_remove_internal(netif, addr);
}
}
}
DEBUG("nib: Changed hardware address, due to DAD\n");
_auto_configure_addr(netif, &ipv6_addr_link_local_prefix, 64U);
}
return hwaddr_reconf;
}

void _remove_tentative_addr(gnrc_netif_t *netif, const ipv6_addr_t *addr)
{
DEBUG("nib: other node has TENTATIVE address %s assigned "
"=> removing that address\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)));
gnrc_netif_ipv6_addr_remove_internal(netif, addr);

if (!ipv6_addr_is_link_local(addr) ||
!_try_addr_reconfiguration(netif)) {
/* 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);
}
}

static int _get_netif_state(gnrc_netif_t **netif, const ipv6_addr_t *addr)
{
*netif = gnrc_netif_get_by_ipv6_addr(addr);
if (*netif != NULL) {
int idx;

gnrc_netif_acquire(*netif);
idx = gnrc_netif_ipv6_addr_idx(*netif, addr);
return ((idx >= 0) && gnrc_netif_ipv6_addr_dad_trans(*netif, idx)) ?
idx : -1;
}
return -1;
}

void _handle_dad(const ipv6_addr_t *addr)
{
ipv6_addr_t sol_nodes;
gnrc_netif_t *netif = NULL;
int idx = _get_netif_state(&netif, addr);
if (idx >= 0) {
ipv6_addr_set_solicited_nodes(&sol_nodes, addr);
_snd_ns(addr, netif, &ipv6_addr_unspecified, &sol_nodes);
_evtimer_add((void *)addr, GNRC_IPV6_NIB_VALID_ADDR,
&netif->ipv6.addrs_timers[idx],
netif->ipv6.retrans_time);
}
if (netif != NULL) {
/* was acquired in `_get_netif_state()` */
gnrc_netif_release(netif);
}
}

void _handle_valid_addr(const ipv6_addr_t *addr)
{
gnrc_netif_t *netif = NULL;
int idx = _get_netif_state(&netif, addr);

if (idx >= 0) {
netif->ipv6.addrs_flags[idx] &= ~GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_MASK;
netif->ipv6.addrs_flags[idx] |= GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID;
}
if (netif != NULL) {
/* was acquired in `_get_netif_state()` */
gnrc_netif_release(netif);
}
}
#else /* GNRC_IPV6_NIB_CONF_SLAAC */
typedef int dont_be_pedantic;
#endif /* GNRC_IPV6_NIB_CONF_SLAAC */

/** @} */
83 changes: 83 additions & 0 deletions sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2018 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup net_gnrc_ipv6_nib
* @brief
* @{
*
* @file
* @brief Definions related to SLAAC functionality of the NIB
* @see @ref GNRC_IPV6_NIB_CONF_SLAAC
* @internal
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef PRIV_NIB_SLAAC_H
#define PRIV_NIB_SLAAC_H

#include <stdint.h>

#include "net/gnrc/ipv6/nib/conf.h"
#include "net/gnrc/netif.h"
#include "net/ipv6/addr.h"

#ifdef __cplusplus
extern "C" {
#endif

#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC || defined(DOXYGEN)
/**
* @brief Auto-configures an address from a given prefix
*
* @param[in] netif The network interface the address should be added to.
* @param[in] pfx The prefix for the address.
* @param[in] pfx_len Length of @p pfx in bits.
*/
void _auto_configure_addr(gnrc_netif_t *netif, const ipv6_addr_t *pfx,
uint8_t pfx_len);
#else /* GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC */
#define _auto_configure_addr(netif, pfx, pfx_len) \
(void)netif; (void)pfx; (void)pfx_len;
#endif /* GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC */
#if GNRC_IPV6_NIB_CONF_SLAAC || defined(DOXYGE)
/**
* @brief Removes a tentative address from the interface and tries to
* reconfigure a new address
*
* @param[in] netif The network interface the address is to be removed from.
* @param[in] addr The address to remove.
*/
void _remove_tentative_addr(gnrc_netif_t *netif, const ipv6_addr_t *addr);

/**
* @brief Handle @ref GNRC_IPV6_NIB_DAD event
*
* @param[in] addr A TENTATIVE address.
*/
void _handle_dad(const ipv6_addr_t *addr);

/**
* @brief Handle @ref GNRC_IPV6_NIB_VALID_ADDR event
*
* @param[in] addr A TENTATIVE address.
*/
void _handle_valid_addr(const ipv6_addr_t *addr);
#else /* GNRC_IPV6_NIB_CONF_SLAAC */
#define _remove_tentative_addr(netif, addr) \
(void)netif; (void)addr
#define _handle_dad(addr) (void)addr
#define _handle_valid_addr(addr) (void)addr
#endif /* GNRC_IPV6_NIB_CONF_SLAAC */

#ifdef __cplusplus
}
#endif

#endif /* PRIV_NIB_SLAAC_H */
/** @} */
Loading

0 comments on commit 55adbee

Please sign in to comment.