1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #7014 from miri64/gnrc_ipv6_nib/feat/ndp-link-local

nib: implement public NIB functions up to link-local AR
This commit is contained in:
Martine Lenders 2017-10-10 10:30:10 +02:00 committed by GitHub
commit 8d941d59ea
28 changed files with 3956 additions and 18 deletions

View File

@ -319,8 +319,27 @@ ifneq (,$(filter gnrc_ipv6_nc,$(USEMODULE)))
USEMODULE += ipv6_addr
endif
ifneq (,$(filter gnrc_ipv6_nib_6lbr,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6lr
endif
ifneq (,$(filter gnrc_ipv6_nib_6lr,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6ln
USEMODULE += gnrc_ipv6_nib_router
endif
ifneq (,$(filter gnrc_ipv6_nib_6ln,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib
USEMODULE += gnrc_sixlowpan_nd
endif
ifneq (,$(filter gnrc_ipv6_nib_router,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib
endif
ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE)))
USEMODULE += evtimer
USEMODULE += gnrc_ndp2
USEMODULE += ipv6_addr
USEMODULE += random
endif

View File

@ -11,6 +11,10 @@ PSEUDOMODULES += emb6_router
PSEUDOMODULES += gnrc_ipv6_default
PSEUDOMODULES += gnrc_ipv6_router
PSEUDOMODULES += gnrc_ipv6_router_default
PSEUDOMODULES += gnrc_ipv6_nib_6lbr
PSEUDOMODULES += gnrc_ipv6_nib_6ln
PSEUDOMODULES += gnrc_ipv6_nib_6lr
PSEUDOMODULES += gnrc_ipv6_nib_router
PSEUDOMODULES += gnrc_netdev_default
PSEUDOMODULES += gnrc_neterr
PSEUDOMODULES += gnrc_netapi_callbacks

View File

@ -88,6 +88,11 @@
#include "net/gcoap.h"
#endif
#ifdef MODULE_GNRC_IPV6_NIB
#include "net/gnrc/ipv6/nib.h"
#endif
#define ENABLE_DEBUG (0)
#include "debug.h"
@ -157,6 +162,10 @@ void auto_init(void)
extern void auto_init_devfs(void);
auto_init_devfs();
#endif
#ifdef MODULE_GNRC_IPV6_NIB
DEBUG("Auto init gnrc_ipv6_nib module.\n");
gnrc_ipv6_nib_init();
#endif
/* initialize network devices */
#ifdef MODULE_AUTO_INIT_GNRC_NETIF

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

@ -24,6 +24,47 @@
extern "C" {
#endif
/* some pseudo-module based configuration, doc: see below */
#ifdef MODULE_GNRC_IPV6_NIB_6LBR
#ifndef GNRC_IPV6_NIB_CONF_6LBR
#define GNRC_IPV6_NIB_CONF_6LBR (1)
#endif
#endif
#ifdef MODULE_GNRC_IPV6_NIB_6LR
#ifndef GNRC_IPV6_NIB_CONF_6LR
#define GNRC_IPV6_NIB_CONF_6LR (1)
#endif
#ifndef GNRC_IPV6_NIB_CONF_SLAAC
#define GNRC_IPV6_NIB_CONF_SLAAC (0)
#endif
#endif
#ifdef MODULE_GNRC_IPV6_NIB_6LN
#ifndef GNRC_IPV6_NIB_CONF_6LN
#define GNRC_IPV6_NIB_CONF_6LN (1)
#endif
#ifndef GNRC_IPV6_NIB_CONF_SLAAC
#define GNRC_IPV6_NIB_CONF_SLAAC (0)
#endif
#ifndef GNRC_IPV6_NIB_CONF_QUEUE_PKT
#define GNRC_IPV6_NIB_CONF_QUEUE_PKT (0)
#endif
#if !GNRC_IPV6_NIB_CONF_6LR
# ifndef GNRC_IPV6_NIB_CONF_ARSM
# define GNRC_IPV6_NIB_CONF_ARSM (0)
# endif
# ifndef GNRC_IPV6_NIB_NUMOF
/* only needs to store default router */
# define GNRC_IPV6_NIB_NUMOF (1)
# endif
#endif
#endif
#ifdef MODULE_GNRC_IPV6_NIB_ROUTER
#define GNRC_IPV6_NIB_CONF_ROUTER (1)
#endif
/**
* @name Compile flags
* @brief Compile flags to (de-)activate certain features for NIB

View File

@ -23,7 +23,11 @@
#include "kernel_types.h"
#include "net/ipv6/hdr.h"
#include "net/gnrc.h"
#ifndef MODULE_GNRC_IPV6_NIB
#include "net/gnrc/ndp.h"
#else
#include "net/gnrc/ipv6/nib.h"
#endif
#include "net/protnum.h"
#include "od.h"
#include "utlist.h"
@ -94,6 +98,7 @@ void gnrc_icmpv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt)
break;
#endif
#ifndef MODULE_GNRC_IPV6_NIB
#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
case ICMPV6_RTR_SOL:
DEBUG("icmpv6: router solicitation received\n");
@ -126,6 +131,18 @@ void gnrc_icmpv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt)
DEBUG("icmpv6: redirect message received\n");
/* TODO */
break;
#else /* MODULE_GNRC_IPV6_NIB */
case ICMPV6_RTR_SOL:
case ICMPV6_RTR_ADV:
case ICMPV6_NBR_SOL:
case ICMPV6_NBR_ADV:
case ICMPV6_REDIRECT:
case ICMPV6_DAR:
case ICMPV6_DAC:
DEBUG("icmpv6: NDP message received. Handle with gnrc_ipv6_nib\n");
gnrc_ipv6_nib_handle_pkt(iface, ipv6->data, hdr, icmpv6->size);
break;
#endif /* MODULE_GNRC_IPV6_NIB */
default:
DEBUG("icmpv6: unknown type field %u\n", hdr->type);

View File

@ -29,7 +29,11 @@
#include "thread.h"
#include "utlist.h"
#ifndef MODULE_GNRC_IPV6_NIB
#include "net/gnrc/ipv6/nc.h"
#else
#include "net/gnrc/ipv6/nib.h"
#endif
#include "net/gnrc/ipv6/netif.h"
#include "net/gnrc/ipv6/whitelist.h"
#include "net/gnrc/ipv6/blacklist.h"
@ -286,6 +290,7 @@ static void *_event_loop(void *args)
msg_reply(&msg, &reply);
break;
#ifndef MODULE_GNRC_IPV6_NIB
#ifdef MODULE_GNRC_NDP
case GNRC_NDP_MSG_RTR_TIMEOUT:
DEBUG("ipv6: Router timeout received\n");
@ -361,6 +366,26 @@ static void *_event_loop(void *args)
&(nc_entry->ipv6_addr), false);
break;
#endif
#else /* MODULE_GNRC_IPV6_NIB */
case GNRC_IPV6_NIB_SND_UC_NS:
case GNRC_IPV6_NIB_SND_MC_NS:
case GNRC_IPV6_NIB_SND_NA:
case GNRC_IPV6_NIB_SEARCH_RTR:
case GNRC_IPV6_NIB_RECONFIRM_RTR:
case GNRC_IPV6_NIB_REPLY_RS:
case GNRC_IPV6_NIB_SND_MC_RA:
case GNRC_IPV6_NIB_REACH_TIMEOUT:
case GNRC_IPV6_NIB_DELAY_TIMEOUT:
case GNRC_IPV6_NIB_ADDR_REG_TIMEOUT:
case GNRC_IPV6_NIB_6LO_CTX_TIMEOUT:
case GNRC_IPV6_NIB_ABR_TIMEOUT:
case GNRC_IPV6_NIB_PFX_TIMEOUT:
case GNRC_IPV6_NIB_RTR_TIMEOUT:
case GNRC_IPV6_NIB_RECALC_REACH_TIME:
DEBUG("ipv6: NIB timer event received\n");
gnrc_ipv6_nib_handle_timer_event(msg.content.ptr, msg.type);
break;
#endif /* MODULE_GNRC_IPV6_NIB */
default:
break;
}
@ -624,6 +649,7 @@ static void _send_multicast(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
#endif /* GNRC_NETIF_NUMOF */
}
#ifndef MODULE_GNRC_IPV6_NIB
static inline kernel_pid_t _next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len,
kernel_pid_t iface, ipv6_addr_t *dst,
gnrc_pktsnip_t *pkt)
@ -653,6 +679,7 @@ static inline kernel_pid_t _next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len
#endif
return found_iface;
}
#endif /* MODULE_GNRC_IPV6_NIB */
static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr)
{
@ -744,6 +771,7 @@ static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr)
}
}
else {
#ifndef MODULE_GNRC_IPV6_NIB
uint8_t l2addr_len = GNRC_IPV6_NC_L2_ADDR_MAX;
uint8_t l2addr[l2addr_len];
@ -764,6 +792,26 @@ static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr)
}
_send_unicast(iface, l2addr, l2addr_len, pkt);
#else /* MODULE_GNRC_IPV6_NIB */
gnrc_ipv6_nib_nc_t nce;
if (gnrc_ipv6_nib_get_next_hop_l2addr(&hdr->dst, iface, pkt,
&nce) < 0) {
/* packet is released by NIB */
return;
}
if (prep_hdr) {
if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) {
/* error on filling up header */
gnrc_pktbuf_release(pkt);
return;
}
}
_send_unicast(gnrc_ipv6_nib_nc_get_iface(&nce), nce.l2addr,
nce.l2addr_len, pkt);
#endif /* MODULE_GNRC_IPV6_NIB */
}
}

