diff --git a/Makefile.dep b/Makefile.dep index e5239c3a49873ca34dd9ecb0145d844b55b91bee..8504c0addc30018e790dc0b35639e98d70b1d1e9 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -52,6 +52,7 @@ endif ifneq (,$(filter gnrc_sixlowpan_default,$(USEMODULE))) USEMODULE += gnrc_ipv6_default USEMODULE += gnrc_sixlowpan + USEMODULE += gnrc_sixlowpan_nd USEMODULE += gnrc_sixlowpan_frag USEMODULE += gnrc_sixlowpan_iphc endif @@ -77,10 +78,24 @@ ifneq (,$(filter gnrc_sixlowpan_ctx,$(USEMODULE))) USEMODULE += vtimer endif +ifneq (,$(filter gnrc_sixlowpan_nd,$(USEMODULE))) + USEMODULE += gnrc_ndp + USEMODULE += gnrc_ndp_internal + USEMODULE += gnrc_sixlowpan_ctx + USEMODULE += random + USEMODULE += vtimer +endif + ifneq (,$(filter gnrc_ipv6_default,$(USEMODULE))) USEMODULE += gnrc_ipv6 USEMODULE += gnrc_icmpv6 - USEMODULE += gnrc_ndp_host + ifeq (1,$(GNRC_NETIF_NUMOF)) + ifeq (,$(filter gnrc_sixlowpan_nd,$(USEMODULE))) + USEMODULE += gnrc_ndp_host + endif + else + USEMODULE += gnrc_ndp_host + endif endif ifneq (,$(filter gnrc_ipv6_router_default,$(USEMODULE))) diff --git a/sys/include/net/gnrc/ipv6/netif.h b/sys/include/net/gnrc/ipv6/netif.h index ef5961c06980804d175f21e43e44e1692e3ec959..4027bb6ebe5f6190cfe54e70ef3db489dcf86067 100644 --- a/sys/include/net/gnrc/ipv6/netif.h +++ b/sys/include/net/gnrc/ipv6/netif.h @@ -277,7 +277,7 @@ typedef struct { uint16_t flags; /**< flags for 6LoWPAN and Neighbor Discovery */ uint16_t mtu; /**< Maximum Transmission Unit (MTU) of the interface */ uint8_t cur_hl; /**< current hop limit for the interface */ -#ifdef MODULE_GNRC_NDP_HOST +#if defined(MODULE_GNRC_NDP_HOST) || defined(MODULE_GNRC_SIXLOWPAN_ND) /** * @brief Counter for send router solicitations. */ diff --git a/sys/include/net/gnrc/sixlowpan/nd.h b/sys/include/net/gnrc/sixlowpan/nd.h new file mode 100644 index 0000000000000000000000000000000000000000..6a3bb8b61ff36a83fd4841e3ea1f9b34ecf64efa --- /dev/null +++ b/sys/include/net/gnrc/sixlowpan/nd.h @@ -0,0 +1,190 @@ +/* + * 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 net_gnrc_sixlowpan_nd 6LoWPAN neighbor discovery + * @ingroup net_gnrc_sixlowpan + * @brief Neighbor Discovery Optimization for 6LoWPAN + * @see <a href="https://tools.ietf.org/html/rfc6775"> + * RFC 6775 + * </a> + * @{ + * + * @file + * @brief General 6LoWPAN ND definitions + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef GNRC_SIXLOWPAN_ND_H_ +#define GNRC_SIXLOWPAN_ND_H_ + +#include <stdint.h> + +#include "kernel_types.h" +#include "net/gnrc/ipv6/nc.h" +#include "net/gnrc/ipv6/netif.h" +#include "net/ipv6/addr.h" +#include "net/ndp.h" +#include "net/sixlowpan/nd.h" +#include "timex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Message type for next multicast router solicitation. + */ +#define GNRC_SIXLOWPAN_ND_MSG_MC_RTR_SOL (0x0220) + +/** + * @brief Message type for next unicast router solicitation. + */ +#define GNRC_SIXLOWPAN_ND_MSG_UC_RTR_SOL (0x0221) + +/** + * @brief Message type for removing 6LoWPAN contexts. + */ +#define GNRC_SIXLOWPAN_ND_MSG_DELETE_CTX (0x0222) + +#ifndef GNRC_SIXLOWPAN_ND_AR_LTIME +/** + * @brief Registration lifetime in minutes for the address registration option + * + * This value should be adapted to the devices power-lifecycle so that it is greater than the + * time the device spends sleeping. + * + * @see <a href="https://tools.ietf.org/html/rfc6775#section-5.8.1"> + * RFC 6775, section 5.8.1 + * </a> + */ +#define GNRC_SIXLOWPAN_ND_AR_LTIME (15U) +#endif + +/** + * @name Host constants + * @{ + * @see <a href="https://tools.ietf.org/html/rfc6775#section-9"> + * RFC 6775, section 9 + * </a> + */ +#define GNRC_SIXLOWPAN_ND_RTR_SOL_INT (10U) /**< replacement value (in seconds) for + * @ref GNRC_NDP_MAX_RTR_SOL_INT */ +#define GNRC_SIXLOWPAN_ND_MAX_RTR_SOL_INT (60U) /**< retransmission increment for exponential + * backoff of subsequent RS */ +/** @} */ + +/** + * @brief Initializes 6LoWPAN neighbor discovery for the interface. + * @pre @p iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN + * @param[in] iface An IPv6 interface. + */ +void gnrc_sixlowpan_nd_init(gnrc_ipv6_netif_t *iface); + +/** + * @brief Multicasts a router solicitation over @p iface + * @pre @p iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN + * @param[in] iface An IPv6 interface. + */ +void gnrc_sixlowpan_nd_mc_rtr_sol(gnrc_ipv6_netif_t *iface); + +/** + * @brief Unicasts a router solicitation to the neighbor represented by @p nce + * @pre @p nce->iface is an IPv6 interface and @ref GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN is set + * in its flags. + * @param[in] nce The neighbor to send the router solicitation to. + */ +void gnrc_sixlowpan_nd_uc_rtr_sol(gnrc_ipv6_nc_t *nce); + +/** + * @brief Get link-layer address and interface for next hop to destination + * IPv6 address. + * + * @param[out] l2addr The link-layer for the next hop to @p dst. + * @param[out] l2addr_len Length of @p l2addr. + * @param[in] iface The interface to search the next hop on. + * May be @ref KERNEL_PID_UNDEF if not specified. + * @param[in] dst An IPv6 address to search the next hop for. + * + * @return The PID of the interface, on success. + * @return -EHOSTUNREACH, if @p dst is not reachable. + * @return -ENOBUFS, if @p l2addr_len was smaller than the resulting @p l2addr + * would be long. + */ +kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, + kernel_pid_t iface, ipv6_addr_t *dst); + +/** + * @brief Reschedules the next router advertisement for a neighboring router. + * + * @pre nce != NULL && sec_delay != 0U + * + * @param[in] nce Neighbor cache entry representing the neighboring router. + * @param[in] sec_delay The delay for the next router solicitation in seconds. + */ +void gnrc_sixlowpan_nd_rtr_sol_reschedule(gnrc_ipv6_nc_t *nce, uint32_t sec_delay); + +/** + * @brief Builds the address registration option. + * + * @param[in] status Status for the ARO. + * @param[in] ltime Registration lifetime for the ARO. + * @param[in] eui64 The EUI-64 for the ARO + * @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 + */ +gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_ar_build(uint8_t status, uint16_t ltime, eui64_t *eui64, + gnrc_pktsnip_t *next); + +/** + * @brief Handles address registration option. + * + * @param[in] iface The interface the ARO was received on. + * @param[in] ipv6 The IPv6 header the ARO was received in. + * @param[in] icmpv6_type Message type of the ICMPv6 message that contained. + * this message. + * @param[in] ar_opt The address registration option. + * @param[in] sl2a The link-layer source address contained in SL2A accompanying this + * option. May be NULL for icmpv6_type == ICMPV6_NBR_ADV. + * @param[in] sl2a_len Length of @p sl2a. May be 0 if sl2a == NULL. + * + * @return Status for the ARO in the replying NA (always 0 if icmpv6_type == ICMPV6_NBR_ADV). + */ +uint8_t gnrc_sixlowpan_nd_opt_ar_handle(kernel_pid_t iface, ipv6_hdr_t *ipv6, + uint8_t icmpv6_type, sixlowpan_nd_opt_ar_t *ar_opt, + uint8_t *sl2a, size_t sl2a_len); + +/** + * @brief Handles 6LoWPAN context option. + * + * @param[in] icmpv6_type Message type of the ICMPv6 message that contained. + * this message. + * @param[in] ctx_opt The 6LoWPAN context option. + * + * @return true, when 6LoWPAN context option was correct. + * @return false, when it was incorrect. + */ +bool gnrc_sixlowpan_nd_opt_6ctx_handle(uint8_t icmpv6_type, sixlowpan_nd_opt_6ctx_t *ctx_opt); + +/** + * @brief Handles registration calls after node-wakeup. + * + * @see <a href="https://tools.ietf.org/html/rfc6775#section-5.8.2"> + * RFC 6776, section 5.8.2 + * </a> + */ +void gnrc_sixlowpan_nd_wakeup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_SIXLOWPAN_ND_H_ */ +/** @} */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 69439a9aa96f42887252774505b8f47cf8ce9159..ce91aa59c2e88ae4ad6a487572579665f093f0b2 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -79,6 +79,9 @@ endif ifneq (,$(filter gnrc_sixlowpan_iphc,$(USEMODULE))) DIRS += network_layer/sixlowpan/iphc endif +ifneq (,$(filter gnrc_sixlowpan_nd,$(USEMODULE))) + DIRS += network_layer/sixlowpan/nd +endif ifneq (,$(filter gnrc_sixlowpan_netif,$(USEMODULE))) DIRS += network_layer/sixlowpan/netif endif diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index 0f876f2425f52d7187f412e1486d57488c6964af..19edf024ff350be6d98f186ef362027e32b79f31 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -19,9 +19,11 @@ #include "byteorder.h" #include "cpu_conf.h" #include "kernel_types.h" -#include "net/gnrc/icmpv6.h" #include "net/gnrc.h" +#include "net/gnrc/icmpv6.h" #include "net/gnrc/ndp.h" +#include "net/gnrc/sixlowpan/ctx.h" +#include "net/gnrc/sixlowpan/nd.h" #include "net/protnum.h" #include "thread.h" #include "utlist.h" @@ -228,7 +230,21 @@ static void *_event_loop(void *args) gnrc_ndp_host_retrans_rtr_sol((gnrc_ipv6_netif_t *)msg.content.ptr); break; #endif - +#ifdef MODULE_GNRC_SIXLOWPAN_ND + case GNRC_SIXLOWPAN_ND_MSG_MC_RTR_SOL: + DEBUG("ipv6: Multicast router solicitation event received\n"); + gnrc_sixlowpan_nd_mc_rtr_sol((gnrc_ipv6_netif_t *)msg.content.ptr); + break; + case GNRC_SIXLOWPAN_ND_MSG_UC_RTR_SOL: + DEBUG("ipv6: Unicast router solicitation event received\n"); + gnrc_sixlowpan_nd_uc_rtr_sol((gnrc_ipv6_nc_t *)msg.content.ptr); + break; + case GNRC_SIXLOWPAN_ND_MSG_DELETE_CTX: + DEBUG("ipv6: Delete 6LoWPAN context event received\n"); + gnrc_sixlowpan_ctx_remove(((((gnrc_sixlowpan_ctx_t *)msg.content.ptr)->flags_id) & + GNRC_SIXLOWPAN_CTX_FLAGS_CID_MASK)); + break; +#endif default: break; } @@ -497,16 +513,24 @@ static inline kernel_pid_t _next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len kernel_pid_t iface, ipv6_addr_t *dst, gnrc_pktsnip_t *pkt) { -#ifdef MODULE_GNRC_NDP_NODE - return gnrc_ndp_node_next_hop_l2addr(l2addr, l2addr_len, iface, dst, pkt); -#else +#if defined(MODULE_GNRC_SIXLOWPAN_ND) + (void)pkt; + iface = gnrc_sixlowpan_nd_next_hop_l2addr(l2addr, l2addr_len, iface, dst); + if (iface <= KERNEL_PID_UNDEF) { + return iface; + } +#endif +#if defined(MODULE_GNRC_NDP_NODE) + iface = gnrc_ndp_node_next_hop_l2addr(l2addr, l2addr_len, iface, dst, pkt); +#elif !defined(MODULE_GNRC_SIXLOWPAN_ND) + iface = KERNEL_PID_UNDEF; (void)l2addr; (void)iface; (void)dst; (void)pkt; *l2addr_len = 0; - return KERNEL_PID_UNDEF; #endif + return iface; } static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr) diff --git a/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c b/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c index 2967a07962d269a0aa86e92bf0888fe47c897722..cc7d7fb9f5660c22dabd0390dd7357865708ceb7 100644 --- a/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c +++ b/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c @@ -29,6 +29,7 @@ #include "net/gnrc/netapi.h" #include "net/gnrc/netif.h" #include "net/gnrc/netif/hdr.h" +#include "net/gnrc/sixlowpan/nd.h" #include "net/gnrc/sixlowpan/netif.h" #include "net/gnrc/ipv6/netif.h" @@ -90,8 +91,6 @@ static ipv6_addr_t *_add_addr_to_entry(gnrc_ipv6_netif_t *entry, const ipv6_addr tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST; } else { - ipv6_addr_t sol_node; - if (!ipv6_addr_is_link_local(addr)) { /* add also corresponding link-local address */ ipv6_addr_t ll_addr; @@ -117,9 +116,18 @@ static ipv6_addr_t *_add_addr_to_entry(gnrc_ipv6_netif_t *entry, const ipv6_addr else { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK; } - - ipv6_addr_set_solicited_nodes(&sol_node, addr); - _add_addr_to_entry(entry, &sol_node, IPV6_ADDR_BIT_LEN, 0); +#if defined(MODULE_GNRC_NDP_NODE) || defined(MODULE_GNRC_SIXLOWPAN_ND) + /* add solicited-nodes multicast address for new address if interface is not a + * 6LoWPAN host interface (see: https://tools.ietf.org/html/rfc6775#section-5.2) */ + if (!(entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) || + (entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { + ipv6_addr_t sol_node; + ipv6_addr_set_solicited_nodes(&sol_node, addr); + _add_addr_to_entry(entry, &sol_node, IPV6_ADDR_BIT_LEN, 0); + } +#endif + /* TODO: send NS with ARO on 6LoWPAN interfaces, but not so many and only for the new + * source address. */ } return &(tmp_addr->addr); @@ -804,6 +812,12 @@ void gnrc_ipv6_netif_init_by_dev(void) #if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER)) gnrc_ipv6_netif_set_router(ipv6_if, true); #endif +#ifdef MODULE_GNRC_SIXLOWPAN_ND + if (ipv6_if->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { + gnrc_sixlowpan_nd_init(ipv6_if); + continue; /* skip gnrc_ndp_host_init() */ + } +#endif #ifdef MODULE_GNRC_NDP_HOST /* start periodic router solicitations */ gnrc_ndp_host_init(ipv6_if); diff --git a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c index 42683a64698fdaaf8338ddbc3214e237dca3d170..4b246e9caef67a03b3a8efe88aa198fb2e98a774 100644 --- a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c +++ b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c @@ -23,7 +23,9 @@ #include "net/ipv6/ext/rh.h" #include "net/gnrc/icmpv6.h" #include "net/gnrc/ipv6.h" +#include "net/gnrc/sixlowpan/nd.h" #include "net/gnrc.h" +#include "net/sixlowpan/nd.h" #include "random.h" #include "utlist.h" #include "thread.h" @@ -175,9 +177,14 @@ void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, /* invalid target link-layer address option */ return; } - break; - +#ifdef MODULE_GNRC_SIXLOWPAN_ND + case NDP_OPT_AR: + /* address registration option is always ignored when invalid */ + gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6, nbr_adv->type, + (sixlowpan_nd_opt_ar_t *)opt, NULL, 0); + break; +#endif default: /* silently discard all other options */ break; @@ -194,6 +201,13 @@ void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, } if (l2tgt_len != -ENOTSUP) { +#ifdef MODULE_GNRC_SIXLOWPAN_ND + /* check if entry wasn't removed by ARO, ideally there should not be any TL2A in here */ + nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt); + if (nc_entry == NULL) { + return; + } +#endif if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) { if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len == 0)) { /* link-layer has addresses, but no TLLAO supplied: discard silently @@ -358,6 +372,9 @@ void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t gnrc_ipv6_nc_t *nc_entry = NULL; gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface); uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX]; +#ifdef MODULE_GNRC_SIXLOWPAN_ND + uint32_t next_rtr_sol = 0; +#endif int sicmpv6_size = (int)icmpv6_size, l2src_len = 0; uint16_t opt_offset = 0; @@ -388,9 +405,12 @@ void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t } /* set router life timer */ if (rtr_adv->ltime.u16 != 0) { + uint16_t ltime = byteorder_ntohs(rtr_adv->ltime); +#ifdef MODULE_GNRC_SIXLOWPAN_ND + next_rtr_sol = ltime; +#endif vtimer_remove(&nc_entry->rtr_timeout); - vtimer_set_msg(&nc_entry->rtr_timeout, - timex_set(byteorder_ntohs(rtr_adv->ltime), 0), + vtimer_set_msg(&nc_entry->rtr_timeout, timex_set(ltime, 0), thread_getpid(), GNRC_NDP_MSG_RTR_TIMEOUT, nc_entry); } /* set current hop limit from message if available */ @@ -437,10 +457,51 @@ void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t /* invalid prefix information option */ return; } +#ifdef MODULE_GNRC_SIXLOWPAN_ND + if (byteorder_ntohl(((ndp_opt_pi_t *)opt)->valid_ltime) < + next_rtr_sol) { + next_rtr_sol = byteorder_ntohl(((ndp_opt_pi_t *)opt)->valid_ltime); + } +#endif + break; +#ifdef MODULE_GNRC_SIXLOWPAN_ND + case NDP_OPT_6CTX: + if (!gnrc_sixlowpan_nd_opt_6ctx_handle(rtr_adv->type, + (sixlowpan_nd_opt_6ctx_t *)opt)) { + /* invalid 6LoWPAN context option */ + return; + } + if (byteorder_ntohs(((sixlowpan_nd_opt_6ctx_t *)opt)->ltime) < + (next_rtr_sol / 60)) { + next_rtr_sol = byteorder_ntohs(((sixlowpan_nd_opt_6ctx_t *)opt)->ltime) * 60; + } + break; +#endif } } +#if ENABLE_DEBUG && defined(MODULE_NG_SIXLOWPAN_ND) + if ((if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (l2src_len <= 0)) { + DEBUG("ndp: Router advertisement did not contain any source address information\n"); + } +#endif _stale_nc(iface, &ipv6->src, l2src, l2src_len); +#ifdef MODULE_NG_SIXLOWPAN_ND + if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { + timex_t t = { 0, GNRC_NDP_RETRANS_TIMER }; + /* stop multicast router solicitation retransmission timer */ + vtimer_remove(&if_entry->rtr_sol_timer); + /* 3/4 of the time should be "well before" enough the respective timeout + * not to run out; see https://tools.ietf.org/html/rfc6775#section-5.4.3 */ + next_rtr_sol *= 3; + next_rtr_sol >>= 2; + gnrc_sixlowpan_nd_rtr_sol_reschedule(nc_entry, next_rtr_sol); + gnrc_ndp_internal_send_nbr_sol(ifs[i], &nc_entry->ipv6_addr, &nc_entry->ipv6_addr); + vtimer_remove(&nc_entry->nbr_sol_timer); + vtimer_set_msg(&nc_entry->nbr_sol_timer, t, gnrc_ipv6_pid, GNRC_NDP_MSG_NBR_SOL_RETRANS, + nc_entry); + } +#endif } void gnrc_ndp_retrans_nbr_sol(gnrc_ipv6_nc_t *nc_entry) diff --git a/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c b/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c index 870dffcf773d1d42af011f3c82f13ef852e97f99..32016c438757571dbb68e8e5203b95e82f59e225 100644 --- a/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c +++ b/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c @@ -14,8 +14,10 @@ #include <stdlib.h> +#include "net/eui64.h" #include "net/gnrc/ipv6.h" #include "net/gnrc/ndp.h" +#include "net/gnrc/sixlowpan/nd.h" #include "random.h" #include "timex.h" #include "vtimer.h" @@ -228,8 +230,17 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_addr_t *dst) { +#ifdef MODULE_GNRC_SIXLOWPAN_ND + gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface); + assert(ipv6_iface != NULL); +#endif gnrc_pktsnip_t *hdr, *pkt = NULL; ipv6_addr_t *src = NULL; + /* both suppressions, since they are needed in the MODULE_GNRC_SIXLOWPAN_ND branch */ + /* cppcheck-suppress variableScope */ + uint8_t l2src[8]; + /* cppcheck-suppress variableScope */ + size_t l2src_len = 0; DEBUG("ndp internal: send neighbor solicitation (iface: %" PRIkernel_pid ", tgt: %s, ", iface, ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); @@ -237,8 +248,6 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt, /* check if there is a fitting source address to target */ if ((src = gnrc_ipv6_netif_find_best_src_addr(iface, tgt)) != NULL) { - uint8_t l2src[8]; - size_t l2src_len; l2src_len = _get_l2src(iface, l2src, sizeof(l2src)); if (l2src_len > 0) { @@ -253,6 +262,27 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt, } } +#ifdef MODULE_GNRC_SIXLOWPAN_ND + if (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { + if (l2src_len != sizeof(eui64_t)) { + l2src_len = (uint16_t)gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, l2src, + sizeof(l2src)); + if (l2src_len != sizeof(eui64_t)) { + DEBUG("ndp internal: can't get EUI-64 of the interface\n"); + gnrc_pktbuf_release(pkt); + return; + } + } + hdr = gnrc_sixlowpan_nd_opt_ar_build(0, GNRC_SIXLOWPAN_ND_AR_LTIME, (eui64_t *)l2src, pkt); + if (hdr == NULL) { + DEBUG("ndp internal: error allocatin Address Registration option.\n"); + gnrc_pktbuf_release(pkt); + return; + } + pkt = hdr; + } +#endif + hdr = gnrc_ndp_nbr_sol_build(tgt, pkt); if (hdr == NULL) { @@ -571,6 +601,13 @@ bool gnrc_ndp_internal_pi_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type, /* else discard silently */ return true; } +#ifdef MODULE_GNRC_SIXLOWPAN_ND + if ((gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && + (pi_opt->flags & NDP_OPT_PI_FLAGS_L)) { + /* ignore: see https://tools.ietf.org/html/rfc6775#section-5.4 */ + return true; + } +#endif prefix = gnrc_ipv6_netif_find_addr(iface, &pi_opt->prefix); if (((prefix == NULL) || (gnrc_ipv6_netif_addr_get(prefix)->prefix_len != pi_opt->prefix_len)) && diff --git a/sys/net/gnrc/network_layer/sixlowpan/nd/Makefile b/sys/net/gnrc/network_layer/sixlowpan/nd/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..cbcef5d3cf9e9be85f3afa12195349eba676fcbb --- /dev/null +++ b/sys/net/gnrc/network_layer/sixlowpan/nd/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sixlowpan_nd + +include $(RIOTBASE)/Makefile.base 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 new file mode 100644 index 0000000000000000000000000000000000000000..d87f19cfbd7615f3cdcb94dd88cc71c7ec19b1b0 --- /dev/null +++ b/sys/net/gnrc/network_layer/sixlowpan/nd/gnrc_sixlowpan_nd.c @@ -0,0 +1,296 @@ +/* + * 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/eui64.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/ndp.h" +#include "net/gnrc/ndp/internal.h" +#include "net/gnrc/netif.h" +#include "net/gnrc/sixlowpan.h" +#include "net/gnrc/sixlowpan/ctx.h" +#include "random.h" +#include "timex.h" + +#include "net/gnrc/sixlowpan/nd.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static inline void _rtr_sol_reschedule(gnrc_ipv6_netif_t *iface, uint32_t sec_delay) +{ + vtimer_remove(&iface->rtr_sol_timer); + vtimer_set_msg(&iface->rtr_sol_timer, timex_set(sec_delay, 0), gnrc_ipv6_pid, + GNRC_SIXLOWPAN_ND_MSG_MC_RTR_SOL, iface); +} + +static inline uint32_t _binary_exp_backoff(uint32_t base_sec, unsigned int exp) +{ + return genrand_uint32_range(0, (1 << exp) - 1) * base_sec; +} + +static inline void _revert_iid(uint8_t *iid) +{ + iid[0] ^= 0x02; +} + +void gnrc_sixlowpan_nd_init(gnrc_ipv6_netif_t *iface) +{ + assert(iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN); + mutex_lock(&iface->mutex); + iface->rtr_sol_count = 0; /* first will be send immediately */ + + DEBUG("6lo nd: retransmit multicast rtr sol in 10 sec\n"); + _rtr_sol_reschedule(iface, GNRC_SIXLOWPAN_ND_RTR_SOL_INT); + mutex_unlock(&iface->mutex); + gnrc_ndp_internal_send_rtr_sol(iface->pid, NULL); +} + +void gnrc_sixlowpan_nd_mc_rtr_sol(gnrc_ipv6_netif_t *iface) +{ + uint32_t interval; + assert(iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN); + mutex_lock(&iface->mutex); + if (iface->rtr_sol_count < GNRC_NDP_MAX_RTR_SOL_NUMOF) { + DEBUG("6lo nd: retransmit multicast rtr sol in 10 sec\n"); + iface->rtr_sol_count++; + interval = GNRC_SIXLOWPAN_ND_RTR_SOL_INT; + } + else { + unsigned int exp = (unsigned int)(iface->rtr_sol_count - GNRC_NDP_MAX_RTR_SOL_NUMOF); + interval = _binary_exp_backoff(1, exp); + if (((1U << exp) - 1U) < GNRC_SIXLOWPAN_ND_MAX_RTR_SOL_INT) { + /* XXX Not sure if this is the correct interpretation of the truncation described in + * https://tools.ietf.org/html/rfc6775#section-5.3. In every source I've read the + * truncating value was the exponent, not the target value, so I'm very confused + * about this sentencing. Anyway, since 60 sec is a maximum value this should only + * affect the energy consumption of the implementation by sending the next RS too fast + * but not its interoperability. */ + iface->rtr_sol_count++; + } + + DEBUG("6lo nd: retransmit multicast rtr sol in %" PRIu32 " sec\n", interval); + iface->rtr_sol_count--; + } + _rtr_sol_reschedule(iface, interval); + mutex_unlock(&iface->mutex); + gnrc_ndp_internal_send_rtr_sol(iface->pid, NULL); +} + +void gnrc_sixlowpan_nd_uc_rtr_sol(gnrc_ipv6_nc_t *nce) +{ + assert(gnrc_ipv6_netif_get(nce->iface)->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN); + /* neighbor is not a router anymore */ + if (!(nce->flags & GNRC_IPV6_NC_IS_ROUTER) || ipv6_addr_is_unspecified(&nce->ipv6_addr)) { + /* and there are no routers anymore */ + if (gnrc_ipv6_nc_get_next_router(NULL) == NULL) { + /* start search for routers */ + gnrc_sixlowpan_nd_init(gnrc_ipv6_netif_get(nce->iface)); + } + /* otherwise ignore this call */ + return; + } + /* next RS is rescheduled by RA handle function */ + gnrc_ndp_internal_send_rtr_sol(nce->iface, &nce->ipv6_addr); +} + +kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, + kernel_pid_t iface, ipv6_addr_t *dst) +{ + ipv6_addr_t *next_hop = NULL; + gnrc_ipv6_nc_t *nc_entry = NULL; + +#ifdef MODULE_GNRC_IPV6_EXT_RH + ipv6_hdr_t *hdr; + gnrc_pktsnip_t *ipv6; + LL_SEARCH_SCALAR(pkt, ipv6, type, GNRC_NETTYPE_IPV6); + assert(ipv6); + hdr = ipv6->data; + next_hop = ipv6_ext_rh_next_hop(hdr); +#endif +#ifdef MODULE_FIB + ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */ + /* don't look-up link local addresses in FIB */ + if ((next_hop == NULL) && !ipv6_addr_is_link_local(dst)) { + size_t next_hop_size = sizeof(ipv6_addr_t); + uint32_t next_hop_flags = 0; + if ((next_hop == NULL) && + (fib_get_next_hop(&gnrc_ipv6_fib_table, &iface, next_hop_actual.u8, &next_hop_size, + &next_hop_flags, (uint8_t *)dst, + sizeof(ipv6_addr_t), 0) >= 0) && + (next_hop_size == sizeof(ipv6_addr_t))) { + next_hop = &next_hop_actual; + } + } +#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" */ + /* multicast is not handled here anyway so we don't need to check that */ + next_hop = dst; + } + else if (next_hop == NULL) { /* prefix is off-link */ + next_hop = gnrc_ndp_internal_default_router(); + } + + /* address resolution of next_hop: https://tools.ietf.org/html/rfc6775#section-5.7 */ + if (ipv6_addr_is_link_local(next_hop)) { + kernel_pid_t ifs[GNRC_NETIF_NUMOF]; + size_t ifnum = gnrc_netif_get(ifs); + /* we don't need address resolution, the EUI-64 is in next_hop's IID */ + *l2addr_len = sizeof(eui64_t); + memcpy(l2addr, &next_hop->u8[8], sizeof(eui64_t)); + _revert_iid(l2addr); + if (iface == KERNEL_PID_UNDEF) { + for (unsigned i = 0; i < ifnum; i++) { + gnrc_ipv6_netif_t *ipv6_if = gnrc_ipv6_netif_get(ifs[i]); + if ((ipv6_if != NULL) && (ipv6_if->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { + /* always take the first 6LoWPAN interface we can find */ + return ifs[i]; + } + } + } + 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; + } +} + +void gnrc_sixlowpan_nd_rtr_sol_reschedule(gnrc_ipv6_nc_t *nce, uint32_t sec_delay) +{ + assert(nce != NULL); + assert(sec_delay != 0U); + vtimer_remove(&nce->rtr_sol_timer); + vtimer_set_msg(&nce->rtr_sol_timer, timex_set(sec_delay, 0), gnrc_ipv6_pid, + GNRC_SIXLOWPAN_ND_MSG_MC_RTR_SOL, nce); +} + +gnrc_pktsnip_t *gnrc_sixlowpan_nd_opt_ar_build(uint8_t status, uint16_t ltime, eui64_t *eui64, + gnrc_pktsnip_t *next) +{ + gnrc_pktsnip_t *pkt = gnrc_ndp_opt_build(NDP_OPT_AR, sizeof(sixlowpan_nd_opt_ar_t), next); + + if (pkt != NULL) { + sixlowpan_nd_opt_ar_t *ar_opt = pkt->data; + ar_opt->status = status; + ar_opt->resv[0] = ar_opt->resv[1] = ar_opt->resv[2] = 0; + ar_opt->ltime = byteorder_htons(ltime); + memcpy(&ar_opt->eui64, eui64, sizeof(eui64_t)); + } + + return pkt; +} + +uint8_t gnrc_sixlowpan_nd_opt_ar_handle(kernel_pid_t iface, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + sixlowpan_nd_opt_ar_t *ar_opt, uint8_t *sl2a, + size_t sl2a_len) +{ + eui64_t eui64; + gnrc_ipv6_netif_t *ipv6_iface; + gnrc_ipv6_nc_t *nc_entry; + (void)sl2a; + (void)sl2a_len; + if (ar_opt->len != SIXLOWPAN_ND_OPT_AR_LEN) { + /* discard silently: see https://tools.ietf.org/html/rfc6775#section-5.5.2 */ + return 0; + } + if (gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, &eui64, + sizeof(eui64)) < 0) { + /* discard silently: see https://tools.ietf.org/html/rfc6775#section-5.5.2 */ + return 0; + } + ipv6_iface = gnrc_ipv6_netif_get(iface); + nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src); + switch (icmpv6_type) { + case ICMPV6_NBR_ADV: + if (!(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { + DEBUG("6lo nd: interface not a 6LoWPAN interface\n"); + return 0; + } + if (eui64.uint64.u64 != ar_opt->eui64.uint64.u64) { + /* discard silently: see https://tools.ietf.org/html/rfc6775#section-5.5.2 */ + return 0; + } + switch (ar_opt->status) { + case SIXLOWPAN_ND_STATUS_SUCCESS: + DEBUG("6lo nd: address registration successful\n"); + mutex_lock(&ipv6_iface->mutex); + vtimer_remove(&nc_entry->nbr_sol_timer); + vtimer_set_msg(&nc_entry->nbr_sol_timer, ipv6_iface->retrans_timer, + gnrc_ipv6_pid, GNRC_NDP_MSG_NBR_SOL_RETRANS, nc_entry); + mutex_unlock(&ipv6_iface->mutex); + break; + case SIXLOWPAN_ND_STATUS_DUP: + DEBUG("6lo nd: address registration determined duplicated\n"); + /* TODO: handle DAD failed case */ + gnrc_ipv6_netif_remove_addr(iface, &ipv6->dst); + /* address should not be used anymore */ + break; + case SIXLOWPAN_ND_STATUS_NC_FULL: + DEBUG("6lo nd: neighbor cache on router is full\n"); + gnrc_ipv6_nc_remove(iface, &ipv6->src); + /* try to find another router */ + gnrc_sixlowpan_nd_init(ipv6_iface); + break; + default: + DEBUG("6lo nd: unknown status for registration received\n"); + break; + } + default: + break; + } + + return 0; +} + +bool gnrc_sixlowpan_nd_opt_6ctx_handle(uint8_t icmpv6_type, 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))) { + DEBUG("6lo nd: invalid 6LoWPAN context option received\n"); + return false; + } + if (icmpv6_type != ICMPV6_RTR_ADV) { + /* discard silently */ + return true; + } + /* don't care for result */ + gnrc_sixlowpan_ctx_update(sixlowpan_nd_opt_6ctx_get_cid(ctx_opt), (ipv6_addr_t *)(ctx_opt + 1), + ctx_opt->ctx_len, byteorder_ntohs(ctx_opt->ltime), + sixlowpan_nd_opt_6ctx_is_comp(ctx_opt)); + return true; +} + +void gnrc_sixlowpan_nd_wakeup(void) +{ + gnrc_ipv6_nc_t *router = gnrc_ipv6_nc_get_next_router(NULL); + while (router) { + timex_t t = { 0, GNRC_NDP_RETRANS_TIMER }; + vtimer_remove(&router->rtr_sol_timer); + gnrc_sixlowpan_nd_uc_rtr_sol(router); + gnrc_ndp_internal_send_nbr_sol(router->iface, &router->ipv6_addr, &router->ipv6_addr); + vtimer_remove(&router->nbr_sol_timer); + vtimer_set_msg(&router->nbr_sol_timer, t, gnrc_ipv6_pid, GNRC_NDP_MSG_NBR_SOL_RETRANS, + router); + } +} + +/** @} */