diff --git a/Makefile.dep b/Makefile.dep index 8504c0addc30018e790dc0b35639e98d70b1d1e9..303b888587d1b316af110e56302cc9825cae2e6d 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -39,13 +39,13 @@ ifneq (,$(filter ieee802154,$(USEMODULE))) USEMODULE += gnrc_sixlowpan endif ifneq (,$(filter gnrc_ipv6_router, $(USEMODULE))) - USEMODULE += gnrc_sixlowpan # TODO: replace with gnrc_sixlowpan_router + USEMODULE += gnrc_sixlowpan_router endif ifneq (,$(filter gnrc_ipv6_default, $(USEMODULE))) USEMODULE += gnrc_sixlowpan_default endif ifneq (,$(filter gnrc_ipv6_router_default, $(USEMODULE))) - USEMODULE += gnrc_sixlowpan_default # TODO: replace with gnrc_sixlowpan_router_default + USEMODULE += gnrc_sixlowpan_router_default endif endif @@ -57,6 +57,17 @@ ifneq (,$(filter gnrc_sixlowpan_default,$(USEMODULE))) USEMODULE += gnrc_sixlowpan_iphc endif +ifneq (,$(filter gnrc_sixlowpan_router_default,$(USEMODULE))) + USEMODULE += gnrc_ipv6_router_default + USEMODULE += gnrc_sixlowpan_router + USEMODULE += gnrc_sixlowpan_frag + USEMODULE += gnrc_sixlowpan_iphc +endif + +ifneq (,$(filter gnrc_sixlowpan_router,$(USEMODULE))) + USEMODULE += gnrc_sixlowpan +endif + ifneq (,$(filter gnrc_sixlowpan_frag,$(USEMODULE))) USEMODULE += gnrc_sixlowpan USEMODULE += vtimer @@ -78,6 +89,10 @@ ifneq (,$(filter gnrc_sixlowpan_ctx,$(USEMODULE))) USEMODULE += vtimer endif +ifneq (,$(filter gnrc_sixlowpan_nd_router,$(USEMODULE))) + USEMODULE += gnrc_sixlowpan_nd +endif + ifneq (,$(filter gnrc_sixlowpan_nd,$(USEMODULE))) USEMODULE += gnrc_ndp USEMODULE += gnrc_ndp_internal @@ -101,7 +116,13 @@ endif ifneq (,$(filter gnrc_ipv6_router_default,$(USEMODULE))) USEMODULE += gnrc_ipv6_router USEMODULE += gnrc_icmpv6 - USEMODULE += gnrc_ndp_router + ifeq (1,$(GNRC_NETIF_NUMOF)) + ifeq (,$(filter gnrc_sixlowpan_nd_router,$(USEMODULE))) + USEMODULE += gnrc_ndp_router + endif + else + USEMODULE += gnrc_ndp_router + endif endif ifneq (,$(filter gnrc_ndp_host,$(USEMODULE))) diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules index b2bd7c549d6cc497b4f42194071da88b4abab0a1..a9c4dea1d12c2ca94cc631c2ad5c06fe7877d997 100644 --- a/Makefile.pseudomodules +++ b/Makefile.pseudomodules @@ -3,6 +3,8 @@ PSEUDOMODULES += gnrc_ipv6_default PSEUDOMODULES += gnrc_ipv6_router PSEUDOMODULES += gnrc_ipv6_router_default PSEUDOMODULES += gnrc_sixlowpan_default +PSEUDOMODULES += gnrc_sixlowpan_router +PSEUDOMODULES += gnrc_sixlowpan_router_default PSEUDOMODULES += gnrc_pktbuf PSEUDOMODULES += ieee802154 PSEUDOMODULES += log diff --git a/doc/doxygen/riot.doxyfile b/doc/doxygen/riot.doxyfile index 74e66b4cb3445e74ae5cb326e5a8ab27da190b39..0c7a5e369150ce0596bcebc96c0655b80b0eb159 100644 --- a/doc/doxygen/riot.doxyfile +++ b/doc/doxygen/riot.doxyfile @@ -2000,7 +2000,8 @@ PREDEFINED = DOXYGEN \ DEVELHELP \ ENABLE_DEBUG \ TEST_SUITES \ - MODULE_GNRC_NDP_ROUTER + MODULE_GNRC_NDP_ROUTER \ + MODULE_GNRC_SIXLOWPAN_ND_ROUTER # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this diff --git a/sys/include/net/gnrc/sixlowpan/nd.h b/sys/include/net/gnrc/sixlowpan/nd.h index 6a3bb8b61ff36a83fd4841e3ea1f9b34ecf64efa..919008a13e2c16991cb6613917b512009b46bb6d 100644 --- a/sys/include/net/gnrc/sixlowpan/nd.h +++ b/sys/include/net/gnrc/sixlowpan/nd.h @@ -33,6 +33,8 @@ #include "net/sixlowpan/nd.h" #include "timex.h" +#include "net/gnrc/sixlowpan/nd/router.h" + #ifdef __cplusplus extern "C" { #endif @@ -52,6 +54,16 @@ extern "C" { */ #define GNRC_SIXLOWPAN_ND_MSG_DELETE_CTX (0x0222) +/** + * @brief Message type for authoritative border router timeout + */ +#define GNRC_SIXLOWPAN_ND_MSG_ABR_TIMEOUT (0x0223) + +/** + * @brief Message type for address registration timeout + */ +#define GNRC_SIXLOWPAN_ND_MSG_AR_TIMEOUT (0x0224) + #ifndef GNRC_SIXLOWPAN_ND_AR_LTIME /** * @brief Registration lifetime in minutes for the address registration option @@ -78,6 +90,28 @@ extern "C" { #define GNRC_SIXLOWPAN_ND_MAX_RTR_SOL_INT (60U) /**< retransmission increment for exponential * backoff of subsequent RS */ /** @} */ +/** + * @name Router constants + * @{ + * @see <a href="https://tools.ietf.org/html/rfc6775#section-9"> + * RFC 6775, section 9 + * </a> + */ +#define GNRC_SIXLOWPAN_ND_MIN_RTR_ADV_DELAY (10U) /**< replacement value (in seconds) for + * @ref GNRC_NDP_MIN_RTR_ADV_DELAY */ +/** + * @brief replacement value (in microseconds) for @ref GNRC_NDP_MAX_RTR_ADV_DELAY + */ +#define GNRC_SIXLOWPAN_ND_MAX_RTR_ADV_DELAY (2U * SEC_IN_USEC) +/** + * @brief Lifetime of a tentative address entry in seconds + */ +#define GNRC_SIXLOWPAN_ND_TENTATIVE_NCE_LIFETIME (20U) +/** + * @brief 6LoWPAN Multihop Hoplimit + */ +#define GNRC_SIXLOWPAN_ND_MULTIHOP_HOPLIMIT (64U) +/** @} */ /** * @brief Initializes 6LoWPAN neighbor discovery for the interface. @@ -182,6 +216,55 @@ bool gnrc_sixlowpan_nd_opt_6ctx_handle(uint8_t icmpv6_type, sixlowpan_nd_opt_6ct */ void gnrc_sixlowpan_nd_wakeup(void); +#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER +/** + * @brief Handles authoritative border router option. + * + * @param[in] iface Interface the source link-layer option was received + * on. + * @param[in] rtr_adv The router advertisement containing the ABRO. + * @param[in] icmpv6_size The size of the @p rtr_adv. + * @param[in] abr_opt The ABRO. + * + * @note Erroneous ABROs are always ignored silently. + */ +void gnrc_sixlowpan_nd_opt_abr_handle(kernel_pid_t iface, ndp_rtr_adv_t *rtr_adv, int icmpv6_size, + sixlowpan_nd_opt_abr_t *abr_opt); + +/** + * @brief Builds the 6LoWPAN context option. + * + * @param[in] prefix_len The length of the context's prefix. + * @param[in] flags Flags + CID for the context. + * @param[in] ltime Lifetime of the context. + * @param[in] prefix The context's prefix + * @param[in] next More options in the packet. NULL, if there are none. + * + * @return The pkt snip list of options, on success + * @return NULL, if packet buffer is full or on error + */ +gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_6ctx_build(uint8_t prefix_len, uint8_t flags, uint16_t ltime, + ipv6_addr_t *prefix, gnrc_pktsnip_t *next); + +/** + * @brief Builds the authoritative border router option. + * + * @param[in] version Version of the border router information. + * @param[in] ltime Registration lifetime for the border router. + * @param[in] braddr The IPv6 address of the border router. + * @param[in] next More options in the packet. NULL, if there are none. + * + * @return The pkt snip list of options, on success + * @return NULL, if packet buffer is full or on error + */ +gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_abr_build(uint32_t version, uint16_t ltime, + ipv6_addr_t *braddr, gnrc_pktsnip_t *next); +#else +#define gnrc_sixlowpan_nd_opt_abr_handle(iface, rtr_adv, icmpv6_size, abr_opt) +#define gnrc_sixlowpan_nd_opt_6ctx_build(prefix_len, flags, ltime, prefix, next) (NULL) +#define gnrc_sixlowpan_nd_opt_abr_build(version, ltime, braddr, next) (NULL) +#endif + #ifdef __cplusplus } #endif diff --git a/sys/include/net/gnrc/sixlowpan/nd/router.h b/sys/include/net/gnrc/sixlowpan/nd/router.h new file mode 100644 index 0000000000000000000000000000000000000000..9d02c7eca634581fb8a9f115913dce1d778c1437 --- /dev/null +++ b/sys/include/net/gnrc/sixlowpan/nd/router.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * 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. + */ + +/** + * @defgroup gnrc_sixlowpan_nd_router Router-part of 6LoWPAN-ND + * @ingroup gnrc_sixlowpan_nd + * @brief Router-part of 6LoWPAN-ND + * @{ + * + * @file + * @brief Router-definitions for 6LoWPAN-ND. + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef GNRC_SIXLOWPAN_ND_ROUTER_H_ +#define GNRC_SIXLOWPAN_ND_ROUTER_H_ + +#include <stdbool.h> + +#include "bitfield.h" +#include "net/gnrc/sixlowpan/ctx.h" +#include "net/gnrc/ipv6/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Number of registerable border routers + * + * @note More than one border routers require some way of synchronization + * of the context information (see + * [RFC 6775, section 8.1](https://tools.ietf.org/html/rfc6775#section-8.1)) + */ +#ifndef GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF +#define GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF (1) +#endif + +/** + * @brief The number of non-link-local prefixes associated with border routers + * at maximum. + */ +#ifndef GNRC_SIXLOWPAN_ND_ROUTER_ABR_PRF_NUMOF +#define GNRC_SIXLOWPAN_ND_ROUTER_ABR_PRF_NUMOF (GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF) +#endif + +/** + * @brief Representation for prefixes coming from a router + */ +typedef struct gnrc_sixlowpan_nd_router_prf { + struct gnrc_sixlowpan_nd_router_prf *next; /**< next prefix */ + gnrc_ipv6_netif_t *iface; /**< interface the prefix is registered too */ + gnrc_ipv6_netif_addr_t *prefix; /**< prefix on the interface/in the prefix list */ +} gnrc_sixlowpan_nd_router_prf_t; + +/** + * @brief Abstract representation of a border router on all (border) routers. + */ +typedef struct { + ipv6_addr_t addr; /**< the IPv6 address of the border router (BR) */ + uint32_t version; /**< version of the information dissiminated by the + * BR */ + uint16_t ltime; /**< the time in minutes until deletion */ + BITFIELD(ctxs, GNRC_SIXLOWPAN_CTX_SIZE);/**< contexts associated with BR */ + gnrc_sixlowpan_nd_router_prf_t *prfs; /**< prefixes associated with BR */ + vtimer_t ltimer; /**< timer for deletion */ +} gnrc_sixlowpan_nd_router_abr_t; + +/** + * @brief Removes tentetative neighbor cache entries or sets registered ones to + * garbage-collectible. + * + * @param[in] nc_entry A neighbor cache entry. + */ +static inline void gnrc_sixlowpan_nd_router_gc_nc(gnrc_ipv6_nc_t *nc_entry) +{ + switch (gnrc_ipv6_nc_get_type(nc_entry)) { + case GNRC_IPV6_NC_TYPE_TENTATIVE: + case GNRC_IPV6_NC_TYPE_REGISTERED: + gnrc_ipv6_nc_remove(nc_entry->iface, &nc_entry->ipv6_addr); + break; + default: + break; + } + +} + +/** + * @brief Set @p netif to router mode. + * + * @details This sets/unsets the GNRC_IPV6_NETIF_FLAGS_ROUTER and initializes or ceases router + * behavior for 6LoWPAN neighbor discovery. + * + * @param[in] netif An IPv6 interface. Must not be NULL. + * @param[in] enable Status for the GNRC_IPV6_NETIF_FLAGS_ROUTER flag. + */ +static inline void gnrc_sixlowpan_nd_router_set_router(gnrc_ipv6_netif_t *netif, bool enable) +{ + if (enable) { + netif->flags |= GNRC_IPV6_NETIF_FLAGS_ROUTER; + } + else { + netif->flags &= ~GNRC_IPV6_NETIF_FLAGS_ROUTER; + } +} + +/** + * @brief Set/Unset GNRC_IPV6_NETIF_FLAGS_RTR_ADV flag for @p netif. + * + * @details GNRC_IPV6_NETIF_FLAGS_RTR_ADV and initializes or ceases + * periodic router advertising behavior for neighbor discovery. + * + * @param[in] netif An IPv6 interface. Must not be NULL. + * @param[in] enable Status for the GNRC_IPV6_NETIF_FLAGS_RTR_ADV flag. + */ +static inline void gnrc_sixlowpan_nd_router_set_rtr_adv(gnrc_ipv6_netif_t *netif, bool enable) +{ + if (enable) { + netif->flags |= GNRC_IPV6_NETIF_FLAGS_RTR_ADV; + } + else { + netif->flags &= ~GNRC_IPV6_NETIF_FLAGS_RTR_ADV; + } +} + +/** + * @brief Get's the border router for this router. + * + * @return The border router, if one is specified. + * @return NULL, otherwise. + */ +gnrc_sixlowpan_nd_router_abr_t *gnrc_sixlowpan_nd_router_abr_get(void); + +/** + * @brief Checks if the version data @p abr_opt is older than the version of the currently + * registered border router. + * + * @param[in] abr_opt An authoritative border router option containing potentially new + * information on the currently registered border router. + * + * @return true, if the information in @p abr_opt is newer. + * @return false, if the information in @p abr_opt is older. + */ +bool gnrc_sixlowpan_nd_router_abr_older(sixlowpan_nd_opt_abr_t *abr_opt); + +/** + * @brief Removes the border router and all the prefixes and contexts it disseminated through + * the network for this node. + * + * @param[in] abr The border router. + */ +void gnrc_sixlowpan_nd_router_abr_remove(gnrc_sixlowpan_nd_router_abr_t *abr); + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_SIXLOWPAN_ND_ROUTER_H_ */ +/** @} */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index ce91aa59c2e88ae4ad6a487572579665f093f0b2..0b577bdd88281cfd2194d28de88970f8b8e4ddd3 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -82,6 +82,9 @@ endif ifneq (,$(filter gnrc_sixlowpan_nd,$(USEMODULE))) DIRS += network_layer/sixlowpan/nd endif +ifneq (,$(filter gnrc_sixlowpan_nd_router,$(USEMODULE))) + DIRS += network_layer/sixlowpan/nd/router +endif ifneq (,$(filter gnrc_sixlowpan_netif,$(USEMODULE))) DIRS += network_layer/sixlowpan/netif endif diff --git a/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c b/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c index d87f19cfbd7615f3cdcb94dd88cc71c7ec19b1b0..cae74d1d1b900c2b1cd2eb82451f409be7f26822 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c +++ b/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c @@ -132,6 +132,13 @@ kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_ next_hop = &next_hop_actual; } } +#endif +#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER + /* next hop determination: https://tools.ietf.org/html/rfc6775#section-6.5.4 */ + nc_entry = gnrc_ipv6_nc_get(iface, dst); + if ((nc_entry != NULL) && (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_REGISTERED)) { + next_hop = dst; + } #endif /* next hop determination according to: https://tools.ietf.org/html/rfc6775#section-5.6 */ if ((next_hop == NULL) && ipv6_addr_is_link_local(dst)) { /* prefix is "on-link" */ @@ -143,6 +150,14 @@ kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_ } /* address resolution of next_hop: https://tools.ietf.org/html/rfc6775#section-5.7 */ + if ((nc_entry == NULL) || (next_hop != dst)) { + /* get if not gotten from previous check */ + nc_entry = gnrc_ipv6_nc_get(iface, next_hop); + } + if ((nc_entry == NULL) || (!gnrc_ipv6_nc_is_reachable(nc_entry)) || + (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_TENTATIVE)) { + return KERNEL_PID_UNDEF; + } if (ipv6_addr_is_link_local(next_hop)) { kernel_pid_t ifs[GNRC_NETIF_NUMOF]; size_t ifnum = gnrc_netif_get(ifs); @@ -162,16 +177,12 @@ kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_ return iface; } else { - nc_entry = gnrc_ipv6_nc_get(iface, next_hop); - if ((nc_entry == NULL) || (!gnrc_ipv6_nc_is_reachable(nc_entry))) { - return KERNEL_PID_UNDEF; - } if (nc_entry->l2_addr_len > 0) { memcpy(l2addr, nc_entry->l2_addr, nc_entry->l2_addr_len); } *l2addr_len = nc_entry->l2_addr_len; - return nc_entry->iface; } + return nc_entry->iface; } void gnrc_sixlowpan_nd_rtr_sol_reschedule(gnrc_ipv6_nc_t *nce, uint32_t sec_delay) @@ -206,6 +217,7 @@ uint8_t gnrc_sixlowpan_nd_opt_ar_handle(kernel_pid_t iface, ipv6_hdr_t *ipv6, ui eui64_t eui64; gnrc_ipv6_netif_t *ipv6_iface; gnrc_ipv6_nc_t *nc_entry; + uint8_t status = 0; (void)sl2a; (void)sl2a_len; if (ar_opt->len != SIXLOWPAN_ND_OPT_AR_LEN) { @@ -254,11 +266,56 @@ uint8_t gnrc_sixlowpan_nd_opt_ar_handle(kernel_pid_t iface, ipv6_hdr_t *ipv6, ui DEBUG("6lo nd: unknown status for registration received\n"); break; } +#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER + case ICMPV6_NBR_SOL: + if (!(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && + !(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { + DEBUG("6lo nd: interface not a 6LoWPAN or forwarding interface\n"); + return 0; + } + if ((ar_opt->status != 0) || + ipv6_addr_is_unspecified(&ipv6->src)) { + /* discard silently */ + return 0; + } + /* TODO multihop DAD */ + if ((nc_entry != NULL) && + ((gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_REGISTERED) || + (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_TENTATIVE)) && + (ar_opt->eui64.uint64.u64 != nc_entry->eui64.uint64.u64)) { + /* there is already another node with this address */ + DEBUG("6lo nd: duplicate address detected\n"); + status = SIXLOWPAN_ND_STATUS_DUP; + } + else if ((nc_entry != NULL) && (ar_opt->ltime.u16 == 0)) { + gnrc_ipv6_nc_remove(iface, &ipv6->src); + /* TODO, notify routing protocol */ + } + else if (ar_opt->ltime.u16 != 0) { + /* TODO: multihop DAD behavior */ + uint16_t reg_ltime; + if (nc_entry == NULL) { + if ((nc_entry = gnrc_ipv6_nc_add(iface, &ipv6->src, sl2a, sl2a_len, + GNRC_IPV6_NC_STATE_STALE)) == NULL) { + DEBUG("6lo nd: neighbor cache is full\n"); + return SIXLOWPAN_ND_STATUS_NC_FULL; + } + nc_entry->eui64 = ar_opt->eui64; + } + nc_entry->flags &= ~GNRC_IPV6_NC_TYPE_MASK; + nc_entry->flags |= GNRC_IPV6_NC_TYPE_REGISTERED; + reg_ltime = byteorder_ntohs(ar_opt->ltime); + /* TODO: notify routing protocol */ + vtimer_remove(&nc_entry->type_timeout); + vtimer_set_msg(&nc_entry->type_timeout, timex_set(reg_ltime * 60, 0), + gnrc_ipv6_pid, GNRC_SIXLOWPAN_ND_MSG_AR_TIMEOUT, nc_entry); + } +#endif default: break; } - return 0; + return status; } bool gnrc_sixlowpan_nd_opt_6ctx_handle(uint8_t icmpv6_type, sixlowpan_nd_opt_6ctx_t *ctx_opt) @@ -293,4 +350,6 @@ void gnrc_sixlowpan_nd_wakeup(void) } } +/* gnrc_sixlowpan_nd_opt_abr_handle etc. implemented in gnrc_sixlowpan_nd_router */ + /** @} */ diff --git a/sys/net/gnrc/network_layer/sixlowpan/nd/router/Makefile b/sys/net/gnrc/network_layer/sixlowpan/nd/router/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..bb02b5bd956f2c85318fb9060c2cd526d3cdc54f --- /dev/null +++ b/sys/net/gnrc/network_layer/sixlowpan/nd/router/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sixlowpan_nd_router + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c b/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c new file mode 100644 index 0000000000000000000000000000000000000000..a30c25bf1c994bdd18ba597d0e52a2cd0b4fc4b7 --- /dev/null +++ b/sys/net/gnrc/network_layer/sixlowpan/nd/router/gnrc_sixlowpan_nd_router.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * 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 + */ + +#include "net/gnrc/ipv6.h" +#include "net/gnrc/ndp.h" +#include "net/gnrc/sixlowpan/ctx.h" +#include "net/gnrc/sixlowpan/nd.h" +#include "net/icmpv6.h" +#include "net/ndp.h" +#include "net/sixlowpan/nd.h" + +#include "net/gnrc/sixlowpan/nd/router.h" + +static gnrc_sixlowpan_nd_router_abr_t _abrs[GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF]; +static gnrc_sixlowpan_nd_router_prf_t _prefixes[GNRC_SIXLOWPAN_ND_ROUTER_ABR_PRF_NUMOF]; + +static gnrc_sixlowpan_nd_router_abr_t *_get_abr(ipv6_addr_t *addr) +{ + gnrc_sixlowpan_nd_router_abr_t *abr = NULL; + + for (int i = 0; i < GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF; i++) { + if (ipv6_addr_equal(&_abrs[i].addr, addr)) { + return &_abrs[i]; + } + + if ((abr == NULL) && ipv6_addr_is_unspecified(&_abrs[i].addr)) { + abr = &_abrs[i]; + } + } + + return abr; +} + +static gnrc_sixlowpan_nd_router_prf_t *_get_free_prefix(ipv6_addr_t *prefix, size_t prefix_len) +{ + gnrc_sixlowpan_nd_router_prf_t *prf = NULL; + + for (int i = 0; i < GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF; i++) { + if ((ipv6_addr_match_prefix(&_prefixes[i].prefix->addr, prefix) >= prefix_len) && + (_prefixes[i].prefix->prefix_len == prefix_len)) { + return &_prefixes[i]; + } + + if ((prf == NULL) && ipv6_addr_is_unspecified(&_prefixes[i].prefix->addr)) { + prf = &_prefixes[i]; + } + } + + return prf; +} + +static void _add_prefix(kernel_pid_t iface, gnrc_sixlowpan_nd_router_abr_t *abr, + ndp_opt_pi_t *pi_opt) +{ + gnrc_sixlowpan_nd_router_prf_t *prf_ent; + gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface); + ipv6_addr_t *prefix; + + if ((pi_opt->len != NDP_OPT_PI_LEN) || ipv6_addr_is_link_local(&pi_opt->prefix) || + (pi_opt->flags & NDP_OPT_PI_FLAGS_A) || + (pi_opt->flags & NDP_OPT_PI_FLAGS_L) || + (pi_opt->valid_ltime.u32 == 0)) { + return; + } + + prefix = gnrc_ipv6_netif_match_prefix(iface, &pi_opt->prefix); + + prf_ent = _get_free_prefix(&pi_opt->prefix, pi_opt->prefix_len); + + if (prf_ent != NULL) { + prf_ent->iface = ipv6_iface; + prf_ent->prefix = container_of(prefix, gnrc_ipv6_netif_addr_t, addr); + } + + LL_PREPEND(abr->prfs, prf_ent); +} + +static void _add_ctx(gnrc_sixlowpan_nd_router_abr_t *abr, sixlowpan_nd_opt_6ctx_t *ctx_opt) +{ + if (((ctx_opt->ctx_len < 64) && (ctx_opt->len != 2)) || + ((ctx_opt->ctx_len >= 64) && (ctx_opt->len != 3))) { + return; + } + bf_set(abr->ctxs, sixlowpan_nd_opt_6ctx_get_cid(ctx_opt)); +} + +gnrc_sixlowpan_nd_router_abr_t *gnrc_sixlowpan_nd_router_abr_get(void) +{ + if (ipv6_addr_is_unspecified(&_abrs[0].addr)) { + return NULL; + } + return _abrs; +} + +bool gnrc_sixlowpan_nd_router_abr_older(sixlowpan_nd_opt_abr_t *abr_opt) +{ + gnrc_sixlowpan_nd_router_abr_t *abr; + uint32_t version; + + if (abr_opt->len != SIXLOWPAN_ND_OPT_ABR_LEN) { + /* invalid option received */ + return true; + } + + abr = _get_abr(&abr_opt->braddr); + + if (abr == NULL) { + return false; + } + + version = byteorder_ntohs(abr_opt->vlow); + version |= byteorder_ntohs(abr_opt->vhigh) << 16; + + return (version < abr->version); +} + +void gnrc_sixlowpan_nd_router_abr_remove(gnrc_sixlowpan_nd_router_abr_t *abr) +{ + for (int i = 0; i < GNRC_SIXLOWPAN_CTX_SIZE; i++) { + if (bf_isset(abr->ctxs, i)) { + gnrc_sixlowpan_ctx_remove(i); + bf_unset(abr->ctxs, i); + } + } + + while (abr->prfs != NULL) { + gnrc_sixlowpan_nd_router_prf_t *prefix = abr->prfs; + LL_DELETE(abr->prfs, prefix); + gnrc_ipv6_netif_remove_addr(prefix->iface->pid, &prefix->prefix->addr); + prefix->next = NULL; + prefix->iface = NULL; + prefix->prefix = NULL; + } + ipv6_addr_set_unspecified(&abr->addr); + abr->version = 0; +} + +/* router-only functions from net/gnrc/sixlowpan/nd.h */ +void gnrc_sixlowpan_nd_opt_abr_handle(kernel_pid_t iface, ndp_rtr_adv_t *rtr_adv, int sicmpv6_size, + sixlowpan_nd_opt_abr_t *abr_opt) +{ + uint16_t opt_offset = 0; + uint8_t *buf = (uint8_t *)(rtr_adv + 1); + gnrc_sixlowpan_nd_router_abr_t *abr; + timex_t t = { 0, 0 }; + + /* validity and version was checked in previously called + * gnrc_sixlowpan_nd_router_abr_older() */ + + abr = _get_abr(&abr_opt->braddr); + + if (abr == NULL) { + return; + } + + abr->ltime = byteorder_ntohs(abr_opt->ltime); + + if (abr->ltime == 0) { + gnrc_sixlowpan_nd_router_abr_remove(abr); + return; + } + + sicmpv6_size -= sizeof(ndp_rtr_adv_t); + + while (sicmpv6_size > 0) { + ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset); + + switch (opt->type) { + case NDP_OPT_PI: + _add_prefix(iface, abr, (ndp_opt_pi_t *)opt); + + case NDP_OPT_6CTX: + _add_ctx(abr, (sixlowpan_nd_opt_6ctx_t *)opt); + + default: + break; + } + + opt_offset += (opt->len * 8); + sicmpv6_size -= (opt->len * 8); + } + + abr->version = byteorder_ntohs(abr_opt->vlow); + abr->version |= byteorder_ntohs(abr_opt->vhigh) << 16; + abr->addr.u64[0] = abr_opt->braddr.u64[0]; + abr->addr.u64[1] = abr_opt->braddr.u64[1]; + memset(abr->ctxs, 0, sizeof(abr->ctxs)); + abr->prfs = NULL; + + t.seconds = abr->ltime * 60; + + vtimer_set_msg(&abr->ltimer, t, gnrc_ipv6_pid, + GNRC_SIXLOWPAN_ND_MSG_ABR_TIMEOUT, abr); +} + +gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_6ctx_build(uint8_t prefix_len, uint8_t flags, uint16_t ltime, + ipv6_addr_t *prefix, gnrc_pktsnip_t *next) +{ + gnrc_pktsnip_t *pkt = gnrc_ndp_opt_build(NDP_OPT_6CTX, + sizeof(sixlowpan_nd_opt_6ctx_t) + (prefix_len / 8), + next); + + if (pkt != NULL) { + sixlowpan_nd_opt_6ctx_t *ctx_opt = pkt->data; + ctx_opt->ctx_len = prefix_len; + ctx_opt->resv_c_cid = flags; + ctx_opt->resv.u16 = 0; + ctx_opt->ltime = byteorder_htons(ltime); + /* Bits beyond prefix_len MUST be 0 */ + memset(ctx_opt + 1, 0, pkt->size - sizeof(sixlowpan_nd_opt_6ctx_t)); + ipv6_addr_init_prefix((ipv6_addr_t *)(ctx_opt + 1), prefix, prefix_len); + } + + return pkt; +} + +gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_abr_build(uint32_t version, uint16_t ltime, + ipv6_addr_t *braddr, gnrc_pktsnip_t *next) +{ + gnrc_pktsnip_t *pkt = gnrc_ndp_opt_build(NDP_OPT_ABR, sizeof(sixlowpan_nd_opt_abr_t), next); + + if (pkt != NULL) { + sixlowpan_nd_opt_abr_t *abr_opt = pkt->data; + abr_opt->vlow = byteorder_htons(version & 0xffff); + abr_opt->vhigh = byteorder_htons(version >> 16); + abr_opt->ltime = byteorder_htons(ltime); + abr_opt->braddr.u64[0] = braddr->u64[0]; + abr_opt->braddr.u64[1] = braddr->u64[1]; + } + + return pkt; +} + +/** @} */