diff --git a/Makefile.dep b/Makefile.dep index 45958925f611e0e9e7cbcfbf233d6a5a53f8414f..63e48af89a76e219163f2893986ceb2e918c724f 100644 --- a/Makefile.dep +++ b/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 diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h new file mode 100644 index 0000000000000000000000000000000000000000..f132517a4ffbff7e0cf2c750aad823a949f01337 --- /dev/null +++ b/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 */ +/** @} */ diff --git a/sys/include/net/gnrc/ipv6/nib/conf.h b/sys/include/net/gnrc/ipv6/nib/conf.h new file mode 100644 index 0000000000000000000000000000000000000000..0368a695c80f1dedbfd80bc77620669ad2e95f1c --- /dev/null +++ b/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 */ +/** @} */ diff --git a/sys/include/net/gnrc/ipv6/nib/nc.h b/sys/include/net/gnrc/ipv6/nib/nc.h new file mode 100644 index 0000000000000000000000000000000000000000..fd6144fd792bf2dd94853cb14b4edf9e34325004 --- /dev/null +++ b/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 */ +/** @} */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 05798bbbe1882439dabdaccea7029b6a3ccff328..e03f6f26d661acd1e07dfeec6d223c68ca55c8c9 100644 --- a/sys/net/gnrc/Makefile +++ b/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 diff --git a/sys/net/gnrc/network_layer/ipv6/nib/Makefile b/sys/net/gnrc/network_layer/ipv6/nib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..600c639297f5286d05021b118e17cf00e219d151 --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_ipv6_nib + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c new file mode 100644 index 0000000000000000000000000000000000000000..0a316bb5adef61588bfd2fcda4b2c34238ee9c8e --- /dev/null +++ b/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; + } +} + +/** @} */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h new file mode 100644 index 0000000000000000000000000000000000000000..980565cc1466822e865b4fc52ab512d0b4837097 --- /dev/null +++ b/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. + */ +static inline void _nib_dst_remove(_nib_offl_entry_t *nib_offl, uint8_t mode) +{ + _nib_onl_entry_t *node = nib_offl->next_hop; + + if (node != NULL) { + node->mode &= ~mode; + } + _nib_dst_clear(nib_offl); +} + +#if defined(GNRC_IPV6_NIB_CONF_DC) || DOXYGEN +/** + * @brief Creates or gets an existing destination cache entry by its addresses + * + * @pre `(next_hop != NULL)` + * @pre `(dst != NULL)` + * + * @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. + * + * @note Only available if @ref GNRC_IPV6_NIB_CONF_DC. + * + * @return A new or existing destination cache entry with + * _nib_onl_entry_t::ipv6 of _nib_dr_entry_t::next_hop set to + * @p next_hop. + * @return NULL, if no space is left. + */ +static inline _nib_offl_entry_t *_nib_dc_add(const ipv6_addr_t *next_hop, + unsigned iface, + const ipv6_addr_t *dst) +{ + return _nib_dst_add(next_hop, iface, dst, IPV6_ADDR_BIT_LEN, _DC); +} + +/** + * @brief Removes a destination cache entry + * + * @param[in,out] nib_dr An entry. + * + * Corresponding on-link entry is removed, too. + * + * @note Only available if @ref GNRC_IPV6_NIB_CONF_DC. + */ +static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) +{ + _nib_dst_remove(nib_offl, _DC); +} +#endif /* GNRC_IPV6_NIB_CONF_DC */ + +/** + * @brief Creates or gets an existing prefix list entry by its prefix + * + * @pre `(next_hop != NULL)` + * @pre `(pfx != NULL) && (pfx != "::") && (pfx_len != 0) && (pfx_len <= 128)` + * + * @param[in] iface The interface to the prefix is added to. + * @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 prefix list 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_pl_add(unsigned iface, + const ipv6_addr_t *pfx, + unsigned pfx_len) +{ + return _nib_dst_add(NULL, iface, pfx, pfx_len, _PL); +} + +/** + * @brief Removes a prefix list entry + * + * @param[in,out] nib_offl An entry. + * + * Corresponding on-link entry is removed, too. + */ +static inline void _nib_pl_remove(_nib_offl_entry_t *nib_offl) +{ + _nib_dst_remove(nib_offl, _PL); +} + +#if defined(GNRC_IPV6_NIB_CONF_ROUTER) || DOXYGEN +/** + * @brief Creates or gets an existing forwarding table entry by its prefix + * + * @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. + * + * @note Only available if @ref GNRC_IPV6_NIB_CONF_ROUTER. + * + * @return A new or existing forwarding table 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_ft_add(const ipv6_addr_t *next_hop, + unsigned iface, + const ipv6_addr_t *pfx, + unsigned pfx_len) +{ + return _nib_dst_add(next_hop, iface, pfx, pfx_len, _FT); +} + +/** + * @brief Removes a forwarding table entry + * + * @param[in,out] nib_offl An entry. + * + * Corresponding on-link entry is removed, too. + * + * @note Only available if @ref GNRC_IPV6_NIB_CONF_ROUTER. + */ +static inline void _nib_ft_remove(_nib_offl_entry_t *nib_offl) +{ + _nib_dst_remove(nib_offl, _FT); +} +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ + +/** + * @brief Gets (or creates if it not exists) interface information for + * neighbor discovery + * + * @pre `(iface <= _NIB_IF_MAX)` + * + * @param[in] iface Interface identifier to get information for. + * + * @return Interface information on @p iface. + * @return NULL, if no space left for interface. + */ +_nib_iface_t *_nib_iface_get(unsigned iface); + +#ifdef __cplusplus +} +#endif + +#endif /* PRIV_NIB_INTERNAL_H */ +/** @} */ diff --git a/tests/unittests/tests-gnrc_ipv6_nib/Makefile b/tests/unittests/tests-gnrc_ipv6_nib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2 --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include b/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include new file mode 100644 index 0000000000000000000000000000000000000000..ccae089522020a367e728a7b0e5ab6a6ee0d196c --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include @@ -0,0 +1,6 @@ +USEMODULE += gnrc_ipv6_nib + +CFLAGS += -DGNRC_IPV6_NIB_NUMOF=16 +CFLAGS += -DGNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF=4 + +INCLUDES += -I$(RIOTBASE)/sys/net/gnrc/network_layer/ipv6/nib diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c new file mode 100644 index 0000000000000000000000000000000000000000..622c59d8381ee4326dd0dcdb05d6a4658324f4a4 --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c @@ -0,0 +1,1060 @@ +/* + * 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 <inttypes.h> + +#include "net/ipv6/addr.h" +#include "net/ndp.h" +#include "net/gnrc/ipv6/nib/conf.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/netif.h" + +#include "_nib-internal.h" + +#include "unittests-constants.h" + +#include "tests-gnrc_ipv6_nib.h" + +#define LINK_LOCAL_PREFIX { 0xfe, 0x08, 0, 0, 0, 0, 0, 0 } +#define GLOBAL_PREFIX { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0 } +#define IFACE (26) + +static void set_up(void) +{ + _nib_init(); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF persistent entries with different IP addresses + * and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_alloc__no_space_left_diff_addr(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + node->mode |= _DRL; + addr.u64[1].u64++; + } + TEST_ASSERT_NULL(_nib_onl_alloc(&addr, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF persistent entries with different interface + * identifiers and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_alloc__no_space_left_diff_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, iface))); + node->mode |= _DAD; + iface++; + } + TEST_ASSERT_NULL(_nib_onl_alloc(&addr, iface)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF persistent entries with different IP addresses + * and interface identifiers and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_alloc__no_space_left_diff_addr_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, iface))); + node->mode |= _DC; + addr.u64[1].u64++; + iface++; + } + TEST_ASSERT_NULL(_nib_onl_alloc(&addr, iface)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF persistent entries with different IP addresses + * and interface identifiers and then tries to add another that is equal to the + * last. + * Expected result: should return not NULL (the last) + */ +static void test_nib_alloc__success_duplicate(void) +{ + _nib_onl_entry_t *node; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + addr.u64[1].u64++; + iface++; + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, iface))); + node->mode |= _PL; + } + TEST_ASSERT(node == _nib_onl_alloc(&addr, iface)); +} + +/* + * Creates an non-persistent entry. + * Expected result: new entry should contain the given address and interface + */ +static void test_nib_alloc__success(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + TEST_ASSERT(ipv6_addr_equal(&addr, &node->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(node)); +} + +/* + * Creates an persistent entry and tries to clear it. + * Expected result: _nib_onl_clear returns false and entry should still be first + */ +static void test_nib_clear__persistent(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + node->mode |= _DRL; + TEST_ASSERT(!_nib_onl_clear(node)); + TEST_ASSERT(node == _nib_onl_iter(NULL)); +} + +/* + * Creates a non-persistent entry with all other flags set. + * Expected result: _nib_onl_clear returns false and entry should still be first + */ +static void test_nib_clear__non_persistent_but_content(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + node->mode |= ~(_FT); + TEST_ASSERT(!_nib_onl_clear(node)); + TEST_ASSERT(node == _nib_onl_iter(NULL)); +} + +/* + * Creates a non-persistent entry. + * Expected result: _nib_onl_clear returns true and entry is cleared + */ +static void test_nib_clear__empty(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + TEST_ASSERT(ipv6_addr_equal(&addr, &node->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(node)); + TEST_ASSERT(_nib_onl_clear(node)); + TEST_ASSERT_NULL(_nib_onl_iter(NULL)); + TEST_ASSERT(ipv6_addr_is_unspecified(&node->ipv6)); + TEST_ASSERT_EQUAL_INT(0, _nib_onl_get_if(node)); + +} + +/* + * Iterates over empty NIB + * Expected result: _nib_onl_iter returns NULL + */ +static void test_nib_iter__empty(void) +{ + TEST_ASSERT_NULL(_nib_onl_iter(NULL)); +} + +/* + * Iterates over NIB with one element + * Expected result: _nib_onl_iter returns element with NULL, and with that element + * NULL. + */ +static void test_nib_iter__one_elem(void) +{ + _nib_onl_entry_t *node, *res; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + node->mode = _FT; + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(NULL))); + TEST_ASSERT(res == node); + TEST_ASSERT_NULL(_nib_onl_iter(res)); +} + +/* + * Iterates over NIB with two element + * Expected result: _nib_onl_iter returns element with NULL, with that element + * another, and with the last NULL. + */ +static void test_nib_iter__two_elem(void) +{ + _nib_onl_entry_t *node1, *node2, *res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_onl_alloc(&addr, IFACE))); + node1->mode = _DC; + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node2 = _nib_onl_alloc(&addr, IFACE))); + node2->mode = _PL; + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(NULL))); + TEST_ASSERT(res == node1); + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(res))); + TEST_ASSERT(res == node2); + TEST_ASSERT_NULL(_nib_onl_iter(res)); +} + +/* + * Iterates over NIB with three element + * Expected result: _nib_onl_iter returns element with NULL, with that element + * another, with that element yet another and with the last NULL. + */ +static void test_nib_iter__three_elem(void) +{ + _nib_onl_entry_t *node1, *node2, *node3, *res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_onl_alloc(&addr, IFACE))); + node1->mode = _DAD; + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node2 = _nib_onl_alloc(&addr, IFACE))); + node2->mode = _DRL; + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node3 = _nib_onl_alloc(&addr, IFACE))); + node3->mode = _FT; + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(NULL))); + TEST_ASSERT(res == node1); + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(res))); + TEST_ASSERT(res == node2); + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(res))); + TEST_ASSERT(res == node3); + TEST_ASSERT_NULL(_nib_onl_iter(res)); +} + +/* + * Iterates over NIB with three element + * Expected result: _nib_onl_iter returns element with NULL, with that element + * another, with that element yet another and with the last NULL. + */ +static void test_nib_iter__three_elem_middle_removed(void) +{ + _nib_onl_entry_t *node1, *node2, *node3, *res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_onl_alloc(&addr, IFACE))); + node1->mode = _PL; + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node2 = _nib_onl_alloc(&addr, IFACE))); + node2->mode = _FT; + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node3 = _nib_onl_alloc(&addr, IFACE))); + node3->mode = _DRL; + node2->mode = _EMPTY; + TEST_ASSERT(_nib_onl_clear(node2)); + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(NULL))); + TEST_ASSERT(res == node1); + TEST_ASSERT_NOT_NULL((res = _nib_onl_iter(res))); + TEST_ASSERT(res == node3); + TEST_ASSERT_NULL(_nib_onl_iter(res)); +} + +/* + * Tries to get a NIB entry from an empty NIB. + * Expected result: _nib_onl_get() returns NULL + */ +static void test_nib_get__not_in_nib(void) +{ + _nib_onl_entry_t *nib_alloced; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((nib_alloced = _nib_onl_alloc(&addr, IFACE))); + nib_alloced->mode = _FT; + addr.u64[1].u64++; + TEST_ASSERT_NULL(_nib_onl_get(&addr, IFACE)); +} + +/* + * Tries to get a NIB entry from an empty NIB. + * Expected result: _nib_onl_get() returns NULL + */ +static void test_nib_get__success(void) +{ + _nib_onl_entry_t *nib_alloced, *nib_got; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((nib_alloced = _nib_onl_alloc(&addr, IFACE))); + nib_alloced->mode = _NC; + TEST_ASSERT_NOT_NULL((nib_got = _nib_onl_get(&addr, IFACE))); + TEST_ASSERT(nib_alloced == nib_got); +} + +/* + * Tries to get a NIB entry that is not in the NIB. + * Expected result: _nib_onl_get() returns NULL + */ +static void test_nib_get__empty(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NULL(_nib_onl_get(&addr, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different IP + * addresses and a non-garbage-collectible AR state and then tries to add + * another. + * Expected result: should return NULL + */ +static void test_nib_nc_add__no_space_left_diff_addr(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE))); + node->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED; + addr.u64[1].u64++; + } + TEST_ASSERT_NULL(_nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different interface + * identifiers and a non-garbage-collectible AR state and then tries to add + * another. + * Expected result: should return NULL + */ +static void test_nib_nc_add__no_space_left_diff_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, iface, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE))); + node->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_TENTATIVE; + iface++; + } + TEST_ASSERT_NULL(_nib_nc_add(&addr, iface, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different IP + * addresses and interface identifiers and a non-garbage-collectible AR state + * and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_nc_add__no_space_left_diff_addr_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, iface, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED))); + node->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED; + addr.u64[1].u64++; + iface++; + } + TEST_ASSERT_NULL(_nib_nc_add(&addr, iface, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different IP + * addresses and interface identifiers and a non-garbage-collectible AR state + * and then tries to add another that is equal to the last. + * Expected result: should return not NULL (the last) + */ +static void test_nib_nc_add__success_duplicate(void) +{ + _nib_onl_entry_t *node; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 0; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + addr.u64[1].u64++; + iface++; + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, iface, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE))); + node->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED; + } + TEST_ASSERT(node == _nib_nc_add(&addr, iface, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE)); +} + +/* + * Creates an neighbor cache entry. + * Expected result: new entry should contain the given address and interface + */ +static void test_nib_nc_add__success(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE))); + TEST_ASSERT(node->mode & _NC); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE, + (node->info & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK)); + TEST_ASSERT(ipv6_addr_equal(&addr, &node->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(node)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with differnt IP address. + * Expected result: new entries should still be able to be created and further + * should be different than the previous created ones + */ +static void test_nib_nc_add__success_full_but_garbage_collectible(void) +{ + _nib_onl_entry_t *last = NULL, *node; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE))); + TEST_ASSERT(last != node); + addr.u64[1].u64++; + last = node; + } + TEST_ASSERT_NOT_NULL((last = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED))); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE))); + TEST_ASSERT(last != node); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE))); + TEST_ASSERT(last != node); +} + +/* + * Creates a neighbor cache entry and sets it reachable + * Expected result: node->info flags set to NUD_STATE_REACHABLE and NIB's event + * timer contains a GNRC_IPV6_NIB_MSG_NUD_SET_STALE event + */ +static void test_nib_nc_set_reachable__success(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE))); + _nib_nc_set_reachable(node); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE, + (node->info & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK)); + /* check existence of event in event timer first */ + /* TODO: check NIB's event timer */ +} + +/* + * Creates a neighbor cache entry, sets another flag, and tries to remove it. + * Expected result: The entry should still exist + */ +static void test_nib_nc_remove__uncleared(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE))); + node->mode |= _DC; + _nib_nc_remove(node); + TEST_ASSERT(node == _nib_onl_iter(NULL)); +} + +/* + * Creates a neighbor cache entry and tries to remove it. + * Expected result: The NIB should be empty + */ +static void test_nib_nc_remove__cleared(void) +{ + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE))); + _nib_nc_remove(node); + TEST_ASSERT_NULL(_nib_onl_iter(NULL)); +} + +/* + * Creates GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF default router list entries with + * different IP addresses and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_drl_add__no_space_left_diff_addr(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (int i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) { + TEST_ASSERT_NOT_NULL(_nib_drl_add(&addr, IFACE)); + addr.u64[1].u64++; + } + TEST_ASSERT_NULL(_nib_drl_add(&addr, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF persistent enties entries with + * different IP addresses and then tries to add a default router list entry with + * yet another address. + * Expected result: should return NULL + */ +static void test_nib_drl_add__no_space_left_nib_full(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (int i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + _nib_onl_entry_t *node; + + TEST_ASSERT_NOT_NULL((node = _nib_onl_alloc(&addr, IFACE))); + node->mode |= _PL; + addr.u64[1].u64++; + } + TEST_ASSERT_NULL(_nib_drl_add(&addr, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF default router list entries with + * different interface identifiers and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_drl_add__no_space_left_diff_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) { + TEST_ASSERT_NOT_NULL(_nib_drl_add(&addr, iface)); + iface++; + } + TEST_ASSERT_NULL(_nib_drl_add(&addr, iface)); +} + +/* + * Creates GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF default router list entries with + * different IP addresses and interface identifiers and then tries to add + * another. + * Expected result: should return NULL + */ +static void test_nib_drl_add__no_space_left_diff_addr_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) { + TEST_ASSERT_NOT_NULL(_nib_drl_add(&addr, iface)); + addr.u64[1].u64++; + iface++; + } + TEST_ASSERT_NULL(_nib_drl_add(&addr, iface)); +} + +/* + * Creates GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF default router list entries with + * different IP addresses and interface identifiers and then tries to add + * another that is equal to the last. + * Expected result: should return not NULL (the last) + */ +static void test_nib_drl_add__success_duplicate(void) +{ + _nib_dr_entry_t *nib_dr; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = 1; + + for (int i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) { + addr.u64[1].u64++; + iface++; + TEST_ASSERT_NOT_NULL((nib_dr = _nib_drl_add(&addr, iface))); + } + TEST_ASSERT(nib_dr == _nib_drl_add(&addr, iface)); +} + +/* + * Creates an default router list entry. + * Expected result: new entry should contain the given address and interface + */ +static void test_nib_drl_add__success(void) +{ + _nib_dr_entry_t *nib_dr; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((nib_dr = _nib_drl_add(&addr, IFACE))); + TEST_ASSERT_NOT_NULL(nib_dr->next_hop); + TEST_ASSERT(nib_dr->next_hop == _nib_onl_iter(NULL)); + TEST_ASSERT(nib_dr->next_hop->mode & (_DRL)); + TEST_ASSERT(ipv6_addr_equal(&addr, &nib_dr->next_hop->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(nib_dr->next_hop)); +} + +/* + * Creates a default router list entry, sets another flag, and tries to remove + * it. + * Expected result: The entry default router list entry is removed, but the + * NIB entry should still exist + */ +static void test_nib_drl_remove__uncleared(void) +{ + _nib_dr_entry_t *nib_dr; + _nib_onl_entry_t *node; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((nib_dr = _nib_drl_add(&addr, IFACE))); + node = nib_dr->next_hop; + node->mode |= _NC; + _nib_drl_remove(nib_dr); + TEST_ASSERT_NULL(_nib_drl_iter(NULL)); + TEST_ASSERT(node == _nib_onl_iter(NULL)); +} + +/* + * Creates a default router list entry and tries to remove it. + * Expected result: The NIB should be empty + */ +static void test_nib_drl_remove__cleared(void) +{ + _nib_dr_entry_t *nib_dr; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((nib_dr = _nib_drl_add(&addr, IFACE))); + _nib_drl_remove(nib_dr); + TEST_ASSERT_NULL(_nib_drl_iter(NULL)); + TEST_ASSERT_NULL(_nib_onl_iter(NULL)); +} + +/* + * Iterates over empty default router list + * Expected result: _nib_drl_iter returns NULL + */ +static void test_nib_drl_iter__empty(void) +{ + TEST_ASSERT_NULL(_nib_drl_iter(NULL)); +} + +/* + * Iterates over default router list with one element + * Expected result: _nib_drl_iter returns element with NULL, and with that + * element NULL. + */ +static void test_nib_drl_iter__one_elem(void) +{ + _nib_dr_entry_t *node, *res; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_drl_add(&addr, IFACE))); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(NULL))); + TEST_ASSERT(res == node); + TEST_ASSERT_NULL(_nib_drl_iter(res)); +} + +/* + * Iterates over default router list with two element + * Expected result: _nib_drl_iter returns element with NULL, with that element + * another, and with the last NULL. + */ +static void test_nib_drl_iter__two_elem(void) +{ + _nib_dr_entry_t *node1, *node2, *res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(NULL))); + TEST_ASSERT(res == node1); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(res))); + TEST_ASSERT(res == node2); + TEST_ASSERT_NULL(_nib_drl_iter(res)); +} + +/* + * Iterates over default router list with three element + * Expected result: _nib_drl_iter returns element with NULL, with that element + * another, with that element yet another and with the last NULL. + */ +static void test_nib_drl_iter__three_elem(void) +{ + _nib_dr_entry_t *node1, *node2, *node3, *res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node3 = _nib_drl_add(&addr, IFACE))); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(NULL))); + TEST_ASSERT(res == node1); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(res))); + TEST_ASSERT(res == node2); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(res))); + TEST_ASSERT(res == node3); + TEST_ASSERT_NULL(_nib_drl_iter(res)); +} + +/* + * Iterates over default router list with three element + * Expected result: _nib_drl_iter returns element with NULL, with that element + * another, with that element yet another and with the last NULL. + */ +static void test_nib_drl_iter__three_elem_middle_removed(void) +{ + _nib_dr_entry_t *node1, *node2, *node3, *res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + TEST_ASSERT_NOT_NULL((node3 = _nib_drl_add(&addr, IFACE))); + node2->next_hop->mode = _EMPTY; + _nib_drl_remove(node2); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(NULL))); + TEST_ASSERT(res == node1); + TEST_ASSERT_NOT_NULL((res = _nib_drl_iter(res))); + TEST_ASSERT(res == node3); + TEST_ASSERT_NULL(_nib_drl_iter(res)); +} + +/* + * Tries to get a default router list entry from an empty NIB. + * Expected result: _nib_drl_get() returns NULL + */ +static void test_nib_drl_get__not_in_nib(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL(_nib_drl_add(&addr, IFACE)); + addr.u64[1].u64++; + TEST_ASSERT_NULL(_nib_drl_get(&addr, IFACE)); +} + +/* + * Tries to get a default router list entry + * Expected result: _nib_drl_get() returns + */ +static void test_nib_drl_get__success(void) +{ + _nib_dr_entry_t *nib_alloced, *nib_got; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((nib_alloced = _nib_drl_add(&addr, IFACE))); + TEST_ASSERT_NOT_NULL((nib_got = _nib_drl_get(&addr, IFACE))); + TEST_ASSERT(nib_alloced == nib_got); +} + +/* + * Tries to get the default router from an empty default router list + * Expected result: _nib_drl_get_dr() returns NULL + */ +static void test_nib_drl_get_dr__empty(void) +{ + TEST_ASSERT_NULL(_nib_drl_get_dr()); +} + +/* + * Tries to get the default router from a list of one unreachable routers + * Expected result: _nib_drl_get_dr() returns always the one unreachable router + */ +static void test_nib_drl_get_dr__round_robin1(void) +{ + _nib_dr_entry_t *node, *nib_res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node = _nib_drl_add(&addr, IFACE))); + node->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node); +} + +/* + * Tries to get the default router from a list of two unreachable routers + * Expected result: _nib_drl_get_dr() returns one router first, then the other, + * then the first again, etc. + */ +static void test_nib_drl_get_dr__round_robin2(void) +{ + _nib_dr_entry_t *node1, *node2, *nib_res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + node1->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + node2->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node1); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node2); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node1); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node2); +} + +/* + * Tries to get the default router from a list of two routers + * the first reachable, the second unreachable + * Expected result: _nib_drl_get_dr() always returns the first router + */ +static void test_nib_drl_get_dr__success1(void) +{ + _nib_dr_entry_t *node1, *node2, *nib_res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + node1->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + node2->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node1); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node1); +} + +/* + * Tries to get the default router from a list of three routers + * the first two unreachable, the third reachable + * Expected result: _nib_drl_get_dr() always returns the third router + */ +static void test_nib_drl_get_dr__success2(void) +{ + _nib_dr_entry_t *node1, *node2, *node3, *nib_res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + node1->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + node2->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE; + TEST_ASSERT_NOT_NULL((node3 = _nib_drl_add(&addr, IFACE))); + node3->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node3); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node3); +} + +/* + * Tries to get the default router from a list of three routers + * the first and the third unreachable, the second reachable. Afterwards, the + * second becomes unreachable + * Expected result: _nib_drl_get_dr() at first always returns the second router + * but continues to round robin as soon as the second router becomes + * unreachable. + */ +static void test_nib_drl_get_dr__success3(void) +{ + _nib_dr_entry_t *node1, *node2, *node3, *nib_res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + node1->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + node2->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE; + TEST_ASSERT_NOT_NULL((node3 = _nib_drl_add(&addr, IFACE))); + node3->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node2); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node2); + node2->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res != node2); +} + +/* + * Tries to get the default router from a list of three routers + * the first and the third unreachable, the second reachable. Afterwards, the + * second is deleted + * Expected result: _nib_drl_get_dr() at first always returns the second router + * but continues to round robin as soon as the second router is deleted. + */ +static void test_nib_drl_get_dr__success4(void) +{ + _nib_dr_entry_t *node1, *node2, *node3, *nib_res; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_drl_add(&addr, IFACE))); + addr.u64[1].u64++; + node1->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE; + TEST_ASSERT_NOT_NULL((node2 = _nib_drl_add(&addr, IFACE))); + node2->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE; + TEST_ASSERT_NOT_NULL((node3 = _nib_drl_add(&addr, IFACE))); + node3->next_hop->info = GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE; + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node2); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res == node2); + _nib_drl_remove(node2); + TEST_ASSERT_NOT_NULL((nib_res = _nib_drl_get_dr())); + TEST_ASSERT(nib_res != node2); +} + +/* + * Creates GNRC_NETIF_NUMOF interfaces and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_iface_get__no_space_left(void) +{ + unsigned iface = 1; + + for (int i = 0; i < GNRC_NETIF_NUMOF; i++) { + TEST_ASSERT_NOT_NULL(_nib_iface_get(iface++)); + } + TEST_ASSERT_NULL(_nib_iface_get(iface)); +} + +/* + * Creates an interface and then gets the same interface. + * Expected result: interface pointers should equal + */ +static void test_nib_iface_get__success(void) +{ + _nib_iface_t *ni1, *ni2; + + TEST_ASSERT_NOT_NULL((ni1 = _nib_iface_get(IFACE))); + TEST_ASSERT_NOT_NULL((ni2 = _nib_iface_get(IFACE))); + TEST_ASSERT(ni1 == ni2); +} + +Test *tests_gnrc_ipv6_nib_internal_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_nib_alloc__no_space_left_diff_addr), + new_TestFixture(test_nib_alloc__no_space_left_diff_iface), + new_TestFixture(test_nib_alloc__no_space_left_diff_addr_iface), + new_TestFixture(test_nib_alloc__success_duplicate), + new_TestFixture(test_nib_alloc__success), + new_TestFixture(test_nib_clear__persistent), + new_TestFixture(test_nib_clear__non_persistent_but_content), + new_TestFixture(test_nib_clear__empty), + new_TestFixture(test_nib_iter__empty), + new_TestFixture(test_nib_iter__one_elem), + new_TestFixture(test_nib_iter__two_elem), + new_TestFixture(test_nib_iter__three_elem), + new_TestFixture(test_nib_iter__three_elem_middle_removed), + new_TestFixture(test_nib_get__empty), + new_TestFixture(test_nib_get__not_in_nib), + new_TestFixture(test_nib_get__success), + new_TestFixture(test_nib_nc_add__no_space_left_diff_addr), + new_TestFixture(test_nib_nc_add__no_space_left_diff_iface), + new_TestFixture(test_nib_nc_add__no_space_left_diff_addr_iface), + new_TestFixture(test_nib_nc_add__success_duplicate), + new_TestFixture(test_nib_nc_add__success), + new_TestFixture(test_nib_nc_add__success_full_but_garbage_collectible), + new_TestFixture(test_nib_nc_remove__uncleared), + new_TestFixture(test_nib_nc_remove__cleared), + new_TestFixture(test_nib_nc_set_reachable__success), + new_TestFixture(test_nib_drl_add__no_space_left_diff_addr), + new_TestFixture(test_nib_drl_add__no_space_left_diff_iface), + new_TestFixture(test_nib_drl_add__no_space_left_diff_addr_iface), + new_TestFixture(test_nib_drl_add__no_space_left_nib_full), + new_TestFixture(test_nib_drl_add__success_duplicate), + new_TestFixture(test_nib_drl_add__success), + new_TestFixture(test_nib_drl_remove__uncleared), + new_TestFixture(test_nib_drl_remove__cleared), + new_TestFixture(test_nib_drl_iter__empty), + new_TestFixture(test_nib_drl_iter__one_elem), + new_TestFixture(test_nib_drl_iter__two_elem), + new_TestFixture(test_nib_drl_iter__three_elem), + new_TestFixture(test_nib_drl_iter__three_elem_middle_removed), + new_TestFixture(test_nib_drl_get__not_in_nib), + new_TestFixture(test_nib_drl_get__success), + new_TestFixture(test_nib_drl_get_dr__empty), + new_TestFixture(test_nib_drl_get_dr__round_robin1), + new_TestFixture(test_nib_drl_get_dr__round_robin2), + new_TestFixture(test_nib_drl_get_dr__success1), + new_TestFixture(test_nib_drl_get_dr__success2), + new_TestFixture(test_nib_drl_get_dr__success3), + new_TestFixture(test_nib_drl_get_dr__success4), + new_TestFixture(test_nib_iface_get__no_space_left), + new_TestFixture(test_nib_iface_get__success), + }; + + EMB_UNIT_TESTCALLER(tests, set_up, NULL, + fixtures); + + return (Test *)&tests; +} diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c new file mode 100644 index 0000000000000000000000000000000000000000..c1c4f83815bc29fcaa1e99aeed8e3f8598a534c6 --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c @@ -0,0 +1,21 @@ +/* + * 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 "tests-gnrc_ipv6_nib.h" + +void tests_gnrc_ipv6_nib(void) +{ + TESTS_RUN(tests_gnrc_ipv6_nib_internal_tests()); +} diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h new file mode 100644 index 0000000000000000000000000000000000000000..1e3952b6a068d5df6d1d2cf1e365b1b9adf8bb3f --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h @@ -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. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``gnrc_ipv6_nib`` module + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef TESTS_GNRC_IPV6_NIB_H +#define TESTS_GNRC_IPV6_NIB_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_gnrc_ipv6_nib(void); + +/** + * @brief Generates tests for internal layer + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_gnrc_ipv6_nib_internal_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_GNRC_IPV6_NIB_H */ +/** @} */