Browse Source

Merge pull request #6325 from miri64/gnrc_ipv6_nib/feat/internal

gnrc_ipv6_nib: initial import of internal NIB functions
master
Cenk Gündoğan 6 years ago committed by GitHub
parent
commit
72c3c9cf6a
  1. 5
      Makefile.dep
  2. 186
      sys/include/net/gnrc/ipv6/nib.h
  3. 192
      sys/include/net/gnrc/ipv6/nib/conf.h
  4. 134
      sys/include/net/gnrc/ipv6/nib/nc.h
  5. 3
      sys/net/gnrc/Makefile
  6. 3
      sys/net/gnrc/network_layer/ipv6/nib/Makefile
  7. 387
      sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
  8. 633
      sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h
  9. 1
      tests/unittests/tests-gnrc_ipv6_nib/Makefile
  10. 6
      tests/unittests/tests-gnrc_ipv6_nib/Makefile.include
  11. 1060
      tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c
  12. 21
      tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c
  13. 44
      tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h

5
Makefile.dep

@ -315,6 +315,11 @@ ifneq (,$(filter gnrc_ipv6_nc,$(USEMODULE)))
USEMODULE += ipv6_addr
endif
ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE)))
USEMODULE += ipv6_addr
USEMODULE += random
endif
ifneq (,$(filter gnrc_ipv6_netif,$(USEMODULE)))
USEMODULE += ipv6_addr
USEMODULE += gnrc_netif

186
sys/include/net/gnrc/ipv6/nib.h

@ -0,0 +1,186 @@
/*
* 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 net_gnrc_ipv6_nib Neighbor Information Base for IPv6
* @ingroup net_gnrc_ipv6
* @brief Neighbor Information Base (NIB) for IPv6
*
* @todo Add detailed description
* @{
*
* @file
* @brief NIB definitions
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef NET_GNRC_IPV6_NIB_H
#define NET_GNRC_IPV6_NIB_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Timer event message types
* @anchor net_gnrc_ipv6_nib_msg
* @{
*/
/**
* @brief (Re-)transmit unicast Neighbor Solicitation event.
*
* This message type is for the event of (re-)transmitting of unicast Neighbor
* Solicitation. The expected message context is a pointer to a valid on-link
* entry representing the neighbor to which the Neighbor Solicitation is
* supposed to be sent.
*/
#define GNRC_IPV6_NIB_SND_UC_NS (0x4fc0U)
/**
* @brief (Re-)transmit multicast Neighbor Solicitation event.
*
* This message type is for the event of (re-)transmitting of multicast Neighbor
* Solicitation. The expected message context is a pointer to a valid on-link
* entry representing the neighbor to which [solicited
* nodes](https://tools.ietf.org/html/rfc4291#section-2.7.1) group
* the Neighbor Solicitation is supposed to be sent.
*/
#define GNRC_IPV6_NIB_SND_MC_NS (0x4fc1U)
/**
* @brief Send delayed Neighbor Advertisement event.
*
* This message type is for the event of sending delayed Neighbor
* Advertisements. The expected message context is a pointer to a valid
* [packet snip](@ref gnrc_pktsnip_t) in *sending order*, representing the
* Neighbor Advertisement.
*/
#define GNRC_IPV6_NIB_SND_NA (0x4fc2U)
/**
* @brief Search router event.
*
* This message type is for the event of searching a (new) router (which
* implies sending a multicast Router Solicitation). The expected message
* context is a pointer to a valid interface behind which the router is
* searched.
*/
#define GNRC_IPV6_NIB_SEARCH_RTR (0x4fc3U)
/**
* @brief Reconfirm router event.
*
* This message type is for the event the reconfirmation of a router (which
* implies sending a unicast Router Solicitation). The expected message context
* is a pointer to a valid on-link entry representing the router that is to be
* confirmed.
*/
#define GNRC_IPV6_NIB_RECONFIRM_RTR (0x4fc4U)
/**
* @brief Reply Router Solicitation event.
*
* This message type is for the event of the delayed reply to a Router
* Solicitaion with a Router Advertisement. The expected message context is a
* pointer to a valid on-link entry representing the neighbor that sent the
* Router Solicitation.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ROUTER != 0
*/
#define GNRC_IPV6_NIB_REPLY_RS (0x4fc5U)
/**
* @brief (Re-)transmit multicast Router Advertisement event.
*
* This message type is for the event of (Re)transmit Advertisements
* event. The expected message context is a pointer to a valid interface over
* which the Router Advertisement will be sent and by which parameters it will
* be configured.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ROUTER != 0
*/
#define GNRC_IPV6_NIB_SND_MC_RA (0x4fc6U)
/**
* @brief Reachability timeout event.
*
* This message type is for the event of a REACHABLE state timeout.
* The expected message context is a pointer to a valid on-link entry
* representing the neighbor cache entry that faces a state change.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ARSM != 0
*/
#define GNRC_IPV6_NIB_REACH_TIMEOUT (0x4fc7U)
/**
* @brief Delay timeout event.
*
* This message type is for the event of the DELAY state timeout.
* The expected message context is a pointer to a valid on-link entry
* representing the neighbor cache entry that faces a state change.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_ARSM != 0
*/
#define GNRC_IPV6_NIB_DELAY_TIMEOUT (0x4fc8U)
/**
* @brief Address registration timeout event.
*
* This message type is for the event of a 6LoWPAN address registration state
* timeout. The expected message context is a pointer to a valid on-link entry
* representing the neighbor which faces a timeout of its address registration.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_6LR != 0
*/
#define GNRC_IPV6_NIB_ADDR_REG_TIMEOUT (0x4fc9U)
/**
* @brief 6LoWPAN context timeout event.
*
* This message type is for the event of a 6LoWPAN compression context timeout.
* The expected message context is the compression context's numerical
* identifier.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_6LN != 0
*/
#define GNRC_IPV6_NIB_6LO_CTX_TIMEOUT (0x4fcaU)
/**
* @brief Authoritative border router timeout event.
*
* This message type is for the event of an Authoritative border router timeout.
* The expected message context is the NIB-internal state of the authoritative
* border router.
*
* @note Only handled with @ref GNRC_IPV6_NIB_CONF_MULTIHOP_P6C != 0
*/
#define GNRC_IPV6_NIB_ABR_TIMEOUT (0x4fcbU)
/**
* @brief Prefix timeout event.
*
* This message type is for the event of a prefix timeout. The expected message
* context is a valid off-link entry representing the prefix.
*/
#define GNRC_IPV6_NIB_PFX_TIMEOUT (0x4fccU)
/**
* @brief Router timeout event.
*
* This message type is for the event of a router timeout. The expected message
* context is a valid default router entry representing the router.
*/
#define GNRC_IPV6_NIB_RTR_TIMEOUT (0x4fcdU)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_IPV6_NIB_H */
/** @} */