View File

@ -25,6 +25,9 @@
#include "net/eui64.h"
#include "net/ipv6/addr.h"
#ifdef MODULE_GNRC_IPV6_NIB
#include "net/gnrc/ipv6/nib.h"
#endif
#include "net/gnrc/ndp.h"
#include "net/gnrc/netapi.h"
#include "net/gnrc/netif.h"
@ -173,9 +176,11 @@ static void _ipv6_netif_remove(gnrc_ipv6_netif_t *entry)
return;
}
#ifndef MODULE_GNRC_IPV6_NIB
#ifdef MODULE_GNRC_NDP
gnrc_ndp_netif_remove(entry);
#endif
#endif /* MODULE_GNRC_IPV6_NIB */
mutex_lock(&entry->mutex);
xtimer_remove(&entry->rtr_sol_timer);
@ -235,9 +240,13 @@ void gnrc_ipv6_netif_add(kernel_pid_t pid)
mutex_unlock(&free_entry->mutex);
#ifndef MODULE_GNRC_IPV6_NIB
#ifdef MODULE_GNRC_NDP
gnrc_ndp_netif_add(free_entry);
#endif
#else /* MODULE_GNRC_IPV6_NIB */
gnrc_ipv6_nib_init_iface(pid);
#endif /* MODULE_GNRC_IPV6_NIB */
DEBUG(" * pid = %" PRIkernel_pid " ", free_entry->pid);
DEBUG("cur_hl = %d ", free_entry->cur_hl);

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

@ -0,0 +1,21 @@
# name of your application
APPLICATION = gnrc_ipv6_nib
include ../Makefile.tests_common
BOARD_INSUFFICIENT_MEMORY := chronos nucleo32-f031 nucleo32-f042
USEMODULE += gnrc_ipv6
USEMODULE += gnrc_ipv6_nib
USEMODULE += embunit
CFLAGS += -DDEVELHELP
CFLAGS += -DGNRC_NETTYPE_NDP2=GNRC_NETTYPE_TEST
CFLAGS += -DGNRC_PKTBUF_SIZE=512
CFLAGS += -DTEST_SUITES
include $(RIOTBASE)/Makefile.include
test:
# `testrunner` calls `make term` recursively, results in duplicated `TERMFLAGS`.
# So clears `TERMFLAGS` before run.
TERMFLAGS= tests/01-run.py

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
/**
* @defgroup tests_gnrc_ipv6_nib Common header for GNRC's NIB tests
* @ingroup tests
* @brief Common definitions for GNRC's NIB tests
* @{
*
* @file
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef COMMON_H
#define COMMON_H
#include <stdio.h>
#include "net/gnrc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define _CALL(fn) _common_set_up(); _set_up(); puts("Calling " # fn); fn()
extern kernel_pid_t _mock_netif_pid;
void _tests_init(void);
int _mock_netif_get(gnrc_netapi_opt_t *opt);
void _common_set_up(void);
#ifdef __cplusplus
}
#endif
#endif /* COMMON_H */
/** @} */

792
tests/gnrc_ipv6_nib/main.c Normal file
View File

