diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h
index a119d4d6ca28429451bdd33fbd59b143d1111735..facc9e6ccffefbb7df5fb97e373b6b0339d32fde 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 cc77981d08bbdf0194e13e816cad48e1277b9ed6..94edf3b59254e57266c183bca19a9b573251f2bf 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 79f2c239448161c5eafca96c9008ae8e061451e0..e507a3f9fb5632e674deabf7c71b5b4802873bca 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 0daddc216902102d6031db275532427324615f6d..68fffe6d386b2e2c31721fdf44aeee1b1147a4bb 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 0000000000000000000000000000000000000000..f34f88c5ced11409c68e9b56a07979ea9ba5e1e9
--- /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 0000000000000000000000000000000000000000..76264c82ce2acf2a753be82646e7fe66c5b14056
--- /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 50b90e0f976ae049de930aa7d3c608fd5f24df4e..4bc923948a5680ae9a5e20942bb9204b55596208 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 */
-
 /** @} */
diff --git a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
index 7373d454be2fa1829e97de2829e300cee934c8cd..afce23dac935626f401d143f7f98385b0839bb72 100644
--- a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
+++ b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c
@@ -475,6 +475,11 @@ void gnrc_ndp_rtr_adv_send(gnrc_netif_t *netif, const ipv6_addr_t *src,
             /* get address from source selection algorithm.
              * Only link local addresses may be used (RFC 4861 section 4.1) */
             src = gnrc_netif_ipv6_addr_best_src(netif, dst, true);
+
+            if (src == NULL) {
+                DEBUG("ndp rtr: no VALID source address found for RA\n");
+                break;
+            }
         }
         /* add SL2A for source address */
         if (src != NULL) {
diff --git a/tests/gnrc_ipv6_nib/main.c b/tests/gnrc_ipv6_nib/main.c
index 3e699ee4017fd8a138982753e8ab99ceb95cf55d..f430b3f2a4d978828dd7d9e745d340ef8e621479 100644
--- a/tests/gnrc_ipv6_nib/main.c
+++ b/tests/gnrc_ipv6_nib/main.c
@@ -449,45 +449,15 @@ static void test_handle_pkt__nbr_sol__invalid_dst(void)
     TEST_ASSERT_EQUAL_INT(0, msg_avail());
 }
 
