mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
nib: implement public NIB functions up to link-local AR
This commit is contained in:
parent
d30216dcbe
commit
1b36cdf79e
@ -330,6 +330,7 @@ endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_nib_6ln,$(USEMODULE)))
|
||||
USEMODULE += gnrc_ipv6_nib
|
||||
USEMODULE += gnrc_sixlowpan_nd
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_nib_router,$(USEMODULE)))
|
||||
|
@ -12,6 +12,8 @@
|
||||
* @brief Neighbor Information Base (NIB) for IPv6
|
||||
*
|
||||
* @todo Add detailed description
|
||||
* @todo Implement multihop DAD
|
||||
* @todo Implement classic SLAAC
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
@ -27,6 +29,12 @@
|
||||
#include "net/gnrc/ipv6/nib/nc.h"
|
||||
#include "net/gnrc/ipv6/nib/pl.h"
|
||||
|
||||
#include "net/icmpv6.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/ipv6/hdr.h"
|
||||
#include "net/gnrc/ipv6/nib/nc.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -181,8 +189,109 @@ extern "C" {
|
||||
* context is a valid default router entry representing the router.
|
||||
*/
|
||||
#define GNRC_IPV6_NIB_RTR_TIMEOUT (0x4fcdU)
|
||||
|
||||
/**
|
||||
* @brief Recalculate reachability timeout time.
|
||||
*
|
||||
* This message type is for the event of recalculating the reachability timeout
|
||||
* time. The expected message context is a valid interface.
|
||||
*
|
||||
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ARSM != 0
|
||||
*/
|
||||
#define GNRC_IPV6_NIB_RECALC_REACH_TIME (0x4fceU)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Initialize NIB
|
||||
*/
|
||||
void gnrc_ipv6_nib_init(void);
|
||||
|
||||
/**
|
||||
* @brief Adds an interface to be managed by the NIB.
|
||||
*
|
||||
* @pre `(KERNEL_PID_UNDEF < iface)`
|
||||
*
|
||||
* @param[in] iface The interface to be managed by the NIB
|
||||
*/
|
||||
void gnrc_ipv6_nib_init_iface(kernel_pid_t iface);
|
||||
|
||||
/**
|
||||
* @brief Gets link-layer address of next hop to a destination address
|
||||
*
|
||||
* @pre `(dst != NULL) && (nce != NULL)`
|
||||
*
|
||||
* @param[in] dst Destination address of a packet.
|
||||
* @param[in] iface Restrict search to this interface. May be
|
||||
* `KERNEL_PID_UNDEF` for any interface.
|
||||
* @param[in] pkt The IPv6 packet in sending order for which the next hop
|
||||
* is searched. Needed for queuing for with reactive
|
||||
* routing or address resolution. May be `NULL`.
|
||||
* Will be released properly on error.
|
||||
* @param[out] nce The neighbor cache entry of the next hop to @p dst.
|
||||
*
|
||||
* @return 0, on success.
|
||||
* @return -ENETUNREACH if there is no route to host.
|
||||
* @return -EHOSTUNREACH if the next hop is not reachable or if @p dst was
|
||||
* link-local, but @p iface was @ref KERNEL_PID_UNDEF (no neighbor
|
||||
* cache entry will be created in this case and no neighbor
|
||||
* solicitation sent).
|
||||
*/
|
||||
int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst,
|
||||
kernel_pid_t iface, gnrc_pktsnip_t *pkt,
|
||||
gnrc_ipv6_nib_nc_t *nce);
|
||||
|
||||
/**
|
||||
* @brief Handles a received ICMPv6 packet
|
||||
*
|
||||
* @pre `iface != KERNEL_PID_UNDEF`
|
||||
* @pre `ipv6 != NULL`
|
||||
* @pre `icmpv6 != NULL`
|
||||
* @pre `icmpv6_len > sizeof(icmpv6_hdr_t)`
|
||||
*
|
||||
* @attention The ICMPv6 checksum is supposed to be checked externally!
|
||||
*
|
||||
* @note @p ipv6 is just used for the addresses and hop limit. The next
|
||||
* header field will not be checked for correctness (but should be
|
||||
* @ref PROTNUM_ICMPV6)
|
||||
*
|
||||
* @see [RFC 4861, section 6.1](https://tools.ietf.org/html/rfc4861#section-6.1)
|
||||
* @see [RFC 4861, section 6.2.6](https://tools.ietf.org/html/rfc4861#section-6.2.6)
|
||||
* @see [RFC 4861, section 6.3.4](https://tools.ietf.org/html/rfc4861#section-6.3.4)
|
||||
* @see [RFC 4861, section 7.1](https://tools.ietf.org/html/rfc4861#section-7.1)
|
||||
* @see [RFC 4861, section 7.2.3](https://tools.ietf.org/html/rfc4861#section-7.2.3)
|
||||
* @see [RFC 4861, section 7.2.5](https://tools.ietf.org/html/rfc4861#section-7.2.5)
|
||||
* @see [RFC 4861, section 8.1](https://tools.ietf.org/html/rfc4861#section-8.1)
|
||||
* @see [RFC 4861, section 8.3](https://tools.ietf.org/html/rfc4861#section-8.3)
|
||||
* @see [RFC 4862, section 5.4.3](https://tools.ietf.org/html/rfc4862#section-5.4.3)
|
||||
* @see [RFC 4862, section 5.4.4](https://tools.ietf.org/html/rfc4862#section-5.4.4)
|
||||
* @see [RFC 4862, section 5.5.3](https://tools.ietf.org/html/rfc4862#section-5.5.3)
|
||||
* @see [RFC 6775, section 5.5.2](https://tools.ietf.org/html/rfc6775#section-5.5.2)
|
||||
* @see [RFC 6775, section 5.4](https://tools.ietf.org/html/rfc6775#section-5.4)
|
||||
* @see [RFC 6775, section 6.3](https://tools.ietf.org/html/rfc6775#section-6.3)
|
||||
* @see [RFC 6775, section 6.5](https://tools.ietf.org/html/rfc6775#section-6.5)
|
||||
* @see [RFC 6775, section 8.1.3](https://tools.ietf.org/html/rfc6775#section-8.1.3)
|
||||
* @see [RFC 6775, section 8.2.1](https://tools.ietf.org/html/rfc6775#section-8.2.1)
|
||||
* @see [RFC 6775, section 8.2.4](https://tools.ietf.org/html/rfc6775#section-8.2.4)
|
||||
* @see [RFC 6775, section 8.2.5](https://tools.ietf.org/html/rfc6775#section-8.2.5)
|
||||
*
|
||||
* @param[in] iface The interface the packet came over.
|
||||
* @param[in] ipv6 The IPv6 header of the received packet.
|
||||
* @param[in] icmpv6 The ICMPv6 header and payload of the received
|
||||
* packet.
|
||||
* @param[in] icmpv6_len The number of bytes at @p icmpv6.
|
||||
*/
|
||||
void gnrc_ipv6_nib_handle_pkt(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const icmpv6_hdr_t *icmpv6, size_t icmpv6_len);
|
||||
|
||||
/**
|
||||
* @brief Handles a timer event
|
||||
*
|
||||
* @param[in] ctx Context of the timer event.
|
||||
* @param[in] type Type of the timer event (see [timer event
|
||||
* types](@ref net_gnrc_ipv6_nib_msg))
|
||||
*/
|
||||
void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
151
sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c
Normal file
151
sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 "_nib-6ln.h"
|
||||
#include "_nib-6lr.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_6LN
|
||||
#if ENABLE_DEBUG
|
||||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
#endif
|
||||
|
||||
static bool _is_iface_eui64(kernel_pid_t iface, const eui64_t *eui64)
|
||||
{
|
||||
eui64_t iface_eui64;
|
||||
|
||||
/* XXX: this *should* return successful so don't test it ;-) */
|
||||
gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0,
|
||||
&iface_eui64, sizeof(iface_eui64));
|
||||
return (memcmp(&iface_eui64, eui64, sizeof(iface_eui64)) != 0);
|
||||
}
|
||||
|
||||
bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, kernel_pid_t iface,
|
||||
gnrc_ipv6_nib_nc_t *nce)
|
||||
{
|
||||
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(iface);
|
||||
bool res = (netif != NULL) && _is_6ln(netif) &&
|
||||
ipv6_addr_is_link_local(dst);
|
||||
|
||||
if (res) {
|
||||
memcpy(&nce->ipv6, dst, sizeof(nce->ipv6));
|
||||
memcpy(&nce->l2addr, &dst->u64[1], sizeof(dst->u64[1]));
|
||||
nce->l2addr[0] ^= 0x02;
|
||||
nce->info = 0;
|
||||
nce->info |= (iface << GNRC_IPV6_NIB_NC_INFO_IFACE_POS) &
|
||||
GNRC_IPV6_NIB_NC_INFO_IFACE_MASK;
|
||||
nce->info |= GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE;
|
||||
nce->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED;
|
||||
nce->l2addr_len = sizeof(dst->u64[1]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t _handle_aro(kernel_pid_t iface, 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)
|
||||
{
|
||||
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(iface);
|
||||
|
||||
#if !GNRC_IPV6_NIB_CONF_6LR
|
||||
(void)sl2ao;
|
||||
#endif
|
||||
assert(netif != NULL);
|
||||
if (_is_6ln(netif) && (aro->len == SIXLOWPAN_ND_OPT_AR_LEN)) {
|
||||
DEBUG("nib: valid ARO received\n");
|
||||
DEBUG(" - length: %u\n", aro->len);
|
||||
DEBUG(" - status: %u\n", aro->status);
|
||||
DEBUG(" - registration lifetime: %u\n", byteorder_ntohs(aro->ltime));
|
||||
DEBUG(" - EUI-64: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
aro->eui64.uint8[0], aro->eui64.uint8[1], aro->eui64.uint8[2],
|
||||
aro->eui64.uint8[3], aro->eui64.uint8[4], aro->eui64.uint8[5],
|
||||
aro->eui64.uint8[6], aro->eui64.uint8[7]);
|
||||
if (icmpv6->type == ICMPV6_NBR_ADV) {
|
||||
if (!_is_iface_eui64(iface, &aro->eui64)) {
|
||||
DEBUG("nib: ARO EUI-64 is not mine, ignoring ARO\n");
|
||||
return _ADDR_REG_STATUS_IGNORE;
|
||||
}
|
||||
switch (aro->status) {
|
||||
case SIXLOWPAN_ND_STATUS_SUCCESS: {
|
||||
uint16_t ltime = byteorder_ntohs(aro->ltime);
|
||||
uint32_t next_ns;
|
||||
/* 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 %ums\n",
|
||||
next_ns);
|
||||
assert(nce != NULL);
|
||||
_evtimer_add(nce, GNRC_IPV6_NIB_SND_UC_NS, &nce->nud_timeout,
|
||||
next_ns);
|
||||
break;
|
||||
}
|
||||
case SIXLOWPAN_ND_STATUS_DUP:
|
||||
DEBUG("nib: Address registration reports duplicate. "
|
||||
"Removing address %s%%%u\n",
|
||||
ipv6_addr_to_str(addr_str,
|
||||
&((ndp_nbr_adv_t *)icmpv6)->tgt,
|
||||
sizeof(addr_str)),
|
||||
iface);
|
||||
gnrc_ipv6_netif_remove_addr(iface,
|
||||
&((ndp_nbr_adv_t *)icmpv6)->tgt);
|
||||
/* TODO: generate new address */
|
||||
break;
|
||||
case SIXLOWPAN_ND_STATUS_NC_FULL: {
|
||||
DEBUG("nib: Router's neighbor cache is full. "
|
||||
"Searching new router for DAD\n");
|
||||
_nib_dr_entry_t *dr = _nib_drl_get(&ipv6->src, iface);
|
||||
assert(dr != NULL); /* otherwise we wouldn't be here */
|
||||
_nib_drl_remove(dr);
|
||||
if (_nib_drl_iter(NULL) == NULL) { /* no DRL left */
|
||||
_nib_iface_t *nib_iface = _nib_iface_get(iface);
|
||||
nib_iface->rs_sent = 0;
|
||||
/* TODO: search new router */
|
||||
}
|
||||
else {
|
||||
assert(dr->next_hop != NULL);
|
||||
_snd_uc_ns(dr->next_hop, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return aro->status;
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_6LR
|
||||
else if (_is_6lr(netif) && (icmpv6->type == ICMPV6_NBR_SOL)) {
|
||||
return _reg_addr_upstream(iface, ipv6, icmpv6, aro, sl2ao);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#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
|
||||
return _ADDR_REG_STATUS_IGNORE;
|
||||
}
|
||||
#else /* GNRC_IPV6_NIB_CONF_6LN */
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LN */
|
||||
|
||||
|
||||
/** @} */
|
106
sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h
Normal file
106
sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definitions related to 6Lo node (6LN) functionality of the NIB
|
||||
* @see @ref GNRC_IPV6_NIB_CONF_6LN
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef PRIV_NIB_6LN_H
|
||||
#define PRIV_NIB_6LN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "net/gnrc/ipv6/nib/conf.h"
|
||||
#include "net/sixlowpan/nd.h"
|
||||
|
||||
#include "_nib-arsm.h"
|
||||
#include "_nib-internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Additional (local) status to ARO status values for tentative
|
||||
* addresses
|
||||
*/
|
||||
#define _ADDR_REG_STATUS_TENTATIVE (3)
|
||||
|
||||
/**
|
||||
* @brief Additional (local) status to ARO status values for return values
|
||||
* to signify that the address was ignored
|
||||
*/
|
||||
#define _ADDR_REG_STATUS_IGNORE (4)
|
||||
|
||||
/**
|
||||
* @brief Checks if interface represents a 6LN
|
||||
*
|
||||
* @todo Use corresponding function in `gnrc_netif2` instead.
|
||||
*
|
||||
* @param[in] netif A network interface.
|
||||
*
|
||||
* @return true, when the @p netif represents a 6LN.
|
||||
* @return false, when the @p netif does not represent a 6LN.
|
||||
*/
|
||||
static inline bool _is_6ln(const gnrc_ipv6_netif_t *netif)
|
||||
{
|
||||
return (netif->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resolves address statically from destination address using reverse
|
||||
* translation of the IID
|
||||
*
|
||||
* @param[in] dst A destination address.
|
||||
* @param[in] iface The interface to @p dst.
|
||||
* @param[out] nce Neighbor cache entry to resolve into
|
||||
*
|
||||
* @return true when @p nce was set, false when not.
|
||||
*/
|
||||
bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, kernel_pid_t iface,
|
||||
gnrc_ipv6_nib_nc_t *nce);
|
||||
|
||||
/**
|
||||
* @brief Handles ARO
|
||||
*
|
||||
* @param[in] iface The interface the ARO-carrying message came over.
|
||||
* @param[in] ipv6 The IPv6 header of the message carrying the ARO.
|
||||
* @param[in] icmpv6 The message carrying the ARO.
|
||||
* @param[in] aro ARO that carries the address registration information.
|
||||
* @param[in] sl2ao SL2AO associated with the ARO.
|
||||
* @param[in] nce Neighbor cache entry the ARO is supposed to change.
|
||||
*
|
||||
* @return registration status of the address (including
|
||||
* @ref _ADDR_REG_STATUS_TENTATIVE and @ref _ADDR_REG_STATUS_IGNORE).
|
||||
*/
|
||||
uint8_t _handle_aro(kernel_pid_t iface, 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);
|
||||
#else /* GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) */
|
||||
#define _is_6ln(netif) (false)
|
||||
#define _resolve_addr_from_ipv6(dst, iface, 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
|
||||
*/
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LN || defined(DOXYGEN) */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PRIV_NIB_6LN_H */
|
||||
/** @} */
|
125
sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.c
Normal file
125
sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include "net/gnrc/ipv6/nib.h"
|
||||
#include "net/gnrc/sixlowpan/nd.h"
|
||||
|
||||
#include "_nib-6lr.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_6LR
|
||||
#if ENABLE_DEBUG
|
||||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
#endif
|
||||
|
||||
static uint8_t _update_nce_ar_state(const sixlowpan_nd_opt_ar_t *aro,
|
||||
_nib_onl_entry_t *nce)
|
||||
{
|
||||
if (nce != NULL) {
|
||||
memcpy(&nce->eui64, &aro->eui64, sizeof(aro->eui64));
|
||||
_evtimer_add(nce, GNRC_IPV6_NIB_ADDR_REG_TIMEOUT,
|
||||
&nce->addr_reg_timeout,
|
||||
byteorder_ntohs(aro->ltime) * SEC_PER_MIN * MS_PER_SEC);
|
||||
_set_ar_state(nce,
|
||||
GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED);
|
||||
DEBUG("nib: Successfully registered %s\n",
|
||||
ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)));
|
||||
return SIXLOWPAN_ND_STATUS_SUCCESS;
|
||||
}
|
||||
else {
|
||||
DEBUG("nib: Could not register %s, neighbor cache was full\n",
|
||||
ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)));
|
||||
return SIXLOWPAN_ND_STATUS_NC_FULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _reg_addr_upstream(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const icmpv6_hdr_t *icmpv6,
|
||||
const sixlowpan_nd_opt_ar_t *aro,
|
||||
const ndp_opt_t *sl2ao)
|
||||
{
|
||||
if (!ipv6_addr_is_unspecified(&ipv6->src) && (sl2ao != NULL)) {
|
||||
_nib_onl_entry_t *nce = _nib_onl_get(&ipv6->src, iface);
|
||||
|
||||
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)),
|
||||
aro->eui64.uint8[0], aro->eui64.uint8[1], aro->eui64.uint8[2],
|
||||
aro->eui64.uint8[3], aro->eui64.uint8[4], aro->eui64.uint8[5],
|
||||
aro->eui64.uint8[6], aro->eui64.uint8[7]);
|
||||
if ((nce == NULL) || !(nce->mode & _NC) ||
|
||||
(memcmp(&nce->eui64, &aro->eui64, sizeof(aro->eui64)) == 0)) {
|
||||
#if GNRC_IPV6_NIB_CONF_MULTIHOP_DAD
|
||||
/* TODO */
|
||||
#endif
|
||||
if (byteorder_ntohs(aro->ltime) != 0) {
|
||||
_handle_sl2ao(iface, ipv6, icmpv6, sl2ao);
|
||||
_update_nce_ar_state(aro, nce);
|
||||
}
|
||||
else if (nce != NULL) {
|
||||
_nib_nc_remove(nce);
|
||||
return SIXLOWPAN_ND_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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)),
|
||||
nce->eui64.uint8[0], nce->eui64.uint8[1], nce->eui64.uint8[2],
|
||||
nce->eui64.uint8[3], nce->eui64.uint8[4], nce->eui64.uint8[5],
|
||||
nce->eui64.uint8[6], nce->eui64.uint8[7]);
|
||||
return SIXLOWPAN_ND_STATUS_DUP;
|
||||
}
|
||||
}
|
||||
return _ADDR_REG_STATUS_IGNORE;
|
||||
}
|
||||
|
||||
gnrc_pktsnip_t *_copy_and_handle_aro(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const ndp_nbr_sol_t *nbr_sol,
|
||||
const sixlowpan_nd_opt_ar_t *aro,
|
||||
const ndp_opt_t *sl2ao)
|
||||
{
|
||||
gnrc_pktsnip_t *reply_aro = NULL;
|
||||
|
||||
if (aro != NULL) {
|
||||
uint8_t status = _handle_aro(iface, ipv6, (icmpv6_hdr_t *)nbr_sol, aro,
|
||||
sl2ao, NULL);
|
||||
|
||||
if ((status != _ADDR_REG_STATUS_TENTATIVE) &&
|
||||
(status != _ADDR_REG_STATUS_IGNORE)) {
|
||||
reply_aro = gnrc_sixlowpan_nd_opt_ar_build(status,
|
||||
byteorder_ntohs(aro->ltime),
|
||||
(eui64_t *)&aro->eui64,
|
||||
NULL);
|
||||
if (reply_aro == NULL) {
|
||||
DEBUG("nib: No space left in packet buffer. Not replying NS");
|
||||
}
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_MULTIHOP_DAD
|
||||
else if (status != _ADDR_REG_STATUS_IGNORE) {
|
||||
DEBUG("nib: Address was marked TENTATIVE => not replying NS, "
|
||||
"waiting for DAC\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return reply_aro;
|
||||
}
|
||||
#else /* GNRC_IPV6_NIB_CONF_6LR */
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LR */
|
||||
|
||||
/** @} */
|
147
sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.h
Normal file
147
sys/net/gnrc/network_layer/ipv6/nib/_nib-6lr.h
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definitions related to 6Lo router (6LR) functionality of the NIB
|
||||
* @see @ref GNRC_IPV6_NIB_CONF_6LR
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef PRIV_NIB_6LR_H
|
||||
#define PRIV_NIB_6LR_H
|
||||
|
||||
|
||||
#include "net/gnrc/ipv6/nib/conf.h"
|
||||
#include "net/ndp.h"
|
||||
#include "net/sixlowpan/nd.h"
|
||||
|
||||
#include "_nib-arsm.h"
|
||||
#include "_nib-6ln.h"
|
||||
#include "_nib-internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Checks if interface represents a 6LR
|
||||
*
|
||||
* @todo Use corresponding function in `gnrc_netif2` instead.
|
||||
*
|
||||
* @param[in] netif A network interface.
|
||||
*
|
||||
* @return true, when the @p netif represents a 6LR.
|
||||
* @return false, when the @p netif does not represent a 6LR.
|
||||
*/
|
||||
static inline bool _is_6lr(const gnrc_ipv6_netif_t *netif)
|
||||
{
|
||||
return _is_6ln(netif) && (netif->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets address registration state of a neighbor
|
||||
*
|
||||
* @param[in] entry Neighbor cache entry representing the neighbor.
|
||||
*
|
||||
* @return Address registration state of the @p entry.
|
||||
*/
|
||||
static inline uint16_t _get_ar_state(const _nib_onl_entry_t *entry)
|
||||
{
|
||||
return (entry->info & GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets address registration state of a neighbor
|
||||
*
|
||||
* @param[in] entry Neighbor cache entry representing the neighbor.
|
||||
* @param[in] state Address registration state for the neighbor.
|
||||
*/
|
||||
static inline void _set_ar_state(_nib_onl_entry_t *entry, uint16_t state)
|
||||
{
|
||||
entry->info &= ~GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK;
|
||||
entry->info |= state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the received message is a router solicitation and
|
||||
* the interface represents a 6Lo router
|
||||
*
|
||||
* @see [RFC 6775](https://tools.ietf.org/html/rfc6775#section-6.3)
|
||||
*
|
||||
* @param[in] netif A network interface.
|
||||
* @param[in] icmpv6 An ICMPv6 message.
|
||||
*/
|
||||
static inline bool _rtr_sol_on_6lr(const gnrc_ipv6_netif_t *netif,
|
||||
const icmpv6_hdr_t *icmpv6)
|
||||
{
|
||||
return _is_6lr(netif) && (icmpv6->type == ICMPV6_RTR_SOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers an address to the (upstream; in case of multihop DAD)
|
||||
* router
|
||||
*
|
||||
* @param[in] iface The interface the ARO-carrying NS came over.
|
||||
* @param[in] ipv6 The IPv6 header of the message carrying the ARO.
|
||||
* @param[in] icmpv6 The neighbor solicitation carrying the ARO
|
||||
* (handed over as @ref icmpv6_hdr_t, since it is just
|
||||
* handed to the SL2AO handler function).
|
||||
* @param[in] aro ARO that carries the address registration information.
|
||||
* @param[in] sl2ao SL2AO associated with the ARO.
|
||||
*
|
||||
* @return registration status of the address (including
|
||||
* @ref _ADDR_REG_STATUS_TENTATIVE and @ref _ADDR_REG_STATUS_IGNORE).
|
||||
*/
|
||||
uint8_t _reg_addr_upstream(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const icmpv6_hdr_t *icmpv6,
|
||||
const sixlowpan_nd_opt_ar_t *aro,
|
||||
const ndp_opt_t *sl2ao);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handles and copies ARO from NS to NA
|
||||
*
|
||||
* @param[in] iface The interface the ARO-carrying NS came over.
|
||||
* @param[in] ipv6 The IPv6 header of the message carrying the original
|
||||
* ARO.
|
||||
* @param[in] nbr_sol The neighbor solicitation carrying the original ARO
|
||||
* (handed over as @ref icmpv6_hdr_t, since it is just
|
||||
* handed to @ref _handle_aro()).
|
||||
* @param[in] aro The original ARO
|
||||
* @param[in] sl2ao SL2AO associated with the ARO.
|
||||
*
|
||||
* @return registration status of the address (including
|
||||
* @ref _ADDR_REG_STATUS_TENTATIVE and @ref _ADDR_REG_STATUS_IGNORE).
|
||||
*/
|
||||
gnrc_pktsnip_t *_copy_and_handle_aro(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const ndp_nbr_sol_t *nbr_sol,
|
||||
const sixlowpan_nd_opt_ar_t *aro,
|
||||
const ndp_opt_t *sl2ao);
|
||||
#else /* GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN) */
|
||||
#define _is_6lr(netif) (false)
|
||||
#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(iface, 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
|
||||
*/
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PRIV_NIB_6LR_H */
|
||||
/** @} */
|
475
sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c
Normal file
475
sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c
Normal file
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* 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 "xtimer.h"
|
||||
#include "net/gnrc/ndp2.h"
|
||||
#include "net/gnrc/ipv6/nib.h"
|
||||
|
||||
#include "_nib-arsm.h"
|
||||
#include "_nib-6lr.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Determines supposed link-layer address from interface and option
|
||||
* length
|
||||
*
|
||||
* @param[in] netif A network interface.
|
||||
* @param[in] opt A SL2AO or TL2AO.
|
||||
*
|
||||
* @return The length of the L2 address carried in @p opt.
|
||||
*/
|
||||
static inline unsigned _get_l2addr_len(gnrc_ipv6_netif_t *netif,
|
||||
const ndp_opt_t *opt);
|
||||
|
||||
void _snd_ns(const ipv6_addr_t *tgt, gnrc_ipv6_netif_t *netif,
|
||||
const ipv6_addr_t *src, const ipv6_addr_t *dst)
|
||||
{
|
||||
gnrc_pktsnip_t *ext_opt = NULL;
|
||||
|
||||
gnrc_ndp2_nbr_sol_send(tgt, netif, src, dst, ext_opt);
|
||||
}
|
||||
|
||||
void _snd_uc_ns(_nib_onl_entry_t *nbr, bool reset)
|
||||
{
|
||||
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(_nib_onl_get_if(nbr));
|
||||
_nib_iface_t *iface = _nib_iface_get(_nib_onl_get_if(nbr));
|
||||
|
||||
DEBUG("unicast to %s (retrans. timer = %ums)\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr->ipv6, sizeof(addr_str)),
|
||||
(unsigned)iface->retrans_time);
|
||||
assert((netif != NULL) && (iface != NULL));
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
if (reset) {
|
||||
nbr->ns_sent = 0;
|
||||
}
|
||||
#else
|
||||
(void)reset;
|
||||
#endif
|
||||
_snd_ns(&nbr->ipv6, netif, NULL, &nbr->ipv6);
|
||||
_evtimer_add(nbr, GNRC_IPV6_NIB_SND_UC_NS, &nbr->nud_timeout,
|
||||
iface->retrans_time);
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
nbr->ns_sent++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void _handle_sl2ao(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const icmpv6_hdr_t *icmpv6, const ndp_opt_t *sl2ao)
|
||||
{
|
||||
_nib_onl_entry_t *nce = _nib_onl_get(&ipv6->src, iface);
|
||||
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(iface);
|
||||
unsigned l2addr_len;
|
||||
|
||||
assert(netif != NULL);
|
||||
l2addr_len = _get_l2addr_len(netif, sl2ao);
|
||||
if (l2addr_len == 0U) {
|
||||
DEBUG("nib: Unexpected SL2AO length. Ignoring SL2AO\n");
|
||||
return;
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
if ((nce != NULL) && (nce->mode & _NC) &&
|
||||
((nce->l2addr_len != l2addr_len) ||
|
||||
(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)) {
|
||||
DEBUG("nib: L2 address differs. Setting STALE\n");
|
||||
evtimer_del(&_nib_evtimer, &nce->nud_timeout.event);
|
||||
_set_nud_state(nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE);
|
||||
}
|
||||
#endif /* GNRC_IPV6_NIB_CONF_ARSM */
|
||||
if ((nce == NULL) || !(nce->mode & _NC)) {
|
||||
DEBUG("nib: Creating NCE for (ipv6 = %s, iface = %u, nud_state = STALE)\n",
|
||||
ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str)), iface);
|
||||
nce = _nib_nc_add(&ipv6->src, iface,
|
||||
GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE);
|
||||
if (nce != NULL) {
|
||||
if (icmpv6->type == ICMPV6_NBR_SOL) {
|
||||
nce->info &= ~GNRC_IPV6_NIB_NC_INFO_IS_ROUTER;
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_MULTIHOP_DAD && GNRC_IPV6_NIB_CONF_6LR
|
||||
else if (_rtr_sol_on_6lr(netif, icmpv6)) {
|
||||
DEBUG("nib: Setting newly created entry to tentative\n");
|
||||
_set_ar_state(nce, GNRC_IPV6_NIB_NC_INFO_AR_STATE_TENTATIVE);
|
||||
_evtimer_add(nce, GNRC_IPV6_NIB_ADDR_REG_TIMEOUT,
|
||||
&nce->addr_reg_timeout,
|
||||
SIXLOWPAN_ND_TENTATIVE_NCE_SEC_LTIME * MS_PER_SEC);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if ENABLE_DEBUG
|
||||
else {
|
||||
DEBUG("nib: Neighbor cache full\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* not else to include NCE created in nce == NULL branch */
|
||||
if ((nce != NULL) && (nce->mode & _NC)) {
|
||||
if (icmpv6->type == ICMPV6_RTR_ADV) {
|
||||
DEBUG("nib: %s%%%u is a router\n",
|
||||
ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str)),
|
||||
iface);
|
||||
nce->info |= GNRC_IPV6_NIB_NC_INFO_IS_ROUTER;
|
||||
}
|
||||
else if (icmpv6->type != ICMPV6_NBR_SOL) {
|
||||
DEBUG("nib: %s%%%u is probably not a router\n",
|
||||
ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str)),
|
||||
iface);
|
||||
nce->info &= ~GNRC_IPV6_NIB_NC_INFO_IS_ROUTER;
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
/* 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 */
|
||||
if (!_rtr_sol_on_6lr(netif, icmpv6)) {
|
||||
nce->l2addr_len = l2addr_len;
|
||||
memcpy(nce->l2addr, sl2ao + 1, l2addr_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned _get_l2addr_len(gnrc_ipv6_netif_t *netif,
|
||||
const ndp_opt_t *opt)
|
||||
{
|
||||
#if GNRC_IPV6_NIB_CONF_6LN
|
||||
if (_is_6ln(netif)) {
|
||||
switch (opt->len) {
|
||||
case 1U:
|
||||
return 2U;
|
||||
case 2U:
|
||||
return 8U;
|
||||
default:
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)netif;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LN */
|
||||
if (opt->len == 1U) {
|
||||
return 6U;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
/**
|
||||
* @brief Calculates exponential back-off for retransmission timer for
|
||||
* neighbor solicitations
|
||||
*
|
||||
* @param[in] ns_sent Neighbor solicitations sent up until now.
|
||||
* @param[in] retrans_timer Currently configured retransmission timer.
|
||||
*
|
||||
* @return exponential back-off of the retransmission timer
|
||||
*/
|
||||
static inline uint32_t _exp_backoff_retrans_timer(uint8_t ns_sent,
|
||||
uint32_t retrans_timer);
|
||||
#if GNRC_IPV6_NIB_CONF_REDIRECT
|
||||
/**
|
||||
* @brief Checks if the carrier of the TL2AO was a redirect message
|
||||
*
|
||||
* @param[in] icmpv6 An ICMPv6 header.
|
||||
* @param[in] tl2ao A TL2AO.
|
||||
*
|
||||
* @return result of icmpv6_hdr_t::type == ICMPV6_REDIRECT for @p icmp and
|
||||
* ndp_opt_t::type == NDP_OPT_TL2A for @p tl2ao.
|
||||
*/
|
||||
static inline bool _redirect_with_tl2ao(icmpv6_hdr_t *icmpv6, ndp_opt_t *tl2ao);
|
||||
#else /* GNRC_IPV6_NIB_CONF_REDIRECT */
|
||||
/* just fall through if redirect not handled */
|
||||
#define _redirect_with_tl2ao(a, b) (false)
|
||||
#endif /* GNRC_IPV6_NIB_CONF_REDIRECT */
|
||||
|
||||
static inline bool _oflag_set(const ndp_nbr_adv_t *nbr_adv);
|
||||
static inline bool _sflag_set(const ndp_nbr_adv_t *nbr_adv);
|
||||
static inline bool _rflag_set(const ndp_nbr_adv_t *nbr_adv);
|
||||
|
||||
/**
|
||||
* @brief Checks if the information in the TL2AO would change the
|
||||
* corresponding neighbor cache entry
|
||||
*
|
||||
* @param[in] nce A neighbor cache entry.
|
||||
* @param[in] tl2ao The TL2AO.
|
||||
* @param[in] iface The interface the TL2AO came over.
|
||||
* @param[in] tl2ao_addr_len Length of the L2 address in the TL2AO.
|
||||
*
|
||||
* @return `true`, if the TL2AO changes the NCE.
|
||||
* @return `false`, if the TL2AO does not change the NCE.
|
||||
*/
|
||||
static inline bool _tl2ao_changes_nce(_nib_onl_entry_t *nce,
|
||||
const ndp_opt_t *tl2ao,
|
||||
kernel_pid_t iface,
|
||||
unsigned tl2ao_addr_len);
|
||||
|
||||
void _handle_snd_ns(_nib_onl_entry_t *nbr)
|
||||
{
|
||||
const uint16_t state = _get_nud_state(nbr);
|
||||
|
||||
DEBUG("nib: Retransmit neighbor solicitation\n");
|
||||
switch (state) {
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE:
|
||||
if (nbr->ns_sent >= NDP_MAX_MC_SOL_NUMOF) {
|
||||
_nib_nc_remove(nbr);
|
||||
return;
|
||||
}
|
||||
_probe_nbr(nbr, false);
|
||||
break;
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE:
|
||||
if (nbr->ns_sent >= NDP_MAX_UC_SOL_NUMOF) {
|
||||
_set_nud_state(nbr, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE);
|
||||
}
|
||||
/* falls through intentionally */
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE:
|
||||
_probe_nbr(nbr, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_state_timeout(_nib_onl_entry_t *nbr)
|
||||
{
|
||||
uint16_t new_state = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE;
|
||||
|
||||
switch (_get_nud_state(nbr)) {
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE:
|
||||
DEBUG("nib: Timeout reachability\n");
|
||||
new_state = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE;
|
||||
/* falls through intentionally */
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY:
|
||||
_set_nud_state(nbr, new_state);
|
||||
if (new_state == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE) {
|
||||
DEBUG("nib: Timeout DELAY state\n");
|
||||
_probe_nbr(nbr, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
DEBUG("UNMANAGED entry %s => skipping\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr->ipv6, sizeof(addr_str)));
|
||||
break;
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE:
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE: {
|
||||
_nib_iface_t *iface = _nib_iface_get(_nib_onl_get_if(nbr));
|
||||
uint32_t next_ns = _evtimer_lookup(nbr,
|
||||
GNRC_IPV6_NIB_SND_MC_NS);
|
||||
if (next_ns > iface->retrans_time) {
|
||||
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(_nib_onl_get_if(nbr));
|
||||
ipv6_addr_t sol_nodes;
|
||||
uint32_t retrans_time = iface->retrans_time;
|
||||
|
||||
DEBUG("multicast to %s's solicited nodes ",
|
||||
ipv6_addr_to_str(addr_str, &nbr->ipv6,
|
||||
sizeof(addr_str)));
|
||||
assert(netif != NULL);
|
||||
if (reset) {
|
||||
nbr->ns_sent = 0;
|
||||
}
|
||||
if (state == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE) {
|
||||
/* first 3 retransmissions in PROBE, assume 1 higher to
|
||||
* not send after iface->retrans_timer sec again,
|
||||
* but the next backoff after that => subtract 2 */
|
||||
retrans_time = _exp_backoff_retrans_timer(nbr->ns_sent - 2,
|
||||
retrans_time);
|
||||
}
|
||||
DEBUG("(retrans. timer = %ums)\n", (unsigned)retrans_time);
|
||||
ipv6_addr_set_solicited_nodes(&sol_nodes, &nbr->ipv6);
|
||||
_snd_ns(&nbr->ipv6, netif, NULL, &sol_nodes);
|
||||
_evtimer_add(nbr, GNRC_IPV6_NIB_SND_MC_NS, &nbr->nud_timeout,
|
||||
retrans_time);
|
||||
if (nbr->ns_sent < UINT8_MAX) {
|
||||
/* cap ns_sent at UINT8_MAX to prevent backoff reset */
|
||||
nbr->ns_sent++;
|
||||
}
|
||||
}
|
||||
#if ENABLE_DEBUG
|
||||
else {
|
||||
DEBUG("multicast to %s's solicited nodes (skipping since there is already "
|
||||
"a multicast NS within %ums)\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr->ipv6,
|
||||
sizeof(addr_str)),
|
||||
(unsigned)iface->retrans_time);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE:
|
||||
default:
|
||||
_snd_uc_ns(nbr, reset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_adv_l2(kernel_pid_t iface, _nib_onl_entry_t *nce,
|
||||
const icmpv6_hdr_t *icmpv6, const ndp_opt_t *tl2ao)
|
||||
{
|
||||
gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(iface);
|
||||
unsigned l2addr_len = 0;
|
||||
|
||||
assert(nce != NULL);
|
||||
assert(netif != NULL);
|
||||
if (tl2ao != NULL) {
|
||||
l2addr_len = _get_l2addr_len(netif, tl2ao);
|
||||
if (l2addr_len == 0U) {
|
||||
DEBUG("nib: Unexpected TL2AO length. Ignoring TL2AO\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((_get_nud_state(nce) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE) ||
|
||||
_oflag_set((ndp_nbr_adv_t *)icmpv6) ||
|
||||
_redirect_with_tl2ao(icmpv6, tl2ao) ||
|
||||
_tl2ao_changes_nce(nce, tl2ao, iface, l2addr_len)) {
|
||||
bool nce_was_incomplete =
|
||||
(_get_nud_state(nce) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE);
|
||||
if (tl2ao != NULL) {
|
||||
nce->l2addr_len = l2addr_len;
|
||||
memcpy(nce->l2addr, tl2ao + 1, l2addr_len);
|
||||
}
|
||||
else {
|
||||
nce->l2addr_len = 0;
|
||||
}
|
||||
if (_sflag_set((ndp_nbr_adv_t *)icmpv6)) {
|
||||
_set_reachable(iface, nce);
|
||||
}
|
||||
else if ((icmpv6->type != ICMPV6_NBR_ADV) ||
|
||||
!_sflag_set((ndp_nbr_adv_t *)icmpv6) ||
|
||||
(!nce_was_incomplete &&
|
||||
_tl2ao_changes_nce(nce, tl2ao, iface, l2addr_len))) {
|
||||
DEBUG("nib: Set %s%%%u to STALE\n",
|
||||
ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str)),
|
||||
iface);
|
||||
evtimer_del(&_nib_evtimer, &nce->nud_timeout.event);
|
||||
_set_nud_state(nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE);
|
||||
}
|
||||
if (_oflag_set((ndp_nbr_adv_t *)icmpv6) ||
|
||||
((icmpv6->type == ICMPV6_NBR_ADV) && nce_was_incomplete)) {
|
||||
if (_rflag_set((ndp_nbr_adv_t *)icmpv6)) {
|
||||
nce->info |= GNRC_IPV6_NIB_NC_INFO_IS_ROUTER;
|
||||
}
|
||||
else {
|
||||
nce->info &= ~GNRC_IPV6_NIB_NC_INFO_IS_ROUTER;
|
||||
}
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_QUEUE_PKT && MODULE_GNRC_IPV6
|
||||
/* send queued packets */
|
||||
gnrc_pktqueue_t *ptr;
|
||||
DEBUG("nib: Sending queued packets\n");
|
||||
while ((ptr = gnrc_pktqueue_remove_head(&nce->pktqueue)) != NULL) {
|
||||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6,
|
||||
GNRC_NETREG_DEMUX_CTX_ALL,
|
||||
ptr->pkt)) {
|
||||
DEBUG("nib: No receivers for packet\n");
|
||||
gnrc_pktbuf_release_error(ptr->pkt, EBADF);
|
||||
}
|
||||
ptr->pkt = NULL;
|
||||
}
|
||||
#endif /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */
|
||||
if ((icmpv6->type == ICMPV6_NBR_ADV) &&
|
||||
!_sflag_set((ndp_nbr_adv_t *)icmpv6) &&
|
||||
(_get_nud_state(nce) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE) &&
|
||||
_tl2ao_changes_nce(nce, tl2ao, iface, l2addr_len)) {
|
||||
evtimer_del(&_nib_evtimer, &nce->nud_timeout.event);
|
||||
_set_nud_state(nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE);
|
||||
}
|
||||
}
|
||||
else if ((icmpv6->type == ICMPV6_NBR_ADV) &&
|
||||
(_get_nud_state(nce) != GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE) &&
|
||||
(_get_nud_state(nce) != GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED) &&
|
||||
_sflag_set((ndp_nbr_adv_t *)icmpv6) &&
|
||||
!_tl2ao_changes_nce(nce, tl2ao, iface, l2addr_len)) {
|
||||
_set_reachable(iface, nce);
|
||||
}
|
||||
}
|
||||
|
||||
void _set_reachable(unsigned iface, _nib_onl_entry_t *nce)
|
||||
{
|
||||
_nib_iface_t *nib_netif = _nib_iface_get(iface);
|
||||
|
||||
DEBUG("nib: Set %s%%%u to REACHABLE for %ums\n",
|
||||
ipv6_addr_to_str(addr_str, &nce->ipv6, sizeof(addr_str)),
|
||||
iface, (unsigned)nib_netif->reach_time);
|
||||
_set_nud_state(nce, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE);
|
||||
_evtimer_add(nce, GNRC_IPV6_NIB_REACH_TIMEOUT, &nce->nud_timeout,
|
||||
nib_netif->reach_time);
|
||||
}
|
||||
|
||||
/* internal functions */
|
||||
static inline uint32_t _exp_backoff_retrans_timer(uint8_t ns_sent,
|
||||
uint32_t retrans_timer)
|
||||
{
|
||||
uint32_t tmp = random_uint32_range(NDP_MIN_RANDOM_FACTOR,
|
||||
NDP_MAX_RANDOM_FACTOR);
|
||||
|
||||
/* backoff according to https://tools.ietf.org/html/rfc7048 with
|
||||
* BACKOFF_MULTIPLE == 2 */
|
||||
tmp = ((1 << ns_sent) * retrans_timer * tmp) / US_PER_MS;
|
||||
/* random factors were statically multiplied with 1000 ^ */
|
||||
if (tmp > NDP_MAX_RETRANS_TIMER_MS) {
|
||||
tmp = NDP_MAX_RETRANS_TIMER_MS;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_REDIRECT
|
||||
static inline bool _redirect_with_tl2ao(icmpv6_hdr_t *icmpv6, ndp_opt_t *tl2ao)
|
||||
{
|
||||
return (icmpv6->type == ICMPV6_REDIRECT) && (tl2ao != NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool _tl2ao_changes_nce(_nib_onl_entry_t *nce,
|
||||
const ndp_opt_t *tl2ao,
|
||||
kernel_pid_t iface,
|
||||
unsigned tl2ao_addr_len)
|
||||
{
|
||||
return ((tl2ao != NULL) &&
|
||||
(((nce->l2addr_len != tl2ao_addr_len) &&
|
||||
(memcmp(nce->l2addr, tl2ao + 1, tl2ao_addr_len) != 0)) ||
|
||||
(_nib_onl_get_if(nce) != (unsigned)iface)));
|
||||
}
|
||||
|
||||
static inline bool _oflag_set(const ndp_nbr_adv_t *nbr_adv)
|
||||
{
|
||||
return (nbr_adv->type == ICMPV6_NBR_ADV) &&
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_O);
|
||||
}
|
||||
|
||||
static inline bool _sflag_set(const ndp_nbr_adv_t *nbr_adv)
|
||||
{
|
||||
return (nbr_adv->type == ICMPV6_NBR_ADV) &&
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S);
|
||||
}
|
||||
|
||||
static inline bool _rflag_set(const ndp_nbr_adv_t *nbr_adv)
|
||||
{
|
||||
return (nbr_adv->type == ICMPV6_NBR_ADV) &&
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_R);
|
||||
}
|
||||
|
||||
#endif /* GNRC_IPV6_NIB_CONF_ARSM */
|
||||
|
||||
/** @} */
|
190
sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h
Normal file
190
sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 the address resolution state machine (ARSM)
|
||||
* of the NIB
|
||||
* @see @ref GNRC_IPV6_NIB_CONF_ARSM
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef PRIV_NIB_ARSM_H
|
||||
#define PRIV_NIB_ARSM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "net/gnrc/ipv6/nib/conf.h"
|
||||
#include "net/ndp.h"
|
||||
#include "net/icmpv6.h"
|
||||
|
||||
#include "_nib-internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sends neighbor solicitation (including ARO if required)
|
||||
*
|
||||
* @pre `(tgt != NULL) && !ipv6_addr_is_multicast(tgt)`
|
||||
* @pre `(netif != NULL) && (dst != NULL)`
|
||||
*
|
||||
* @param[in] tgt The target address of the neighbor solicitation.
|
||||
* May not be NULL and **MUST NOT** be multicast.
|
||||
* @param[in] netif Interface to send over. May not be NULL.
|
||||
* @param[in] src Source address for the neighbor solicitation. Will be
|
||||
* chosen from the interface according to @p dst, if NULL.
|
||||
* @param[in] dst Destination address for neighbor solicitation. May not
|
||||
* be NULL.
|
||||
*/
|
||||
void _snd_ns(const ipv6_addr_t *tgt, gnrc_ipv6_netif_t *netif,
|
||||
const ipv6_addr_t *src, const ipv6_addr_t *dst);
|
||||
|
||||
/**
|
||||
* @brief Sends unicast neighbor solicitation and reset corresponding timer
|
||||
* event
|
||||
*
|
||||
* @note Neighbor solicitations are used *by* the ARSM, but also by other
|
||||
* mechanisms (e.g. duplicate address detection 6Lo address
|
||||
* resolution). This is why it is defined here, but not exclusively
|
||||
* available when @ref GNRC_IPV6_NIB_CONF_ARSM is set.
|
||||
*
|
||||
* @param[in] nbr Neighbor to send neighbor solicitation to.
|
||||
* @param[in] reset Reset probe counter.
|
||||
*/
|
||||
void _snd_uc_ns(_nib_onl_entry_t *nbr, bool reset);
|
||||
|
||||
/**
|
||||
* @brief Handles SL2AO
|
||||
*
|
||||
* @note This is here (but not only available with
|
||||
* @ref GNRC_IPV6_NIB_CONF_ARSM set) since it is closely related
|
||||
* to the ARSM, but ARSM isn't the only mechanism using it (e.g. the
|
||||
* 6Lo address registration uses it).
|
||||
*
|
||||
* @param[in] iface Interface the SL2AO was sent over.
|
||||
* @param[in] ipv6 IPv6 header of the message carrying the SL2AO.
|
||||
* @param[in] icmpv6 ICMPv6 header of the message carrying the SL2AO.
|
||||
* @param[in] sl2ao The SL2AO
|
||||
*/
|
||||
void _handle_sl2ao(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const icmpv6_hdr_t *icmpv6, const ndp_opt_t *sl2ao);
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Handler for @ref GNRC_IPV6_NIB_SND_UC_NS and
|
||||
* @ref GNRC_IPV6_NIB_SND_UC_NS event handler
|
||||
*
|
||||
* @param[in] nbr Neighbor to send the neighbor solicitation to.
|
||||
*/
|
||||
void _handle_snd_ns(_nib_onl_entry_t *nbr);
|
||||
|
||||
/**
|
||||
* @brief Handler for @ref GNRC_IPV6_NIB_DELAY_TIMEOUT and
|
||||
* @ref GNRC_IPV6_NIB_REACH_TIMEOUT event handler
|
||||
*
|
||||
* @param[in] nbr Neighbor to handle the state timeout for to.
|
||||
*/
|
||||
void _handle_state_timeout(_nib_onl_entry_t *nbr);
|
||||
|
||||
/**
|
||||
* @brief Probes neighbor with neighbor solicitations
|
||||
*
|
||||
* @param[in] nbr Neighbor to probe.
|
||||
* @param[in] reset Reset probe counter.
|
||||
*/
|
||||
void _probe_nbr(_nib_onl_entry_t *nbr, bool reset);
|
||||
|
||||
/**
|
||||
* @brief Handles advertised link-layer information
|
||||
*
|
||||
* This can either be an TL2AO or for a link-layer without addresses just a
|
||||
* neighbor advertisement.
|
||||
*
|
||||
* @param[in] iface Interface the link-layer information was advertised
|
||||
* over.
|
||||
* @param[in] nce Neighbor cache entry that is updated by the advertised
|
||||
* link-layer information.
|
||||
* @param[in] icmpv6 The ICMPv6 message (neighbor advertisement or redirect
|
||||
* message) that carries the link-layer information.
|
||||
* @param[in] tl2ao The TL2AO carrying the link-layer information. May be
|
||||
* NULL for link-layers without addresses.
|
||||
*/
|
||||
void _handle_adv_l2(kernel_pid_t iface, _nib_onl_entry_t *nce,
|
||||
const icmpv6_hdr_t *icmpv6, const ndp_opt_t *tl2ao);
|
||||
|
||||
/**
|
||||
* @brief Sets a neighbor cache entry reachable and starts the required
|
||||
* event timers
|
||||
*
|
||||
* @param[in] iface Interface to the NCE
|
||||
* @param[in] nce The neighbor cache entry to set reachable
|
||||
*/
|
||||
void _set_reachable(unsigned iface, _nib_onl_entry_t *nce);
|
||||
|
||||
/**
|
||||
* @brief Initializes interface for address registration state machine
|
||||
*
|
||||
* @param[in] nib_iface An interface
|
||||
*/
|
||||
static inline void _init_iface_arsm(_nib_iface_t *nib_iface)
|
||||
{
|
||||
nib_iface->reach_time_base = NDP_REACH_MS;
|
||||
nib_iface->retrans_time = NDP_RETRANS_TIMER_MS;
|
||||
_nib_iface_recalc_reach_time(nib_iface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets neighbor unreachability state of a neighbor
|
||||
*
|
||||
* @param[in] entry Neighbor cache entry representing the neighbor.
|
||||
*
|
||||
* @return Neighbor unreachability state of the @p entry.
|
||||
*/
|
||||
static inline uint16_t _get_nud_state(_nib_onl_entry_t *entry)
|
||||
{
|
||||
return (entry->info & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets neighbor unreachablility state of a neighbor
|
||||
*
|
||||
* @param[in] entry Neighbor cache entry representing the neighbor.
|
||||
* @param[in] state Neighbor unreachability state for the neighbor.
|
||||
*/
|
||||
static inline void _set_nud_state(_nib_onl_entry_t *entry, uint16_t state)
|
||||
{
|
||||
entry->info &= ~GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK;
|
||||
entry->info |= state;
|
||||
}
|
||||
|
||||
#else /* GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN) */
|
||||
#define _handle_snd_ns(ctx) (void)ctx
|
||||
#define _handle_state_timeout(ctx) (void)ctx
|
||||
#define _probe_nbr(nbr, reset) (void)nbr; (void)reset
|
||||
#define _init_iface_arsm(netif) (void)netif
|
||||
#define _handle_adv_l2(netif, nce, icmpv6, tl2ao) (void)netif; (void)nce; \
|
||||
(void)icmpv6; (void)tl2ao
|
||||
#define _set_reachable(netif, nce) (void)netif; (void)nce
|
||||
#define _init_iface_arsm(netif) (void)netif
|
||||
|
||||
#define _get_nud_state(entry) (GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED)
|
||||
#define _set_nud_state(entry, state) (void)entry; (void)state
|
||||
#endif /* GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PRIV_NIB_ARSM_H */
|
||||
/** @} */
|
@ -13,6 +13,7 @@
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -245,9 +246,23 @@ void _nib_nc_remove(_nib_onl_entry_t *node)
|
||||
ipv6_addr_to_str(addr_str, &node->ipv6, sizeof(addr_str)),
|
||||
_nib_onl_get_if(node));
|
||||
node->mode &= ~(_NC);
|
||||
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
|
||||
#if GNRC_IPV6_NIB_CONF_6LR
|
||||
evtimer_del((evtimer_t *)&_nib_evtimer, &node->addr_reg_timeout.event);
|
||||
#endif
|
||||
#if GNRC_IPV6_NIB_CONF_QUEUE_PKT
|
||||
gnrc_pktqueue_t *tmp;
|
||||
for (gnrc_pktqueue_t *ptr = node->pktqueue;
|
||||
(ptr != NULL) && (tmp = (ptr->next), 1);
|
||||
ptr = tmp) {
|
||||
gnrc_pktqueue_t *entry = gnrc_pktqueue_remove(&node->pktqueue, ptr);
|
||||
gnrc_pktbuf_release_error(entry->pkt, EHOSTUNREACH);
|
||||
entry->pkt = NULL;
|
||||
}
|
||||
#endif /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */
|
||||
_nib_onl_clear(node);
|
||||
}
|
||||
|
||||
@ -772,6 +787,20 @@ _nib_iface_t *_nib_iface_get(unsigned iface)
|
||||
return ni;
|
||||
}
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
void _nib_iface_recalc_reach_time(_nib_iface_t *iface)
|
||||
{
|
||||
uint32_t factor = random_uint32_range(NDP_MIN_RANDOM_FACTOR,
|
||||
NDP_MAX_RANDOM_FACTOR);
|
||||
|
||||
/* random factor was times 1000 so we need to divide it again */
|
||||
iface->reach_time = (iface->reach_time_base * factor) / 1000;
|
||||
_evtimer_add(iface, GNRC_IPV6_NIB_RECALC_REACH_TIME,
|
||||
&iface->recalc_reach_time,
|
||||
GNRC_IPV6_NIB_CONF_REACH_TIME_RESET);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _override_node(const ipv6_addr_t *addr, unsigned iface,
|
||||
_nib_onl_entry_t *node)
|
||||
{
|
||||
@ -799,7 +828,7 @@ uint32_t _evtimer_lookup(const void *ctx, uint16_t type)
|
||||
evtimer_msg_event_t *event = (evtimer_msg_event_t *)_nib_evtimer.events;
|
||||
uint32_t offset = 0;
|
||||
|
||||
DEBUG("nib: lookup ctx = %p, type = %u\n", (void *)ctx, type);
|
||||
DEBUG("nib: lookup ctx = %p, type = %04x\n", (void *)ctx, type);
|
||||
while (event != NULL) {
|
||||
offset += event->event.offset;
|
||||
if ((event->msg.type == type) &&
|
||||
|
@ -108,16 +108,31 @@ typedef struct _nib_onl_entry {
|
||||
* @note Only available if @ref GNRC_IPV6_NIB_CONF_ARSM != 0.
|
||||
*/
|
||||
uint8_t l2addr[GNRC_IPV6_NIB_L2ADDR_MAX_LEN];
|
||||
#endif
|
||||
/**
|
||||
* @brief Event for @ref GNRC_IPV6_NIB_REACH_TIMEOUT and
|
||||
* @brief Event for @ref GNRC_IPV6_NIB_SND_UC_NS,
|
||||
* @ref GNRC_IPV6_NIB_SND_MC_NS, @ref GNRC_IPV6_NIB_REACH_TIMEOUT and
|
||||
* @ref GNRC_IPV6_NIB_DELAY_TIMEOUT
|
||||
*
|
||||
* @note Events of these types can't be in the event queue at the same
|
||||
* time (since they only have one NUD state at a time). Because of
|
||||
* this we can use one event for both of them (but need the
|
||||
* different types, since the events are handled differently)
|
||||
* @note Four event types
|
||||
* 1. To easier distinguish multicast probes in _evtimer_lookup for
|
||||
* rate-limiting from unicast probes.
|
||||
* 2. Since the types can't be in the event queue at the same time
|
||||
* (since they only have one NUD state at a time and probing is
|
||||
* one of these states). Because of this we can use one event
|
||||
* for all of them (but need the different types, since the
|
||||
* events are handled differently).
|
||||
* @note This is also available with @ref GNRC_IPV6_NIB_CONF_ARSM == 0,
|
||||
* since 6Lo address registration uses it to time the sending of
|
||||
* neighbor solicitations.
|
||||
*/
|
||||
evtimer_msg_event_t nud_timeout;
|
||||
/**
|
||||
* @brief Event for @ref GNRC_IPV6_NIB_SND_NA
|
||||
*/
|
||||
evtimer_msg_event_t snd_na;
|
||||
#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
|
||||
|
||||
/**
|
||||
@ -135,14 +150,12 @@ 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
|
||||
*
|
||||
@ -190,8 +203,8 @@ typedef struct {
|
||||
*/
|
||||
uint32_t reach_time_base;
|
||||
uint32_t reach_time; /**< reachable time (in ms) */
|
||||
uint32_t retrans_time; /**< retransmission time (in ms) */
|
||||
#endif
|
||||
uint32_t retrans_time; /**< retransmission time (in ms) */
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief timestamp in milliseconds of last unsolicited router
|
||||
@ -200,6 +213,12 @@ typedef struct {
|
||||
* @note Only available if @ref GNRC_IPV6_NIB_CONF_ROUTER.
|
||||
*/
|
||||
uint32_t last_ra;
|
||||
#endif
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Event for @ref GNRC_IPV6_NIB_RECALC_REACH_TIME
|
||||
*/
|
||||
evtimer_msg_event_t recalc_reach_time;
|
||||
#endif
|
||||
kernel_pid_t pid; /**< identifier of the interface */
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER || defined(DOXYGEN)
|
||||
@ -789,6 +808,17 @@ int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *ctx,
|
||||
*/
|
||||
_nib_iface_t *_nib_iface_get(unsigned iface);
|
||||
|
||||
/**
|
||||
* @brief Recalculates randomized reachable time of an interface.
|
||||
*
|
||||
* @param[in] iface An interface.
|
||||
*/
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
void _nib_iface_recalc_reach_time(_nib_iface_t *iface);
|
||||
#else
|
||||
#define _nib_iface_recalc_reach_time(iface) (void)iface
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Looks up if an event is queued in the event timer
|
||||
*
|
||||
|
641
sys/net/gnrc/network_layer/ipv6/nib/nib.c
Normal file
641
sys/net/gnrc/network_layer/ipv6/nib/nib.c
Normal file
@ -0,0 +1,641 @@
|
||||
/*
|
||||
* 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 <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/gnrc/nettype.h"
|
||||
#include "net/gnrc/ipv6/netif.h"
|
||||
#include "net/gnrc/ipv6/nib.h"
|
||||
#include "net/gnrc/ndp2.h"
|
||||
#include "net/gnrc/pktqueue.h"
|
||||
#include "net/gnrc/sixlowpan/nd.h"
|
||||
#include "net/ndp.h"
|
||||
#include "net/sixlowpan/nd.h"
|
||||
|
||||
#include "_nib-internal.h"
|
||||
#include "_nib-arsm.h"
|
||||
#include "_nib-6ln.h"
|
||||
#include "_nib-6lr.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
#if ENABLE_DEBUG
|
||||
#include "xtimer.h"
|
||||
#endif
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
#endif
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_QUEUE_PKT
|
||||
static gnrc_pktqueue_t _queue_pool[GNRC_IPV6_NIB_NUMOF];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @{
|
||||
*/
|
||||
static void _handle_nbr_sol(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const ndp_nbr_sol_t *nbr_sol, size_t icmpv6_len);
|
||||
static void _handle_nbr_adv(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const ndp_nbr_adv_t *nbr_adv, size_t icmpv6_len);
|
||||
|
||||
static bool _resolve_addr(const ipv6_addr_t *dst, kernel_pid_t iface,
|
||||
gnrc_pktsnip_t *pkt, gnrc_ipv6_nib_nc_t *nce,
|
||||
_nib_onl_entry_t *entry);
|
||||
|
||||
static void _handle_snd_na(gnrc_pktsnip_t *pkt);
|
||||
|
||||
/* interface flag checks */
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER
|
||||
static inline bool _is_rtr(const gnrc_ipv6_netif_t *netif)
|
||||
{
|
||||
return (netif->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER);
|
||||
}
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
void gnrc_ipv6_nib_init(void)
|
||||
{
|
||||
evtimer_event_t *tmp;
|
||||
|
||||
mutex_lock(&_nib_mutex);
|
||||
for (evtimer_event_t *ptr = _nib_evtimer.events;
|
||||
(ptr != NULL) && (tmp = (ptr->next), 1);
|
||||
ptr = tmp) {
|
||||
evtimer_del((evtimer_t *)(&_nib_evtimer), ptr);
|
||||
}
|
||||
_nib_init();
|
||||
mutex_unlock(&_nib_mutex);
|
||||
}
|
||||
|
||||
void gnrc_ipv6_nib_init_iface(kernel_pid_t iface)
|
||||
{
|
||||
_nib_iface_t *nib_iface;
|
||||
|
||||
assert(iface > KERNEL_PID_UNDEF);
|
||||
DEBUG("nib: Initialize interface %u\n", (unsigned)iface);
|
||||
mutex_lock(&_nib_mutex);
|
||||
nib_iface = _nib_iface_get(iface);
|
||||
#ifdef TEST_SUITES
|
||||
if (nib_iface == NULL) {
|
||||
/* in the unittests old NC and NIB are mixed, so this function leads to
|
||||
* crashes. To prevent this we early exit here, if the interface was
|
||||
* not found
|
||||
* TODO: remove when gnrc_ipv6_nc is removed.
|
||||
*/
|
||||
mutex_unlock(&_nib_mutex);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
assert(nib_iface != NULL);
|
||||
#endif
|
||||
/* 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(nib_iface);
|
||||
nib_iface->rs_sent = 0;
|
||||
nib_iface->na_sent = 0;
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER
|
||||
nib_iface->last_ra = UINT32_MAX;
|
||||
nib_iface->ra_sent = 0;
|
||||
#endif
|
||||
mutex_unlock(&_nib_mutex);
|
||||
}
|
||||
|
||||
int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst,
|
||||
kernel_pid_t iface, gnrc_pktsnip_t *pkt,
|
||||
gnrc_ipv6_nib_nc_t *nce)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
mutex_lock(&_nib_mutex);
|
||||
do { /* XXX: hidden goto ;-) */
|
||||
if (ipv6_addr_is_link_local(dst)) {
|
||||
/* TODO: Prefix-based on-link determination */
|
||||
if ((iface == KERNEL_PID_UNDEF) ||
|
||||
!_resolve_addr(dst, iface, pkt, nce,
|
||||
_nib_onl_get(dst, iface))) {
|
||||
res = -EHOSTUNREACH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO: Off-link next hop determination */
|
||||
res = -EHOSTUNREACH;
|
||||
}
|
||||
} while (0);
|
||||
mutex_unlock(&_nib_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
void gnrc_ipv6_nib_handle_pkt(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const icmpv6_hdr_t *icmpv6, size_t icmpv6_len)
|
||||
{
|
||||
DEBUG("nib: Handle packet (icmpv6->type = %u)\n", icmpv6->type);
|
||||
mutex_lock(&_nib_mutex);
|
||||
switch (icmpv6->type) {
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER
|
||||
case ICMPV6_RTR_SOL:
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_ROUTER */
|
||||
case ICMPV6_RTR_ADV:
|
||||
/* TODO */
|
||||
break;
|
||||
case ICMPV6_NBR_SOL:
|
||||
_handle_nbr_sol(iface, ipv6, (ndp_nbr_sol_t *)icmpv6, icmpv6_len);
|
||||
break;
|
||||
case ICMPV6_NBR_ADV:
|
||||
_handle_nbr_adv(iface, ipv6, (ndp_nbr_adv_t *)icmpv6, icmpv6_len);
|
||||
break;
|
||||
#if GNRC_IPV6_NIB_CONF_REDIRECT
|
||||
case ICMPV6_REDIRECT:
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_REDIRECT */
|
||||
#if GNRC_IPV6_NIB_CONF_MULTIHOP_DAD
|
||||
case ICMPV6_DAR:
|
||||
/* TODO */
|
||||
break;
|
||||
case ICMPV6_DAC:
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_DAD */
|
||||
}
|
||||
mutex_unlock(&_nib_mutex);
|
||||
}
|
||||
|
||||
void gnrc_ipv6_nib_handle_timer_event(void *ctx, uint16_t type)
|
||||
{
|
||||
DEBUG("nib: Handle timer event (ctx = %p, type = 0x%04x, now = %ums)\n",
|
||||
ctx, type, (unsigned)xtimer_now_usec() / 1000);
|
||||
mutex_lock(&_nib_mutex);
|
||||
switch (type) {
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
case GNRC_IPV6_NIB_SND_UC_NS:
|
||||
case GNRC_IPV6_NIB_SND_MC_NS:
|
||||
_handle_snd_ns(ctx);
|
||||
break;
|
||||
case GNRC_IPV6_NIB_REACH_TIMEOUT:
|
||||
case GNRC_IPV6_NIB_DELAY_TIMEOUT:
|
||||
_handle_state_timeout(ctx);
|
||||
break;
|
||||
case GNRC_IPV6_NIB_RECALC_REACH_TIME:
|
||||
_nib_iface_recalc_reach_time(ctx);
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_ARSM */
|
||||
case GNRC_IPV6_NIB_SND_NA:
|
||||
_handle_snd_na(ctx);
|
||||
break;
|
||||
case GNRC_IPV6_NIB_SEARCH_RTR:
|
||||
/* TODO */
|
||||
break;
|
||||
case GNRC_IPV6_NIB_RECONFIRM_RTR:
|
||||
/* TODO */
|
||||
break;
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER
|
||||
case GNRC_IPV6_NIB_REPLY_RS:
|
||||
/* TODO */
|
||||
break;
|
||||
case GNRC_IPV6_NIB_SND_MC_RA:
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_ROUTER */
|
||||
#if GNRC_IPV6_NIB_CONF_6LN
|
||||
case GNRC_IPV6_NIB_ADDR_REG_TIMEOUT:
|
||||
/* TODO */
|
||||
break;
|
||||
case GNRC_IPV6_NIB_6LO_CTX_TIMEOUT:
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LN */
|
||||
#if GNRC_IPV6_NIB_CONF_MULTIHOP_P6C
|
||||
case GNRC_IPV6_NIB_ABR_TIMEOUT:
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_MULTIHOP_P6C */
|
||||
case GNRC_IPV6_NIB_PFX_TIMEOUT:
|
||||
/* TODO */
|
||||
break;
|
||||
case GNRC_IPV6_NIB_RTR_TIMEOUT:
|
||||
/* TODO */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&_nib_mutex);
|
||||
}
|
||||
|
||||
/* Iterator for NDP options in a packet */
|
||||
#define FOREACH_OPT(ndp_pkt, opt, icmpv6_len) \
|
||||
for (opt = (ndp_opt_t *)(ndp_pkt + 1); \
|
||||
icmpv6_len > 0; \
|
||||
icmpv6_len -= (opt->len << 3), \
|
||||
opt = (ndp_opt_t *)(((uint8_t *)opt) + (opt->len << 3)))
|
||||
|
||||
static size_t _get_l2src(kernel_pid_t iface, uint8_t *l2src,
|
||||
size_t l2src_maxlen)
|
||||
{
|
||||
bool try_long = false;
|
||||
int res;
|
||||
uint16_t l2src_len;
|
||||
/* maximum address length that fits into a minimum length (8) S/TL2A
|
||||
* option */
|
||||
const uint16_t max_short_len = 6;
|
||||
|
||||
/* try getting source address */
|
||||
if ((gnrc_netapi_get(iface, NETOPT_SRC_LEN, 0, &l2src_len,
|
||||
sizeof(l2src_len)) >= 0) &&
|
||||
(l2src_len > max_short_len)) {
|
||||
try_long = true;
|
||||
}
|
||||
|
||||
if (try_long && ((res = gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0,
|
||||
l2src, l2src_maxlen)) > max_short_len)) {
|
||||
l2src_len = (uint16_t)res;
|
||||
}
|
||||
else if ((res = gnrc_netapi_get(iface, NETOPT_ADDRESS, 0, l2src,
|
||||
l2src_maxlen)) >= 0) {
|
||||
l2src_len = (uint16_t)res;
|
||||
}
|
||||
else {
|
||||
DEBUG("nib: No link-layer address found.\n");
|
||||
l2src_len = 0;
|
||||
}
|
||||
|
||||
return l2src_len;
|
||||
}
|
||||
|
||||
static void _send_delayed_nbr_adv(const gnrc_ipv6_netif_t *netif,
|
||||
const ipv6_addr_t *tgt,
|
||||
const ipv6_addr_t *dst,
|
||||
gnrc_pktsnip_t *reply_aro)
|
||||
{
|
||||
gnrc_pktsnip_t *nbr_adv, *extra_opts = reply_aro;
|
||||
_nib_onl_entry_t *nce;
|
||||
uint8_t reply_flags = NDP_NBR_ADV_FLAGS_S;
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_ROUTER
|
||||
if (_is_rtr(netif)) {
|
||||
reply_flags |= NDP_NBR_ADV_FLAGS_R;
|
||||
}
|
||||
#endif
|
||||
if (ipv6_addr_is_multicast(dst)) {
|
||||
uint8_t l2addr[GNRC_IPV6_NIB_L2ADDR_MAX_LEN];
|
||||
size_t l2addr_len = _get_l2src(netif->pid, l2addr, sizeof(l2addr));
|
||||
if (l2addr_len > 0) {
|
||||
extra_opts = gnrc_ndp2_opt_tl2a_build(l2addr, l2addr_len,
|
||||
extra_opts);
|
||||
if (extra_opts == NULL) {
|
||||
DEBUG("nib: No space left in packet buffer. Not replying NS");
|
||||
gnrc_pktbuf_release(reply_aro);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
reply_flags |= NDP_NBR_ADV_FLAGS_O;
|
||||
}
|
||||
}
|
||||
else {
|
||||
reply_flags |= NDP_NBR_ADV_FLAGS_O;
|
||||
}
|
||||
nbr_adv = gnrc_ndp2_nbr_adv_build(tgt, reply_flags, extra_opts);
|
||||
if (nbr_adv == NULL) {
|
||||
DEBUG("nib: No space left in packet buffer. Not replying NS");
|
||||
gnrc_pktbuf_release(extra_opts);
|
||||
return;
|
||||
}
|
||||
nce = _nib_onl_get(tgt, netif->pid);
|
||||
if ((nce != NULL) && (nce->mode & _NC)) {
|
||||
/* 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,
|
||||
random_uint32_range(0, NDP_MAX_ANYCAST_MS_DELAY));
|
||||
}
|
||||
}
|
||||
|
||||
static void _handle_nbr_sol(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const ndp_nbr_sol_t *nbr_sol, size_t icmpv6_len)
|
||||
{
|
||||
size_t tmp_len = icmpv6_len - sizeof(ndp_nbr_sol_t);
|
||||
ndp_opt_t *opt;
|
||||
ipv6_addr_t *local;
|
||||
|
||||
/* check validity, see: https://tools.ietf.org/html/rfc4861#section-7.1.1 */
|
||||
/* checksum is checked by GNRC's ICMPv6 module */
|
||||
if ((ipv6->hl != 255U) || (nbr_sol->code != 0U) ||
|
||||
(icmpv6_len < sizeof(ndp_nbr_sol_t)) ||
|
||||
ipv6_addr_is_multicast(&nbr_sol->tgt) ||
|
||||
(ipv6_addr_is_unspecified(&ipv6->src) &&
|
||||
!ipv6_addr_is_solicited_node(&ipv6->dst))) {
|
||||
DEBUG("nib: Received neighbor solicitation is invalid. Discarding silently\n");
|
||||
DEBUG(" - IP Hop Limit: %u (should be 255)\n", ipv6->hl);
|
||||
DEBUG(" - ICMP code: %u (should be 0)\n", nbr_sol->code);
|
||||
DEBUG(" - ICMP length: %u (should > %u)\n", icmpv6_len,
|
||||
sizeof(ndp_nbr_sol_t));
|
||||
DEBUG(" - Target address: %s (should not be multicast)\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr_sol->tgt, sizeof(addr_str)));
|
||||
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",
|
||||
ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str)));
|
||||
return;
|
||||
}
|
||||
/* check if target is assigned only now in case the length was wrong */
|
||||
local = gnrc_ipv6_netif_find_addr(iface, &nbr_sol->tgt);
|
||||
if (local == NULL) {
|
||||
DEBUG("nib: Target address %s is not assigned to a local interface\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr_sol->tgt, sizeof(addr_str)));
|
||||
return;
|
||||
}
|
||||
/* pre-check option length */
|
||||
FOREACH_OPT(nbr_sol, opt, tmp_len) {
|
||||
if (tmp_len > icmpv6_len) {
|
||||
DEBUG("nib: Payload length (%u) of NS doesn't align with options\n",
|
||||
(unsigned)icmpv6_len);
|
||||
return;
|
||||
}
|
||||
if (opt->len == 0U) {
|
||||
DEBUG("nib: Option of length 0 detected. "
|
||||
"Discarding neighbor solicitation silently\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
DEBUG("nib: Received valid neighbor solicitation:\n");
|
||||
DEBUG(" - Target address: %s\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr_sol->tgt, sizeof(addr_str)));
|
||||
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 GNRC_IPV6_NIB_CONF_SLAAC
|
||||
/* TODO SLAAC behavior */
|
||||
#endif /* GNRC_IPV6_NIB_CONF_SLAAC */
|
||||
if (!ipv6_addr_is_unspecified(&ipv6->src)) {
|
||||
#if GNRC_IPV6_NIB_CONF_6LR
|
||||
ndp_opt_t *sl2ao = NULL;
|
||||
sixlowpan_nd_opt_ar_t *aro = NULL;
|
||||
#else /* GNRC_IPV6_NIB_CONF_6LR */
|
||||
#define sl2ao (NULL)
|
||||
#define aro (NULL)
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LR */
|
||||
gnrc_ipv6_netif_t *netif;
|
||||
gnrc_pktsnip_t *reply_aro = NULL;
|
||||
tmp_len = icmpv6_len - sizeof(ndp_nbr_sol_t);
|
||||
|
||||
netif = gnrc_ipv6_netif_get(iface);
|
||||
/* TODO: Set STALE NCE if link-layer has no addresses */
|
||||
FOREACH_OPT(nbr_sol, opt, tmp_len) {
|
||||
switch (opt->type) {
|
||||
case NDP_OPT_SL2A:
|
||||
#if GNRC_IPV6_NIB_CONF_6LR
|
||||
if (_is_6lr(netif)) {
|
||||
DEBUG("nib: Storing SL2AO for later handling\n");
|
||||
sl2ao = opt;
|
||||
break;
|
||||
}
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LR */
|
||||
_handle_sl2ao(iface, ipv6, (const icmpv6_hdr_t *)nbr_sol,
|
||||
opt);
|
||||
break;
|
||||
#if GNRC_IPV6_NIB_CONF_6LR
|
||||
case NDP_OPT_AR:
|
||||
DEBUG("nib: Storing ARO for later handling\n");
|
||||
aro = (sixlowpan_nd_opt_ar_t *)opt;
|
||||
break;
|
||||
#endif /* GNRC_IPV6_NIB_CONF_6LR */
|
||||
default:
|
||||
DEBUG("nib: Ignoring unrecognized option type %u for NS\n",
|
||||
opt->type);
|
||||
}
|
||||
}
|
||||
reply_aro = _copy_and_handle_aro(iface, ipv6, nbr_sol, aro, sl2ao);
|
||||
/* check if target address is anycast */
|
||||
if (gnrc_ipv6_netif_addr_is_non_unicast(local)) {
|
||||
_send_delayed_nbr_adv(netif, &nbr_sol->tgt, &ipv6->dst, reply_aro);
|
||||
}
|
||||
else {
|
||||
gnrc_ndp2_nbr_adv_send(&nbr_sol->tgt, netif, &ipv6->src,
|
||||
ipv6_addr_is_multicast(&ipv6->dst),
|
||||
reply_aro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _handle_nbr_adv(kernel_pid_t iface, const ipv6_hdr_t *ipv6,
|
||||
const ndp_nbr_adv_t *nbr_adv, size_t icmpv6_len)
|
||||
{
|
||||
size_t tmp_len = icmpv6_len - sizeof(ndp_nbr_adv_t);
|
||||
ndp_opt_t *opt;
|
||||
_nib_onl_entry_t *nce;
|
||||
|
||||
/* check validity, see: https://tools.ietf.org/html/rfc4861#section-7.1.2 */
|
||||
/* checksum is checked by GNRC's ICMPv6 module */
|
||||
if ((ipv6->hl != 255U) || (nbr_adv->code != 0U) ||
|
||||
(icmpv6_len < sizeof(ndp_nbr_adv_t)) ||
|
||||
ipv6_addr_is_multicast(&nbr_adv->tgt) ||
|
||||
(ipv6_addr_is_multicast(&ipv6->dst) &&
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S))) {
|
||||
DEBUG("nib: Received neighbor 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", nbr_adv->code);
|
||||
DEBUG(" - ICMP length: %u (should > %u)\n", icmpv6_len,
|
||||
sizeof(ndp_nbr_adv_t));
|
||||
DEBUG(" - Target address: %s (should not be multicast)\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr_adv->tgt, sizeof(addr_str)));
|
||||
DEBUG(" - Destination address: %s\n",
|
||||
ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str)));
|
||||
DEBUG(" - Flags: %c%c%c (S must not be set if destination is multicast)\n",
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) ? 'R' : '-',
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) ? 'S' : '-',
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) ? 'O' : '-');
|
||||
return;
|
||||
}
|
||||
/* pre-check option length */
|
||||
FOREACH_OPT(nbr_adv, opt, tmp_len) {
|
||||
if (tmp_len > icmpv6_len) {
|
||||
DEBUG("nib: Payload length (%u) of NA doesn't align with options\n",
|
||||
(unsigned)icmpv6_len);
|
||||
return;
|
||||
}
|
||||
if (opt->len == 0U) {
|
||||
DEBUG("nib: Option of length 0 detected. "
|
||||
"Discarding neighbor advertisement silently\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
DEBUG("nib: Received valid neighbor advertisement:\n");
|
||||
DEBUG(" - Target address: %s\n",
|
||||
ipv6_addr_to_str(addr_str, &nbr_adv->tgt, sizeof(addr_str)));
|
||||
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(" - Flags: %c%c%c\n",
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) ? 'R' : '-',
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) ? 'S' : '-',
|
||||
(nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) ? 'O' : '-');
|
||||
#if GNRC_IPV6_NIB_CONF_SLAAC
|
||||
/* TODO SLAAC behavior */
|
||||
#endif
|
||||
if (((nce = _nib_onl_get(&nbr_adv->tgt, iface)) != NULL) &&
|
||||
(nce->mode & _NC)) {
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
bool tl2ao_avail = false;
|
||||
#endif
|
||||
|
||||
tmp_len = icmpv6_len - sizeof(ndp_nbr_adv_t);
|
||||
FOREACH_OPT(nbr_adv, opt, tmp_len) {
|
||||
switch (opt->type) {
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
case NDP_OPT_TL2A:
|
||||
_handle_adv_l2(iface, nce, (icmpv6_hdr_t *)nbr_adv, opt);
|
||||
tl2ao_avail = true;
|
||||
break;
|
||||
#endif
|
||||
#if GNRC_IPV6_NIB_CONF_6LN
|
||||
case NDP_OPT_AR:
|
||||
_handle_aro(iface, ipv6, (const icmpv6_hdr_t *)nbr_adv,
|
||||
(const sixlowpan_nd_opt_ar_t *)opt, opt, nce);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
DEBUG("nib: Ignoring unrecognized option type %u for NA\n",
|
||||
opt->type);
|
||||
}
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
if (!tl2ao_avail && (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) &&
|
||||
(_get_nud_state(nce) != GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE)) {
|
||||
/* reachability confirmed without TL2AO */
|
||||
_set_reachable(iface, nce);
|
||||
}
|
||||
/* TODO: handling for of advertised link-layer with link-layers without
|
||||
* addresses */
|
||||
/* _handle_adv_l2(iface, nce, (icmpv6_hdr_t *)nbr_adv, NULL); */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool _is_reachable(_nib_onl_entry_t *entry)
|
||||
{
|
||||
(void)entry; /* _get_nud_state() might just resolved to UNMANAGED as macro */
|
||||
switch (_get_nud_state(entry)) {
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE:
|
||||
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#if GNRC_IPV6_NIB_CONF_QUEUE_PKT
|
||||
static gnrc_pktqueue_t *_alloc_queue_entry(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) {
|
||||
if (_queue_pool[i].pkt == NULL) {
|
||||
_queue_pool[i].pkt = pkt;
|
||||
return &_queue_pool[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool _resolve_addr(const ipv6_addr_t *dst, kernel_pid_t iface,
|
||||
gnrc_pktsnip_t *pkt, gnrc_ipv6_nib_nc_t *nce,
|
||||
_nib_onl_entry_t *entry)
|
||||
{
|
||||
bool res = false;
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
if ((entry != NULL) && (entry->mode & _NC) && _is_reachable(entry)) {
|
||||
if (_get_nud_state(entry) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE) {
|
||||
_set_nud_state(entry, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY);
|
||||
_evtimer_add(entry, GNRC_IPV6_NIB_DELAY_TIMEOUT,
|
||||
&entry->nud_timeout, NDP_DELAY_FIRST_PROBE_MS);
|
||||
}
|
||||
_nib_nc_get(entry, nce);
|
||||
res = true;
|
||||
}
|
||||
#else
|
||||
if (entry != NULL) {
|
||||
_nib_nc_get(entry, nce);
|
||||
res = true;
|
||||
}
|
||||
#endif
|
||||
else if (!(res = _resolve_addr_from_ipv6(dst, iface, nce))) {
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
bool reset = false;
|
||||
#endif
|
||||
if ((entry == NULL) || !(entry->mode & _NC)) {
|
||||
entry = _nib_nc_add(dst, iface,
|
||||
GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE);
|
||||
if (entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
reset = true;
|
||||
#endif
|
||||
}
|
||||
if (pkt != NULL) {
|
||||
#if GNRC_IPV6_NIB_CONF_QUEUE_PKT
|
||||
if (_get_nud_state(entry) == GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE) {
|
||||
gnrc_pktqueue_t *queue_entry = _alloc_queue_entry(pkt);
|
||||
|
||||
if (queue_entry != NULL) {
|
||||
gnrc_pktqueue_add(&entry->pktqueue, queue_entry);
|
||||
}
|
||||
}
|
||||
else {
|
||||
gnrc_pktbuf_release_error(pkt, EHOSTUNREACH);
|
||||
}
|
||||
#else /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */
|
||||
gnrc_pktbuf_release_error(pkt, EHOSTUNREACH);
|
||||
#endif /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */
|
||||
}
|
||||
#if GNRC_IPV6_NIB_CONF_ARSM
|
||||
_probe_nbr(entry, reset);
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _handle_snd_na(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
DEBUG("nib: Send delayed neighbor advertisement\n");
|
||||
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL,
|
||||
pkt)) {
|
||||
DEBUG("nib: No receivers for neighbor advertisement\n");
|
||||
gnrc_pktbuf_release_error(pkt, EBADF);
|
||||
}
|
||||
#else
|
||||
(void)pkt;
|
||||
DEBUG("nib: No IPv6 module to send delayed neighbor advertisement\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} */
|
@ -31,14 +31,7 @@
|
||||
|
||||
static void set_up(void)
|
||||
{
|
||||
evtimer_event_t *tmp;
|
||||
|
||||
for (evtimer_event_t *ptr = _nib_evtimer.events;
|
||||
(ptr != NULL) && (tmp = (ptr->next), 1);
|
||||
ptr = tmp) {
|
||||
evtimer_del((evtimer_t *)(&_nib_evtimer), ptr);
|
||||
}
|
||||
_nib_init();
|
||||
gnrc_ipv6_nib_init();
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user