@ -0,0 +1,792 @@
/*
* 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 tests
* @{
*
* @file
* @brief Tests default configuration of GNRC's Network Information Base
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*
* @}
*/
#include <errno.h>
#include <stdio.h>
#include "cib.h"
#include "common.h"
#include "embUnit.h"
#include "embUnit/embUnit.h"
#include "net/ethernet.h"
#include "net/gnrc.h"
#include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/ndp.h"
#include "sched.h"
#define _BUFFER_SIZE (128)
#define _LL0 (0xce)
#define _LL1 (0xab)
#define _LL2 (0xfe)
#define _LL3 (0xad)
#define _LL4 (0xf7)
#define _LL5 (0x26)
static const uint8_t _loc_l2[] = { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5 };
static const ipv6_addr_t _loc_ll = { {
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
_LL0 ^ 2, _LL1, _LL2, 0xff, 0xfe, _LL3, _LL4, _LL5
} };
static const ipv6_addr_t _loc_sol_nodes = { {
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xff, _LL3, _LL4, _LL5
} };
#define _loc_iid _loc_ll.u64[1].u8
static const uint8_t _rem_l2[] = { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5 + 1 };
static const ipv6_addr_t _rem_ll = { {
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
_LL0 ^ 2, _LL1, _LL2, 0xff, 0xfe, _LL3, _LL4, _LL5 + 1
} };
#define _rem_iid _rem_ll.u64[1].u8
static uint8_t _buffer[_BUFFER_SIZE];
static ipv6_hdr_t *ipv6 = (ipv6_hdr_t *)&_buffer[0];
static icmpv6_hdr_t *icmpv6 = (icmpv6_hdr_t *)&_buffer[sizeof(ipv6_hdr_t)];
static inline size_t ceil8(size_t size);
static void _set_up(void)
{
_common_set_up();
memset(_buffer, 0, sizeof(_buffer));
gnrc_pktbuf_init();
/* remove messages */
while (msg_avail()) {
msg_t msg;
msg_receive(&msg);
}
}
static void test_get_next_hop_l2addr__link_local_EHOSTUNREACH(kernel_pid_t iface)
{
msg_t msg;
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
gnrc_pktsnip_t *pkt;
TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH,
gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll, iface,
NULL, &nce));
if (iface != KERNEL_PID_UNDEF) {
ndp_nbr_sol_t *nbr_sol;
bool contains_sl2ao = false;
TEST_ASSERT_MESSAGE(gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"Expected neighbor cache entry");
TEST_ASSERT_MESSAGE(ipv6_addr_equal(&_rem_ll, &nce.ipv6),
"_rem_ll != nce->ipv6");
TEST_ASSERT_EQUAL_INT(0, nce.l2addr_len);
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE,
gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
TEST_ASSERT_EQUAL_INT(iface, gnrc_ipv6_nib_nc_get_iface(&nce));
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC,
gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(1, msg_avail());
msg_receive(&msg);
TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
pkt = msg.content.ptr;
TEST_ASSERT_NOT_NULL(pkt->next);
TEST_ASSERT_NOT_NULL(pkt->next->next);
TEST_ASSERT_EQUAL_INT(sizeof(ndp_nbr_sol_t), pkt->next->next->size);
nbr_sol = pkt->next->next->data;
TEST_ASSERT_EQUAL_INT(ICMPV6_NBR_SOL, nbr_sol->type);
TEST_ASSERT_NOT_NULL(pkt->next->next->next);
for (gnrc_pktsnip_t *opt_snip = pkt->next->next->next; opt_snip != NULL;
opt_snip = opt_snip->next) {
ndp_opt_t *opt = opt_snip->data;
if (opt->type == NDP_OPT_SL2A) {
contains_sl2ao = true;
TEST_ASSERT_EQUAL_INT(1U, opt->len);
TEST_ASSERT_MESSAGE(memcmp(&_loc_l2, opt + 1,
sizeof(_loc_l2)) == 0,
"src_l2 != pkt->l2");
}
}
TEST_ASSERT_MESSAGE(contains_sl2ao, "Sent NS does not contain SL2AO");
gnrc_pktbuf_release(pkt);
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
}
static void test_get_next_hop_l2addr__link_local_EHOSTUNREACH_no_iface(void)
{
test_get_next_hop_l2addr__link_local_EHOSTUNREACH(KERNEL_PID_UNDEF);
}
static void test_get_next_hop_l2addr__link_local_EHOSTUNREACH_iface(void)
{
test_get_next_hop_l2addr__link_local_EHOSTUNREACH(_mock_netif_pid);
}
static void test_get_next_hop_l2addr__link_local_static_conf(void)
{
gnrc_ipv6_nib_nc_t nce;
TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&_rem_ll, _mock_netif_pid,
_rem_l2, sizeof(_rem_l2)));
TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll,
_mock_netif_pid,
NULL, &nce));
TEST_ASSERT_MESSAGE((memcmp(&_rem_ll, &nce.ipv6, sizeof(_rem_ll)) == 0),
"_rem_ll != nce.ipv6");
TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), nce.l2addr_len);
TEST_ASSERT_MESSAGE((memcmp(&_rem_l2, &nce.l2addr, nce.l2addr_len) == 0),
"_rem_l2 != nce.l2addr");
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED,
gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, gnrc_ipv6_nib_nc_get_iface(&nce));
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL,
gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(0, msg_avail());
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
void _simulate_ndp_handshake(const ipv6_addr_t *src, const ipv6_addr_t *dst,
uint8_t adv_flags)
{
msg_t msg;
gnrc_ipv6_nib_nc_t nce;
ndp_nbr_adv_t *nbr_adv = (ndp_nbr_adv_t *)icmpv6;
ndp_opt_t *tl2ao = (ndp_opt_t *)&_buffer[sizeof(ipv6_hdr_t) +
sizeof(ndp_nbr_adv_t)];
/* trigger sending of neighbor discovery */
TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH,
gnrc_ipv6_nib_get_next_hop_l2addr(dst,
_mock_netif_pid,
NULL, &nce));
TEST_ASSERT_EQUAL_INT(1, msg_avail());
/* clear message queue */
msg_receive(&msg);
TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
gnrc_pktbuf_release(msg.content.ptr);
/* generate neighbor advertisement */
ipv6_hdr_set_version(ipv6);
ipv6->hl = 255U;
/* this simulates a reply, so dst and src need to be switched */
memcpy(&ipv6->src, dst, sizeof(ipv6->src));
memcpy(&ipv6->dst, src, sizeof(ipv6->dst));
nbr_adv->type = ICMPV6_NBR_ADV;
/* checksum isn't checked by gnrc_ipv6_nib_handle_pkt() */
nbr_adv->flags = adv_flags;
memcpy(&nbr_adv->tgt, dst, sizeof(nbr_adv->tgt));
tl2ao->type = NDP_OPT_TL2A;
tl2ao->len = 1;
memcpy(tl2ao + 1, _rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, (icmpv6_hdr_t *)nbr_adv,
sizeof(ndp_nbr_adv_t) + 8U);
}
static void test_get_next_hop_l2addr__link_local_after_handshake(uint8_t adv_flags)
{
gnrc_ipv6_nib_nc_t nce;
_simulate_ndp_handshake(&_loc_ll, &_rem_ll, adv_flags);
TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll,
_mock_netif_pid,
NULL, &nce));
TEST_ASSERT_MESSAGE((memcmp(&_rem_ll, &nce.ipv6, sizeof(_rem_ll)) == 0),
"_rem_ll != nce.ipv6");
TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), nce.l2addr_len);
TEST_ASSERT_MESSAGE((memcmp(&_rem_l2, &nce.l2addr, nce.l2addr_len) == 0),
"_rem_l2 != nce.l2addr");
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE,
gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, gnrc_ipv6_nib_nc_get_iface(&nce));
if (adv_flags & NDP_NBR_ADV_FLAGS_R) {
TEST_ASSERT(gnrc_ipv6_nib_nc_is_router(&nce));
}
else {
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
}
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC,
gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(0, msg_avail());
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static void test_get_next_hop_l2addr__link_local_after_handshake_iface(void)
{
test_get_next_hop_l2addr__link_local_after_handshake(NDP_NBR_ADV_FLAGS_S);
}
static void test_get_next_hop_l2addr__link_local_after_handshake_iface_router(void)
{
test_get_next_hop_l2addr__link_local_after_handshake(NDP_NBR_ADV_FLAGS_S |
NDP_NBR_ADV_FLAGS_R);
}
static void test_get_next_hop_l2addr__link_local_after_handshake_no_iface(void)
{
gnrc_ipv6_nib_nc_t nce;
_simulate_ndp_handshake(&_loc_ll, &_rem_ll, NDP_NBR_ADV_FLAGS_S);
TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH,
gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll,
KERNEL_PID_UNDEF,
NULL, &nce));
}
static void test_handle_pkt__unknown_type(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
ipv6_hdr_set_version(ipv6);
ipv6->hl = 255U;
memcpy(&ipv6->src, &_loc_ll, sizeof(ipv6->src));
memcpy(&ipv6->dst, &_rem_ll, sizeof(ipv6->dst));
icmpv6->type = ICMPV6_ECHO_REQ;
icmpv6->code = 0;
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6,
sizeof(icmpv6_hdr_t));
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
}
static size_t _set_nbr_sol(const ipv6_addr_t *ipv6_src,
const ipv6_addr_t *ipv6_dst,
uint8_t ipv6_hl, uint8_t nbr_sol_code,
const ipv6_addr_t *nbr_sol_tgt,
const uint8_t *sl2ao_addr, size_t sl2ao_addr_len)
{
size_t icmpv6_len = sizeof(ndp_nbr_sol_t);
ndp_nbr_sol_t *nbr_sol = (ndp_nbr_sol_t *)icmpv6;
ipv6_hdr_set_version(ipv6);
ipv6->hl = ipv6_hl;
memcpy(&ipv6->src, ipv6_src, sizeof(ipv6->src));
memcpy(&ipv6->dst, ipv6_dst, sizeof(ipv6->dst));
nbr_sol->type = ICMPV6_NBR_SOL;
nbr_sol->code = nbr_sol_code;
memcpy(&nbr_sol->tgt, nbr_sol_tgt, sizeof(nbr_sol->tgt));
if ((sl2ao_addr != NULL) && (sl2ao_addr_len > 0)) {
ndp_opt_t *sl2ao = (ndp_opt_t *)&_buffer[sizeof(ipv6_hdr_t) +
sizeof(ndp_nbr_sol_t)];
sl2ao->type = NDP_OPT_SL2A;
sl2ao->len = ceil8(sizeof(ndp_opt_t) + sl2ao_addr_len) / 8;
memcpy(sl2ao + 1, sl2ao_addr, sl2ao_addr_len);
icmpv6_len += ceil8(sizeof(ndp_opt_t) + sl2ao_addr_len);
}
return icmpv6_len;
}
static void test_handle_pkt__nbr_sol__invalid_hl(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes, 194U, 0U,
&_loc_ll, _rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_code(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes, 255U, 201U,
&_loc_ll, _rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_icmpv6_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
_set_nbr_sol(&_rem_ll, &_loc_sol_nodes, 255U, 0U, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6,
sizeof(ndp_nbr_sol_t) - 1);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_tgt(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes, 255U, 0U,
&ipv6_addr_all_routers_site_local, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_opt_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes, 255U, 0U,
&_loc_ll, _rem_l2, sizeof(_rem_l2));
ndp_opt_t *opt = (ndp_opt_t *)&_buffer[icmpv6_len];
opt->type = NDP_OPT_SL2A;
opt->len = 0U;
icmpv6_len += sizeof(ndp_opt_t);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_dst(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&ipv6_addr_unspecified, &_loc_ll, 255U, 0U,
&_loc_ll, NULL, 0);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_sl2ao(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&ipv6_addr_unspecified, &_loc_sol_nodes,
255U, 0U, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__tgt_not_assigned(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes,
255U, 0U, &_rem_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_pkt_is_nbr_adv(gnrc_pktsnip_t *pkt, const ipv6_addr_t *dst,
const ipv6_addr_t *tgt,
const uint8_t *tgt_l2addr,
size_t tgt_l2addr_len)
{
gnrc_netif_hdr_t *netif_hdr;
ipv6_hdr_t *ipv6_hdr;
ndp_nbr_adv_t *nbr_adv;
ndp_opt_t *tl2ao;
/* first snip is a netif header to _mock_netif_pid */
TEST_ASSERT_NOT_NULL(pkt);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_NETIF, pkt->type);
TEST_ASSERT(sizeof(gnrc_netif_hdr_t) <= pkt->size);
netif_hdr = pkt->data;
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, netif_hdr->if_pid);
/* second snip is an IPv6 header to dst */
TEST_ASSERT_NOT_NULL(pkt->next);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_IPV6, pkt->next->type);
TEST_ASSERT_EQUAL_INT(sizeof(ipv6_hdr_t), pkt->next->size);
ipv6_hdr = pkt->next->data;
TEST_ASSERT(!ipv6_addr_is_multicast(&ipv6_hdr->dst));
TEST_ASSERT_MESSAGE(ipv6_addr_equal(dst, &ipv6_hdr->dst),
"dst != ipv6_hdr->dst");
TEST_ASSERT_EQUAL_INT(255, ipv6_hdr->hl);
/* third snip is a valid solicited neighbor advertisement to tgt */
TEST_ASSERT_NOT_NULL(pkt->next->next);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_ICMPV6, pkt->next->next->type);
TEST_ASSERT_EQUAL_INT(sizeof(ndp_nbr_adv_t), pkt->next->next->size);
nbr_adv = pkt->next->next->data;
TEST_ASSERT_EQUAL_INT(ICMPV6_NBR_ADV, nbr_adv->type);
TEST_ASSERT_EQUAL_INT(0, nbr_adv->code);
TEST_ASSERT(!ipv6_addr_is_multicast(&nbr_adv->tgt));
TEST_ASSERT_MESSAGE(ipv6_addr_equal(tgt, &nbr_adv->tgt),
"tgt != nbr_adv->tgt");
TEST_ASSERT(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S);
/* fourth snip is a TL2AO for tgt_l2addr */
TEST_ASSERT_NOT_NULL(pkt->next->next->next);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_UNDEF, pkt->next->next->next->type);
TEST_ASSERT_EQUAL_INT(ceil8(sizeof(ndp_opt_t) + tgt_l2addr_len),
pkt->next->next->next->size);
tl2ao = pkt->next->next->next->data;
TEST_ASSERT_EQUAL_INT(NDP_OPT_TL2A, tl2ao->type);
TEST_ASSERT_EQUAL_INT(1, tl2ao->len);
TEST_ASSERT_MESSAGE(memcmp(tl2ao + 1, tgt_l2addr, tgt_l2addr_len) == 0,
"tl2ao.l2addr != tgt_l2addr");
/* no further options */
TEST_ASSERT_NULL(pkt->next->next->next->next);
}
static void test_handle_pkt__nbr_sol__ll_src(unsigned exp_nud_state,
unsigned exp_ar_state)
{
msg_t msg;
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes,
255U, 0U, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"Expected neighbor cache entry");
TEST_ASSERT_MESSAGE(ipv6_addr_equal(&_rem_ll, &nce.ipv6),
"_rem_ll != nce->ipv6");
TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), nce.l2addr_len);
TEST_ASSERT_MESSAGE(memcmp(_rem_l2, nce.l2addr, nce.l2addr_len) == 0,
"_rem_l2 != nce.l2addr");
TEST_ASSERT_EQUAL_INT(exp_nud_state, gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, gnrc_ipv6_nib_nc_get_iface(&nce));
TEST_ASSERT_EQUAL_INT(exp_ar_state, gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(1, msg_avail());
msg_receive(&msg);
TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
test_pkt_is_nbr_adv(msg.content.ptr, &_rem_ll, &_loc_ll, _loc_l2,
sizeof(_loc_l2));
gnrc_pktbuf_release(msg.content.ptr);
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static void test_handle_pkt__nbr_sol__ll_src_empty_nc(void)
{
test_handle_pkt__nbr_sol__ll_src(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE,
GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC);
}
static void test_handle_pkt__nbr_sol__ll_src_unmanaged_nce(void)
{
test_get_next_hop_l2addr__link_local_static_conf();
/* unmanaged entry stays unmanaged */
test_handle_pkt__nbr_sol__ll_src(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED,
GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL);
}
static void test_handle_pkt__nbr_sol__ll_src_no_sl2ao(void)
{
msg_t msg;
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_sol_nodes,
255U, 0U, &_loc_ll, NULL, 0);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
TEST_ASSERT_EQUAL_INT(1, msg_avail());
msg_receive(&msg);
TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
test_pkt_is_nbr_adv(msg.content.ptr, &_rem_ll, &_loc_ll, _loc_l2,
sizeof(_loc_l2));
gnrc_pktbuf_release(msg.content.ptr);
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static size_t _set_nbr_adv(const ipv6_addr_t *ipv6_src,
const ipv6_addr_t *ipv6_dst,
uint8_t ipv6_hl, uint8_t nbr_adv_code,
uint8_t nbr_adv_flags,
const ipv6_addr_t *nbr_adv_tgt,
const uint8_t *tl2ao_addr, size_t tl2ao_addr_len)
{
size_t icmpv6_len = sizeof(ndp_nbr_adv_t);
ndp_nbr_adv_t *nbr_adv = (ndp_nbr_adv_t *)icmpv6;
ipv6_hdr_set_version(ipv6);
ipv6->hl = ipv6_hl;
memcpy(&ipv6->src, ipv6_src, sizeof(ipv6->src));
memcpy(&ipv6->dst, ipv6_dst, sizeof(ipv6->dst));
nbr_adv->type = ICMPV6_NBR_ADV;
nbr_adv->code = nbr_adv_code;
nbr_adv->flags = nbr_adv_flags;
memcpy(&nbr_adv->tgt, nbr_adv_tgt, sizeof(nbr_adv->tgt));
if ((tl2ao_addr != NULL) || (tl2ao_addr_len > 0)) {
ndp_opt_t *tl2ao = (ndp_opt_t *)&_buffer[sizeof(ipv6_hdr_t) +
sizeof(ndp_nbr_adv_t)];
tl2ao->type = NDP_OPT_TL2A;
tl2ao->len = ceil8(sizeof(ndp_opt_t) + tl2ao_addr_len) / 8;
memcpy(tl2ao + 1, tl2ao_addr, tl2ao_addr_len);
icmpv6_len += ceil8(sizeof(ndp_opt_t) + tl2ao_addr_len);
}
return icmpv6_len;
}
static void test_handle_pkt__nbr_adv__invalid_hl(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 194U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_code(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 201U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_icmpv6_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
_set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U, NDP_NBR_ADV_FLAGS_S,
&_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6,
sizeof(ndp_nbr_adv_t) - 1);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_tgt(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S,
&ipv6_addr_all_routers_site_local, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_flags(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &ipv6_addr_all_nodes_link_local,
255U, 0U, NDP_NBR_ADV_FLAGS_S, &_loc_ll,
NULL, 0);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_opt_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
ndp_opt_t *opt = (ndp_opt_t *)&_buffer[icmpv6_len];
opt->type = NDP_OPT_SL2A;
opt->len = 0U;
icmpv6_len += sizeof(ndp_opt_t);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__unspecified_src(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&ipv6_addr_unspecified, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__unsolicited(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_sol_nodes, 255U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll,
_rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static Test *tests_gnrc_ipv6_nib(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
/* gnrc_ipv6_nib_init() and gnrc_ipv6_nib_init_iface() "tested" in
* set-up (otherwise the following tests wouldn't work) */
/* TODO: ENETUNREACH when non-link-local communication is implemented */
new_TestFixture(test_get_next_hop_l2addr__link_local_EHOSTUNREACH_no_iface),
new_TestFixture(test_get_next_hop_l2addr__link_local_EHOSTUNREACH_iface),
new_TestFixture(test_get_next_hop_l2addr__link_local_static_conf),
new_TestFixture(test_get_next_hop_l2addr__link_local_after_handshake_iface),
new_TestFixture(test_get_next_hop_l2addr__link_local_after_handshake_iface_router),
new_TestFixture(test_get_next_hop_l2addr__link_local_after_handshake_no_iface),
new_TestFixture(test_handle_pkt__unknown_type),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_hl),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_code),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_icmpv6_len),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_tgt),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_opt_len),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_dst),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_sl2ao),
new_TestFixture(test_handle_pkt__nbr_sol__tgt_not_assigned),
/* TODO add tests for unspecified source (involves SLAAC) */
new_TestFixture(test_handle_pkt__nbr_sol__ll_src_empty_nc),
new_TestFixture(test_handle_pkt__nbr_sol__ll_src_unmanaged_nce),
new_TestFixture(test_handle_pkt__nbr_sol__ll_src_no_sl2ao),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_hl),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_code),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_icmpv6_len),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_tgt),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_flags),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_opt_len),
new_TestFixture(test_handle_pkt__nbr_adv__unspecified_src),
new_TestFixture(test_handle_pkt__nbr_adv__unsolicited),
/* solicited tested in get_next_hop_l2addr */
/* gnrc_ipv6_nib_handle_timer_event not testable in this context since
* we do not have access to the (internally defined) contexts required
* for it */
};
EMB_UNIT_TESTCALLER(tests, _set_up, NULL, fixtures);
return (Test *)&tests;
}
int main(void)
{
_tests_init();
TESTS_START();
TESTS_RUN(tests_gnrc_ipv6_nib());
TESTS_END();
return 0;
}
int _mock_netif_get(gnrc_netapi_opt_t *opt)
{
switch (opt->opt) {
case NETOPT_ADDRESS:
if (opt->data_len < sizeof(_loc_l2)) {
return -EOVERFLOW;
}
memcpy(opt->data, _loc_l2, sizeof(_loc_l2));
return sizeof(_loc_l2);
case NETOPT_SRC_LEN: {
uint16_t *val = opt->data;
if (opt->data_len != sizeof(uint16_t)) {
return -EOVERFLOW;
}
*val = sizeof(_loc_l2);
return sizeof(uint16_t);
}
case NETOPT_IPV6_IID:
if (opt->data_len < sizeof(_loc_iid)) {
return -EOVERFLOW;
}
memcpy(opt->data, _loc_iid, sizeof(_loc_iid));
return sizeof(_loc_iid);
case NETOPT_IS_WIRED:
return 1;
case NETOPT_MAX_PACKET_SIZE: {
uint16_t *val = opt->data;
if (opt->data_len != sizeof(uint16_t)) {
return -EOVERFLOW;
}
*val = ETHERNET_DATA_LEN;
return sizeof(uint16_t);
}
default:
return -ENOTSUP;
}
}
static inline size_t ceil8(size_t size)
{
if (size % 8) {
return ((size / 8) + 1) * 8;
}
else {
return size;
}
}