192
sys/include/net/gnrc/ipv6/nib/conf.h

@ -0,0 +1,192 @@
/*
* 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 net_gnrc_ipv6_nib_conf Configuration macros
* @ingroup net_gnrc_ipv6_nib
* @brief Configuration macros for network information base
* @{
*
* @file
* @brief Configuration macro definitions for network information base
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#ifndef NET_GNRC_IPV6_NIB_CONF_H
#define NET_GNRC_IPV6_NIB_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Compile flags
* @brief Compile flags to (de-)activate certain features for NIB
* @{
*/
/**
* @brief enable features for 6Lo border router
*/
#ifndef GNRC_IPV6_NIB_CONF_6LBR
#define GNRC_IPV6_NIB_CONF_6LBR (0)
#endif
/**
* @brief enable features for 6Lo router
*/
#ifndef GNRC_IPV6_NIB_CONF_6LR
#if GNRC_IPV6_NIB_CONF_6LBR
#define GNRC_IPV6_NIB_CONF_6LR (1)
#else
#define GNRC_IPV6_NIB_CONF_6LR (0)
#endif
#endif
/**
* @brief enable features for 6Lo node
*/
#ifndef GNRC_IPV6_NIB_CONF_6LN
#if GNRC_IPV6_NIB_CONF_6LR
#define GNRC_IPV6_NIB_CONF_6LN (1)
#else
#define GNRC_IPV6_NIB_CONF_6LN (0)
#endif
#endif
/**
* @brief enable features for IPv6 routers
*/
#ifndef GNRC_IPV6_NIB_CONF_ROUTER
#if GNRC_IPV6_NIB_CONF_6LR
#define GNRC_IPV6_NIB_CONF_ROUTER (1)
#else
#define GNRC_IPV6_NIB_CONF_ROUTER (0)
#endif
#endif
/**
* @brief (de-)activate router advertising at interface start-up
*/
#ifndef GNRC_IPV6_NIB_CONF_ADV_ROUTER
#if GNRC_IPV6_NIB_CONF_ROUTER && \
(!GNRC_IPV6_NIB_CONF_6LR || GNRC_IPV6_NIB_CONF_6LBR)
#define GNRC_IPV6_NIB_CONF_ADV_ROUTER (1)
#else
#define GNRC_IPV6_NIB_CONF_ADV_ROUTER (0)
#endif
#endif
/**
* @brief (de-)activate NDP address resolution state-machine
*/
#ifndef GNRC_IPV6_NIB_CONF_ARSM
#define GNRC_IPV6_NIB_CONF_ARSM (1)
#endif
/**
* @brief queue packets for address resolution
*/
#ifndef GNRC_IPV6_NIB_CONF_QUEUE_PKT
#if GNRC_IPV6_NIB_CONF_6LN
#define GNRC_IPV6_NIB_CONF_QUEUE_PKT (0)
#else
#define GNRC_IPV6_NIB_CONF_QUEUE_PKT (1)
#endif
#endif
/**
* @brief handle NDP messages according for stateless address
* auto-configuration (if activated on interface)
*
* @see [RFC 4862](https://tools.ietf.org/html/rfc4862)
*/
#ifndef GNRC_IPV6_NIB_CONF_SLAAC
#define GNRC_IPV6_NIB_CONF_SLAAC (1)
#endif
/**
* @brief handle Redirect Messages
*/
#ifndef GNRC_IPV6_NIB_CONF_REDIRECT
#define GNRC_IPV6_NIB_CONF_REDIRECT (0)
#endif
/**
* @brief (de-)activate destination cache
*/
#ifndef GNRC_IPV6_NIB_CONF_DC
#if GNRC_IPV6_NIB_CONF_REDIRECT
#define GNRC_IPV6_NIB_CONF_DC (1)
#else
#define GNRC_IPV6_NIB_CONF_DC (0)
#endif
#endif
/**
* @brief Multihop prefix and 6LoWPAN context distribution
*
* @see [RFC 6775, section 8.1](https://tools.ietf.org/html/rfc6775#section-8.1)
*/
#ifndef GNRC_IPV6_NIB_CONF_MULTIHOP_P6C
#define GNRC_IPV6_NIB_CONF_MULTIHOP_P6C (0)
#endif
/**
* @brief Multihop duplicate address detection
*
* @see [RFC 6775, section 8.2](https://tools.ietf.org/html/rfc6775#section-8.2)
*/
#ifndef GNRC_IPV6_NIB_CONF_MULTIHOP_DAD
#define GNRC_IPV6_NIB_CONF_MULTIHOP_DAD (0)
#endif
/** @} */
/**
* @brief Reset time in milliseconds for the reachability time
*
* @see [RFC 4861, section 6.3.4](https://tools.ietf.org/html/rfc4861#section-6.3.4)
*/
#ifndef GNRC_IPV6_NIB_CONF_REACH_TIME_RESET
#define GNRC_IPV6_NIB_CONF_REACH_TIME_RESET (7200000U)
#endif
/**
* @brief Maximum link-layer address length (aligned)
*/
#if (GNRC_NETIF_HDR_L2ADDR_MAX_LEN % 8)
#define GNRC_IPV6_NIB_L2ADDR_MAX_LEN (((GNRC_NETIF_HDR_L2ADDR_MAX_LEN >> 3) + 1) << 3)
#else
#define GNRC_IPV6_NIB_L2ADDR_MAX_LEN (GNRC_NETIF_HDR_L2ADDR_MAX_LEN)
#endif
/**
* @brief Number of default routers in the default router list.
*
* @note **This number has direct influence on the maximum number of
* default routers**
*/
#ifndef GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF
#define GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF (1)
#endif
/**
* @brief Number of entries in NIB
*
* @note **This number has direct influence on the maximum number of
* neighbors and duplicate address detection table entries**
*/
#ifndef GNRC_IPV6_NIB_NUMOF
#define GNRC_IPV6_NIB_NUMOF (4)
#endif
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_IPV6_NIB_CONF_H */
/** @} */

