1
0
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:
Martine Lenders 2017-05-02 17:05:10 +02:00
parent d30216dcbe
commit 1b36cdf79e
No known key found for this signature in database
GPG Key ID: 8E97A9FE55F25D62
12 changed files with 2015 additions and 18 deletions

View File

@ -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)))

View File

@ -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

View 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 */
/** @} */

View 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 */
/** @} */

View 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 */
/** @} */

View 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 */
/** @} */

View 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 */
/** @} */

View 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 */
/** @} */

View File

@ -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) &&

View File

@ -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
*

View 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
}
/** @} */

View File

@ -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();
}
/*