View File

@ -0,0 +1,81 @@
/*
* 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 "common.h"
#include "msg.h"
#include "net/gnrc.h"
#include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/ipv6/netif.h"
#include "net/gnrc/netdev.h"
#include "sched.h"
#include "thread.h"
#define _MSG_QUEUE_SIZE (2)
kernel_pid_t _mock_netif_pid = KERNEL_PID_UNDEF;
static char _mock_netif_stack[THREAD_STACKSIZE_DEFAULT];
static gnrc_netreg_entry_t dumper;
static msg_t _main_msg_queue[_MSG_QUEUE_SIZE];
static msg_t _mock_netif_msg_queue[_MSG_QUEUE_SIZE];
static void *_mock_netif_thread(void *args)
{
msg_t msg, reply = { .type = GNRC_NETAPI_MSG_TYPE_ACK };
(void)args;
msg_init_queue(_mock_netif_msg_queue, _MSG_QUEUE_SIZE);
while (1) {
msg_receive(&msg);
switch (msg.type) {
case GNRC_NETAPI_MSG_TYPE_GET:
reply.content.value = (uint32_t)_mock_netif_get(msg.content.ptr);
break;
case GNRC_NETAPI_MSG_TYPE_SET:
reply.content.value = (uint32_t)(-ENOTSUP);
break;
case GNRC_NETAPI_MSG_TYPE_SND:
case GNRC_NETAPI_MSG_TYPE_RCV:
gnrc_pktbuf_release(msg.content.ptr);
}
msg_reply(&msg, &reply);
}
return NULL;
}
void _common_set_up(void)
{
gnrc_ipv6_nib_init();
gnrc_ipv6_nib_init_iface(_mock_netif_pid);
}
void _tests_init(void)
{
msg_init_queue(_main_msg_queue, _MSG_QUEUE_SIZE);
_mock_netif_pid = thread_create(_mock_netif_stack,
sizeof(_mock_netif_stack),
GNRC_NETDEV_MAC_PRIO,
THREAD_CREATE_STACKTEST,
_mock_netif_thread, NULL, "mock_netif");
assert(_mock_netif_pid > KERNEL_PID_UNDEF);
gnrc_netif_add(_mock_netif_pid);
gnrc_ipv6_netif_init_by_dev();
thread_yield();
gnrc_netreg_entry_init_pid(&dumper, GNRC_NETREG_DEMUX_CTX_ALL,
sched_active_pid);
gnrc_netreg_register(GNRC_NETTYPE_NDP2, &dumper);
}
/** @} */

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
# Copyright (C) 2016 Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
#
# 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.
import os
import sys
sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
import testrunner
def testfunc(child):
child.expect(r"OK \(\d+ tests\)")
if __name__ == "__main__":
sys.exit(testrunner.run(testfunc, timeout=1))