134
sys/include/net/gnrc/ipv6/nib/nc.h

@ -0,0 +1,134 @@
/*
* 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 net_gnrc_ipv6_nib_nc Neighbor Cache
* @ingroup net_gnrc_ipv6_nib
* @brief Neighbor cache component of network information base
* @{
*
* @file
* @brief Neighbor cache definitions
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef NET_GNRC_IPV6_NIB_NC_H
#define NET_GNRC_IPV6_NIB_NC_H
#include <stdbool.h>
#include <stdint.h>
#include "net/eui64.h"
#include "net/gnrc/netif/hdr.h"
#include "net/gnrc/ipv6/nib/conf.h"
#include "net/ipv6/addr.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Values for gnrc_ipv6_nib_nc_t::info
* @anchor net_gnrc_ipv6_nib_nc_info
* @name Info values
* @{
*/
/**
* @brief Mask for neighbor unreachability detection (NUD) states
*
* @see [RFC 4861, section 7.3.2](https://tools.ietf.org/html/rfc4861#section-7.3.2)
* @see [RFC 7048](https://tools.ietf.org/html/rfc7048)
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK (0x0007)
/**
* @brief not managed by NUD
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED (0x0000)
/**
* @brief entry is not reachable
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE (0x0001)
/**
* @brief address resolution is currently performed
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE (0x0002)
/**
* @brief address might not be reachable
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE (0x0003)
/**
* @brief NUD will be performed in a moment
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY (0x0004)
/**
* @brief NUD is performed
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE (0x0005)
/**
* @brief entry is reachable
*/
#define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE (0x0006)
/**
* @brief gnrc_ipv6_nib_t::next_hop is router
*
* This flag indicates that gnrc_ipv6_nib_t::next_hop is a router, but it does
* not necessarily indicate that it is in the default router list! A neighbor
* that has this flag unset however **must not** appear in the default router
* list.
*
* @see [RFC 4861, Appendix D](https://tools.ietf.org/html/rfc4861#page-91)
*/
#define GNRC_IPV6_NIB_NC_INFO_IS_ROUTER (0x0008)
/**
* @brief Mask for interface identifier
*/
#define GNRC_IPV6_NIB_NC_INFO_IFACE_MASK (0x01f0)
/**
* @brief Shift position of interface identifier
*/
#define GNRC_IPV6_NIB_NC_INFO_IFACE_POS (4)
/**
* @brief Mask for 6LoWPAN address registration (6Lo-AR) states
*
* @see [RFC 6775, section 3.5](https://tools.ietf.org/html/rfc6775#section-3.5)
*/
#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK (0x0600)
/**
* @brief not managed by 6Lo-AR (address can be removed when memory is low
*/
#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC (0x0000)
/**
* @brief address registration still pending at upstream router
*/
#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_TENTATIVE (0x0200)
/**
* @brief address is registered
*/
#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED (0x0600)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* NET_GNRC_IPV6_NIB_NC_H */
/** @} */

3
sys/net/gnrc/Makefile

