From 319c0f9d219dee96f1b0aee9e1ea5bf32ace22ad Mon Sep 17 00:00:00 2001 From: Martine Lenders <m.lenders@fu-berlin.de> Date: Wed, 9 Aug 2017 16:30:23 +0200 Subject: [PATCH] gnrc_ipv6_nib: implement behavior for router discovery --- sys/include/net/gnrc/ipv6/nib.h | 33 +- sys/include/net/gnrc/ipv6/nib/conf.h | 7 + sys/include/net/gnrc/netif2/ipv6.h | 17 +- .../gnrc/application_layer/uhcpc/gnrc_uhcpc.c | 24 +- sys/net/gnrc/netif2/gnrc_netif2.c | 27 +- sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c | 3 +- .../gnrc/network_layer/ipv6/nib/_nib-6ln.c | 189 +++- .../gnrc/network_layer/ipv6/nib/_nib-6ln.h | 64 ++ .../gnrc/network_layer/ipv6/nib/_nib-6lr.c | 13 +- .../gnrc/network_layer/ipv6/nib/_nib-6lr.h | 17 +- .../gnrc/network_layer/ipv6/nib/_nib-arsm.c | 40 +- .../network_layer/ipv6/nib/_nib-internal.c | 50 +- .../network_layer/ipv6/nib/_nib-internal.h | 33 +- .../gnrc/network_layer/ipv6/nib/_nib-router.c | 203 ++++ .../gnrc/network_layer/ipv6/nib/_nib-router.h | 132 +++ sys/net/gnrc/network_layer/ipv6/nib/nib.c | 867 +++++++++++++++--- sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c | 45 +- 17 files changed, 1524 insertions(+), 240 deletions(-) create mode 100644 sys/net/gnrc/network_layer/ipv6/nib/_nib-router.c create mode 100644 sys/net/gnrc/network_layer/ipv6/nib/_nib-router.h diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h index 43fe775635..22f1ff989a 100644 --- a/sys/include/net/gnrc/ipv6/nib.h +++ b/sys/include/net/gnrc/ipv6/nib.h @@ -14,6 +14,7 @@ * @todo Add detailed description * @todo Implement multihop DAD * @todo Implement classic SLAAC + * @todo Implement MLD * @{ * * @file @@ -86,16 +87,6 @@ extern "C" { */ #define GNRC_IPV6_NIB_SEARCH_RTR (0x4fc3U) -/** - * @brief Reconfirm router event. - * - * This message type is for the event the reconfirmation of a router (which - * implies sending a unicast Router Solicitation). The expected message context - * is a pointer to a valid on-link entry representing the router that is to be - * confirmed. - */ -#define GNRC_IPV6_NIB_RECONFIRM_RTR (0x4fc4U) - /** * @brief Reply Router Solicitation event. * @@ -153,17 +144,6 @@ extern "C" { */ #define GNRC_IPV6_NIB_ADDR_REG_TIMEOUT (0x4fc9U) -/** - * @brief 6LoWPAN context timeout event. - * - * This message type is for the event of a 6LoWPAN compression context timeout. - * The expected message context is the compression context's numerical - * identifier. - * - * @note Only handled with @ref GNRC_IPV6_NIB_CONF_6LN != 0 - */ -#define GNRC_IPV6_NIB_6LO_CTX_TIMEOUT (0x4fcaU) - /** * @brief Authoritative border router timeout event. * @@ -201,6 +181,17 @@ extern "C" { * @note Only handled with @ref GNRC_IPV6_NIB_CONF_ARSM != 0 */ #define GNRC_IPV6_NIB_RECALC_REACH_TIME (0x4fceU) + +/** + * @brief Reregister address. + * + * This message type is for the event of reregistering an IPv6 address to the + * upstream router. The expected message context is an IPv6 address assigned to + * one of the nodes interfaces. + * + * @note Only handled with @ref GNRC_IPV6_NIB_CONF_6LN != 0 + */ +#define GNRC_IPV6_NIB_REREG_ADDRESS (0x4fcfU) /** @} */ /** diff --git a/sys/include/net/gnrc/ipv6/nib/conf.h b/sys/include/net/gnrc/ipv6/nib/conf.h index 90992df165..999350eaad 100644 --- a/sys/include/net/gnrc/ipv6/nib/conf.h +++ b/sys/include/net/gnrc/ipv6/nib/conf.h @@ -29,6 +29,9 @@ extern "C" { #ifndef GNRC_IPV6_NIB_CONF_6LBR #define GNRC_IPV6_NIB_CONF_6LBR (1) #endif +#ifndef GNRC_IPV6_NIB_NUMOF +#define GNRC_IPV6_NIB_NUMOF (16) +#endif #endif #ifdef MODULE_GNRC_IPV6_NIB_6LR @@ -174,8 +177,12 @@ extern "C" { * @see [RFC 6775, section 8.1](https://tools.ietf.org/html/rfc6775#section-8.1) */ #ifndef GNRC_IPV6_NIB_CONF_MULTIHOP_P6C +#if GNRC_IPV6_NIB_CONF_6LN +#define GNRC_IPV6_NIB_CONF_MULTIHOP_P6C (1) +#else #define GNRC_IPV6_NIB_CONF_MULTIHOP_P6C (0) #endif +#endif /** * @brief Multihop duplicate address detection diff --git a/sys/include/net/gnrc/netif2/ipv6.h b/sys/include/net/gnrc/netif2/ipv6.h index 4357ddcf5b..56e5dfe26e 100644 --- a/sys/include/net/gnrc/netif2/ipv6.h +++ b/sys/include/net/gnrc/netif2/ipv6.h @@ -148,6 +148,18 @@ typedef struct { * and @ref net_gnrc_ipv6_nib "NIB" */ evtimer_msg_event_t search_rtr; +#if GNRC_IPV6_NIB_CONF_6LN || 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 + * @note Might also be usable in the later default SLAAC implementation + * for NS retransmission timers. + */ + evtimer_msg_event_t addrs_timers[GNRC_NETIF2_IPV6_ADDRS_NUMOF]; +#endif #if GNRC_IPV6_NIB_CONF_ROUTER || DOXYGEN /** @@ -210,16 +222,13 @@ typedef struct { */ uint8_t ra_sent; #endif -#if GNRC_IPV6_NIB_CONF_6LN || DOXYGEN /** * @brief number of unsolicited router solicitations scheduled * * @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 net_gnrc_ipv6_nib "NIB" */ uint8_t rs_sent; -#endif /** * @brief number of unsolicited neighbor advertisements scheduled * diff --git a/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c b/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c index 05984803a0..cdcc01eea0 100644 --- a/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c +++ b/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c @@ -6,12 +6,10 @@ * directory for more details. */ -#include "net/fib.h" +#include "net/gnrc/ipv6/nib.h" #include "net/gnrc/ipv6.h" -#include "net/gnrc/ipv6/nc.h" -#include "net/gnrc/ipv6/netif.h" #include "net/gnrc/netapi.h" -#include "net/gnrc/netif.h" +#include "net/gnrc/netif2.h" #include "net/ipv6/addr.h" #include "net/netdev.h" #include "net/netopt.h" @@ -31,19 +29,13 @@ static void set_interface_roles(void) kernel_pid_t dev = netif->pid; int is_wired = gnrc_netapi_get(dev, NETOPT_IS_WIRED, 0, NULL, 0); if ((!gnrc_border_interface) && (is_wired == 1)) { - ipv6_addr_t addr; + ipv6_addr_t addr, defroute = IPV6_ADDR_UNSPECIFIED; gnrc_border_interface = dev; ipv6_addr_from_str(&addr, "fe80::2"); gnrc_netapi_set(dev, NETOPT_IPV6_ADDR, 64 << 8, &addr, sizeof(addr)); -#ifdef MODULE_FIB - ipv6_addr_t defroute = IPV6_ADDR_UNSPECIFIED; - ipv6_addr_from_str(&addr, "fe80::1"); - fib_add_entry(&gnrc_ipv6_fib_table, dev, defroute.u8, 16, - 0x00, addr.u8, 16, 0, - (uint32_t)FIB_LIFETIME_NO_EXPIRE); -#endif + gnrc_ipv6_nib_ft_add(&defroute, IPV6_ADDR_BIT_LEN, &addr, dev); } else if ((!gnrc_wireless_interface) && (is_wired != 1)) { gnrc_wireless_interface = dev; @@ -92,6 +84,10 @@ void uhcp_handle_prefix(uint8_t *prefix, uint8_t prefix_len, uint16_t lifetime, gnrc_netapi_set(gnrc_wireless_interface, NETOPT_IPV6_ADDR, (64 << 8), prefix, sizeof(ipv6_addr_t)); +#if defined(MODULE_GNRC_IPV6_NIB) && GNRC_IPV6_NIB_CONF_6LBR && \ + GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + gnrc_ipv6_nib_abr_add((ipv6_addr_t *)prefix); +#endif gnrc_netapi_set(gnrc_wireless_interface, NETOPT_IPV6_ADDR_REMOVE, 0, &_prefix, sizeof(_prefix)); print_str("gnrc_uhcpc: uhcp_handle_prefix(): configured new prefix "); @@ -101,6 +97,10 @@ void uhcp_handle_prefix(uint8_t *prefix, uint8_t prefix_len, uint16_t lifetime, if (!ipv6_addr_is_unspecified(&_prefix)) { gnrc_netapi_set(gnrc_wireless_interface, NETOPT_IPV6_ADDR_REMOVE, 0, &_prefix, sizeof(_prefix)); +#if defined(MODULE_GNRC_IPV6_NIB) && GNRC_IPV6_NIB_CONF_6LBR && \ + GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + gnrc_ipv6_nib_abr_del(&_prefix); +#endif print_str("gnrc_uhcpc: uhcp_handle_prefix(): removed old prefix "); ipv6_addr_print(&_prefix); puts("/64"); diff --git a/sys/net/gnrc/netif2/gnrc_netif2.c b/sys/net/gnrc/netif2/gnrc_netif2.c index 2d643bea4f..75ba222795 100644 --- a/sys/net/gnrc/netif2/gnrc_netif2.c +++ b/sys/net/gnrc/netif2/gnrc_netif2.c @@ -550,10 +550,29 @@ int gnrc_netif2_ipv6_addr_add(gnrc_netif2_t *netif, const ipv6_addr_t *addr, } netif->ipv6.addrs_flags[idx] = flags; memcpy(&netif->ipv6.addrs[idx], addr, sizeof(netif->ipv6.addrs[idx])); - /* TODO: - * - update prefix list, if flags == VALID - * - with SLAAC, send out NS otherwise for DAD probing */ +#ifdef MODULE_GNRC_IPV6_NIB + if (_get_state(netif, idx) == GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID) { + void *state = NULL; + gnrc_ipv6_nib_pl_t ple; + bool in_pl = false; + + while (gnrc_ipv6_nib_pl_iter(netif->pid, &state, &ple)) { + if (ipv6_addr_match_prefix(&ple.pfx, addr) >= pfx_len) { + in_pl = true; + } + } + if (!in_pl) { + gnrc_ipv6_nib_pl_set(netif->pid, addr, pfx_len, UINT32_MAX, UINT32_MAX); + } + } +#if GNRC_IPV6_NIB_CONF_SLAAC + else { + /* TODO: send out NS to solicited nodes for DAD probing */ + } +#endif +#else (void)pfx_len; +#endif gnrc_netif2_release(netif); return idx; } @@ -569,8 +588,6 @@ void gnrc_netif2_ipv6_addr_remove(gnrc_netif2_t *netif, if (idx >= 0) { netif->ipv6.addrs_flags[idx] = 0; ipv6_addr_set_unspecified(&netif->ipv6.addrs[idx]); - /* TODO: - * - update prefix list, if necessary */ } gnrc_netif2_release(netif); } diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index e70da32d35..7fd41fb085 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -290,17 +290,16 @@ static void *_event_loop(void *args) case GNRC_IPV6_NIB_SND_MC_NS: case GNRC_IPV6_NIB_SND_NA: case GNRC_IPV6_NIB_SEARCH_RTR: - case GNRC_IPV6_NIB_RECONFIRM_RTR: case GNRC_IPV6_NIB_REPLY_RS: case GNRC_IPV6_NIB_SND_MC_RA: case GNRC_IPV6_NIB_REACH_TIMEOUT: case GNRC_IPV6_NIB_DELAY_TIMEOUT: case GNRC_IPV6_NIB_ADDR_REG_TIMEOUT: - case GNRC_IPV6_NIB_6LO_CTX_TIMEOUT: case GNRC_IPV6_NIB_ABR_TIMEOUT: case GNRC_IPV6_NIB_PFX_TIMEOUT: case GNRC_IPV6_NIB_RTR_TIMEOUT: case GNRC_IPV6_NIB_RECALC_REACH_TIME: + case GNRC_IPV6_NIB_REREG_ADDRESS: 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-6ln.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c index e4beb0e09f..982635b9b8 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c @@ -15,6 +15,7 @@ #include "net/gnrc/netif2/internal.h" #include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/ndp2.h" #include "_nib-6ln.h" #include "_nib-6lr.h" @@ -27,11 +28,13 @@ static char addr_str[IPV6_ADDR_MAX_STR_LEN]; #endif +extern void _handle_search_rtr(gnrc_netif2_t *netif); + static inline bool _is_iface_eui64(gnrc_netif2_t *netif, const eui64_t *eui64) { /* TODO: adapt for short addresses */ return (netif->l2addr_len == sizeof(eui64_t)) && - (memcmp(&netif->l2addr, eui64, netif->l2addr_len) == 0); + (memcmp(&netif->l2addr, eui64, netif->l2addr_len) == 0); } static inline uint8_t _reverse_iid(const ipv6_addr_t *dst, @@ -47,7 +50,7 @@ static inline uint8_t _reverse_iid(const ipv6_addr_t *dst, l2addr[4] = dst->u8[14]; l2addr[5] = dst->u8[15]; return ETHERNET_ADDR_LEN; -#endif +#endif /* MODULE_NETDEV_ETH */ #ifdef MODULE_NETDEV_IEEE802154 case NETDEV_TYPE_IEEE802154: /* assume address was based on EUI-64 @@ -55,12 +58,12 @@ static inline uint8_t _reverse_iid(const ipv6_addr_t *dst, memcpy(l2addr, &dst->u64[1], sizeof(dst->u64[1])); l2addr[0] ^= 0x02; return sizeof(dst->u64[1]); -#endif +#endif /* MODULE_NETDEV_IEEE802154 */ #ifdef MODULE_CC110X case NETDEV_TYPE_CC110X: l2addr[0] = dst->u8[15]; return sizeof(uint8_t); -#endif +#endif /* MODULE_CC110X */ default: (void)dst; (void)l2addr; @@ -104,9 +107,6 @@ uint8_t _handle_aro(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao, _nib_onl_entry_t *nce) { -#if !GNRC_IPV6_NIB_CONF_6LR - (void)sl2ao; -#endif assert(netif != NULL); if (gnrc_netif2_is_6ln(netif) && (aro->len == SIXLOWPAN_ND_OPT_AR_LEN)) { DEBUG("nib: valid ARO received\n"); @@ -125,23 +125,27 @@ uint8_t _handle_aro(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, switch (aro->status) { case SIXLOWPAN_ND_STATUS_SUCCESS: { uint16_t ltime = byteorder_ntohs(aro->ltime); - uint32_t next_ns; + uint32_t rereg_time; + int idx = gnrc_netif2_ipv6_addr_idx(netif, &ipv6->dst); /* if ltime 1min, reschedule NS in 30sec, otherwise 1min * before timeout */ - next_ns = (ltime == 1U) ? (30 * MS_PER_SEC) : - (byteorder_ntohs(aro->ltime) - 1U) * - SEC_PER_MIN * MS_PER_SEC; - DEBUG("nib: Address registration successful. " - "Scheduling re-registration in %" PRIu32 "ms\n", - next_ns); - assert(nce != NULL); - _evtimer_add(nce, GNRC_IPV6_NIB_SND_UC_NS, &nce->nud_timeout, - next_ns); + rereg_time = (ltime == 1U) ? (30 * MS_PER_SEC) : + (ltime - 1U) * SEC_PER_MIN * MS_PER_SEC; + DEBUG("nib: Address registration of %s successful. " + "Scheduling re-registration in %" PRIu32 "ms\n", + ipv6_addr_to_str(addr_str, &ipv6->dst, + sizeof(addr_str)), rereg_time); + netif->ipv6.addrs_flags[idx] &= ~GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK; + netif->ipv6.addrs_flags[idx] |= GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID; + _evtimer_add(&netif->ipv6.addrs[idx], + GNRC_IPV6_NIB_REREG_ADDRESS, + &netif->ipv6.addrs_timers[idx], + rereg_time); break; } case SIXLOWPAN_ND_STATUS_DUP: DEBUG("nib: Address registration reports duplicate. " - "Removing address %s%%%u\n", + "Removing address %s%%%u\n", ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str)), netif->pid); @@ -150,17 +154,18 @@ uint8_t _handle_aro(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, break; case SIXLOWPAN_ND_STATUS_NC_FULL: { DEBUG("nib: Router's neighbor cache is full. " - "Searching new router for DAD\n"); + "Searching new router for DAD\n"); _nib_dr_entry_t *dr = _nib_drl_get(&ipv6->src, netif->pid); assert(dr != NULL); /* otherwise we wouldn't be here */ _nib_drl_remove(dr); if (_nib_drl_iter(NULL) == NULL) { /* no DRL left */ netif->ipv6.rs_sent = 0; - /* TODO: search new router */ + /* search (hopefully) new router */ + _handle_search_rtr(netif); } else { assert(dr->next_hop != NULL); - _snd_uc_ns(dr->next_hop, true); + _handle_rereg_address(&ipv6->dst); } } break; @@ -170,17 +175,153 @@ uint8_t _handle_aro(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, #if GNRC_IPV6_NIB_CONF_6LR else if (gnrc_netif2_is_6lr(netif) && (icmpv6->type == ICMPV6_NBR_SOL)) { - return _reg_addr_upstream(netif, ipv6, icmpv6, aro, sl2ao); + assert(nce != NULL); + return _reg_addr_upstream(netif, ipv6, icmpv6, aro, sl2ao, nce); } -#endif +#else /* GNRC_IPV6_NIB_CONF_6LR */ + (void)sl2ao; + (void)nce; +#endif /* GNRC_IPV6_NIB_CONF_6LR */ } #if ENABLE_DEBUG else if (aro->len != SIXLOWPAN_ND_OPT_AR_LEN) { DEBUG("nib: ARO of unexpected length %u, ignoring ARO\n", aro->len); } -#endif +#endif /* ENABLE_DEBUG */ return _ADDR_REG_STATUS_IGNORE; } + +static inline bool _is_tentative(const gnrc_netif2_t *netif, int idx) +{ + return (gnrc_netif2_ipv6_addr_get_state(netif, idx) & + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE); +} + +static inline bool _is_valid(const gnrc_netif2_t *netif, int idx) +{ + return (gnrc_netif2_ipv6_addr_get_state(netif, idx) == + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID); +} + +void _handle_rereg_address(const ipv6_addr_t *addr) +{ + gnrc_netif2_t *netif = gnrc_netif2_get_by_ipv6_addr(addr); + _nib_dr_entry_t *router = _nib_drl_get_dr(); + + if ((netif != NULL) && (router != NULL)) { + assert((unsigned)netif->pid == _nib_onl_get_if(router->next_hop)); + DEBUG("nib: Re-registering %s", + ipv6_addr_to_str(addr_str, addr, sizeof(addr_str))); + DEBUG(" with upstream router %s\n", + ipv6_addr_to_str(addr_str, &router->next_hop->ipv6, + sizeof(addr_str))); + _snd_ns(&router->next_hop->ipv6, netif, addr, &router->next_hop->ipv6); + } + else { + DEBUG("nib: Couldn't re-register %s, no current router found or address " + "wasn't assigned to any interface anymore.\n", + ipv6_addr_to_str(addr_str, addr, sizeof(addr_str))); + } + if (netif != NULL) { + int idx = gnrc_netif2_ipv6_addr_idx(netif, addr); + + if (_is_valid(netif, idx) || (_is_tentative(netif, idx) && + (gnrc_netif2_ipv6_addr_dad_trans(netif, idx) < + SIXLOWPAN_ND_REG_TRANSMIT_NUMOF))) { + uint32_t retrans_time; + + if (_is_valid(netif, idx)) { + retrans_time = SIXLOWPAN_ND_MAX_RS_SEC_INTERVAL; + } + else { + retrans_time = netif->ipv6.retrans_time; + /* increment encoded retransmission count */ + netif->ipv6.addrs_flags[idx]++; + } + _evtimer_add(&netif->ipv6.addrs[idx], GNRC_IPV6_NIB_REREG_ADDRESS, + &netif->ipv6.addrs_timers[idx], retrans_time); + } + else { + netif->ipv6.rs_sent = 0; + _handle_search_rtr(netif); + } + } +} + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C +_nib_abr_entry_t *_handle_abro(const sixlowpan_nd_opt_abr_t *abro) +{ + _nib_abr_entry_t *abr = NULL; + + if (abro->len != SIXLOWPAN_ND_OPT_ABR_LEN) { + /* ignore silently */ + return NULL; + } + abr = _nib_abr_add(&abro->braddr); + if (abr != NULL) { + uint32_t abro_version = sixlowpan_nd_opt_abr_get_version(abro); + uint16_t ltime = byteorder_ntohs(abro->ltime); + + if (abr->version >= abro_version) { + abr->version = abro_version; + abr->valid_until = _now_min() + ltime; + } + /* correct for default value */ + ltime = (ltime == 0) ? SIXLOWPAN_ND_OPT_ABR_LTIME_DEFAULT : ltime; + _evtimer_add(abr, GNRC_IPV6_NIB_ABR_TIMEOUT, &abr->timeout, + /* UINT16_MAX min < UINT32_MAX ms so no risk of overflow */ + MS_PER_SEC * SEC_PER_MIN * ltime); + } + return abr; +} +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C +uint32_t _handle_6co(const icmpv6_hdr_t *icmpv6, + const sixlowpan_nd_opt_6ctx_t *sixco, + _nib_abr_entry_t *abr) +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +uint32_t _handle_6co(const icmpv6_hdr_t *icmpv6, + const sixlowpan_nd_opt_6ctx_t *sixco) +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +{ + uint16_t ltime; + +#ifdef MODULE_GNRC_SIXLOWPAN_CTX + uint8_t cid; +#endif /* MODULE_GNRC_SIXLOWPAN_CTX */ + + if ((sixco->len != SIXLOWPAN_ND_OPT_6CTX_LEN_MIN) || + ((sixco->len != SIXLOWPAN_ND_OPT_6CTX_LEN_MAX) && + (sixco->ctx_len > 64U)) || + (icmpv6->type != ICMPV6_RTR_ADV)) { + DEBUG("nib: received 6CO of invalid length (%u), must be %u " + "or wasn't delivered by RA." + "\n", + sixco->len, + (sixco->ctx_len > 64U) ? SIXLOWPAN_ND_OPT_6CTX_LEN_MAX : + SIXLOWPAN_ND_OPT_6CTX_LEN_MIN); + return UINT32_MAX; + } + ltime = byteorder_ntohs(sixco->ltime); +#ifdef MODULE_GNRC_SIXLOWPAN_CTX + cid = sixlowpan_nd_opt_6ctx_get_cid(sixco); + gnrc_sixlowpan_ctx_update(cid, (ipv6_addr_t *)(sixco + 1), sixco->ctx_len, + ltime, sixlowpan_nd_opt_6ctx_is_comp(sixco)); +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + assert(abr != NULL); /* should have been set in _handle_abro() */ + if (ltime == 0) { + bf_unset(abr->ctxs, cid); + } + else { + bf_set(abr->ctxs, cid); + } +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +#else /* MODULE_GNRC_SIXLOWPAN_CTX */ + (void)abr; +#endif /* MODULE_GNRC_SIXLOWPAN_CTX */ + return ltime * SEC_PER_MIN * MS_PER_SEC; +} #else /* GNRC_IPV6_NIB_CONF_6LN */ typedef int dont_be_pedantic; #endif /* GNRC_IPV6_NIB_CONF_6LN */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h index 1ba822bf58..d371aebc93 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h @@ -23,6 +23,8 @@ #include "net/gnrc/ipv6/nib/conf.h" #include "net/sixlowpan/nd.h" +#include "timex.h" +#include "xtimer.h" #include "_nib-arsm.h" #include "_nib-internal.h" @@ -31,6 +33,12 @@ extern "C" { #endif +static inline uint32_t _now_min(void) +{ + return (uint32_t)((xtimer_now_usec64() / (US_PER_SEC * SEC_PER_MIN)) & + UINT32_MAX); +} + #if GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) /** * @brief Additional (local) status to ARO status values for tentative @@ -57,6 +65,37 @@ extern "C" { bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, gnrc_netif2_t *netif, gnrc_ipv6_nib_nc_t *nce); +/** + * @brief Calculates exponential backoff for RS retransmissions + * + * @see [RFC 6775, section 5.3](https://tools.ietf.org/html/rfc6775#section-5.3) + * + * @param[in] netif The network interface that the RS will be sent over. + * + * @return The interval in ms to the next RS + */ +static inline uint32_t _get_next_rs_interval(const gnrc_netif2_t *netif) +{ + if (gnrc_netif2_is_6ln(netif)) { + if (netif->ipv6.rs_sent < SIXLOWPAN_ND_MAX_RS_NUMOF) { + return SIXLOWPAN_ND_RS_MSEC_INTERVAL; + } + else { + unsigned exp = netif->ipv6.rs_sent - SIXLOWPAN_ND_MAX_RS_NUMOF; + uint32_t tmp = SIXLOWPAN_ND_RS_MSEC_INTERVAL + + ((1 << exp) * (NDP_RS_MS_INTERVAL)); + + if (tmp > (SIXLOWPAN_ND_MAX_RS_SEC_INTERVAL * MS_PER_SEC)) { + tmp = SIXLOWPAN_ND_MAX_RS_SEC_INTERVAL * MS_PER_SEC; + } + return tmp; + } + } + else { + return NDP_RS_MS_INTERVAL; + } +} + /** * @brief Handles ARO * @@ -74,11 +113,36 @@ uint8_t _handle_aro(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, const icmpv6_hdr_t *icmpv6, const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao, _nib_onl_entry_t *nce); + +/** + * @brief Handler for @ref GNRC_IPV6_NIB_REREG_ADDRESS event handler + * + * @param[in] addr An IPv6 address. + */ +void _handle_rereg_address(const ipv6_addr_t *addr); + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C || defined(DOXYGEN) +_nib_abr_entry_t *_handle_abro(const sixlowpan_nd_opt_abr_t *abro); +uint32_t _handle_6co(const icmpv6_hdr_t *icmpv6, + const sixlowpan_nd_opt_6ctx_t *sixco, + _nib_abr_entry_t *abr); +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C || defined(DOXYGEN) */ +uint32_t _handle_6co(const icmpv6_hdr_t *icmpv6, + const sixlowpan_nd_opt_6ctx_t *sixco); +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C || defined(DOXYGEN) */ #else /* GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) */ #define _resolve_addr_from_ipv6(dst, netif, nce) (false) /* _handle_aro() doesn't make sense without 6LR so don't even use it * => throw error in case it is compiled in => don't define it here as NOP macro */ +#define _get_next_rs_interval(netif) (NDP_RS_MS_INTERVAL) +#define _handle_rereg_address(netif) (void)netif +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C || defined(DOXYGEN) +#define _handle_abro(abro) (NULL) +#define _handle_6co(icmpv6, sixco, abr) (UINT32_MAX) +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C || defined(DOXYGEN) */ +#define _handle_6co(icmpv6, sixco) (UINT32_MAX) +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C || defined(DOXYGEN) */ #endif /* GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.c index 9cfaf55f0a..4342b0b354 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.c @@ -51,11 +51,9 @@ static uint8_t _update_nce_ar_state(const sixlowpan_nd_opt_ar_t *aro, uint8_t _reg_addr_upstream(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, const icmpv6_hdr_t *icmpv6, const sixlowpan_nd_opt_ar_t *aro, - const ndp_opt_t *sl2ao) + const ndp_opt_t *sl2ao, _nib_onl_entry_t *nce) { if (!ipv6_addr_is_unspecified(&ipv6->src) && (sl2ao != NULL)) { - _nib_onl_entry_t *nce = _nib_onl_get(&ipv6->src, netif->pid); - DEBUG("nib: Trying to register %s with EUI-64 " "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)), @@ -66,9 +64,11 @@ uint8_t _reg_addr_upstream(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, (memcmp(&nce->eui64, &aro->eui64, sizeof(aro->eui64)) == 0)) { #if GNRC_IPV6_NIB_CONF_MULTIHOP_DAD /* TODO */ -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_DAD */ if (aro->ltime.u16 != 0) { _handle_sl2ao(netif, ipv6, icmpv6, sl2ao); + /* re-get NCE in case it was updated */ + nce = _nib_onl_get(&ipv6->src, netif->pid); return _update_nce_ar_state(aro, nce); } else if (nce != NULL) { @@ -76,7 +76,8 @@ uint8_t _reg_addr_upstream(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, return SIXLOWPAN_ND_STATUS_SUCCESS; } } - else { + else if (_get_ar_state(nce) != GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC) { + /* ignore address registration requests from upstream */ DEBUG("nib: Could not register %s, duplicate entry with EUI-64 " "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)), @@ -116,7 +117,7 @@ gnrc_pktsnip_t *_copy_and_handle_aro(gnrc_netif2_t *netif, DEBUG("nib: Address was marked TENTATIVE => not replying NS, " "waiting for DAC\n"); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_DAD */ } return reply_aro; } diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.h index 6248600e69..e7b7661a55 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.h @@ -83,6 +83,9 @@ static inline bool _rtr_sol_on_6lr(const gnrc_netif2_t *netif, * handed to the SL2AO handler function). * @param[in] aro ARO that carries the address registration information. * @param[in] sl2ao SL2AO associated with the ARO. + * @param[in] nce The local neighbor cache entry the registration + * information is supposed to be copied into. May be NULL + * (this might create one). * * @return registration status of the address (including * @ref _ADDR_REG_STATUS_TENTATIVE and @ref _ADDR_REG_STATUS_IGNORE). @@ -90,7 +93,7 @@ static inline bool _rtr_sol_on_6lr(const gnrc_netif2_t *netif, uint8_t _reg_addr_upstream(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, const icmpv6_hdr_t *icmpv6, const sixlowpan_nd_opt_ar_t *aro, - const ndp_opt_t *sl2ao); + const ndp_opt_t *sl2ao, _nib_onl_entry_t *nce); /** @@ -112,15 +115,23 @@ gnrc_pktsnip_t *_copy_and_handle_aro(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv const ndp_nbr_sol_t *nbr_sol, const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao); + +/** + * @brief Sets the @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV flags of an interface + * + * @param[in] netif The interface. + */ +void _set_rtr_adv(gnrc_netif2_t *netif); #else /* GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN) */ #define _rtr_sol_on_6lr(netif, icmpv6) (false) #define _get_ar_state(nbr) (_ADDR_REG_STATUS_IGNORE) #define _set_ar_state(nbr, state) (void)nbr; (void)state -#define _copy_and_handle_aro(netif, ipv6, icmpv6, aro, sl2ao) \ - (NULL) /* _reg_addr_upstream() doesn't make sense without 6LR so don't even use it * => throw error in case it is compiled in => don't define it here as NOP macro */ +#define _copy_and_handle_aro(netif, ipv6, icmpv6, aro, sl2ao) \ + (NULL) +#define _set_rtr_adv(netif) (void)netif #endif /* GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN) */ #ifdef __cplusplus diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c index 22ac8c2ce1..a1d814e10e 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c @@ -19,9 +19,10 @@ #include "net/gnrc/netif2/internal.h" #ifdef MODULE_GNRC_SIXLOWPAN_ND #include "net/gnrc/sixlowpan/nd.h" -#endif +#endif /* MODULE_GNRC_SIXLOWPAN_ND */ #include "_nib-arsm.h" +#include "_nib-router.h" #include "_nib-6lr.h" #define ENABLE_DEBUG (0) @@ -88,16 +89,16 @@ void _snd_uc_ns(_nib_onl_entry_t *nbr, bool reset) if (reset) { nbr->ns_sent = 0; } -#else +#else /* GNRC_IPV6_NIB_CONF_ARSM */ (void)reset; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ _snd_ns(&nbr->ipv6, netif, NULL, &nbr->ipv6); _evtimer_add(nbr, GNRC_IPV6_NIB_SND_UC_NS, &nbr->nud_timeout, netif->ipv6.retrans_time); gnrc_netif2_release(netif); #if GNRC_IPV6_NIB_CONF_ARSM nbr->ns_sent++; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } void _handle_sl2ao(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, @@ -118,7 +119,7 @@ void _handle_sl2ao(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, (memcmp(nce->l2addr, sl2ao + 1, nce->l2addr_len) != 0)) && /* a 6LR MUST NOT modify an existing NCE based on an SL2AO in an RS * see https://tools.ietf.org/html/rfc6775#section-6.3 */ - !_rtr_sol_on_6lr(netif, icmpv6)) { + !_rtr_sol_on_6lr(netif, icmpv6)) { DEBUG("nib: L2 address differs. Setting STALE\n"); evtimer_del(&_nib_evtimer, &nce->nud_timeout.event); _set_nud_state(netif, nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE); @@ -142,13 +143,13 @@ void _handle_sl2ao(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, &nce->addr_reg_timeout, SIXLOWPAN_ND_TENTATIVE_NCE_SEC_LTIME * MS_PER_SEC); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_DAD && GNRC_IPV6_NIB_CONF_6LR */ } #if ENABLE_DEBUG else { DEBUG("nib: Neighbor cache full\n"); } -#endif +#endif /* ENABLE_DEBUG */ } /* not else to include NCE created in nce == NULL branch */ if ((nce != NULL) && (nce->mode & _NC)) { @@ -171,7 +172,7 @@ void _handle_sl2ao(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, nce->l2addr_len = l2addr_len; memcpy(nce->l2addr, sl2ao + 1, l2addr_len); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } } @@ -183,17 +184,17 @@ static inline unsigned _get_l2addr_len(gnrc_netif2_t *netif, case NETDEV_TYPE_CC110X: (void)opt; return sizeof(uint8_t); -#endif +#endif /* MODULE_CC110X */ #ifdef MODULE_NETDEV_ETH case NETDEV_TYPE_ETHERNET: (void)opt; return ETHERNET_ADDR_LEN; -#endif +#endif /* MODULE_NETDEV_ETH */ #ifdef MODULE_NETDEV_NRFMIN case NETDEV_TYPE_NRFMIN: (void)opt; return sizeof(uint16_t); -#endif +#endif /* MODULE_NETDEV_NRFMIN */ #ifdef MODULE_NETDEV_IEEE802154 case NETDEV_TYPE_IEEE802154: switch (opt->len) { @@ -204,7 +205,7 @@ static inline unsigned _get_l2addr_len(gnrc_netif2_t *netif, default: return 0U; } -#endif +#endif /* MODULE_NETDEV_IEEE802154 */ default: (void)opt; return 0U; @@ -314,6 +315,7 @@ void _handle_state_timeout(_nib_onl_entry_t *nbr) void _probe_nbr(_nib_onl_entry_t *nbr, bool reset) { const uint16_t state = _get_nud_state(nbr); + DEBUG("nib: Probing "); switch (state) { case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED: @@ -363,7 +365,7 @@ void _probe_nbr(_nib_onl_entry_t *nbr, bool reset) sizeof(addr_str)), (unsigned)netif->ipv6.retrans_time); } -#endif +#endif /* ENABLE_DEBUG */ gnrc_netif2_release(netif); } break; @@ -483,14 +485,14 @@ void _set_nud_state(gnrc_netif2_t *netif, _nib_onl_entry_t *nce, #if GNRC_IPV6_NIB_CONF_ROUTER gnrc_netif2_acquire(netif); - if ((netif != NULL) && (netif->ipv6.route_info_cb)) { - netif->ipv6.route_info_cb(GNRC_IPV6_NIB_ROUTE_INFO_TYPE_NSC, - &nce->ipv6, (void *)((intptr_t)state)); + if (netif != NULL) { + _call_route_info_cb(netif, GNRC_IPV6_NIB_ROUTE_INFO_TYPE_NSC, + &nce->ipv6, (void *)((intptr_t)state)); } gnrc_netif2_release(netif); -#else +#else /* GNRC_IPV6_NIB_CONF_ROUTER */ (void)netif; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ } /* internal functions */ @@ -515,7 +517,7 @@ static inline bool _redirect_with_tl2ao(icmpv6_hdr_t *icmpv6, ndp_opt_t *tl2ao) { return (icmpv6->type == ICMPV6_REDIRECT) && (tl2ao != NULL); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_REDIRECT */ static inline bool _tl2ao_changes_nce(_nib_onl_entry_t *nce, const ndp_opt_t *tl2ao, diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c index 4b1be07f50..98d528d839 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -25,6 +25,7 @@ #include "random.h" #include "_nib-internal.h" +#include "_nib-router.h" #define ENABLE_DEBUG (0) #include "debug.h" @@ -39,7 +40,7 @@ static _nib_dr_entry_t _def_routers[GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF]; #if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C static _nib_abr_entry_t _abrs[GNRC_IPV6_NIB_ABR_NUMOF]; -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ #if ENABLE_DEBUG static char addr_str[IPV6_ADDR_MAX_STR_LEN]; @@ -62,8 +63,8 @@ void _nib_init(void) memset(_dsts, 0, sizeof(_dsts)); #if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C memset(_abrs, 0, sizeof(_abrs)); -#endif -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +#endif /* TEST_SUITES */ evtimer_init_msg(&_nib_evtimer); /* TODO: load ABR information from persistent memory */ } @@ -103,7 +104,7 @@ _nib_onl_entry_t *_nib_onl_alloc(const ipv6_addr_t *addr, unsigned iface) else { DEBUG(" NIB full\n"); } -#endif +#endif /* ENABLE_DEBUG */ return node; } @@ -233,15 +234,15 @@ void _nib_nc_set_reachable(_nib_onl_entry_t *node) if (netif == NULL) { return; } -#endif +#endif /* TEST_SUITES */ DEBUG("nib: set %s%%%u reachable (reachable time = %u)\n", ipv6_addr_to_str(addr_str, &node->ipv6, sizeof(addr_str)), _nib_onl_get_if(node), (unsigned)netif->ipv6.reach_time); _evtimer_add(node, GNRC_IPV6_NIB_REACH_TIMEOUT, &node->nud_timeout, netif->ipv6.reach_time); -#else +#else /* GNRC_IPV6_NIB_CONF_ARSM */ (void)node; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } void _nib_nc_remove(_nib_onl_entry_t *node) @@ -253,10 +254,13 @@ void _nib_nc_remove(_nib_onl_entry_t *node) evtimer_del((evtimer_t *)&_nib_evtimer, &node->snd_na.event); #if GNRC_IPV6_NIB_CONF_ARSM evtimer_del((evtimer_t *)&_nib_evtimer, &node->nud_timeout.event); -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ +#if GNRC_IPV6_NIB_CONF_ROUTER + evtimer_del((evtimer_t *)&_nib_evtimer, &node->reply_rs.event); +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ #if GNRC_IPV6_NIB_CONF_6LR evtimer_del((evtimer_t *)&_nib_evtimer, &node->addr_reg_timeout.event); -#endif +#endif /* GNRC_IPV6_NIB_CONF_6LR */ #if GNRC_IPV6_NIB_CONF_QUEUE_PKT gnrc_pktqueue_t *tmp; for (gnrc_pktqueue_t *ptr = node->pktqueue; @@ -293,17 +297,17 @@ void _nib_nc_get(const _nib_onl_entry_t *node, gnrc_ipv6_nib_nc_t *nce) return; } } -#else +#else /* GNRC_IPV6_NIB_CONF_6LN */ /* Prevent unused function error thrown by clang */ (void)_get_l2addr_from_ipv6; -#endif +#endif /* GNRC_IPV6_NIB_CONF_6LN */ nce->l2addr_len = node->l2addr_len; memcpy(&nce->l2addr, &node->l2addr, node->l2addr_len); -#else +#else /* GNRC_IPV6_NIB_CONF_ARSM */ assert(ipv6_addr_is_link_local(&nce->ipv6)); _get_l2addr_from_ipv6(nce->l2addr, &node->ipv6); nce->l2addr_len = sizeof(uint64_t); -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } _nib_dr_entry_t *_nib_drl_add(const ipv6_addr_t *router_addr, unsigned iface) @@ -495,7 +499,7 @@ static inline bool _in_abrs(const _nib_abr_entry_t *abr) { return (abr < (_abrs + GNRC_IPV6_NIB_ABR_NUMOF)); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ void _nib_offl_clear(_nib_offl_entry_t *dst) { @@ -588,14 +592,22 @@ int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt, (void *)pkt); _nib_offl_entry_t *offl = _nib_offl_get_match(dst); - assert((dst != NULL) && (fte != NULL)); if ((offl == NULL) || (offl->mode == _PL)) { /* give default router precedence over PLE */ _nib_dr_entry_t *router = _nib_drl_get_dr(); if ((router == NULL) && (offl == NULL)) { +#if GNRC_IPV6_NIB_CONF_ROUTER + gnrc_netif2_t *ptr = NULL; + + while ((ptr = gnrc_netif2_iter(ptr))) { + _call_route_info_cb(ptr, + GNRC_IPV6_NIB_ROUTE_INFO_TYPE_RRQ, + dst, pkt); + } +#else /* GNRC_IPV6_NIB_CONF_ROUTER */ (void)pkt; - /* TODO: ask RRP to search for route (using pkt) */ +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ return -ENETUNREACH; } else if (router != NULL) { @@ -629,7 +641,7 @@ void _nib_pl_remove(_nib_offl_entry_t *nib_offl) } } } -#endif +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ } #if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C @@ -660,7 +672,7 @@ _nib_abr_entry_t *_nib_abr_add(const ipv6_addr_t *addr) else { DEBUG(" NIB full\n"); } -#endif +#endif /* ENABLE_DEBUG */ return abr; } @@ -682,7 +694,7 @@ void _nib_abr_remove(const ipv6_addr_t *addr) gnrc_sixlowpan_ctx_remove(i); } } -#endif +#endif /* MODULE_GNRC_SIXLOWPAN_CTX */ memset(abr, 0, sizeof(_nib_abr_entry_t)); } } diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h index 97af9ec93e..9b914c12a1 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -60,6 +60,15 @@ extern "C" { to this @ref _nib_onl_entry_t */ /** @} */ +/** + * @name Off-link entry flags + * @anchor net_gnrc_ipv6_nib_offl_flags + * @{ + */ +#define _PFX_ON_LINK (0x0001) +#define _PFX_SLAAC (0x0002) +/** @} */ + /** * @brief Shorter name for convenience ;-) */ @@ -131,6 +140,9 @@ typedef struct _nib_onl_entry { * @brief Event for @ref GNRC_IPV6_NIB_SND_NA */ evtimer_msg_event_t snd_na; +#if GNRC_IPV6_NIB_CONF_ROUTER || defined(DOXYGEN) + evtimer_msg_event_t reply_rs; /**< Event for @ref GNRC_IPV6_NIB_REPLY_RS */ +#endif #if GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN) evtimer_msg_event_t addr_reg_timeout; /**< Event for @ref GNRC_IPV6_NIB_ADDR_REG_TIMEOUT */ #endif @@ -150,12 +162,14 @@ typedef struct _nib_onl_entry { * @see [Mode flags for entries](@ref net_gnrc_ipv6_nib_mode). */ uint8_t mode; +#if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN) /** * @brief Neighbor solicitations sent for probing + * + * @note Only available if @ref GNRC_IPV6_NIB_CONF_ARSM != 0. */ uint8_t ns_sent; -#if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN) /** * @brief length in bytes of _nib_onl_entry_t::l2addr * @@ -187,6 +201,7 @@ typedef struct { evtimer_msg_event_t pfx_timeout; uint8_t mode; /**< [mode](@ref net_gnrc_ipv6_nib_mode) of the * off-link entry */ + uint16_t flags; /**< [flags](@ref net_gnrc_ipv6_nib_offl_flags */ uint32_t valid_until; /**< timestamp (in ms) until which the prefix valid (UINT32_MAX means forever) */ uint32_t pref_until; /**< timestamp (in ms) until which the prefix @@ -201,6 +216,8 @@ typedef struct { ipv6_addr_t addr; /**< The address of the border router */ uint32_t version; /**< last received version of the info of * the _nib_abr_entry_t::addr */ + uint32_t valid_until; /**< timestamp (in minutes) until which + * information is valid */ evtimer_msg_event_t timeout; /**< timeout of the information */ /** * @brief Bitfield marking the prefixes in the NIB's off-link entries @@ -589,11 +606,15 @@ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) * @pre `(pfx != NULL) && (pfx != "::") && (pfx_len != 0) && (pfx_len <= 128)` * @pre `(pref_ltime <= valid_ltime)` * - * @param[in] iface The interface to the prefix is added to. - * @param[in] pfx The IPv6 prefix or address of the destination. - * May not be NULL or unspecified address. Use - * @ref _nib_drl_add() for default route destinations. - * @param[in] pfx_len The length in bits of @p pfx in bits. + * @param[in] iface The interface to the prefix is added to. + * @param[in] pfx The IPv6 prefix or address of the destination. + * May not be NULL or unspecified address. Use + * @ref _nib_drl_add() for default route destinations. + * @param[in] pfx_len The length in bits of @p pfx in bits. + * @param[in] valid_ltime Valid lifetime in microseconds. `UINT32_MAX` for + * infinite. + * @param[in] pref_ltime Preferred lifetime in microseconds. `UINT32_MAX` for + * infinite. * * @return A new or existing off-link entry with _nib_offl_entry_t::pfx set to * @p pfx. diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-router.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-router.c new file mode 100644 index 0000000000..90d95baad1 --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-router.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2017 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 "net/gnrc/ipv6/nib.h" +#include "net/gnrc/ndp2.h" +#include "net/gnrc/netif2/internal.h" +#include "net/gnrc/sixlowpan/nd.h" + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C +#include "_nib-6ln.h" +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +#include "_nib-router.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if GNRC_IPV6_NIB_CONF_ROUTER +#if ENABLE_DEBUG +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +static void _snd_ra(gnrc_netif2_t *netif, const ipv6_addr_t *dst, + bool final, _nib_abr_entry_t *abr); + +void _handle_reply_rs(_nib_onl_entry_t *host) +{ + gnrc_netif2_t *netif = gnrc_netif2_get_by_pid(_nib_onl_get_if(host)); + + assert(netif != NULL); + gnrc_netif2_acquire(netif); + if (gnrc_netif2_is_rtr_adv(netif)) { + _snd_rtr_advs(netif, &host->ipv6, false); + } + gnrc_netif2_release(netif); +} + +void _handle_snd_mc_ra(gnrc_netif2_t *netif) +{ + gnrc_netif2_acquire(netif); + assert(netif != NULL); + if (!gnrc_netif2_is_6ln(netif)) { + bool final_ra = (netif->ipv6.ra_sent > (UINT8_MAX - NDP_MAX_FIN_RA_NUMOF)); + uint32_t next_ra_time = random_uint32_range(NDP_MIN_RA_INTERVAL_MS, + NDP_MAX_RA_INTERVAL_MS); + + /* router has router advertising interface or the RA is one of the + * (now deactivated) routers final one */ + if (final_ra || gnrc_netif2_is_rtr_adv(netif)) { + _snd_rtr_advs(netif, NULL, final_ra); + netif->ipv6.last_ra = (xtimer_now_usec64() / US_PER_MS) & UINT32_MAX; + if ((netif->ipv6.ra_sent < NDP_MAX_INIT_RA_NUMOF) || final_ra) { + if ((netif->ipv6.ra_sent < NDP_MAX_INIT_RA_NUMOF) && + (next_ra_time > NDP_MAX_INIT_RA_INTERVAL)) { + next_ra_time = NDP_MAX_INIT_RA_INTERVAL; + } + netif->ipv6.ra_sent++; + } + /* netif->ipv6.ra_sent overflowed => this was our last final RA */ + if (netif->ipv6.ra_sent != 0) { + _evtimer_add(netif, GNRC_IPV6_NIB_SND_MC_RA, &netif->ipv6.snd_mc_ra, + next_ra_time); + } + } + } + gnrc_netif2_release(netif); +} + +void _snd_rtr_advs(gnrc_netif2_t *netif, const ipv6_addr_t *dst, bool final) +{ +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + _nib_abr_entry_t *abr = NULL; + + DEBUG("nib: Send router advertisements for each border router:\n"); + while ((abr = _nib_abr_iter(abr))) { + DEBUG(" - %s\n", ipv6_addr_to_str(addr_str, &abr->addr, + sizeof(addr_str))); + _snd_ra(netif, dst, final, abr); + } +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + _snd_ra(netif, dst, final, NULL); +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +} + +static gnrc_pktsnip_t *_offl_to_pio(_nib_offl_entry_t *offl, + gnrc_pktsnip_t *ext_opts) +{ + uint32_t now = (xtimer_now_usec64() / US_PER_MS) & UINT32_MAX; + gnrc_pktsnip_t *pio; + uint8_t flags = 0; + uint32_t valid_ltime = (offl->valid_until == UINT32_MAX) ? UINT32_MAX : + (offl->valid_until - now); + uint32_t pref_ltime = (offl->pref_until == UINT32_MAX) ? UINT32_MAX : + (offl->pref_until - now); + + DEBUG("nib: Build PIO for %s/%u\n", + ipv6_addr_to_str(addr_str, &offl->pfx, sizeof(addr_str)), + offl->pfx_len); + if (offl->flags & _PFX_ON_LINK) { + flags |= NDP_OPT_PI_FLAGS_L; + } + if (offl->flags & _PFX_SLAAC) { + flags |= NDP_OPT_PI_FLAGS_A; + } + pio = gnrc_ndp2_opt_pi_build(&offl->pfx, offl->pfx_len, valid_ltime, + pref_ltime, flags, ext_opts); + + if ((pio == NULL) && (ext_opts != NULL)) { + DEBUG("nib: No space left in packet buffer. Not adding PIO\n"); + return NULL; + } + return pio; +} + +static gnrc_pktsnip_t *_build_ext_opts(gnrc_netif2_t *netif, + _nib_abr_entry_t *abr) +{ + gnrc_pktsnip_t *ext_opts = NULL; + _nib_offl_entry_t *pfx = NULL; + unsigned id = netif->pid; + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + uint16_t ltime; + gnrc_pktsnip_t *abro; + +#ifdef MODULE_GNRC_SIXLOWPAN_CTX + for (int i = 0; i < GNRC_SIXLOWPAN_CTX_SIZE; i++) { + gnrc_sixlowpan_ctx_t *ctx; + if (bf_isset(abr->ctxs, i) && + ((ctx = gnrc_sixlowpan_ctx_lookup_id(i)) != NULL)) { + gnrc_pktsnip_t *sixco = gnrc_sixlowpan_nd_opt_6ctx_build( + ctx->prefix_len, ctx->flags_id, + ctx->ltime, &ctx->prefix, NULL); + if (sixco == NULL) { + DEBUG("nib: No space left in packet buffer. Not adding 6LO\n"); + return NULL; + } + ext_opts = sixco; + } + } +#endif /* MODULE_GNRC_SIXLOWPAN_CTX */ + while ((pfx = _nib_abr_iter_pfx(abr, pfx))) { + if (_nib_onl_get_if(pfx->next_hop) == id) { + if ((ext_opts = _offl_to_pio(pfx, ext_opts)) == NULL) { + return NULL; + } + } + } + ltime = (gnrc_netif2_is_6lbr(netif)) ? + (SIXLOWPAN_ND_OPT_ABR_LTIME_DEFAULT) : + (abr->valid_until - _now_min()); + (void)ltime; /* gnrc_sixlowpan_nd_opt_abr_build might evaluate to NOP */ + abro = gnrc_sixlowpan_nd_opt_abr_build(abr->version, ltime, &abr->addr, + ext_opts); + if (abro == NULL) { + DEBUG("nib: No space left in packet buffer. Not adding ABRO\n"); + return NULL; + } + ext_opts = abro; +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + (void)abr; + while ((pfx = _nib_offl_iter(pfx))) { + if ((pfx->mode & _PL) && (_nib_onl_get_if(pfx->next_hop) == id)) { + if ((ext_opts = _offl_to_pio(pfx, ext_opts)) == NULL) { + return NULL; + } + } + } +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + return ext_opts; +} + +void _set_rtr_adv(gnrc_netif2_t *netif) +{ + DEBUG("nib: set RTR_ADV flag for interface %i\n", netif->pid); + netif->ipv6.ra_sent = 0; + netif->flags |= GNRC_NETIF2_FLAGS_IPV6_RTR_ADV; + _handle_snd_mc_ra(netif); +} + +static void _snd_ra(gnrc_netif2_t *netif, const ipv6_addr_t *dst, + bool final, _nib_abr_entry_t *abr) +{ + gnrc_pktsnip_t *ext_opts = _build_ext_opts(netif, abr); + + gnrc_ndp2_rtr_adv_send(netif, NULL, dst, final, ext_opts); +} +#else /* GNRC_IPV6_NIB_CONF_ROUTER */ +typedef int dont_be_pedantic; +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ + +/** @} */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-router.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-router.h new file mode 100644 index 0000000000..1205dc555f --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-router.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 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 + * @internal + * @{ + * + * @file + * @brief Definitions related to router functionality of NIB + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef PRIV_NIB_ROUTER_H +#define PRIV_NIB_ROUTER_H + +#include "net/gnrc/ipv6/nib/conf.h" +#include "net/gnrc/netif2/internal.h" +#include "net/gnrc/netif2/ipv6.h" +#include "net/ipv6/addr.h" +#include "net/ndp.h" + +#include "_nib-internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if GNRC_IPV6_NIB_CONF_ROUTER || defined(DOXYGEN) +/** + * @brief Initializes interface for router behavior + * + * @param[in] netif An interface. + */ +static inline void _init_iface_router(gnrc_netif2_t *netif) +{ + netif->ipv6.rtr_ltime = NDP_RTR_LTIME_SEC; + netif->ipv6.last_ra = UINT32_MAX; + netif->ipv6.ra_sent = 0; + netif->flags |= GNRC_NETIF2_FLAGS_IPV6_FORWARDING; +#if !GNRC_IPV6_NIB_CONF_6LR || GNRC_IPV6_NIB_CONF_6LBR + netif->flags |= GNRC_NETIF2_FLAGS_IPV6_RTR_ADV; +#endif /* !GNRC_IPV6_NIB_CONF_6LR || GNRC_IPV6_NIB_CONF_6LBR */ +#if GNRC_IPV6_NIB_CONF_6LBR + netif->flags |= GNRC_NETIF2_FLAGS_6LO_ABR; +#endif /* GNRC_IPV6_NIB_CONF_6LBR */ + gnrc_netif2_ipv6_group_join(netif, &ipv6_addr_all_routers_link_local); +} + +/** + * @brief Helper function to safely call the + * [route info callback](@ref gnrc_netif2_ipv6_t::route_info_cb) of an + * interface + * + * @param[in] netif An interface. + * @param[in] type [Type](@ref net_gnrc_ipv6_nib_route_info_type) of the + * route info. + * @param[in] ctx_addr Context address of the route info. + * @param[in] ctx Further context of the route info. + */ +static inline void _call_route_info_cb(gnrc_netif2_t *netif, unsigned type, + const ipv6_addr_t *ctx_addr, + const void *ctx) +{ + if (netif->ipv6.route_info_cb != NULL) { + netif->ipv6.route_info_cb(type, ctx_addr, ctx); + } +} + +/** + * @brief Handler for @ref GNRC_IPV6_NIB_REPLY_RS event handler + * + * @param[in] host Host that sent the router solicitation + */ +void _handle_reply_rs(_nib_onl_entry_t *host); + +/** + * @brief Handler for @ref GNRC_IPV6_NIB_SND_MC_RA event handler + * + * @param[in] netif Network interface to send multicast router advertisement + * over. + */ +void _handle_snd_mc_ra(gnrc_netif2_t *netif); + +/** + * @brief Set the @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV flag for an interface + * and starts advertising that interface as a router + * + * @param[in] netif Interface to set the @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV + * for. + */ +void _set_rtr_adv(gnrc_netif2_t *netif); + +/** + * @brief Send router advertisements + * + * If @ref GNRC_IPV6_NIB_CONF_MULTIHOP_P6C is not 0 this sends one router + * advertisement per configured ABR, otherwise it just sends one single router + * advertisement for the interface. + * + * @param[in] netif The interface to send the router advertisement over. + * @param[in] dst Destination address for the router advertisement. + * @param[in] final The router advertisement are the final ones of the @p netif + * (because it was set to be a non-forwarding interface e.g.). + */ +void _snd_rtr_advs(gnrc_netif2_t *netif, const ipv6_addr_t *dst, + bool final); +#else /* GNRC_IPV6_NIB_CONF_ROUTER */ +#define _init_iface_router(netif) (void)netif +#define _call_route_info_cb(netif, type, ctx_addr, ctx) (void)netif; \ + (void)type; \ + (void)ctx_addr; \ + (void)ctx +#define _handle_reply_rs(host) (void)host +#define _handle_snd_mc_ra(netif) (void)netif +#define _set_rtr_adv(netif) (void)netif +#define _snd_rtr_advs(netif, dst, final) (void)netif; \ + (void)dst; \ + (void)final +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ + +#ifdef __cplusplus +} +#endif + +#endif /* PRIV_NIB_ROUTER_H */ +/** @} */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index 58219d4503..9ff8d15275 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -29,6 +29,7 @@ #include "_nib-internal.h" #include "_nib-arsm.h" +#include "_nib-router.h" #include "_nib-6ln.h" #include "_nib-6lr.h" @@ -44,12 +45,18 @@ static char addr_str[IPV6_ADDR_MAX_STR_LEN]; #if GNRC_IPV6_NIB_CONF_QUEUE_PKT static gnrc_pktqueue_t _queue_pool[GNRC_IPV6_NIB_NUMOF]; -#endif +#endif /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */ /** * @internal * @{ */ +#if GNRC_IPV6_NIB_CONF_ROUTER +static void _handle_rtr_sol(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, + const ndp_rtr_sol_t *rtr_sol, size_t icmpv6_len); +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ +static void _handle_rtr_adv(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, + const ndp_rtr_adv_t *rtr_adv, size_t icmpv6_len); static void _handle_nbr_sol(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, const ndp_nbr_sol_t *nbr_sol, size_t icmpv6_len); static void _handle_nbr_adv(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, @@ -59,7 +66,19 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt, gnrc_ipv6_nib_nc_t *nce, _nib_onl_entry_t *entry); +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_netif2_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_netif2_t *netif); /** @} */ void gnrc_ipv6_nib_init(void) @@ -78,51 +97,20 @@ void gnrc_ipv6_nib_init(void) void gnrc_ipv6_nib_init_iface(gnrc_netif2_t *netif) { - ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED; - assert(netif != NULL); DEBUG("nib: Initialize interface %u\n", netif->pid); gnrc_netif2_acquire(netif); - /* TODO: - * - set link-local address here for stateless address auto-configuration - * and 6LN - * - join solicited nodes group of link-local address here for address - * resolution here - * - join all router group of link-local address here on router node here - * - become an router advertising interface here on non-6LR here */ _init_iface_arsm(netif); netif->ipv6.retrans_time = NDP_RETRANS_TIMER_MS; - netif->ipv6.na_sent = 0; -#if GNRC_IPV6_NIB_CONF_ROUTER - netif->ipv6.rtr_ltime = 1800U; - netif->ipv6.last_ra = UINT32_MAX; - netif->ipv6.ra_sent = 0; - netif->flags |= GNRC_NETIF2_FLAGS_IPV6_FORWARDING; -#if !GNRC_IPV6_NIB_CONF_6LR || GNRC_IPV6_NIB_CONF_6LBR - netif->flags |= GNRC_NETIF2_FLAGS_IPV6_RTR_ADV; -#endif -#if GNRC_IPV6_NIB_CONF_6LBR - netif->flags |= GNRC_NETIF2_FLAGS_6LO_ABR; -#endif - memcpy(&addr, &ipv6_addr_all_routers_link_local, sizeof(addr)); - if (gnrc_netif2_ipv6_group_join(netif, &addr) < 0) { - LOG_ERROR("nib: Can't join link-local all-routers on interface %u\n", - netif->pid); - return; - } -#endif +#if GNRC_IPV6_NIB_CONF_SLAAC || GNRC_IPV6_NIB_CONF_6LN + /* TODO: set differently dependent on GNRC_IPV6_NIB_CONF_SLAAC if + * alternatives exist */ + netif->ipv6.aac_mode = GNRC_NETIF2_AAC_AUTO; +#endif /* GNRC_IPV6_NIB_CONF_SLAAC || GNRC_IPV6_NIB_CONF_6LN */ + _init_iface_router(netif); #if GNRC_IPV6_NIB_CONF_6LN netif->ipv6.rs_sent = 0; -#endif - memcpy(&addr, &ipv6_addr_all_nodes_link_local, sizeof(addr)); - if (gnrc_netif2_ipv6_group_join(netif, &addr) < 0) { - LOG_ERROR("nib: Can't join link-local all-nodes on interface %u\n", - netif->pid); - return; - } -#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC -#if GNRC_IPV6_NIB_CONF_6LN if (netif->device_type == NETDEV_TYPE_IEEE802154) { /* see https://tools.ietf.org/html/rfc6775#section-5.2 */ uint16_t src_len = IEEE802154_LONG_ADDRESS_LEN; @@ -134,35 +122,52 @@ void gnrc_ipv6_nib_init_iface(gnrc_netif2_t *netif) * directly everything else would deadlock anyway */ netif->ops->set(netif, &opt); } -#endif - uint8_t flags = GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID; - /* TODO: set TENTATIVE as soon as there is a SLAAC implementation if not - * 6LN ;-) */ - - gnrc_netif2_ipv6_get_iid(netif, (eui64_t *)&addr.u64[1]); - ipv6_addr_set_link_local_prefix(&addr); - if (gnrc_netif2_ipv6_addr_add(netif, &addr, 64U, flags) < 0) { - LOG_ERROR("nib: Can't add link-local address on interface %u\n", - netif->pid); +#endif /* GNRC_IPV6_NIB_CONF_6LN */ + netif->ipv6.na_sent = 0; + if (gnrc_netif2_ipv6_group_join(netif, + &ipv6_addr_all_nodes_link_local) < 0) { + DEBUG("nib: Can't join link-local all-nodes on interface %u\n", + netif->pid); + gnrc_netif2_release(netif); return; } -#if GNRC_IPV6_NIB_CONF_ARSM - /* TODO: SHOULD delay join between 0 and MAX_RTR_SOLICITATION_DELAY */ - ipv6_addr_set_solicited_nodes(&addr, &addr); - if (gnrc_netif2_ipv6_group_join(netif, &addr) < 0) { - LOG_ERROR("nib: Can't join solicited-nodes of link-local address on " - "interface %u\n", netif->pid); - return; + _auto_configure_addr(netif, &ipv6_addr_link_local_prefix, 64U); + if (!(gnrc_netif2_is_rtr_adv(netif)) || + (gnrc_netif2_is_6ln(netif) && !gnrc_netif2_is_6lbr(netif))) { + uint32_t next_rs_time = random_uint32_range(0, NDP_MAX_RS_MS_DELAY); + + _evtimer_add(netif, GNRC_IPV6_NIB_SEARCH_RTR, &netif->ipv6.search_rtr, + next_rs_time); } -#endif -#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 -#endif +#if GNRC_IPV6_NIB_CONF_ROUTER + else { + _handle_snd_mc_ra(netif); + } +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ gnrc_netif2_release(netif); } +static bool _on_link(const ipv6_addr_t *dst, unsigned *iface) +{ + _nib_offl_entry_t *entry = NULL; + +#if GNRC_IPV6_NIB_CONF_6LN + if (*iface != 0) { + if (gnrc_netif2_is_6ln(gnrc_netif2_get_by_pid(*iface))) { + return ipv6_addr_is_link_local(dst); + } + } +#endif /* GNRC_IPV6_NIB_CONF_6LN */ + while ((entry = _nib_offl_iter(entry))) { + if ((entry->mode & _PL) && (entry->flags & _PFX_ON_LINK) && + (ipv6_addr_match_prefix(dst, &entry->pfx) >= entry->pfx_len)) { + *iface = _nib_onl_get_if(entry->next_hop); + return true; + } + } + return ipv6_addr_is_link_local(dst); +} + int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt, gnrc_ipv6_nib_nc_t *nce) @@ -175,19 +180,71 @@ int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst, gnrc_netif2_acquire(netif); mutex_lock(&_nib_mutex); do { /* XXX: hidden goto ;-) */ - if (ipv6_addr_is_link_local(dst)) { - /* TODO: Prefix-based on-link determination */ + _nib_onl_entry_t *node = _nib_onl_get(dst, + (netif == NULL) ? 0 : netif->pid); + /* consider neighbor cache entries first */ + unsigned iface = (node == NULL) ? 0 : _nib_onl_get_if(node); + + if ((node != NULL) || _on_link(dst, &iface)) { + DEBUG("nib: %s is on-link or in NC, start address resolution\n", + ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); + /* on-link prefixes return their interface */ + if (!ipv6_addr_is_link_local(dst) && (iface != 0)) { + /* release preassumed interface */ + gnrc_netif2_release(netif); + netif = gnrc_netif2_get_by_pid(iface); + gnrc_netif2_acquire(netif); + } if ((netif == NULL) || - !_resolve_addr(dst, netif, pkt, nce, - _nib_onl_get(dst, netif->pid))) { + !_resolve_addr(dst, netif, pkt, nce, node)) { + DEBUG("nib: host unreachable\n"); res = -EHOSTUNREACH; break; } } else { - /* TODO: Off-link next hop determination; - * might need netif locking */ - res = -EHOSTUNREACH; + gnrc_ipv6_nib_ft_t route; + + DEBUG("nib: %s is off-link, resolve route\n", + ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); + res = _nib_get_route(dst, pkt, &route); + if ((res < 0) || ipv6_addr_is_unspecified(&route.next_hop)) { + DEBUG("nib: no route to %s found or is prefix list entry, " + "search neighbor cache\n", + ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); + + if (res == 0) { + DEBUG("nib: prefix list entry => taking dst as next hop\n"); + memcpy(&route.next_hop, dst, sizeof(route.next_hop)); + } + else { + res = -ENETUNREACH; + break; + } + } + if ((netif != NULL) && (netif->pid != route.iface)) { + /* drop pre-assumed netif */ + gnrc_netif2_release(netif); + } + if ((netif == NULL) || (netif->pid != route.iface)) { + /* get actual netif */ + netif = gnrc_netif2_get_by_pid(route.iface); + gnrc_netif2_acquire(netif); + } + node = _nib_onl_get(&route.next_hop, + (netif == NULL) ? netif->pid : 0); + if (_resolve_addr(&route.next_hop, netif, pkt, nce, node)) { + _call_route_info_cb(netif, + GNRC_IPV6_NIB_ROUTE_INFO_TYPE_RN, + &route.dst, + (void *)((intptr_t)route.dst_len)); +#if GNRC_IPV6_NIB_CONF_DC + _nib_dc_add(&route.next_hop, netif->pid, dst); +#endif /* GNRC_IPV6_NIB_CONF_DC */ + } + else { + res = -EHOSTUNREACH; + } } } while (0); mutex_unlock(&_nib_mutex); @@ -205,11 +262,11 @@ void gnrc_ipv6_nib_handle_pkt(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, switch (icmpv6->type) { #if GNRC_IPV6_NIB_CONF_ROUTER case ICMPV6_RTR_SOL: - /* TODO */ + _handle_rtr_sol(netif, ipv6, (ndp_rtr_sol_t *)icmpv6, icmpv6_len); break; #endif /* GNRC_IPV6_NIB_CONF_ROUTER */ case ICMPV6_RTR_ADV: - /* TODO */ + _handle_rtr_adv(netif, ipv6, (ndp_rtr_adv_t *)icmpv6, icmpv6_len); break; case ICMPV6_NBR_SOL: _handle_nbr_sol(netif, ipv6, (ndp_nbr_sol_t *)icmpv6, icmpv6_len); @@ -241,7 +298,6 @@ void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type) ctx, type, (unsigned)xtimer_now_usec() / 1000); mutex_lock(&_nib_mutex); switch (type) { - /* TODO: remember netif locking if ctx is a gnrc_netif2_t */ #if GNRC_IPV6_NIB_CONF_ARSM case GNRC_IPV6_NIB_SND_UC_NS: case GNRC_IPV6_NIB_SND_MC_NS: @@ -259,38 +315,37 @@ void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type) _handle_snd_na(ctx); break; case GNRC_IPV6_NIB_SEARCH_RTR: - /* TODO */ - break; - case GNRC_IPV6_NIB_RECONFIRM_RTR: - /* TODO */ + _handle_search_rtr(ctx); break; #if GNRC_IPV6_NIB_CONF_ROUTER case GNRC_IPV6_NIB_REPLY_RS: - /* TODO */ + _handle_reply_rs(ctx); break; case GNRC_IPV6_NIB_SND_MC_RA: - /* TODO */ + _handle_snd_mc_ra(ctx); break; #endif /* GNRC_IPV6_NIB_CONF_ROUTER */ -#if GNRC_IPV6_NIB_CONF_6LN +#if GNRC_IPV6_NIB_CONF_6LR case GNRC_IPV6_NIB_ADDR_REG_TIMEOUT: - /* TODO */ + _nib_nc_remove(ctx); break; - case GNRC_IPV6_NIB_6LO_CTX_TIMEOUT: - /* TODO */ - break; -#endif /* GNRC_IPV6_NIB_CONF_6LN */ +#endif /* GNRC_IPV6_NIB_CONF_6LR */ #if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C case GNRC_IPV6_NIB_ABR_TIMEOUT: - /* TODO */ + _nib_abr_remove(&((_nib_abr_entry_t *)ctx)->addr); break; #endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ case GNRC_IPV6_NIB_PFX_TIMEOUT: - /* TODO */ + _handle_pfx_timeout(ctx); break; case GNRC_IPV6_NIB_RTR_TIMEOUT: - /* TODO */ + _handle_rtr_timeout(ctx); + break; +#if GNRC_IPV6_NIB_CONF_6LN + case GNRC_IPV6_NIB_REREG_ADDRESS: + _handle_rereg_address(ctx); break; +#endif /* GNRC_IPV6_NIB_CONF_6LN */ default: break; } @@ -300,18 +355,39 @@ void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type) #if GNRC_IPV6_NIB_CONF_ROUTER void gnrc_ipv6_nib_change_rtr_adv_iface(gnrc_netif2_t *netif, bool enable) { + gnrc_netif2_acquire(netif); if (enable) { - netif->flags |= GNRC_NETIF2_FLAGS_IPV6_RTR_ADV; - /* TODO: start router advertisements */ + _set_rtr_adv(netif); } else { + uint32_t next_rs_time = random_uint32_range(0, NDP_MAX_RS_MS_DELAY); + + netif->ipv6.ra_sent = (UINT8_MAX - NDP_MAX_FIN_RA_NUMOF) + 1; netif->flags &= ~GNRC_NETIF2_FLAGS_IPV6_RTR_ADV; - /* TODO: - * - start final router advertisements, - * - start router solicitations? */ + /* send final router advertisements */ + _handle_snd_mc_ra(netif); + _evtimer_add(netif, GNRC_IPV6_NIB_SEARCH_RTR, &netif->ipv6.search_rtr, + next_rs_time); } + gnrc_netif2_release(netif); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ + +/* + * @internal + * @{ + */ +static void _handle_mtuo(gnrc_netif2_t *netif, const icmpv6_hdr_t *icmpv6, + const ndp_opt_mtu_t *mtuo); +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C +static uint32_t _handle_pio(gnrc_netif2_t *netif, const icmpv6_hdr_t *icmpv6, + const ndp_opt_pi_t *pio, + _nib_abr_entry_t *abr); +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +static uint32_t _handle_pio(gnrc_netif2_t *netif, const icmpv6_hdr_t *icmpv6, + const ndp_opt_pi_t *pio); +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +/** @} */ /* Iterator for NDP options in a packet */ #define FOREACH_OPT(ndp_pkt, opt, icmpv6_len) \ @@ -320,16 +396,323 @@ void gnrc_ipv6_nib_change_rtr_adv_iface(gnrc_netif2_t *netif, bool enable) icmpv6_len -= (opt->len << 3), \ opt = (ndp_opt_t *)(((uint8_t *)opt) + (opt->len << 3))) +#if GNRC_IPV6_NIB_CONF_ROUTER +static void _handle_rtr_sol(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, + const ndp_rtr_sol_t *rtr_sol, size_t icmpv6_len) +{ + size_t tmp_len = icmpv6_len - sizeof(ndp_rtr_sol_t); + _nib_onl_entry_t *nce = NULL; + ndp_opt_t *opt; + uint32_t next_ra_delay = random_uint32_range(0, NDP_MAX_RA_DELAY); + + assert(netif != NULL); + /* check validity, see: https://tools.ietf.org/html/rfc4861#section-6.1.1 */ + /* checksum is checked by GNRC's ICMPv6 module */ + if (!(gnrc_netif2_is_rtr(netif)) || (ipv6->hl != 255U) || + (rtr_sol->code != 0U) || (icmpv6_len < sizeof(ndp_rtr_sol_t))) { + DEBUG("nib: Received router solicitation is invalid (or interface %i " + "is not a forwarding interface). Discarding silently\n", + netif->pid); + DEBUG(" - IP Hop Limit: %u (should be 255)\n", ipv6->hl); + DEBUG(" - ICMP code: %u (should be 0)\n", rtr_sol->code); + DEBUG(" - ICMP length: %u (should > %u)\n", icmpv6_len, + sizeof(ndp_rtr_sol_t)); + return; + } + /* pre-check option length */ + FOREACH_OPT(rtr_sol, opt, tmp_len) { + if (tmp_len > icmpv6_len) { + DEBUG("nib: Payload length (%u) of RS doesn't align with options\n", + (unsigned)icmpv6_len); + return; + } + if (opt->len == 0U) { + DEBUG("nib: Option of length 0 detected. " + "Discarding router solicitation silently\n"); + return; + } + if ((opt->type == NDP_OPT_SL2A) && + ipv6_addr_is_unspecified(&ipv6->src)) { + DEBUG("nib: RS contains SLLAO, but source was unspecfied. " + "Discarding router solicitation silently\n"); + return; + } + } + DEBUG("nib: Received valid router solicitation:\n"); + DEBUG(" - Source address: %s\n", + ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); + DEBUG(" - Destination address: %s\n", + ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str))); + if (!ipv6_addr_is_unspecified(&ipv6->src)) { + tmp_len = icmpv6_len - sizeof(ndp_rtr_sol_t); + FOREACH_OPT(rtr_sol, opt, tmp_len) { + switch (opt->type) { + case NDP_OPT_SL2A: + if (!gnrc_netif2_is_6ln(netif)) { + _handle_sl2ao(netif, ipv6, (const icmpv6_hdr_t *)rtr_sol, + opt); + } + + break; + default: + break; + } + } + nce = _nib_onl_get(&ipv6->src, netif->pid); + } + if (!gnrc_netif2_is_6ln(netif)) { + uint32_t next_ra_scheduled = _evtimer_lookup(netif, + GNRC_IPV6_NIB_SND_MC_RA); + if (next_ra_scheduled < next_ra_delay) { + DEBUG("nib: There is a MC RA scheduled within the next %" PRIu32 "ms. " + "Using that to advertise router\n", next_ra_scheduled); + return; + } + else if (nce != NULL) { + /* we send unicast RAs so we do not need to rate-limit as + * https://tools.ietf.org/html/rfc4861#section-6.2.6 asks for */ + _evtimer_add(nce, GNRC_IPV6_NIB_REPLY_RS, &nce->reply_rs, + next_ra_delay); + } + else { + uint32_t now = (xtimer_now_usec64() / MS_PER_SEC) & UINT32_MAX; + + /* check for integer overflows and initial value of last_ra */ + if (((netif->ipv6.last_ra > (UINT32_MAX - NDP_MIN_MS_DELAY_BETWEEN_RAS) && + (now < NDP_MIN_MS_DELAY_BETWEEN_RAS))) || + ((now - NDP_MIN_MS_DELAY_BETWEEN_RAS) > netif->ipv6.last_ra)) { + next_ra_delay += NDP_MIN_MS_DELAY_BETWEEN_RAS; + } + _evtimer_add(netif, GNRC_IPV6_NIB_SND_MC_RA, &netif->ipv6.snd_mc_ra, + next_ra_delay); + } + } +#if GNRC_IPV6_NIB_CONF_6LR + else if (gnrc_netif2_is_rtr(netif) && gnrc_netif2_is_rtr_adv(netif)) { + _snd_rtr_advs(netif, &ipv6->src, false); + } +#endif /* GNRC_IPV6_NIB_CONF_6LR */ +} +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ + +static inline uint32_t _min(uint32_t a, uint32_t b) +{ + return (a < b) ? a : b; +} + +static void _handle_rtr_adv(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, + const ndp_rtr_adv_t *rtr_adv, size_t icmpv6_len) +{ + size_t tmp_len = icmpv6_len - sizeof(ndp_rtr_adv_t); + _nib_dr_entry_t *dr = NULL; + ndp_opt_t *opt; + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + sixlowpan_nd_opt_abr_t *abro = NULL; + _nib_abr_entry_t *abr = NULL; +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + uint32_t next_timeout = UINT32_MAX; + + assert(netif != NULL); + /* check validity, see: https://tools.ietf.org/html/rfc4861#section-6.1.1 */ + /* checksum is checked by GNRC's ICMPv6 module */ + if (!(ipv6_addr_is_link_local(&ipv6->src)) || + (ipv6->hl != 255U) || (rtr_adv->code != 0U) || + (icmpv6_len < sizeof(ndp_rtr_adv_t)) || + (!gnrc_netif2_is_6ln(netif) && + (byteorder_ntohs(rtr_adv->ltime) > NDP_RTR_ADV_LTIME_SEC_MAX))) { + DEBUG("nib: Received router advertisement is invalid. " + "Discarding silently\n"); + DEBUG(" - IP Hop Limit: %u (should be 255)\n", ipv6->hl); + DEBUG(" - ICMP code: %u (should be 0)\n", rtr_adv->code); + DEBUG(" - ICMP length: %u (should > %u)\n", (unsigned)icmpv6_len, + sizeof(ndp_rtr_adv_t)); + DEBUG(" - Source address: %s (should be link-local)\n", + ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); + DEBUG(" - Router lifetime: %u (should be <= 9000 on non-6LN)\n", + byteorder_ntohs(rtr_adv->ltime)); + return; + } + /* pre-check option length */ + FOREACH_OPT(rtr_adv, opt, tmp_len) { + if (tmp_len > icmpv6_len) { + DEBUG("nib: Payload length (%u) of RA doesn't align with options\n", + (unsigned)icmpv6_len); + return; + } + if (opt->len == 0U) { + DEBUG("nib: Option of length 0 detected. " + "Discarding router advertisement silently\n"); + return; + } +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + if (opt->type == NDP_OPT_ABR) { + if (abro != NULL) { + DEBUG("nib: More than one ABRO. " + "Discarding router advertisement silently\n"); + return; + } + abro = (sixlowpan_nd_opt_abr_t *)opt; + } +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + } + DEBUG("nib: Received valid router advertisement:\n"); + DEBUG(" - Source address: %s\n", + ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); + DEBUG(" - Destination address: %s\n", + ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str))); + DEBUG(" - Cur Hop Limit: %u\n", rtr_adv->cur_hl); + DEBUG(" - Flags: %c%c\n", + (rtr_adv->flags & NDP_RTR_ADV_FLAGS_M) ? 'M' : '-', + (rtr_adv->flags & NDP_RTR_ADV_FLAGS_O) ? 'O' : '-'); + DEBUG(" - Router Lifetime: %us\n", byteorder_ntohs(rtr_adv->ltime)); + DEBUG(" - Reachable Time: %" PRIu32 "ms\n", + byteorder_ntohl(rtr_adv->reach_time)); + DEBUG(" - Retrans Timer: %" PRIu32 "ms\n", + byteorder_ntohl(rtr_adv->retrans_timer)); +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + if (abro != NULL) { + if ((abr = _handle_abro(abro)) == NULL) { + DEBUG("nib: could not allocate space for new border router or " + "there is no new information in the RA. " + "Discarding silently\n"); + return; + } + /* UINT16_MAX * 60 * 1000 < UINT32_MAX so there are no overflows */ + next_timeout = _min(next_timeout, + byteorder_ntohs(abro->ltime) * SEC_PER_MIN * + MS_PER_SEC); + } +#if !GNRC_IPV6_NIB_CONF_6LBR + else { + DEBUG("nib: multihop prefix and context dissemination activated,\n" + " but no ABRO found. Discarding router advertisement silently\n"); + return; + } +#endif /* !GNRC_IPV6_NIB_CONF_6LBR */ +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + if (rtr_adv->ltime.u16 != 0) { + dr = _nib_drl_add(&ipv6->src, netif->pid); + if (dr != NULL) { + dr->ltime = byteorder_ntohs(rtr_adv->ltime); + } + else { + DEBUG("nib: default router list is full. Ignoring RA from %s\n", + ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); + return; + } + /* UINT16_MAX * 1000 < UINT32_MAX so there are no overflows */ + next_timeout = _min(next_timeout, dr->ltime * MS_PER_SEC); + } + else { + dr = _nib_drl_get(&ipv6->src, netif->pid); + + DEBUG("nib: router lifetime was 0. Removing router and routes via it."); + if (dr != NULL) { + _handle_rtr_timeout(dr); + } + dr = NULL; + } + if (rtr_adv->cur_hl != 0) { + netif->cur_hl = rtr_adv->cur_hl; + } +#if GNRC_IPV6_NIB_CONF_ARSM + if (rtr_adv->reach_time.u32 != 0) { + uint32_t reach_time = byteorder_ntohl(rtr_adv->reach_time); + + if (reach_time != netif->ipv6.reach_time_base) { + _evtimer_add(netif, GNRC_IPV6_NIB_RECALC_REACH_TIME, + &netif->ipv6.recalc_reach_time, + GNRC_IPV6_NIB_CONF_REACH_TIME_RESET); + netif->ipv6.reach_time_base = reach_time; + _recalc_reach_time(&netif->ipv6); + } + } +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ + if (rtr_adv->retrans_timer.u32 != 0) { + netif->ipv6.retrans_time = byteorder_ntohl(rtr_adv->retrans_timer); + } +#if GNRC_IPV6_NIB_CONF_6LN + if ((dr != NULL) && gnrc_netif2_is_6ln(netif) && + !gnrc_netif2_is_6lbr(netif) && + !(netif->flags & GNRC_NETIF2_FLAGS_6LO_ADDRS_REG)) { + /* (register addresses already assigned)*/ + for (int i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + if ((netif->ipv6.addrs_flags[i] != 0)) { + _handle_rereg_address(&netif->ipv6.addrs[i]); + } + } + netif->flags |= GNRC_NETIF2_FLAGS_6LO_ADDRS_REG; + } +#endif /* GNRC_IPV6_NIB_CONF_6LN */ + tmp_len = icmpv6_len - sizeof(ndp_rtr_adv_t); + FOREACH_OPT(rtr_adv, opt, tmp_len) { + switch (opt->type) { + case NDP_OPT_SL2A: + _handle_sl2ao(netif, ipv6, (const icmpv6_hdr_t *)rtr_adv, + opt); + + break; + case NDP_OPT_MTU: + _handle_mtuo(netif, (const icmpv6_hdr_t *)rtr_adv, + (ndp_opt_mtu_t *)opt); + break; + case NDP_OPT_PI: { + uint32_t min_pfx_timeout; +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + min_pfx_timeout = _handle_pio(netif, + (const icmpv6_hdr_t *)rtr_adv, + (ndp_opt_pi_t *)opt, abr); +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + min_pfx_timeout = _handle_pio(netif, + (const icmpv6_hdr_t *)rtr_adv, + (ndp_opt_pi_t *)opt); +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + next_timeout = _min(next_timeout, min_pfx_timeout); + break; + } + /* ABRO was already secured in the option check above */ +#if GNRC_IPV6_NIB_CONF_6LN + case NDP_OPT_6CTX: +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + next_timeout = _min(_handle_6co((icmpv6_hdr_t *)rtr_adv, + (sixlowpan_nd_opt_6ctx_t *)opt, + abr), next_timeout); +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + next_timeout = _min(_handle_6co((icmpv6_hdr_t *)rtr_adv, + (sixlowpan_nd_opt_6ctx_t *)opt), + next_timeout); +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + break; +#endif /* GNRC_IPV6_NIB_CONF_6LN */ + default: + break; + } + } + /* stop sending router solicitations + * see https://tools.ietf.org/html/rfc4861#section-6.3.7 */ + evtimer_del(&_nib_evtimer, &netif->ipv6.search_rtr.event); +#if GNRC_IPV6_NIB_CONF_6LN + if (gnrc_netif2_is_6ln(netif) && !gnrc_netif2_is_6lbr(netif)) { + _set_rtr_adv(netif); + /* but re-fetch information from router in time */ + _evtimer_add(netif, GNRC_IPV6_NIB_SEARCH_RTR, + &netif->ipv6.search_rtr, (next_timeout >> 2) * 3); + /* i.e. 3/4 of the time before the earliest expires */ + } +#endif /* GNRC_IPV6_NIB_CONF_6LN */ +} + static inline size_t _get_l2src(const gnrc_netif2_t *netif, uint8_t *l2src) { #if GNRC_NETIF2_L2ADDR_MAXLEN > 0 memcpy(l2src, netif->l2addr, netif->l2addr_len); return netif->l2addr_len; -#else +#else /* GNRC_NETIF2_L2ADDR_MAXLEN > 0 */ (void)netif; (void)l2src; return 0; -#endif +#endif /* GNRC_NETIF2_L2ADDR_MAXLEN > 0 */ } static void _send_delayed_nbr_adv(const gnrc_netif2_t *netif, @@ -345,7 +728,7 @@ static void _send_delayed_nbr_adv(const gnrc_netif2_t *netif, if (gnrc_netif2_is_rtr(netif)) { reply_flags |= NDP_NBR_ADV_FLAGS_R; } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ #if GNRC_NETIF2_L2ADDR_MAXLEN > 0 if (ipv6_addr_is_multicast(dst)) { uint8_t l2addr[GNRC_NETIF2_L2ADDR_MAXLEN]; @@ -369,7 +752,7 @@ static void _send_delayed_nbr_adv(const gnrc_netif2_t *netif, } #else /* GNRC_NETIF2_L2ADDR_MAXLEN > 0 */ reply_flags |= NDP_NBR_ADV_FLAGS_O; -#endif +#endif /* GNRC_NETIF2_L2ADDR_MAXLEN > 0 */ /* discard const qualifier */ nbr_adv = gnrc_ndp2_nbr_adv_build(tgt, reply_flags, extra_opts); if (nbr_adv == NULL) { @@ -382,8 +765,7 @@ static void _send_delayed_nbr_adv(const gnrc_netif2_t *netif, /* usually this should be the case, but when NCE is full, just * ignore the sending. Other nodes in this anycast group are * then preferred */ - _evtimer_add(nce, GNRC_IPV6_NIB_SND_NA, - &nce->snd_na, + _evtimer_add(nce, GNRC_IPV6_NIB_SND_NA, &nce->snd_na, random_uint32_range(0, NDP_MAX_ANYCAST_MS_DELAY)); } } @@ -412,7 +794,7 @@ static void _handle_nbr_sol(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, DEBUG(" - Source address: %s\n", ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); DEBUG(" - Destination address: %s (should be of format " - "ff02::1:ffxx:xxxx if source address is ::)\n", + "ff02::1:ffxx:xxxx if source address is ::)\n", ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str))); return; } @@ -484,6 +866,7 @@ static void _handle_nbr_sol(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, default: DEBUG("nib: Ignoring unrecognized option type %u for NS\n", opt->type); + break; } } reply_aro = _copy_and_handle_aro(netif, ipv6, nbr_sol, aro, sl2ao); @@ -554,12 +937,12 @@ static void _handle_nbr_adv(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, (nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) ? 'O' : '-'); #if GNRC_IPV6_NIB_CONF_SLAAC /* TODO SLAAC behavior */ -#endif +#endif /* GNRC_IPV6_NIB_CONF_SLAAC */ if (((nce = _nib_onl_get(&nbr_adv->tgt, netif->pid)) != NULL) && (nce->mode & _NC)) { #if GNRC_IPV6_NIB_CONF_ARSM bool tl2ao_avail = false; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ tmp_len = icmpv6_len - sizeof(ndp_nbr_adv_t); FOREACH_OPT(nbr_adv, opt, tmp_len) { @@ -569,13 +952,13 @@ static void _handle_nbr_adv(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, _handle_adv_l2(netif, nce, (icmpv6_hdr_t *)nbr_adv, opt); tl2ao_avail = true; break; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ #if GNRC_IPV6_NIB_CONF_6LN case NDP_OPT_AR: _handle_aro(netif, ipv6, (const icmpv6_hdr_t *)nbr_adv, (const sixlowpan_nd_opt_ar_t *)opt, opt, nce); break; -#endif +#endif /* GNRC_IPV6_NIB_CONF_6LN */ default: DEBUG("nib: Ignoring unrecognized option type %u for NA\n", opt->type); @@ -590,7 +973,7 @@ static void _handle_nbr_adv(gnrc_netif2_t *netif, const ipv6_hdr_t *ipv6, if (!(netif->flags & GNRC_NETIF2_FLAGS_HAS_L2ADDR)) { _handle_adv_l2(netif, nce, (icmpv6_hdr_t *)nbr_adv, NULL); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } } @@ -606,7 +989,7 @@ static inline bool _is_reachable(_nib_onl_entry_t *entry) return true; } } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ #if GNRC_IPV6_NIB_CONF_QUEUE_PKT static gnrc_pktqueue_t *_alloc_queue_entry(gnrc_pktsnip_t *pkt) @@ -619,13 +1002,14 @@ static gnrc_pktqueue_t *_alloc_queue_entry(gnrc_pktsnip_t *pkt) } return NULL; } -#endif +#endif /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt, gnrc_ipv6_nib_nc_t *nce, _nib_onl_entry_t *entry) { bool res = false; + if ((netif != NULL) && (netif->device_type == NETDEV_TYPE_SLIP)) { /* XXX: Linux doesn't do neighbor discovery for SLIP so no use sending * NS and since SLIP doesn't have link-layer addresses anyway, we can @@ -648,7 +1032,7 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, _nib_nc_get(entry, nce); res = true; } -#else +#else /* GNRC_IPV6_NIB_CONF_ARSM */ if (entry != NULL) { DEBUG("nib: resolve address %s%%%u from neighbor cache\n", ipv6_addr_to_str(addr_str, &entry->ipv6, sizeof(addr_str)), @@ -656,11 +1040,11 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, _nib_nc_get(entry, nce); res = true; } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ else if (!(res = _resolve_addr_from_ipv6(dst, netif, nce))) { #if GNRC_IPV6_NIB_CONF_ARSM bool reset = false; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ DEBUG("nib: resolve address %s by probing neighbors\n", ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); @@ -671,14 +1055,16 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, return false; } #if GNRC_IPV6_NIB_CONF_ROUTER - if ((netif != NULL) && (netif->ipv6.route_info_cb != NULL)) { - netif->ipv6.route_info_cb(GNRC_IPV6_NIB_ROUTE_INFO_TYPE_NSC, - dst, (void *)GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE); + if (netif != NULL) { + _call_route_info_cb(netif, + GNRC_IPV6_NIB_ROUTE_INFO_TYPE_NSC, + dst, + (void *)GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE); } -#endif +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ #if GNRC_IPV6_NIB_CONF_ARSM reset = true; -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } if (pkt != NULL) { #if GNRC_IPV6_NIB_CONF_QUEUE_PKT @@ -698,7 +1084,7 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif2_t *netif, } #if GNRC_IPV6_NIB_CONF_ARSM _probe_nbr(entry, reset); -#endif +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ } return res; } @@ -712,10 +1098,257 @@ static void _handle_snd_na(gnrc_pktsnip_t *pkt) DEBUG("nib: No receivers for neighbor advertisement\n"); gnrc_pktbuf_release_error(pkt, EBADF); } -#else +#else /* MODULE_GNRC_IPV6 */ (void)pkt; DEBUG("nib: No IPv6 module to send delayed neighbor advertisement\n"); -#endif +#endif /* MODULE_GNRC_IPV6 */ +} + +static void _handle_pfx_timeout(_nib_offl_entry_t *pfx) +{ + gnrc_netif2_t *netif = gnrc_netif2_get_by_pid(_nib_onl_get_if(pfx->next_hop)); + uint32_t now = (xtimer_now_usec64() / US_PER_MS) & UINT32_MAX; + + gnrc_netif2_acquire(netif); + if (now >= pfx->valid_until) { + evtimer_del(&_nib_evtimer, &pfx->pfx_timeout.event); + for (int i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + if (ipv6_addr_match_prefix(&netif->ipv6.addrs[i], + &pfx->pfx) >= pfx->pfx_len) { + gnrc_netif2_ipv6_addr_remove(netif, &netif->ipv6.addrs[i]); + } + } + pfx->mode &= ~_PL; + _nib_offl_clear(pfx); + } + else if (now >= pfx->pref_until) { + for (int i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + if (ipv6_addr_match_prefix(&netif->ipv6.addrs[i], + &pfx->pfx) >= pfx->pfx_len) { + netif->ipv6.addrs_flags[i] &= ~GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK; + netif->ipv6.addrs_flags[i] |= GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_DEPRECATED; + } + } + _evtimer_add(pfx, GNRC_IPV6_NIB_PFX_TIMEOUT, &pfx->pfx_timeout, + pfx->valid_until - now); + } + gnrc_netif2_release(netif); +} + +static void _handle_rtr_timeout(_nib_dr_entry_t *router) +{ + if ((router->next_hop != NULL) && (router->next_hop->mode & _DRL)) { + _nib_offl_entry_t *route = NULL; + unsigned iface = _nib_onl_get_if(router->next_hop); + ipv6_addr_t addr; + + memcpy(&addr, &router->next_hop, sizeof(addr)); + _nib_drl_remove(router); + /* also remove all routes to that router */ + while ((route = _nib_offl_iter(route))) { + if ((route->next_hop != NULL) && + (_nib_onl_get_if(route->next_hop) == iface) && + (ipv6_addr_equal(&route->next_hop->ipv6, &addr))) { + route->mode = _EMPTY; + route->next_hop->mode &= ~_DST; + _nib_offl_clear(route); + /* XXX routing protocol get's informed in case NUD + * determines ipv6->src (still in neighbor cache) to be + * unreachable */ + } + } + } +} + +void _handle_search_rtr(gnrc_netif2_t *netif) +{ + gnrc_netif2_acquire(netif); + if (!(gnrc_netif2_is_rtr_adv(netif)) || gnrc_netif2_is_6ln(netif)) { + uint32_t next_rs = _evtimer_lookup(netif, GNRC_IPV6_NIB_SEARCH_RTR); + uint32_t interval = _get_next_rs_interval(netif); + + if (next_rs > interval) { + gnrc_ndp2_rtr_sol_send(netif, &ipv6_addr_all_routers_link_local); + if (netif->ipv6.rs_sent < 10U) { + /* with more the backoff (required in RFC 6775) is truncated + * anyway and this way we prevent overflows. 10 is arbitrary, so + * we do not need a define here */ + netif->ipv6.rs_sent++; + } + if ((netif->ipv6.rs_sent < NDP_MAX_RS_NUMOF) || + gnrc_netif2_is_6ln(netif)) { + /* 6LN will solicitate indefinitely */ + _evtimer_add(netif, GNRC_IPV6_NIB_SEARCH_RTR, + &netif->ipv6.search_rtr, interval); + } + } + } + gnrc_netif2_release(netif); +} + +static void _handle_mtuo(gnrc_netif2_t *netif, const icmpv6_hdr_t *icmpv6, + const ndp_opt_mtu_t *mtuo) +{ + if ((mtuo->len != NDP_OPT_MTU_LEN) || (icmpv6->type != ICMPV6_RTR_ADV)) { + return; + } + if (byteorder_ntohl(mtuo->mtu) >= IPV6_MIN_MTU) { + netif->ipv6.mtu = byteorder_ntohl(mtuo->mtu); + } +} + +static void _remove_prefix(const ipv6_addr_t *pfx, unsigned pfx_len) +{ + _nib_offl_entry_t *offl = NULL; + + while ((offl = _nib_offl_iter(offl))) { + if ((offl->mode & _PL) && + (offl->pfx_len == pfx_len) && + (ipv6_addr_match_prefix(&offl->pfx, pfx) >= pfx_len)) { + _nib_pl_remove(offl); + } + } + return; +} + +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C +static uint32_t _handle_pio(gnrc_netif2_t *netif, const icmpv6_hdr_t *icmpv6, + const ndp_opt_pi_t *pio, _nib_abr_entry_t *abr) +#else /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +static uint32_t _handle_pio(gnrc_netif2_t *netif, const icmpv6_hdr_t *icmpv6, + const ndp_opt_pi_t *pio) +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ +{ + uint32_t valid_ltime; + uint32_t pref_ltime; + + valid_ltime = byteorder_ntohl(pio->valid_ltime); + pref_ltime = byteorder_ntohl(pio->pref_ltime); + if ((pio->len != NDP_OPT_PI_LEN) || (icmpv6->type != ICMPV6_RTR_ADV) || + ipv6_addr_is_link_local(&pio->prefix) || (valid_ltime < pref_ltime)) { + DEBUG("nib: ignoring PIO with invalid data\n"); + return UINT32_MAX; + } + DEBUG("nib: received valid Prefix Information option:\n"); + DEBUG(" - Prefix: %s/%u\n", + ipv6_addr_to_str(addr_str, &pio->prefix, sizeof(addr_str)), + pio->prefix_len); + DEBUG(" - Flags: %c%c\n", + (pio->flags & NDP_OPT_PI_FLAGS_L) ? 'L' : '-', + (pio->flags & NDP_OPT_PI_FLAGS_A) ? 'A' : '-'); + DEBUG(" - Valid lifetime: %" PRIu32 "\n", + byteorder_ntohl(pio->valid_ltime)); + 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_netif2_is_6lr(netif)) { + _nib_offl_entry_t *pfx; + + if (pio->valid_ltime.u32 == 0) { + DEBUG("nib: PIO for %s/%u with lifetime 0. Removing prefix.\n", + ipv6_addr_to_str(addr_str, &pio->prefix, sizeof(addr_str)), + pio->prefix_len); + _remove_prefix(&pio->prefix, pio->prefix_len); + return UINT32_MAX; + } + + if (valid_ltime < UINT32_MAX) { /* UINT32_MAX means infinite lifetime */ + /* the valid lifetime is given in seconds, but our timers work in + * microseconds, so we have to scale down to the smallest possible + * value (UINT32_MAX). This is however alright since we ask for a + * new router advertisement before this timeout expires */ + valid_ltime = (valid_ltime > (UINT32_MAX / MS_PER_SEC)) ? + UINT32_MAX : valid_ltime * MS_PER_SEC; + } + if (pref_ltime < UINT32_MAX) { /* UINT32_MAX means infinite lifetime */ + /* same treatment for pref_ltime */ + pref_ltime = (pref_ltime > (UINT32_MAX / MS_PER_SEC)) ? + UINT32_MAX : pref_ltime * MS_PER_SEC; + } + if ((pfx = _nib_pl_add(netif->pid, &pio->prefix, pio->prefix_len, + valid_ltime, pref_ltime))) { +#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + assert(abr != NULL); /* should have been set in _handle_abro() */ + _nib_abr_add_pfx(abr, pfx); +#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */ + if (pio->flags & NDP_OPT_PI_FLAGS_L) { + pfx->flags |= _PFX_ON_LINK; + } + if (pio->flags & NDP_OPT_PI_FLAGS_A) { + pfx->flags |= _PFX_SLAAC; + } + return _min(pref_ltime, valid_ltime); + } + } + return UINT32_MAX; +} + +#if GNRC_IPV6_NIB_CONF_6LN || GNRC_IPV6_NIB_CONF_SLAAC +static void _auto_configure_addr(gnrc_netif2_t *netif, const ipv6_addr_t *pfx, + uint8_t pfx_len) +{ + ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED; + int idx; + uint8_t flags = GNRC_NETIF2_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_netif2_ipv6_get_iid(netif, (eui64_t *)&addr.u64[1]); + ipv6_addr_init_prefix(&addr, pfx, pfx_len); + if ((idx = gnrc_netif2_ipv6_addr_idx(netif, &addr)) < 0) { + if ((idx = gnrc_netif2_ipv6_addr_add(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 + if (gnrc_netif2_is_6ln(netif)) { + /* don't do this beforehand or risk a deadlock: + * * gnrc_netif2_ipv6_addr_add() 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_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK; + netif->ipv6.addrs_flags[idx] |= GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID; + } +#endif /* GNRC_IPV6_NIB_CONF_6LN */ + (void)idx; + /* TODO: make this line conditional on 6LN when there is a SLAAC + * implementation */ +#if GNRC_IPV6_NIB_CONF_ARSM + /* TODO: SHOULD delay join between 0 and MAX_RTR_SOLICITATION_DELAY + * for SLAAC */ + ipv6_addr_set_solicited_nodes(&addr, &addr); + if (gnrc_netif2_ipv6_group_join(netif, &addr) < 0) { + DEBUG("nib: Can't join solicited-nodes of link-local address on " + "interface %u\n", netif->pid); + return; + } +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ +#if GNRC_IPV6_NIB_CONF_6LN + if (new_address && gnrc_netif2_is_6ln(netif) && + !gnrc_netif2_is_6lbr(netif)) { + _handle_rereg_address(&netif->ipv6.addrs[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/ipv6/nib/nib_pl.c b/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c index 125582a2a7..49abce66a5 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c @@ -18,10 +18,12 @@ #include <stdio.h> #include "net/gnrc/ipv6/nib/pl.h" +#include "net/gnrc/netif2/internal.h" #include "timex.h" #include "xtimer.h" #include "_nib-internal.h" +#include "_nib-router.h" int gnrc_ipv6_nib_pl_set(unsigned iface, const ipv6_addr_t *pfx, unsigned pfx_len, @@ -47,8 +49,40 @@ int gnrc_ipv6_nib_pl_set(unsigned iface, if (dst == NULL) { res = -ENOMEM; } +#ifdef MODULE_GNRC_NETIF2 + gnrc_netif2_t *netif = gnrc_netif2_get_by_pid(iface); + int idx; + + if (netif == NULL) { + mutex_unlock(&_nib_mutex); + return res; + } + gnrc_netif2_acquire(netif); + if (!gnrc_netif2_is_6ln(netif) && + ((idx = gnrc_netif2_ipv6_addr_match(netif, pfx)) >= 0) && + (ipv6_addr_match_prefix(&netif->ipv6.addrs[idx], pfx) >= pfx_len)) { + dst->flags |= _PFX_ON_LINK; + } + if (netif->ipv6.aac_mode == GNRC_NETIF2_AAC_AUTO) { + dst->flags |= _PFX_SLAAC; + } +#if GNRC_IPV6_NIB_CONF_6LBR && GNRC_IPV6_NIB_CONF_MULTIHOP_P6C + if (gnrc_netif2_is_6lbr(netif)) { + _nib_abr_entry_t *abr = NULL; + + while ((abr = _nib_abr_iter(abr))) { + abr->version++; + _nib_abr_add_pfx(abr, dst); + } + } +#endif + gnrc_netif2_release(netif); +#endif /* MODULE_GNRC_NETIF2 */ mutex_unlock(&_nib_mutex); - /* TODO: send RA with PIO, if iface is allowed to send RAs */ +#if defined(MODULE_GNRC_NETIF2) && GNRC_IPV6_NIB_CONF_ROUTER + /* update prefixes down-stream */ + _handle_snd_mc_ra(netif); +#endif return res; } @@ -66,7 +100,14 @@ void gnrc_ipv6_nib_pl_del(unsigned iface, (ipv6_addr_match_prefix(pfx, &dst->pfx) >= pfx_len)) { _nib_pl_remove(dst); mutex_unlock(&_nib_mutex); - /* TODO: send RA with PIO, if iface is allowed to send RAs */ +#if GNRC_IPV6_NIB_CONF_ROUTER + gnrc_netif2_t *netif = gnrc_netif2_get_by_pid(iface); + + if (netif) { + /* update prefixes down-stream */ + _handle_snd_mc_ra(netif); + } +#endif return; } } -- GitLab