View File

@ -0,0 +1,24 @@
# name of your application
APPLICATION = gnrc_ipv6_nib_6ln
include ../Makefile.tests_common
BOARD_INSUFFICIENT_MEMORY := chronos nucleo-f030 nucleo-l053 nucleo32-f031
nucleo32-l031 nucleo32-f042 stm32f0discovery
USEMODULE += gnrc_ipv6
USEMODULE += gnrc_sixlowpan
USEMODULE += gnrc_ipv6_nib_6ln
USEMODULE += embunit
USEMODULE += netopt
CFLAGS += -DDEVELHELP
CFLAGS += -DGNRC_NETTYPE_NDP2=GNRC_NETTYPE_TEST
CFLAGS += -DGNRC_PKTBUF_SIZE=512
CFLAGS += -DTEST_SUITES
include $(RIOTBASE)/Makefile.include
test:
# `testrunner` calls `make term` recursively, results in duplicated `TERMFLAGS`.
# So clears `TERMFLAGS` before run.
TERMFLAGS= tests/01-run.py

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
/**
* @defgroup tests_gnrc_ipv6_nib Common header for GNRC's NIB tests
* @ingroup tests
* @brief Common definitions for GNRC's NIB tests
* @{
*
* @file
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef COMMON_H
#define COMMON_H
#include <stdio.h>
#include "net/gnrc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define _CALL(fn) _common_set_up(); _set_up(); puts("Calling " # fn); fn()
extern kernel_pid_t _mock_netif_pid;
void _tests_init(void);
int _mock_netif_get(gnrc_netapi_opt_t *opt);
void _common_set_up(void);
#ifdef __cplusplus
}
#endif
#endif /* COMMON_H */
/** @} */

