From 55adbee48808bda975dd5ee420c605bbddf4c94c Mon Sep 17 00:00:00 2001
From: Martine Lenders <mail@martine-lenders.eu>
Date: Thu, 22 Mar 2018 19:01:02 +0000
Subject: [PATCH] gnrc_ipv6_nib: add full RFC4862 DAD support

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
---
 sys/include/net/gnrc/ipv6/nib.h               |  20 ++
 sys/include/net/gnrc/netif/ipv6.h             |   5 +-
 sys/net/gnrc/netif/gnrc_netif.c               |  13 +-
 sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c   |   3 +-
 .../gnrc/network_layer/ipv6/nib/_nib-slaac.c  | 205 ++++++++++++++++++
 .../gnrc/network_layer/ipv6/nib/_nib-slaac.h  |  83 +++++++
 sys/net/gnrc/network_layer/ipv6/nib/nib.c     | 114 ++++------
 7 files changed, 367 insertions(+), 76 deletions(-)
 create mode 100644 sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c
 create mode 100644 sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h

diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h
index a119d4d6ca..facc9e6ccf 100644
--- a/sys/include/net/gnrc/ipv6/nib.h
+++ b/sys/include/net/gnrc/ipv6/nib.h
@@ -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)
 /** @} */
 
 /**
diff --git a/sys/include/net/gnrc/netif/ipv6.h b/sys/include/net/gnrc/netif/ipv6.h
index cc77981d08..94edf3b592 100644
--- a/sys/include/net/gnrc/netif/ipv6.h
+++ b/sys/include/net/gnrc/netif/ipv6.h
@@ -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.
      */
diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c
index 79f2c23944..e507a3f9fb 100644
--- a/sys/net/gnrc/netif/gnrc_netif.c
+++ b/sys/net/gnrc/netif/gnrc_netif.c
@@ -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"
@@ -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
@@ -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 */
diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
index 0daddc2169..68fffe6d38 100644
--- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
+++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
@@ -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;
diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c
new file mode 100644
index 0000000000..f34f88c5ce
--- /dev/null
+++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.c
@@ -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 */
+
+/** @} */
diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h
new file mode 100644
index 0000000000..76264c82ce
--- /dev/null
+++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-slaac.h
@@ -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 */
+/** @} */
diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c
index 918e0707bd..dab240bdb0 100644
--- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c
+++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c
@@ -32,6 +32,7 @@
 #include "_nib-router.h"
 #include "_nib-6ln.h"
 #include "_nib-6lr.h"
+#include "_nib-slaac.h"
 
 #define ENABLE_DEBUG    (0)
 #include "debug.h"
@@ -67,14 +68,6 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif,
 static void _handle_pfx_timeout(_nib_offl_entry_t *pfx);
 static void _handle_rtr_timeout(_nib_dr_entry_t *router);
 static void _handle_snd_na(gnrc_pktsnip_t *pkt);
-#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC
-static 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 */
 /* needs to be exported for 6LN's ARO handling */
 void _handle_search_rtr(gnrc_netif_t *netif);
 /** @} */
@@ -356,6 +349,12 @@ void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type)
             _handle_rereg_address(ctx);
             break;
 #endif  /* GNRC_IPV6_NIB_CONF_6LN */
+        case GNRC_IPV6_NIB_DAD:
+            _handle_dad(ctx);
+            break;
+        case GNRC_IPV6_NIB_VALID_ADDR:
+            _handle_valid_addr(ctx);
+            break;
         default:
             break;
     }
@@ -838,9 +837,30 @@ static void _handle_nbr_sol(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6,
     DEBUG("     - Destination address: %s\n",
           ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str)));
 #if GNRC_IPV6_NIB_CONF_SLAAC