-static void test_handle_pkt__nbr_sol__invalid_sl2ao(void)
-{
-    gnrc_ipv6_nib_nc_t nce;
-    void *state = NULL;
-    size_t icmpv6_len = _set_nbr_sol(&ipv6_addr_unspecified, &_loc_sol_nodes,
-                                     255U, 0U, &_loc_ll, _rem_l2,
-                                     sizeof(_rem_l2));
-
-    gnrc_ipv6_nib_handle_pkt(_mock_netif, ipv6, icmpv6, icmpv6_len);
-    TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
-                        "There is an unexpected neighbor cache entry");
-    /* TODO: check other views as well */
-    TEST_ASSERT_EQUAL_INT(0, msg_avail());
-}
-
-static void test_handle_pkt__nbr_sol__tgt_not_assigned(void)
-{
-    gnrc_ipv6_nib_nc_t nce;
-    void *state = NULL;
-    size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes,
-                                     255U, 0U, &_rem_ll, _rem_l2,
-                                     sizeof(_rem_l2));
-
-    gnrc_ipv6_nib_handle_pkt(_mock_netif, ipv6, icmpv6, icmpv6_len);
-    TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
-                        "There is an unexpected neighbor cache entry");
-    /* TODO: check other views as well */
-    TEST_ASSERT_EQUAL_INT(0, msg_avail());
-}
-
 static void test_pkt_is_nbr_adv(gnrc_pktsnip_t *pkt, const ipv6_addr_t *dst,
                                 const ipv6_addr_t *tgt,
                                 const uint8_t *tgt_l2addr,
                                 size_t tgt_l2addr_len)
 {
+    gnrc_pktsnip_t *options;
     gnrc_netif_hdr_t *netif_hdr;
     ipv6_hdr_t *ipv6_hdr;
     ndp_nbr_adv_t *nbr_adv;
-    ndp_opt_t *tl2ao;
 
     /* first snip is a netif header to _mock_netif */
     TEST_ASSERT_NOT_NULL(pkt);
@@ -500,7 +470,9 @@ static void test_pkt_is_nbr_adv(gnrc_pktsnip_t *pkt, const ipv6_addr_t *dst,
     TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_IPV6, pkt->next->type);
     TEST_ASSERT_EQUAL_INT(sizeof(ipv6_hdr_t), pkt->next->size);
     ipv6_hdr = pkt->next->data;
-    TEST_ASSERT(!ipv6_addr_is_multicast(&ipv6_hdr->dst));
+    if ((tgt_l2addr != NULL) && (tgt_l2addr_len > 0)) {
+        TEST_ASSERT(!ipv6_addr_is_multicast(&ipv6_hdr->dst));
+    }
     TEST_ASSERT_MESSAGE(ipv6_addr_equal(dst, &ipv6_hdr->dst),
                         "dst != ipv6_hdr->dst");
     TEST_ASSERT_EQUAL_INT(255, ipv6_hdr->hl);
@@ -514,19 +486,65 @@ static void test_pkt_is_nbr_adv(gnrc_pktsnip_t *pkt, const ipv6_addr_t *dst,
     TEST_ASSERT(!ipv6_addr_is_multicast(&nbr_adv->tgt));
     TEST_ASSERT_MESSAGE(ipv6_addr_equal(tgt, &nbr_adv->tgt),
                         "tgt != nbr_adv->tgt");
-    TEST_ASSERT(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S);
-    /* fourth snip is a TL2AO for tgt_l2addr */
-    TEST_ASSERT_NOT_NULL(pkt->next->next->next);
-    TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_UNDEF, pkt->next->next->next->type);
-    TEST_ASSERT_EQUAL_INT(ceil8(sizeof(ndp_opt_t) + tgt_l2addr_len),
-                          pkt->next->next->next->size);
-    tl2ao = pkt->next->next->next->data;
-    TEST_ASSERT_EQUAL_INT(NDP_OPT_TL2A, tl2ao->type);
-    TEST_ASSERT_EQUAL_INT(1, tl2ao->len);
-    TEST_ASSERT_MESSAGE(memcmp(tl2ao + 1, tgt_l2addr, tgt_l2addr_len) == 0,
-                        "tl2ao.l2addr != tgt_l2addr");
+    options = pkt->next->next->next;
+    if ((tgt_l2addr != NULL) && (tgt_l2addr_len > 0)) {
+        ndp_opt_t *tl2ao;
+
+        TEST_ASSERT(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S);
+
+        /* fourth snip is a TL2AO for tgt_l2addr */
+        TEST_ASSERT_NOT_NULL(options);
+        TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_UNDEF, options->type);
+        TEST_ASSERT_EQUAL_INT(ceil8(sizeof(ndp_opt_t) + tgt_l2addr_len),
+                              options->size);
+        tl2ao = options->data;
+        TEST_ASSERT_EQUAL_INT(NDP_OPT_TL2A, tl2ao->type);
+        TEST_ASSERT_EQUAL_INT(1, tl2ao->len);
+        TEST_ASSERT_MESSAGE(memcmp(tl2ao + 1, tgt_l2addr, tgt_l2addr_len) == 0,
+                            "tl2ao.l2addr != tgt_l2addr");
+    }
     /* no further options */