View File

@ -0,0 +1,668 @@
/*
* 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 tests
* @{
*
* @file
* @brief Tests default configuration of GNRC's Network Information Base
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*
* @}
*/
#include <errno.h>
#include <stdio.h>
#include "cib.h"
#include "common.h"
#include "embUnit.h"
#include "embUnit/embUnit.h"
#include "net/ipv6.h"
#include "net/gnrc.h"
#include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/ndp.h"
#include "sched.h"
#define _BUFFER_SIZE (128)
#define _LL0 (0xce)
#define _LL1 (0xab)
#define _LL2 (0xfe)
#define _LL3 (0xad)
#define _LL4 (0xf7)
#define _LL5 (0x26)
#define _LL6 (0xef)
#define _LL7 (0xa4)
static const uint8_t _loc_l2[] = { _LL0, _LL1, _LL2, _LL3,
_LL4, _LL5, _LL6, _LL7 };
static const ipv6_addr_t _loc_ll = { {
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
_LL0 ^ 2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7
} };
#define _loc_iid _loc_ll.u64[1].u8
static const uint8_t _rem_l2[] = { _LL0, _LL1, _LL2, _LL3,
_LL4, _LL5, _LL6, _LL7 + 1 };
static const ipv6_addr_t _rem_ll = { {
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
_LL0 ^ 2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1
} };
#define _rem_iid _rem_ll.u64[1].u8
static uint8_t _buffer[_BUFFER_SIZE];
static ipv6_hdr_t *ipv6 = (ipv6_hdr_t *)&_buffer[0];
static icmpv6_hdr_t *icmpv6 = (icmpv6_hdr_t *)&_buffer[sizeof(ipv6_hdr_t)];
static inline size_t ceil8(size_t size);
static void _set_up(void)
{
_common_set_up();
memset(_buffer, 0, sizeof(_buffer));
gnrc_pktbuf_init();
/* remove messages */
while (msg_avail()) {
msg_t msg;
msg_receive(&msg);
}
}
static void test_get_next_hop_l2addr__link_local_EHOSTUNREACH(void)
{
gnrc_ipv6_nib_nc_t nce;
TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH,
gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll,
KERNEL_PID_UNDEF,
NULL, &nce));
TEST_ASSERT_EQUAL_INT(0, msg_avail());
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static void test_get_next_hop_l2addr__link_local_static_conf(void)
{
gnrc_ipv6_nib_nc_t nce;
TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&_rem_ll, _mock_netif_pid,
_rem_l2, sizeof(_rem_l2)));
TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll,
_mock_netif_pid,
NULL, &nce));
TEST_ASSERT_MESSAGE((memcmp(&_rem_ll, &nce.ipv6, sizeof(_rem_ll)) == 0),
"_rem_ll != nce.ipv6");
TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), nce.l2addr_len);
TEST_ASSERT_MESSAGE((memcmp(&_rem_l2, &nce.l2addr, nce.l2addr_len) == 0),
"_rem_l2 != nce.l2addr");
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED,
gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, gnrc_ipv6_nib_nc_get_iface(&nce));
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL,
gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(0, msg_avail());
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static void test_get_next_hop_l2addr__link_local(void)
{
gnrc_ipv6_nib_nc_t nce;
TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_get_next_hop_l2addr(&_rem_ll,
_mock_netif_pid,
NULL, &nce));
TEST_ASSERT_MESSAGE((memcmp(&_rem_ll, &nce.ipv6, sizeof(_rem_ll)) == 0),
"_rem_ll != nce.ipv6");
TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), nce.l2addr_len);
TEST_ASSERT_MESSAGE((memcmp(&_rem_l2, &nce.l2addr, nce.l2addr_len) == 0),
"_rem_l2 != nce.l2addr");
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE,
gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, gnrc_ipv6_nib_nc_get_iface(&nce));
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED,
gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(0, msg_avail());
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static void test_handle_pkt__unknown_type(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
ipv6_hdr_set_version(ipv6);
ipv6->hl = 255U;
memcpy(&ipv6->src, &_loc_ll, sizeof(ipv6->src));
memcpy(&ipv6->dst, &_rem_ll, sizeof(ipv6->dst));
icmpv6->type = ICMPV6_ECHO_REQ;
icmpv6->code = 0;
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6,
sizeof(icmpv6_hdr_t));
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
}
static size_t _set_nbr_sol(const ipv6_addr_t *ipv6_src,
const ipv6_addr_t *ipv6_dst,
uint8_t ipv6_hl, uint8_t nbr_sol_code,
const ipv6_addr_t *nbr_sol_tgt,
const uint8_t *sl2ao_addr, size_t sl2ao_addr_len)
{
size_t icmpv6_len = sizeof(ndp_nbr_sol_t);
ndp_nbr_sol_t *nbr_sol = (ndp_nbr_sol_t *)icmpv6;
ipv6_hdr_set_version(ipv6);
ipv6->hl = ipv6_hl;
memcpy(&ipv6->src, ipv6_src, sizeof(ipv6->src));
memcpy(&ipv6->dst, ipv6_dst, sizeof(ipv6->dst));
nbr_sol->type = ICMPV6_NBR_SOL;
nbr_sol->code = nbr_sol_code;
memcpy(&nbr_sol->tgt, nbr_sol_tgt, sizeof(nbr_sol->tgt));
if ((sl2ao_addr != NULL) && (sl2ao_addr_len > 0)) {
ndp_opt_t *sl2ao = (ndp_opt_t *)&_buffer[sizeof(ipv6_hdr_t) +
sizeof(ndp_nbr_sol_t)];
sl2ao->type = NDP_OPT_SL2A;
sl2ao->len = ceil8(sizeof(ndp_opt_t) + sl2ao_addr_len) / 8;
memcpy(sl2ao + 1, sl2ao_addr, sl2ao_addr_len);
icmpv6_len += ceil8(sizeof(ndp_opt_t) + sl2ao_addr_len);
}
return icmpv6_len;
}
static void test_handle_pkt__nbr_sol__invalid_hl(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll, 194U, 0U,
&_loc_ll, _rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_code(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll, 255U, 201U,
&_loc_ll, _rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_icmpv6_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
_set_nbr_sol(&_rem_ll, &_loc_ll, 255U, 0U, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6,
sizeof(ndp_nbr_sol_t) - 1);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_tgt(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll, 255U, 0U,
&ipv6_addr_all_routers_site_local, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_opt_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll, 255U, 0U,
&_loc_ll, _rem_l2, sizeof(_rem_l2));
ndp_opt_t *opt = (ndp_opt_t *)&_buffer[icmpv6_len];
opt->type = NDP_OPT_SL2A;
opt->len = 0U;
icmpv6_len += sizeof(ndp_opt_t);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_dst(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&ipv6_addr_unspecified, &_loc_ll, 255U, 0U,
&_loc_ll, NULL, 0);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__invalid_sl2ao(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&ipv6_addr_unspecified, &_loc_ll,
255U, 0U, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_sol__tgt_not_assigned(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll,
255U, 0U, &_rem_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_pkt_is_nbr_adv(gnrc_pktsnip_t *pkt, const ipv6_addr_t *dst,
const ipv6_addr_t *tgt)
{
gnrc_netif_hdr_t *netif_hdr;
ipv6_hdr_t *ipv6_hdr;
ndp_nbr_adv_t *nbr_adv;
/* first snip is a netif header to _mock_netif_pid */
TEST_ASSERT_NOT_NULL(pkt);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_NETIF, pkt->type);
TEST_ASSERT(sizeof(gnrc_netif_hdr_t) <= pkt->size);
netif_hdr = pkt->data;
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, netif_hdr->if_pid);
/* second snip is an IPv6 header to dst */
TEST_ASSERT_NOT_NULL(pkt->next);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_IPV6, pkt->next->type);
TEST_ASSERT_EQUAL_INT(sizeof(ipv6_hdr_t), pkt->next->size);
ipv6_hdr = pkt->next->data;
TEST_ASSERT(!ipv6_addr_is_multicast(&ipv6_hdr->dst));
TEST_ASSERT_MESSAGE(ipv6_addr_equal(dst, &ipv6_hdr->dst),
"dst != ipv6_hdr->dst");
TEST_ASSERT_EQUAL_INT(255, ipv6_hdr->hl);
/* third snip is a valid solicited neighbor advertisement to tgt */
TEST_ASSERT_NOT_NULL(pkt->next->next);
TEST_ASSERT_EQUAL_INT(GNRC_NETTYPE_ICMPV6, pkt->next->next->type);
TEST_ASSERT_EQUAL_INT(sizeof(ndp_nbr_adv_t), pkt->next->next->size);
nbr_adv = pkt->next->next->data;
TEST_ASSERT_EQUAL_INT(ICMPV6_NBR_ADV, nbr_adv->type);
TEST_ASSERT_EQUAL_INT(0, nbr_adv->code);
TEST_ASSERT(!ipv6_addr_is_multicast(&nbr_adv->tgt));
TEST_ASSERT_MESSAGE(ipv6_addr_equal(tgt, &nbr_adv->tgt),
"tgt != nbr_adv->tgt");
TEST_ASSERT(nbr_adv->flags & NDP_NBR_ADV_FLAGS_S);
/* no further options */
TEST_ASSERT_NULL(pkt->next->next->next);
}
static void test_handle_pkt__nbr_sol__ll_src(unsigned exp_nud_state,
unsigned exp_ar_state)
{
msg_t msg;
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll,
255U, 0U, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"Expected neighbor cache entry");
TEST_ASSERT_MESSAGE(ipv6_addr_equal(&_rem_ll, &nce.ipv6),
"_rem_ll != nce->ipv6");
TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), nce.l2addr_len);
TEST_ASSERT_MESSAGE(memcmp(_rem_l2, nce.l2addr, nce.l2addr_len) == 0,
"_rem_l2 != nce.l2addr");
TEST_ASSERT_EQUAL_INT(exp_nud_state, gnrc_ipv6_nib_nc_get_nud_state(&nce));
TEST_ASSERT(!gnrc_ipv6_nib_nc_is_router(&nce));
TEST_ASSERT_EQUAL_INT(_mock_netif_pid, gnrc_ipv6_nib_nc_get_iface(&nce));
TEST_ASSERT_EQUAL_INT(exp_ar_state, gnrc_ipv6_nib_nc_get_ar_state(&nce));
TEST_ASSERT_EQUAL_INT(1, msg_avail());
msg_receive(&msg);
TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
test_pkt_is_nbr_adv(msg.content.ptr, &_rem_ll, &_loc_ll);
gnrc_pktbuf_release(msg.content.ptr);
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static void test_handle_pkt__nbr_sol__ll_src_empty_nc(void)
{
test_handle_pkt__nbr_sol__ll_src(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE,
GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC);
}
static void test_handle_pkt__nbr_sol__ll_src_unmanaged_nce(void)
{
test_get_next_hop_l2addr__link_local_static_conf();
/* unmanaged entry stays unmanaged */
test_handle_pkt__nbr_sol__ll_src(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED,
GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL);
}
static void test_handle_pkt__nbr_sol__ll_src_no_sl2ao(void)
{
msg_t msg;
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_sol(&_rem_ll, &_loc_ll,
255U, 0U, &_loc_ll, NULL, 0);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
TEST_ASSERT_EQUAL_INT(1, msg_avail());
msg_receive(&msg);
TEST_ASSERT_EQUAL_INT(GNRC_NETAPI_MSG_TYPE_SND, msg.type);
test_pkt_is_nbr_adv(msg.content.ptr, &_rem_ll, &_loc_ll);
gnrc_pktbuf_release(msg.content.ptr);
TEST_ASSERT(gnrc_pktbuf_is_empty());
}
static size_t _set_nbr_adv(const ipv6_addr_t *ipv6_src,
const ipv6_addr_t *ipv6_dst,
uint8_t ipv6_hl, uint8_t nbr_adv_code,
uint8_t nbr_adv_flags,
const ipv6_addr_t *nbr_adv_tgt,
const uint8_t *tl2ao_addr, size_t tl2ao_addr_len)
{
size_t icmpv6_len = sizeof(ndp_nbr_adv_t);
ndp_nbr_adv_t *nbr_adv = (ndp_nbr_adv_t *)icmpv6;
ipv6_hdr_set_version(ipv6);
ipv6->hl = ipv6_hl;
memcpy(&ipv6->src, ipv6_src, sizeof(ipv6->src));
memcpy(&ipv6->dst, ipv6_dst, sizeof(ipv6->dst));
nbr_adv->type = ICMPV6_NBR_ADV;
nbr_adv->code = nbr_adv_code;
nbr_adv->flags = nbr_adv_flags;
memcpy(&nbr_adv->tgt, nbr_adv_tgt, sizeof(nbr_adv->tgt));
if ((tl2ao_addr != NULL) && (tl2ao_addr_len > 0)) {
ndp_opt_t *tl2ao = (ndp_opt_t *)&_buffer[sizeof(ipv6_hdr_t) +
sizeof(ndp_nbr_adv_t)];
tl2ao->type = NDP_OPT_TL2A;
tl2ao->len = ceil8(sizeof(ndp_opt_t) + tl2ao_addr_len) / 8;
memcpy(tl2ao + 1, tl2ao_addr, tl2ao_addr_len);
icmpv6_len += ceil8(sizeof(ndp_opt_t) + tl2ao_addr_len);
}
return icmpv6_len;
}
static void test_handle_pkt__nbr_adv__invalid_hl(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 194U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_code(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 201U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_icmpv6_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
_set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U, NDP_NBR_ADV_FLAGS_S,
&_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6,
sizeof(ndp_nbr_adv_t) - 1);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_tgt(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S,
&ipv6_addr_all_routers_site_local, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_flags(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &ipv6_addr_all_nodes_link_local,
255U, 0U, NDP_NBR_ADV_FLAGS_S, &_loc_ll,
NULL, 0);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__invalid_opt_len(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
ndp_opt_t *opt = (ndp_opt_t *)&_buffer[icmpv6_len];
opt->type = NDP_OPT_SL2A;
opt->len = 0U;
icmpv6_len += sizeof(ndp_opt_t);
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__unspecified_src(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&ipv6_addr_unspecified, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll, _rem_l2,
sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
/* TODO: check other views as well */
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static void test_handle_pkt__nbr_adv__unsolicited(void)
{
gnrc_ipv6_nib_nc_t nce;
void *state = NULL;
size_t icmpv6_len = _set_nbr_adv(&_rem_ll, &_loc_ll, 255U, 0U,
NDP_NBR_ADV_FLAGS_S, &_loc_ll,
_rem_l2, sizeof(_rem_l2));
gnrc_ipv6_nib_handle_pkt(_mock_netif_pid, ipv6, icmpv6, icmpv6_len);
TEST_ASSERT_MESSAGE(!gnrc_ipv6_nib_nc_iter(0, &state, &nce),
"There is an unexpected neighbor cache entry");
TEST_ASSERT_EQUAL_INT(0, msg_avail());
}
static Test *tests_gnrc_ipv6_nib(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
/* gnrc_ipv6_nib_init() and gnrc_ipv6_nib_init_iface() "tested" in
* set-up (otherwise the following tests wouldn't work) */
/* TODO: ENETUNREACH when non-link-local communication is implemented */
new_TestFixture(test_get_next_hop_l2addr__link_local_EHOSTUNREACH),
new_TestFixture(test_get_next_hop_l2addr__link_local_static_conf),
new_TestFixture(test_get_next_hop_l2addr__link_local),
new_TestFixture(test_handle_pkt__unknown_type),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_hl),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_code),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_icmpv6_len),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_tgt),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_opt_len),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_dst),
new_TestFixture(test_handle_pkt__nbr_sol__invalid_sl2ao),
new_TestFixture(test_handle_pkt__nbr_sol__tgt_not_assigned),
/* TODO add tests for unspecified source (involves SLAAC) */
new_TestFixture(test_handle_pkt__nbr_sol__ll_src_empty_nc),
new_TestFixture(test_handle_pkt__nbr_sol__ll_src_unmanaged_nce),
new_TestFixture(test_handle_pkt__nbr_sol__ll_src_no_sl2ao),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_hl),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_code),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_icmpv6_len),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_tgt),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_flags),
new_TestFixture(test_handle_pkt__nbr_adv__invalid_opt_len),
new_TestFixture(test_handle_pkt__nbr_adv__unspecified_src),
new_TestFixture(test_handle_pkt__nbr_adv__unsolicited),
/* solicited tested in get_next_hop_l2addr */
/* gnrc_ipv6_nib_handle_timer_event not testable in this context since
* we do not have access to the (internally defined) contexts required
* for it */
};
EMB_UNIT_TESTCALLER(tests, _set_up, NULL, fixtures);
return (Test *)&tests;
}
int main(void)
{
_tests_init();
TESTS_START();
TESTS_RUN(tests_gnrc_ipv6_nib());
TESTS_END();
return 0;
}
int _mock_netif_get(gnrc_netapi_opt_t *opt)
{
switch (opt->opt) {
case NETOPT_ADDRESS:
if (opt->data_len < sizeof(_loc_l2)) {
return -EOVERFLOW;
}
memcpy(opt->data, _loc_l2, sizeof(_loc_l2));
return sizeof(_loc_l2);
case NETOPT_SRC_LEN: {
uint16_t *val = opt->data;
if (opt->data_len != sizeof(uint16_t)) {
return -EOVERFLOW;
}
*val = sizeof(_loc_l2);
return sizeof(uint16_t);
}
case NETOPT_IPV6_IID:
if (opt->data_len < sizeof(_loc_iid)) {
return -EOVERFLOW;
}
memcpy(opt->data, _loc_iid, sizeof(_loc_iid));
return sizeof(_loc_iid);
case NETOPT_IS_WIRED:
return 1;
case NETOPT_MAX_PACKET_SIZE: {
uint16_t *val = opt->data;
if (opt->data_len != sizeof(uint16_t)) {
return -EOVERFLOW;
}
*val = IPV6_MIN_MTU;
return sizeof(uint16_t);
}
case NETOPT_PROTO: {
gnrc_nettype_t *val = opt->data;
if (opt->data_len != sizeof(gnrc_nettype_t)) {
return -EOVERFLOW;
}
*val = GNRC_NETTYPE_SIXLOWPAN;
return sizeof(gnrc_nettype_t);
}
default:
return -ENOTSUP;
}
}
static inline size_t ceil8(size_t size)
{
if (size % 8) {
return ((size / 8) + 1) * 8;
}
else {
return size;
}
}