-    /* TODO SLAAC behavior */
+    gnrc_netif_t *tgt_netif = gnrc_netif_get_by_ipv6_addr(&nbr_sol->tgt);
+
+    if (tgt_netif != NULL) {
+        int idx = gnrc_netif_ipv6_addr_idx(tgt_netif, &nbr_sol->tgt);
+
+        if (gnrc_netif_ipv6_addr_dad_trans(tgt_netif, idx)) {
+            if (!ipv6_addr_is_unspecified(&ipv6->src)) {
+                /* (see https://tools.ietf.org/html/rfc4862#section-5.4.3) */
+                DEBUG("nib: Neighbor is performing AR, but target address is "
+                      "still TENTATIVE for us => Ignoring NS\n");
+                return;
+            }
+            /* cancel validation timer */
+            evtimer_del(&_nib_evtimer,
+                        &tgt_netif->ipv6.addrs_timers[idx].event);
+            _remove_tentative_addr(tgt_netif, &nbr_sol->tgt);
+            return;
+        }
+    }
 #endif  /* GNRC_IPV6_NIB_CONF_SLAAC */
-    if (!ipv6_addr_is_unspecified(&ipv6->src)) {
+    if (ipv6_addr_is_unspecified(&ipv6->src)) {
+        gnrc_ndp_nbr_adv_send(&nbr_sol->tgt, netif, &ipv6->src, false, NULL);
+    }
+    else {
         gnrc_pktsnip_t *reply_aro = NULL;
 #if GNRC_IPV6_NIB_CONF_6LR
         ndp_opt_t *sl2ao = NULL;
@@ -948,7 +968,21 @@ static void _handle_nbr_adv(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6,
           (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) ? 'S' : '-',
           (nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) ? 'O' : '-');
 #if GNRC_IPV6_NIB_CONF_SLAAC
-    /* TODO SLAAC behavior */
+    gnrc_netif_t *tgt_netif = gnrc_netif_get_by_ipv6_addr(&nbr_adv->tgt);
+
+    if (tgt_netif != NULL) {
+        int idx = gnrc_netif_ipv6_addr_idx(tgt_netif, &nbr_adv->tgt);
+
+        if (gnrc_netif_ipv6_addr_dad_trans(tgt_netif, idx)) {
+            /* cancel validation timer */
+            evtimer_del(&_nib_evtimer,
+                        &tgt_netif->ipv6.addrs_timers[idx].event);
+            _remove_tentative_addr(tgt_netif, &nbr_adv->tgt);
+            return;
+        }
+        /* else case beyond scope of RFC4862:
+         * https://tools.ietf.org/html/rfc4862#section-5.4.4 */
+    }
 #endif  /* GNRC_IPV6_NIB_CONF_SLAAC */
     if (((nce = _nib_onl_get(&nbr_adv->tgt, netif->pid)) != NULL) &&
         (nce->mode & _NC)) {
@@ -1246,11 +1280,9 @@ static uint32_t _handle_pio(gnrc_netif_t *netif, const icmpv6_hdr_t *icmpv6,
     DEBUG("     - Preferred lifetime: %" PRIu32 "\n",
           byteorder_ntohl(pio->pref_ltime));
 
-#if GNRC_IPV6_NIB_CONF_SLAAC || GNRC_IPV6_NIB_CONF_6LN
     if (pio->flags & NDP_OPT_PI_FLAGS_A) {
         _auto_configure_addr(netif, &pio->prefix, pio->prefix_len);
     }
-#endif /* GNRC_IPV6_NIB_CONF_SLAAC || GNRC_IPV6_NIB_CONF_6LN */
     if ((pio->flags & NDP_OPT_PI_FLAGS_L) || gnrc_netif_is_6lr(netif)) {
         _nib_offl_entry_t *pfx;
 
@@ -1293,60 +1325,4 @@ static uint32_t _handle_pio(gnrc_netif_t *netif, const icmpv6_hdr_t *icmpv6,
     return UINT32_MAX;
 }
 
-#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC
-static 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 */
-    /* TODO: make this line conditional on 6LN when there is a SLAAC
-     * implementation */
-#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 */
-#if GNRC_IPV6_NIB_CONF_SLAAC
-    /* TODO send NS to solicited nodes and wait netif->ipv6.retrans_time to
-     * confirm uniqueness of the link-local address */
-#endif  /* GNRC_IPV6_NIB_CONF_SLAAC */
-}
-#endif  /* GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC */
-
 /** @} */
-- 
GitLab