-    TEST_ASSERT_NULL(pkt->next->next->next->next);
+    TEST_ASSERT_NULL(options->next);
+}
+
+static void test_handle_pkt__nbr_sol__invalid_sl2ao(void)
+{
+    msg_t msg;
+    gnrc_ipv6_nib_nc_t nce;
+    void *state = NULL;
+    size_t icmpv6_len = _set_nbr_sol(&ipv6_addr_unspecified, &_loc_sol_nodes,
+                                     255U, 0U, &_loc_ll, _rem_l2,
+                                     sizeof(_rem_l2));
+
+    gnrc_ipv6_nib_handle_pkt(_mock_netif, ipv6, icmpv6, icmpv6_len);
+    TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
+                        "There is an unexpected neighbor cache entry");
+    /* TODO: check other views as well */
+
+    /* check if SLAAC generated neighbor advertisement */
+    TEST_ASSERT_EQUAL_INT(1, msg_avail());
+    msg_receive(&msg);
+    TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
+    test_pkt_is_nbr_adv(msg.content.ptr, &ipv6_addr_all_nodes_link_local,
+                        &_loc_ll, NULL, 0);
+    gnrc_pktbuf_release(msg.content.ptr);
+
+    TEST_ASSERT_EQUAL_INT(0, msg_avail());
+}
+
+static void test_handle_pkt__nbr_sol__tgt_not_assigned(void)
+{
+    gnrc_ipv6_nib_nc_t nce;
+    void *state = NULL;
+    size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes,
+                                     255U, 0U, &_rem_ll, _rem_l2,
+                                     sizeof(_rem_l2));
+
+    gnrc_ipv6_nib_handle_pkt(_mock_netif, ipv6, icmpv6, icmpv6_len);
+    TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
+                        "There is an unexpected neighbor cache entry");
+    /* TODO: check other views as well */
+    TEST_ASSERT_EQUAL_INT(0, msg_avail());
 }
 
 static void test_handle_pkt__nbr_sol__ll_src(unsigned exp_nud_state,
@@ -1096,9 +1114,50 @@ static void test_handle_pkt__rtr_adv__success(uint8_t rtr_adv_flags,
     state = NULL;
     if (pio) {
         if (pio_flags & NDP_OPT_PI_FLAGS_A) {
+            msg_t msg;
+            gnrc_pktsnip_t *pkt;
+            gnrc_netif_hdr_t *netif_hdr;
+            ipv6_hdr_t *ipv6_hdr;
+            ndp_nbr_adv_t *nbr_sol;
+
             TEST_ASSERT_MESSAGE(gnrc_netif_ipv6_addr_idx(_mock_netif,
                                                          &_loc_gb) >= 0,
                                 "Address was not configured by PIO");
+
+            /* Check if SLAAC generated a neighbor solicitation */
+            TEST_ASSERT_EQUAL_INT(1, msg_avail());
+            msg_receive(&msg);
+            TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
+            pkt = msg.content.ptr;
+            /* first snip is a netif header to _mock_netif */
+            TEST_ASSERT_NOT_NULL(pkt);
+            TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_NETIF, pkt->type);
+            TEST_ASSERT(sizeof(gnrc_netif_hdr_t) <= pkt->size);
+            netif_hdr = pkt->data;
+            TEST_ASSERT_EQUAL_INT(_mock_netif->pid, netif_hdr->if_pid);
+            /* second snip is an IPv6 header to solicited nodes of _loc_gb */
+            TEST_ASSERT_NOT_NULL(pkt->next);
+            TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_IPV6, pkt->next->type);
+            TEST_ASSERT_EQUAL_INT(sizeof(ipv6_hdr_t), pkt->next->size);
+            ipv6_hdr = pkt->next->data;
+            TEST_ASSERT_MESSAGE(ipv6_addr_equal(&ipv6_hdr->dst,
+                                                &_loc_sol_nodes),
+                                "ipv6_hdr->dst != _loc_sol_nodes");
+            TEST_ASSERT_EQUAL_INT(255, ipv6_hdr->hl);
+            /* third snip is a valid solicited neighbor solicitation to
+             * _loc_gb */
+            TEST_ASSERT_NOT_NULL(pkt->next->next);
+            TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_ICMPV6, pkt->next->next->type);
+            TEST_ASSERT_EQUAL_INT(sizeof(ndp_nbr_sol_t), pkt->next->next->size);
+            nbr_sol = pkt->next->next->data;
+            TEST_ASSERT_EQUAL_INT(ICMPV6_NBR_SOL, nbr_sol->type);
+            TEST_ASSERT_EQUAL_INT(0, nbr_sol->code);
+            TEST_ASSERT(!ipv6_addr_is_multicast(&nbr_sol->tgt));
+            TEST_ASSERT_MESSAGE(ipv6_addr_equal(&_loc_gb, &nbr_sol->tgt),
+                                "_loc_gb != nbr_sol->tgt");
+            /* no further options */
+            TEST_ASSERT_NULL(pkt->next->next->next);
+            gnrc_pktbuf_release(pkt);
         }
         else {
             TEST_ASSERT_MESSAGE(gnrc_netif_ipv6_addr_idx(_mock_netif,
diff --git a/tests/gnrc_udp/Makefile b/tests/gnrc_udp/Makefile
index d5f4e65a8d1d20f589d314ee4f5d914a79e2d544..9496288ee718ca40a5eec6690d9db3bd4187a77f 100644
--- a/tests/gnrc_udp/Makefile
+++ b/tests/gnrc_udp/Makefile
@@ -1,6 +1,6 @@
 include ../Makefile.tests_common
 
-BOARD_INSUFFICIENT_MEMORY := calliope-mini chronos microbit msb-430 msb-430h \
+BOARD_INSUFFICIENT_MEMORY := calliope-mini chronos hifive1 microbit msb-430 msb-430h \
                              nucleo-f031k6 nucleo-f042k6 nucleo-f303k8 nucleo-l031k6 \
                              nucleo-f030r8 nucleo-f070rb nucleo-f072rb nucleo-f103rb nucleo-f302r8 \
                              nucleo-f334r8 nucleo-l053r8 spark-core stm32f0discovery telosb \