@ -22,6 +22,9 @@ endif
ifneq (,$(filter gnrc_ipv6_netif,$(USEMODULE)))
DIRS += network_layer/ipv6/netif
endif
ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE)))
DIRS += network_layer/ipv6/nib
endif
ifneq (,$(filter gnrc_ipv6_whitelist,$(USEMODULE)))
DIRS += network_layer/ipv6/whitelist
endif

3
sys/net/gnrc/network_layer/ipv6/nib/Makefile

@ -0,0 +1,3 @@
MODULE = gnrc_ipv6_nib
include $(RIOTBASE)/Makefile.base

387
sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c

@ -0,0 +1,387 @@
/*
* 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 <stdbool.h>
#include <string.h>
#include "net/gnrc/ipv6.h"
#include "net/gnrc/ipv6/nib/conf.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/netif.h"
#include "random.h"
#include "_nib-internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* pointers for default router selection */
static _nib_dr_entry_t *_prime_def_router = NULL;
static clist_node_t _next_removable = { NULL };
static _nib_onl_entry_t _nodes[GNRC_IPV6_NIB_NUMOF];
static _nib_dr_entry_t _def_routers[GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF];
static _nib_iface_t _nis[GNRC_NETIF_NUMOF];
#if ENABLE_DEBUG
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#endif
mutex_t _nib_mutex = MUTEX_INIT;
static void _override_node(const ipv6_addr_t *addr, unsigned iface,
_nib_onl_entry_t *node);
static inline bool _node_unreachable(_nib_onl_entry_t *node);
void _nib_init(void)
{
#ifdef TEST_SUITES
_prime_def_router = NULL;
_next_removable.next = NULL;
memset(_nodes, 0, sizeof(_nodes));
memset(_def_routers, 0, sizeof(_def_routers));
memset(_nis, 0, sizeof(_nis));
#endif
/* TODO: load ABR information from persistent memory */
}
_nib_onl_entry_t *_nib_onl_alloc(const ipv6_addr_t *addr, unsigned iface)
{
_nib_onl_entry_t *node = NULL;
assert(addr != NULL);
DEBUG("nib: Allocating on-link node entry (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), iface);
for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) {
_nib_onl_entry_t *tmp = &_nodes[i];
if ((_nib_onl_get_if(tmp) == iface) &&
(ipv6_addr_equal(addr, &tmp->ipv6))) {
/* exact match */
DEBUG(" %p is an exact match\n", (void *)tmp);
return tmp;
}
if ((node == NULL) && (tmp->mode == _EMPTY)) {
node = tmp;
}
}
if (node != NULL) {
DEBUG(" using %p\n", (void *)node);
_override_node(addr, iface, node);
}
#if ENABLE_DEBUG
else {
DEBUG(" NIB full\n");
}
#endif
return node;
}
static inline bool _is_gc(_nib_onl_entry_t *node)
{
return ((node->mode & ~(_NC)) == 0) &&
((node->info & GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK) ==
GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC);
}
static inline _nib_onl_entry_t *_cache_out_onl_entry(const ipv6_addr_t *addr,
unsigned iface,
uint16_t cstate)
{
/* Use clist as FIFO for caching */
_nib_onl_entry_t *first = (_nib_onl_entry_t *)clist_lpop(&_next_removable);
_nib_onl_entry_t *tmp = first, *res = NULL;
DEBUG("nib: Searching for replaceable entries (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), iface);
if (tmp == NULL) {
return NULL;
}
do {
if (_is_gc(tmp)) {
DEBUG("nib: Removing neighbor cache entry (addr = %s, "
"iface = %u) ",
ipv6_addr_to_str(addr_str, &tmp->ipv6,
sizeof(addr_str)),
_nib_onl_get_if(tmp));
DEBUG("for (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)),
iface);
res = tmp;
res->mode = _EMPTY;
_override_node(addr, iface, res);
/* cstate masked above already */
res->info = cstate;
res->mode = _NC;
break;
}
/* requeue if not garbage collectible at the moment */
DEBUG("nib: Requeing (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, &tmp->ipv6,
sizeof(addr_str)),
_nib_onl_get_if(tmp));
clist_rpush(&_next_removable, (clist_node_t *)tmp);
tmp = (_nib_onl_entry_t *)clist_lpop(&_next_removable);
} while (tmp != first);
return res;
}
_nib_onl_entry_t *_nib_nc_add(const ipv6_addr_t *addr, unsigned iface,
uint16_t cstate)
{
cstate &= GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK;
assert(cstate != GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY);
assert(cstate != GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE);
assert(cstate != GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE);
_nib_onl_entry_t *node = _nib_onl_alloc(addr, iface);
if (node == NULL) {
return _cache_out_onl_entry(addr, iface, cstate);
}
DEBUG("nib: Adding to neighbor cache (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), iface);
if (!(node->mode & _NC)) {
node->info &= ~GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK;
/* masked above already */
node->info |= cstate;
node->mode |= _NC;
}
if (node->next == NULL) {
DEBUG("nib: queueing (addr = %s, iface = %u) for potential removal\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), iface);
/* add to next removable list, if not already in it */
clist_rpush(&_next_removable, (clist_node_t *)node);
}
return node;
}
_nib_onl_entry_t *_nib_onl_iter(const _nib_onl_entry_t *last)
{
for (const _nib_onl_entry_t *node = (last) ? last + 1 : _nodes;
node < (_nodes + GNRC_IPV6_NIB_NUMOF);
node++) {
if (node->mode != _EMPTY) {
/* const modifier provided to assure internal consistency.
* Can now be discarded. */
return (_nib_onl_entry_t *)node;
}
}
return NULL;
}
_nib_onl_entry_t *_nib_onl_get(const ipv6_addr_t *addr, unsigned iface)
{
assert(addr != NULL);
DEBUG("nib: Getting on-link node entry (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), iface);
for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) {
_nib_onl_entry_t *node = &_nodes[i];
if ((node->mode != _EMPTY) &&
/* either requested or current interface undefined or
* interfaces equal */
((_nib_onl_get_if(node) == 0) || (iface == 0) ||
(_nib_onl_get_if(node) == iface)) &&
ipv6_addr_equal(&node->ipv6, addr)) {
DEBUG(" Found %p\n", (void *)node);
return node;
}
}
DEBUG(" No suitable entry found\n");
return NULL;
}
void _nib_nc_set_reachable(_nib_onl_entry_t *node)
{
_nib_iface_t *iface = _nib_iface_get(_nib_onl_get_if(node));
DEBUG("nib: set %s%%%u reachable (reachable time = %u)\n",
ipv6_addr_to_str(addr_str, &node->ipv6, sizeof(addr_str)),
_nib_onl_get_if(node), iface->reach_time);
node->info &= ~GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK;
node->info |= GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE;
/* TODO add event for state change to STALE to event timer*/
(void)iface;
}
void _nib_nc_remove(_nib_onl_entry_t *node)
{
DEBUG("nib: remove from neighbor cache (addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, &node->ipv6, sizeof(addr_str)),
_nib_onl_get_if(node));
node->mode &= ~(_NC);
/* TODO: remove NC related timers */
_nib_onl_clear(node);
}
_nib_dr_entry_t *_nib_drl_add(const ipv6_addr_t *router_addr, unsigned iface)
{
_nib_dr_entry_t *def_router = NULL;
DEBUG("nib: Allocating default router list entry "
"(router_addr = %s, iface = %u)\n",
ipv6_addr_to_str(addr_str, router_addr, sizeof(addr_str)), iface);
for (unsigned i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) {
_nib_dr_entry_t *tmp = &_def_routers[i];
_nib_onl_entry_t *tmp_node = tmp->next_hop;
if ((tmp_node != NULL) &&
(_nib_onl_get_if(tmp_node) == iface) &&
(ipv6_addr_equal(router_addr, &tmp_node->ipv6))) {
/* exact match */
DEBUG(" %p is an exact match\n", (void *)tmp);
tmp_node->mode |= _DRL;
return tmp;
}
if ((def_router == NULL) && (tmp_node == NULL)) {
def_router = tmp;
}
}
if (def_router != NULL) {
DEBUG(" using %p\n", (void *)def_router);
def_router->next_hop = _nib_onl_alloc(router_addr, iface);
if (def_router->next_hop == NULL) {
return NULL;
}
_override_node(router_addr, iface, def_router->next_hop);
def_router->next_hop->mode |= _DRL;
}
return def_router;
}
void _nib_drl_remove(_nib_dr_entry_t *nib_dr)
{
if (nib_dr->next_hop != NULL) {
nib_dr->next_hop->mode &= ~(_DRL);
_nib_onl_clear(nib_dr->next_hop);
memset(nib_dr, 0, sizeof(_nib_dr_entry_t));
}
if (nib_dr == _prime_def_router) {
_prime_def_router = NULL;
}
}
_nib_dr_entry_t *_nib_drl_iter(const _nib_dr_entry_t *last)
{
for (const _nib_dr_entry_t *def_router = (last) ? (last + 1) : _def_routers;
def_router < (_def_routers + GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF);
def_router++) {
_nib_onl_entry_t *node = def_router->next_hop;
if ((node != NULL) && (node->mode != _EMPTY)) {
/* const modifier provided to assure internal consistency.
* Can now be discarded. */
return (_nib_dr_entry_t *)def_router;
}
}
return NULL;
}
_nib_dr_entry_t *_nib_drl_get(const ipv6_addr_t *router_addr, unsigned iface)
{
for (unsigned i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) {
_nib_dr_entry_t *def_router = &_def_routers[i];
_nib_onl_entry_t *node = def_router->next_hop;
if ((node != NULL) &&
(_nib_onl_get_if(node) == iface) &&
(ipv6_addr_equal(router_addr, &node->ipv6))) {
/* It is linked to the default router list so it *should* be set */
assert(node->mode & _DRL);
return def_router;
}
}
return NULL;
}
_nib_dr_entry_t *_nib_drl_get_dr(void)
{
_nib_dr_entry_t *ptr = NULL;
/* if there is already a default router selected or
* its reachability is not suspect */
if (!((_prime_def_router == NULL) ||
(_node_unreachable(_prime_def_router->next_hop)))) {
/* take it */
return _prime_def_router;
}
/* else search next reachable router */
do {
ptr = _nib_drl_iter(ptr);
/* if there is no reachable router */
if (ptr == NULL) {
_nib_dr_entry_t *next = _nib_drl_iter(_prime_def_router);
/* if first time called or last selected router is last in
* router list */
if ((_prime_def_router == NULL) || (next == NULL)) {
/* wrap around to first (potentially unreachable) route
* to trigger NUD for it */
_prime_def_router = _nib_drl_iter(NULL);
}
/* there is another default router, choose it regardless of
* reachability to potentially trigger NUD for it */
else if (next != NULL) {
_prime_def_router = next;
}
return _prime_def_router;
}
} while (_node_unreachable(ptr->next_hop));
_prime_def_router = ptr;
return _prime_def_router;
}
_nib_iface_t *_nib_iface_get(unsigned iface)
{
_nib_iface_t *ni = NULL;
assert(iface <= _NIB_IF_MAX);
for (unsigned i = 0; i < GNRC_NETIF_NUMOF; i++) {
_nib_iface_t *tmp = &_nis[i];
if (((unsigned)tmp->pid) == iface) {
return tmp;
}
if ((ni == NULL) && (tmp->pid == KERNEL_PID_UNDEF)) {
ni = tmp;
}
}
if (ni != NULL) {
memset(ni, 0, sizeof(_nib_iface_t));
/* TODO: set random reachable time using constants from #6220 */
ni->pid = (kernel_pid_t)iface;
}
return ni;
}
static void _override_node(const ipv6_addr_t *addr, unsigned iface,
_nib_onl_entry_t *node)
{
_nib_onl_clear(node);
memcpy(&node->ipv6, addr, sizeof(node->ipv6));
_nib_onl_set_if(node, iface);
}
static inline bool _node_unreachable(_nib_onl_entry_t *node)
{
switch (node->info & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK) {
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE:
/* Falls through. */
case GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE:
return true;
default:
return false;
}
}
/** @} */

633
sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h

@ -0,0 +1,633 @@
/*
* 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.
*/
/**
* @addtogroup net_gnrc_ipv6_nib
* @internal
* @{
*
* @file
* @brief Internal definitions
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef PRIV_NIB_INTERNAL_H
#define PRIV_NIB_INTERNAL_H
#include <stdbool.h>
#include <stdint.h>
#include "kernel_types.h"
#include "mutex.h"
#include "net/eui64.h"
#include "net/ipv6/addr.h"
#include "net/gnrc/ipv6/nib/nc.h"
#include "net/gnrc/ipv6/nib/conf.h"
#include "net/gnrc/pktqueue.h"
#include "net/ndp.h"
#include "random.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Mode flags for entries
* @anchor net_gnrc_ipv6_nib_mode
* @{
*/
#define _EMPTY (0x00) /**< empty */
#define _NC (0x01) /**< neighbor cache */
#define _DC (0x02) /**< destination cache */
#define _PL (0x04) /**< prefix list */
#define _DRL (0x08) /**< default router list */
#define _FT (0x10) /**< forwarding table */
#define _DAD (0x20) /**< 6LoWPAN duplicate address detection table */
/** @} */
/**
* @brief Shorter name for convenience ;-)
*/
#define _NIB_IF_MASK (GNRC_IPV6_NIB_NC_INFO_IFACE_MASK)
/**
* @brief Shorter name for convenience ;-)
*/
#define _NIB_IF_POS (GNRC_IPV6_NIB_NC_INFO_IFACE_POS)
/**
* @brief Maximum identifier for the interface
*/
#define _NIB_IF_MAX (_NIB_IF_MASK >> _NIB_IF_POS)
/**
* @brief On-link NIB entry
* @anchor _nib_onl_entry_t
*/
typedef struct _nib_onl_entry {
struct _nib_onl_entry *next; /**< next removable entry */
#if GNRC_IPV6_NIB_CONF_QUEUE_PKT || defined(DOXYGEN)
/**
* @brief queue for packets currently in address resolution
*
* @note Only available if @ref GNRC_IPV6_NIB_CONF_QUEUE_PKT != 0.
*/
gnrc_pktqueue_t *pktqueue;
#endif
/**
* @brief Neighbors IPv6 address
*/
ipv6_addr_t ipv6;
#if GNRC_IPV6_NIB_CONF_6LR || defined(DOXYGEN)
/**
* @brief The neighbors EUI-64 (used for DAD)
*
* @note Only available if @ref GNRC_IPV6_NIB_CONF_6LR != 0.
*/
eui64_t eui64;
#endif
#if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN)
/**
* @brief Link-layer address of _nib_onl_entry_t::next_hop
*
* @note Only available if @ref GNRC_IPV6_NIB_CONF_ARSM != 0.
*/
uint8_t l2addr[GNRC_IPV6_NIB_L2ADDR_MAX_LEN];
#endif
/**
* @brief Information flags
*
* @see [NC info values](@ref net_gnrc_ipv6_nib_nc_info)
*/
uint16_t info;
/**
* @brief NIB entry mode
*
* This field identifies which "views" of the NIB the entry belongs to.
*
* @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;
/**
* @brief length in bytes of _nib_onl_entry_t::l2addr
*
* @note Only available if @ref GNRC_IPV6_NIB_CONF_ARSM != 0.
*/
uint8_t l2addr_len;
#endif
} _nib_onl_entry_t;
/**
* @brief Default route NIB entry
*/
typedef struct {
_nib_onl_entry_t *next_hop; /**< next hop to destination */
uint16_t ltime; /**< lifetime in seconds */
} _nib_dr_entry_t;
/**
* @brief Off-link NIB entry
*/
typedef struct {
_nib_onl_entry_t *next_hop; /**< next hop to destination */
ipv6_addr_t pfx; /**< prefix to the destination */
unsigned pfx_len; /**< prefix-length in bits of
* _nib_onl_entry_t::pfx */
} _nib_offl_entry_t;
/**
* @brief Interface specific information for Neighbor Discovery
*/
typedef struct {
#if GNRC_IPV6_NIB_CONF_ARSM
/**
* @brief base for random reachable time calculation
*/
uint32_t reach_time_base;
uint32_t reach_time; /**< reachable time (in ms) */
uint32_t retrans_time; /**< retransmission time (in ms) */
#endif
#if GNRC_IPV6_NIB_CONF_ROUTER || defined(DOXYGEN)
/**
* @brief timestamp in milliseconds of last unsolicited router
* advertisement
*
* @note Only available if @ref GNRC_IPV6_NIB_CONF_ROUTER.
*/
uint32_t last_ra;
#endif
kernel_pid_t pid; /**< identifier of the interface */
#if GNRC_IPV6_NIB_CONF_ROUTER || defined(DOXYGEN)
/**
* @brief number of unsolicited router advertisements sent
*
* This only counts up to the first @ref NDP_MAX_INIT_RA_NUMOF on interface
* initialization. The last @ref NDP_MAX_FIN_RA_NUMOF of an advertising
* interface are counted from UINT8_MAX - @ref NDP_MAX_FIN_RA_NUMOF + 1.
*
* @note Only available if @ref GNRC_IPV6_NIB_CONF_ROUTER.
*/
uint8_t ra_sent;
#endif
/**
* @brief number of unsolicited router solicitations scheduled
*/
uint8_t rs_sent;
/**
* @brief number of unsolicited neighbor advertisements scheduled
*/
uint8_t na_sent;
} _nib_iface_t;
/**
* @brief Mutex for locking the NIB
*/
extern mutex_t _nib_mutex;
/**
* @brief Initializes NIB internally
*/
void _nib_init(void);
/**
* @brief Gets interface identifier from a NIB entry
*
* @param[in] node A NIB entry.
*
* @return The NIB entry's interface identifier.
*/
static inline unsigned _nib_onl_get_if(const _nib_onl_entry_t *node)
{
return (node->info & _NIB_IF_MASK) >> _NIB_IF_POS;
}
/**
* @brief Sets interface for a NIB entry
*
* @param[in,out] node A NIB entry.
* @param[in] iface An interface identifier.
*/
static inline void _nib_onl_set_if(_nib_onl_entry_t *node, unsigned iface)
{
assert(iface <= _NIB_IF_MAX);
node->info &= ~(_NIB_IF_MASK);
node->info |= ((iface << _NIB_IF_POS) & _NIB_IF_MASK);
}
/**
* @brief Creates or gets an existing on-link entry by address
*
* @pre `(addr != NULL)`.
*
* @param[in] addr An IPv6 address. May not be NULL.
* *May also be a global address!*
* @param[in] iface The interface to the node.
*
* @return A new or existing on-link entry with _nib_onl_entry_t::ipv6 set to
* @p addr.
* @return NULL, if no space is left.
*/
_nib_onl_entry_t *_nib_onl_alloc(const ipv6_addr_t *addr, unsigned iface);
/**
* @brief Clears out a NIB entry (on-link version)
*
* @param[in,out] node An entry.
*
* @return true, if entry was cleared.
* @return false, if entry was not cleared.
*/
static inline bool _nib_onl_clear(_nib_onl_entry_t *node)
{
if (node->mode == _EMPTY) {
memset(node, 0, sizeof(_nib_onl_entry_t));
return true;
}
return false;
}
/**
* @brief Iterates over on-link entries
*
* @param[in] last Last entry (NULL to start).
*
* @return entry after @p last.
*/
_nib_onl_entry_t *_nib_onl_iter(const _nib_onl_entry_t *last);
/**
* @brief Gets a node by IPv6 address and interface
*
* @pre `(addr != NULL)`
*
* @param[in] addr The address of a node. May not be NULL.
* @param[in] iface The interface to the node. May be 0 for any interface.
*
* @return The NIB entry for node with @p addr and @p iface on success.
* @return NULL, if there is no such entry.
*/
_nib_onl_entry_t *_nib_onl_get(const ipv6_addr_t *addr, unsigned iface);
/**
* @brief Creates or gets an existing node from the neighbor cache by address
*
* @pre `(addr != NULL)`
* @pre `((cstate & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK) !=
* GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY)`
* @pre `((cstate & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK) !=
* GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE)`
* @pre `((cstate & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK) !=
* GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE)`
*
* @param[in] addr The address of a node. May not be NULL.
* @param[in] iface The interface to the node.
* @param[in] cstate Creation state. State of the entry *if* the entry is
* newly created.
*
* @return The NIB entry for the new neighbor cache entry on success.
* @return NULL, if there is no space left.
*/
_nib_onl_entry_t *_nib_nc_add(const ipv6_addr_t *addr, unsigned iface,
uint16_t cstate);
/**
* @brief Removes a node from the neighbor cache
*
* @param[in,out] node A node.
*/
void _nib_nc_remove(_nib_onl_entry_t *node);
/**
* @brief Sets a NUD-managed neighbor cache entry to reachable and sets the
* respective event in @ref _nib_evtimer "event timer"
* (@ref GNRC_IPV6_NIB_MSG_NUD_SET_STALE)
*
* @param[in,out] node A node.
*/
void _nib_nc_set_reachable(_nib_onl_entry_t *node);
/**
* @brief Creates or gets an existing node from the DAD table by address
*
* @pre `addr != NULL`
*
* @param[in] addr The address of a node. May not be NULL.
*
* @return The NIB entry for the new DAD table entry on success.
* @return NULL, if there is no space left.
*/
static inline _nib_onl_entry_t *_nib_dad_add(const ipv6_addr_t *addr)
{
_nib_onl_entry_t *node = _nib_onl_alloc(addr, 0);
if (node != NULL) {
node->mode |= (_DAD);
}
return node;
}
/**
* @brief Removes a node from the DAD table
*
* @param[in] node A node.
*/
static inline void _nib_dad_remove(_nib_onl_entry_t *node)
{
node->mode &= ~(_DAD);
_nib_onl_clear(node);
}
/**
* @brief Creates or gets an existing default router list entry by address
*
* @pre `(router_addr != NULL)`
*
* @param[in] addr An IPv6 address. May not be NULL.
* *May also be a global address!*
* @param[in] iface The interface to the router.
*
* @return A new or existing default router entry with _nib_onl_entry_t::ipv6
* of _nib_dr_entry_t::next_hop set to @p router_addr.
* @return NULL, if no space is left.
*/
_nib_dr_entry_t *_nib_drl_add(const ipv6_addr_t *router_addr, unsigned iface);
/**
* @brief Removes a default router list entry
*
* @param[in,out] nib_dr An entry.
*
* Corresponding on-link entry is removed, too.
*/
void _nib_drl_remove(_nib_dr_entry_t *nib_dr);
/**
* @brief Iterates over default router list
*
* @param[in] last Last entry (NULL to start).
*
* @return entry after @p last.
*/
_nib_dr_entry_t *_nib_drl_iter(const _nib_dr_entry_t *last);
/**
* @brief Gets a default router by IPv6 address and interface
*
* @pre `(router_addr != NULL)`
*
* @param[in] router_addr The address of a default router. May not be NULL.
* @param[in] iface The interface to the node. May be 0 for any
* interface.
*
* @return The NIB entry for node with @p router_addr and @p iface onsuccess.
* @return NULL, if there is no such entry.
*/
_nib_dr_entry_t *_nib_drl_get(const ipv6_addr_t *router_addr, unsigned iface);
/**
* @brief Gets *the* default router
*
* @see [RFC 4861, section 6.3.6](https://tools.ietf.org/html/rfc4861#section-6.3.6)
*
* @return The current default router, on success.
* @return NULL, if there is no default router in the list.
*/
_nib_dr_entry_t *_nib_drl_get_dr(void);
/**
* @brief Creates or gets an existing off-link entry by next hop and prefix
*
* @pre `(next_hop != NULL)`
* @pre `(pfx != NULL) && (pfx != "::") && (pfx_len != 0) && (pfx_len <= 128)`
*
* @param[in] next_hop An IPv6 address to next hop. May not be NULL.
* *May also be a global address!*
* @param[in] iface The interface to @p next_hop.
* @param[in] pfx The IPv6 prefix or address of the destination.
* May not be NULL or unspecified address. Use
* @ref _nib_drl_add() for default route destinations.
* @param[in] pfx_len The length in bits of @p pfx in bits.
*
* @return A new or existing off-link entry with _nib_dr_entry_t::pfx set to
* @p pfx.
* @return NULL, if no space is left.
*/
_nib_offl_entry_t *_nib_dst_alloc(const ipv6_addr_t *next_hop, unsigned iface,
const ipv6_addr_t *pfx, unsigned pfx_len);
/**
* @brief Clears out a NIB entry (off-link version)
*
* @param[in,out] dst An entry.
*/
void _nib_dst_clear(_nib_offl_entry_t *dst);
/**
* @brief Iterates over off-link entries
*
* @param[in] last Last entry (NULL to start).
*
* @return entry after @p last.
*/
_nib_offl_entry_t *_nib_dst_iter(const _nib_offl_entry_t *last);
/**
* @brief Helper function for view-level add-functions below
*
* @pre `(next_hop != NULL)`
* @pre `(pfx != NULL) && (pfx != "::") && (pfx_len != 0) && (pfx_len <= 128)`
*
* @param[in] next_hop Next hop to the destination. May not be NULL.
* *May also be a global address!*
* @param[in] iface The interface to the destination.
* @param[in] pfx The IPv6 prefix or address of the destination.
* May not be NULL or unspecified address. Use
* @ref _nib_drl_add() for default route destinations.
* @param[in] pfx_len The length in bits of @p pfx in bits.
* @param[in] mode [NIB-mode](_nib_onl_entry_t::mode).
*
* @return A new or existing off-link entry with _nib_dr_entry_t::pfx set to
* @p pfx.
* @return NULL, if no space is left.
*/
static inline _nib_offl_entry_t *_nib_dst_add(const ipv6_addr_t *next_hop,
unsigned iface,
const ipv6_addr_t *pfx,
unsigned pfx_len, uint8_t mode)
{
_nib_offl_entry_t *nib_offl = _nib_dst_alloc(next_hop, iface, pfx, pfx_len);
if (nib_offl != NULL) {
nib_offl->next_hop->mode |= (mode);
}
return nib_offl;
}
/**
* @brief Helper function for the view-level remove-functions below
*
* @param[in,out] nib_offl An entry.
*/