View File

@ -0,0 +1,81 @@
/*
* 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 "common.h"
#include "msg.h"
#include "net/gnrc.h"
#include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/ipv6/netif.h"
#include "net/gnrc/netdev.h"
#include "sched.h"
#include "thread.h"
#define _MSG_QUEUE_SIZE (2)
kernel_pid_t _mock_netif_pid = KERNEL_PID_UNDEF;
static char _mock_netif_stack[THREAD_STACKSIZE_DEFAULT];
static gnrc_netreg_entry_t dumper;
static msg_t _main_msg_queue[_MSG_QUEUE_SIZE];
static msg_t _mock_netif_msg_queue[_MSG_QUEUE_SIZE];
static void *_mock_netif_thread(void *args)
{
msg_t msg, reply = { .type = GNRC_NETAPI_MSG_TYPE_ACK };
(void)args;
msg_init_queue(_mock_netif_msg_queue, _MSG_QUEUE_SIZE);
while (1) {
msg_receive(&msg);
switch (msg.type) {
case GNRC_NETAPI_MSG_TYPE_GET:
reply.content.value = (uint32_t)_mock_netif_get(msg.content.ptr);
break;
case GNRC_NETAPI_MSG_TYPE_SET:
reply.content.value = (uint32_t)(-ENOTSUP);
break;
case GNRC_NETAPI_MSG_TYPE_SND:
case GNRC_NETAPI_MSG_TYPE_RCV:
gnrc_pktbuf_release(msg.content.ptr);
}
msg_reply(&msg, &reply);
}
return NULL;
}
void _common_set_up(void)
{
gnrc_ipv6_nib_init();
gnrc_ipv6_nib_init_iface(_mock_netif_pid);
}
void _tests_init(void)
{
msg_init_queue(_main_msg_queue, _MSG_QUEUE_SIZE);
_mock_netif_pid = thread_create(_mock_netif_stack,
sizeof(_mock_netif_stack),
GNRC_NETDEV_MAC_PRIO,
THREAD_CREATE_STACKTEST,
_mock_netif_thread, NULL, "mock_netif");
assert(_mock_netif_pid > KERNEL_PID_UNDEF);
gnrc_netif_add(_mock_netif_pid);
gnrc_ipv6_netif_init_by_dev();
thread_yield();
gnrc_netreg_entry_init_pid(&dumper, GNRC_NETREG_DEMUX_CTX_ALL,
sched_active_pid);
gnrc_netreg_register(GNRC_NETTYPE_NDP2, &dumper);
}
/** @} */

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
# Copyright (C) 2016 Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
#
# 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.
import os
import sys
sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
import testrunner
def testfunc(child):
child.expect(r"OK \(\d+ tests\)")
if __name__ == "__main__":
sys.exit(testrunner.run(testfunc, timeout=1))

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