diff --git a/sys/include/net/gnrc/ipv6/nib/conf.h b/sys/include/net/gnrc/ipv6/nib/conf.h index 2bd6fb3cee..adacc37908 100644 --- a/sys/include/net/gnrc/ipv6/nib/conf.h +++ b/sys/include/net/gnrc/ipv6/nib/conf.h @@ -184,6 +184,16 @@ extern "C" { #define GNRC_IPV6_NIB_NUMOF (4) #endif +/** + * @brief Number of off-link entries in NIB + * + * @attention This number is equal to the maximum number of forwarding table + * and prefix list entries in NIB + */ +#ifndef GNRC_IPV6_NIB_OFFL_NUMOF +#define GNRC_IPV6_NIB_OFFL_NUMOF (8) +#endif + #ifdef __cplusplus } #endif diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c index 15349b3af9..383fddfcfb 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -33,6 +33,7 @@ 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_offl_entry_t _dsts[GNRC_IPV6_NIB_OFFL_NUMOF]; static _nib_dr_entry_t _def_routers[GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF]; static _nib_iface_t _nis[GNRC_NETIF_NUMOF]; @@ -54,34 +55,42 @@ void _nib_init(void) _next_removable.next = NULL; memset(_nodes, 0, sizeof(_nodes)); memset(_def_routers, 0, sizeof(_def_routers)); + memset(_dsts, 0, sizeof(_dsts)); memset(_nis, 0, sizeof(_nis)); #endif evtimer_init_msg(&_nib_evtimer); /* TODO: load ABR information from persistent memory */ } +static inline bool _addr_equals(const ipv6_addr_t *addr, + const _nib_onl_entry_t *node) +{ + return (addr == NULL) || ipv6_addr_is_unspecified(&node->ipv6) || + (ipv6_addr_equal(addr, &node->ipv6)); +} + _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); + (addr == NULL) ? "NULL" : 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))) { + if ((_nib_onl_get_if(tmp) == iface) && _addr_equals(addr, tmp)) { /* exact match */ DEBUG(" %p is an exact match\n", (void *)tmp); - return tmp; + node = tmp; + break; } if ((node == NULL) && (tmp->mode == _EMPTY)) { + DEBUG(" using %p\n", (void *)node); node = tmp; } } if (node != NULL) { - DEBUG(" using %p\n", (void *)node); _override_node(addr, iface, node); } #if ENABLE_DEBUG @@ -144,6 +153,7 @@ static inline _nib_onl_entry_t *_cache_out_onl_entry(const ipv6_addr_t *addr, _nib_onl_entry_t *_nib_nc_add(const ipv6_addr_t *addr, unsigned iface, uint16_t cstate) { + assert(addr != NULL); 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); @@ -384,6 +394,95 @@ _nib_dr_entry_t *_nib_drl_get_dr(void) return _prime_def_router; } +_nib_offl_entry_t *_nib_offl_alloc(const ipv6_addr_t *next_hop, unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len) +{ + _nib_offl_entry_t *dst = NULL; + + assert((pfx != NULL) && (!ipv6_addr_is_unspecified(pfx)) && + (pfx_len > 0) && (pfx_len <= 128)); + DEBUG("nib: Allocating off-link-entry entry " + "(next_hop = %s, iface = %u, ", + (next_hop == NULL) ? "NULL" : ipv6_addr_to_str(addr_str, next_hop, + sizeof(addr_str)), + iface); + DEBUG("pfx = %s/%u)\n", ipv6_addr_to_str(addr_str, pfx, + sizeof(addr_str)), pfx_len); + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + _nib_offl_entry_t *tmp = &_dsts[i]; + _nib_onl_entry_t *tmp_node = tmp->next_hop; + + if ((tmp->pfx_len == pfx_len) && /* prefix length matches and */ + (tmp_node != NULL) && /* there is a next hop that */ + (_nib_onl_get_if(tmp_node) == iface) && /* has a matching interface and */ + _addr_equals(next_hop, tmp_node) && /* equal address to next_hop, also */ + (ipv6_addr_match_prefix(&tmp->pfx, pfx) >= pfx_len)) { /* the prefix matches */ + /* exact match (or next hop address was previously unset) */ + DEBUG(" %p is an exact match\n", (void *)tmp); + if (next_hop != NULL) { + memcpy(&tmp_node->ipv6, next_hop, sizeof(tmp_node->ipv6)); + } + tmp->next_hop->mode |= _DST; + return tmp; + } + if ((dst == NULL) && (tmp_node == NULL)) { + dst = tmp; + } + } + if (dst != NULL) { + DEBUG(" using %p\n", (void *)dst); + dst->next_hop = _nib_onl_alloc(next_hop, iface); + + if (dst->next_hop == NULL) { + memset(dst, 0, sizeof(_nib_offl_entry_t)); + return NULL; + } + _override_node(next_hop, iface, dst->next_hop); + dst->next_hop->mode |= _DST; + ipv6_addr_init_prefix(&dst->pfx, pfx, pfx_len); + dst->pfx_len = pfx_len; + } + return dst; +} + +static inline bool _in_dsts(const _nib_offl_entry_t *dst) +{ + return (dst < (_dsts + GNRC_IPV6_NIB_OFFL_NUMOF)); +} + +void _nib_offl_clear(_nib_offl_entry_t *dst) +{ + if (dst->next_hop != NULL) { + _nib_offl_entry_t *ptr; + for (ptr = _dsts; _in_dsts(ptr); ptr++) { + /* there is another dst pointing to next-hop => only remove dst */ + if ((dst != ptr) && (dst->next_hop == ptr->next_hop)) { + break; + } + } + /* we iterated and found no further dst pointing to next-hop */ + if (!_in_dsts(ptr)) { + dst->next_hop->mode &= ~(_DST); + _nib_onl_clear(dst->next_hop); + } + memset(dst, 0, sizeof(_nib_offl_entry_t)); + } +} + +_nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last) +{ + for (const _nib_offl_entry_t *dst = (last) ? (last + 1) : _dsts; + _in_dsts(dst); + dst++) { + if (dst->mode != _EMPTY) { + /* const modifier provided to assure internal consistency. + * Can now be discarded. */ + return (_nib_offl_entry_t *)dst; + } + } + return NULL; +} + _nib_iface_t *_nib_iface_get(unsigned iface) { _nib_iface_t *ni = NULL; @@ -410,7 +509,9 @@ 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)); + if (addr != NULL) { + memcpy(&node->ipv6, addr, sizeof(node->ipv6)); + } _nib_onl_set_if(node, iface); } diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h index a1a9a711db..491633b769 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -19,6 +19,7 @@ #ifndef PRIV_NIB_INTERNAL_H #define PRIV_NIB_INTERNAL_H +#include #include #include @@ -52,6 +53,8 @@ extern "C" { #define _DRL (0x08) /**< default router list */ #define _FT (0x10) /**< forwarding table */ #define _DAD (0x20) /**< 6LoWPAN duplicate address detection table */ +#define _DST (0x40) /**< there is @ref _nib_offl_entry_t pointing + to this @ref _nib_onl_entry_t */ /** @} */ /** @@ -162,6 +165,8 @@ typedef struct { ipv6_addr_t pfx; /**< prefix to the destination */ unsigned pfx_len; /**< prefix-length in bits of * _nib_onl_entry_t::pfx */ + uint8_t mode; /**< [mode](@ref net_gnrc_ipv6_nib_mode) of the + * off-link entry */ } _nib_offl_entry_t; /** @@ -251,10 +256,8 @@ static inline void _nib_onl_set_if(_nib_onl_entry_t *node, unsigned iface) /** * @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] addr An IPv6 address. May be NULL (to be pointed to by a prefix + * list entry). *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 @@ -305,7 +308,6 @@ _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) != @@ -313,7 +315,8 @@ _nib_onl_entry_t *_nib_onl_get(const ipv6_addr_t *addr, unsigned iface); * @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] addr The address of a node. May be NULL for prefix list + * entries. * @param[in] iface The interface to the node. * @param[in] cstate Creation state. State of the entry *if* the entry is * newly created. @@ -362,6 +365,7 @@ void _nib_nc_set_reachable(_nib_onl_entry_t *node); */ static inline _nib_onl_entry_t *_nib_dad_add(const ipv6_addr_t *addr) { + assert(addr != NULL); _nib_onl_entry_t *node = _nib_onl_alloc(addr, 0); if (node != NULL) { @@ -441,11 +445,10 @@ _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] next_hop An IPv6 address to next hop. May be NULL (for prefix + * list). *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 @@ -456,15 +459,15 @@ _nib_dr_entry_t *_nib_drl_get_dr(void); * @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); +_nib_offl_entry_t *_nib_offl_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); +void _nib_offl_clear(_nib_offl_entry_t *dst); /** * @brief Iterates over off-link entries @@ -473,16 +476,15 @@ void _nib_dst_clear(_nib_offl_entry_t *dst); * * @return entry after @p last. */ -_nib_offl_entry_t *_nib_dst_iter(const _nib_offl_entry_t *last); +_nib_offl_entry_t *_nib_offl_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] next_hop An IPv6 address to next hop. May be NULL (for prefix + * list). *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 @@ -490,19 +492,19 @@ _nib_offl_entry_t *_nib_dst_iter(const _nib_offl_entry_t *last); * @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 + * @return A new or existing off-link entry with _nib_offl_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) +static inline _nib_offl_entry_t *_nib_offl_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); + _nib_offl_entry_t *nib_offl = _nib_offl_alloc(next_hop, iface, pfx, pfx_len); if (nib_offl != NULL) { - nib_offl->next_hop->mode |= (mode); + nib_offl->mode |= mode; } return nib_offl; } @@ -512,17 +514,13 @@ static inline _nib_offl_entry_t *_nib_dst_add(const ipv6_addr_t *next_hop, * * @param[in,out] nib_offl An entry. */ -static inline void _nib_dst_remove(_nib_offl_entry_t *nib_offl, uint8_t mode) +static inline void _nib_offl_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); + nib_offl->mode &= ~mode; + _nib_offl_clear(nib_offl); } -#if defined(GNRC_IPV6_NIB_CONF_DC) || DOXYGEN +#if GNRC_IPV6_NIB_CONF_DC || DOXYGEN /** * @brief Creates or gets an existing destination cache entry by its addresses * @@ -535,16 +533,16 @@ static inline void _nib_dst_remove(_nib_offl_entry_t *nib_offl, uint8_t mode) * * @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 A new or existing off-link entry with _nib_offl_entry_t::pfx set to + * @p pfx. * @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); + assert((next_hop != NULL) && (dst != NULL)); + return _nib_offl_add(next_hop, iface, dst, IPV6_ADDR_BIT_LEN, _DC); } /** @@ -558,7 +556,7 @@ static inline _nib_offl_entry_t *_nib_dc_add(const ipv6_addr_t *next_hop, */ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) { - _nib_dst_remove(nib_offl, _DC); + _nib_offl_remove(nib_offl, _DC); } #endif /* GNRC_IPV6_NIB_CONF_DC */ @@ -574,7 +572,7 @@ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) * @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 + * @return A new or existing off-link entry with _nib_offl_entry_t::pfx set to * @p pfx. * @return NULL, if no space is left. */ @@ -582,7 +580,7 @@ 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); + return _nib_offl_add(NULL, iface, pfx, pfx_len, _PL); } /** @@ -594,10 +592,10 @@ static inline _nib_offl_entry_t *_nib_pl_add(unsigned iface, */ static inline void _nib_pl_remove(_nib_offl_entry_t *nib_offl) { - _nib_dst_remove(nib_offl, _PL); + _nib_offl_remove(nib_offl, _PL); } -#if defined(GNRC_IPV6_NIB_CONF_ROUTER) || DOXYGEN +#if GNRC_IPV6_NIB_CONF_ROUTER || DOXYGEN /** * @brief Creates or gets an existing forwarding table entry by its prefix * @@ -614,8 +612,8 @@ static inline void _nib_pl_remove(_nib_offl_entry_t *nib_offl) * * @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 A new or existing off-link entry with _nib_offl_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, @@ -623,7 +621,7 @@ static inline _nib_offl_entry_t *_nib_ft_add(const ipv6_addr_t *next_hop, const ipv6_addr_t *pfx, unsigned pfx_len) { - return _nib_dst_add(next_hop, iface, pfx, pfx_len, _FT); + return _nib_offl_add(next_hop, iface, pfx, pfx_len, _FT); } /** @@ -637,7 +635,7 @@ static inline _nib_offl_entry_t *_nib_ft_add(const ipv6_addr_t *next_hop, */ static inline void _nib_ft_remove(_nib_offl_entry_t *nib_offl) { - _nib_dst_remove(nib_offl, _FT); + _nib_offl_remove(nib_offl, _FT); } #endif /* GNRC_IPV6_NIB_CONF_ROUTER */ diff --git a/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include b/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include index 2370879fe5..69ef7e06ee 100644 --- a/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include +++ b/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include @@ -1,7 +1,9 @@ USEMODULE += gnrc_ipv6_nib USEMODULE += gnrc_ipv6_netif +CFLAGS += -DGNRC_IPV6_NIB_CONF_ROUTER=1 CFLAGS += -DGNRC_IPV6_NIB_NUMOF=16 +CFLAGS += -DGNRC_IPV6_NIB_OFFL_NUMOF=25 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 index 5cd7b8e5f3..9ec6f07160 100644 --- 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 @@ -29,7 +29,8 @@ #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) +#define GLOBAL_PREFIX_LEN (30) +#define IFACE (6) static void set_up(void) { @@ -121,6 +122,23 @@ static void test_nib_alloc__success_duplicate(void) TEST_ASSERT(node == _nib_onl_alloc(&addr, iface)); } +/* + * Creates a persistent on-link entry with no IPv6 address and then tries to + * create another one with the same interface, but with an address + * Expected result: entries should be identical + */ +static void test_nib_alloc__success_noaddr_override(void) +{ + _nib_onl_entry_t *node1, *node2; + const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((node1 = _nib_onl_alloc(NULL, IFACE))); + TEST_ASSERT_NOT_NULL((node2 = _nib_onl_alloc(&addr, IFACE))); + TEST_ASSERT(node1 == node2); + TEST_ASSERT(ipv6_addr_equal(&addr, &node1->ipv6)); +} + /* * Creates an non-persistent entry. * Expected result: new entry should contain the given address and interface @@ -964,6 +982,735 @@ static void test_nib_drl_get_dr__success4(void) TEST_ASSERT(nib_res != node2); } +#if GNRC_IPV6_NIB_NUMOF < GNRC_IPV6_NIB_OFFL_NUMOF +#define MAX_NUMOF (GNRC_IPV6_NIB_NUMOF) +#else /* GNRC_IPV6_NIB_NUMOF < GNRC_IPV6_NIB_OFFL_NUMOF */ +#define MAX_NUMOF (GNRC_IPV6_NIB_OFFL_NUMOF) +#endif + +/* + * Creates MAX_NUMOF off-link entries with different next-hop addresses and + * then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + next_hop.u64[1].u64++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates MAX_NUMOF off-link entries with different interfaces and then tries + * to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_iface(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _PL; + iface++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates MAX_NUMOF off-link entries with different next-hop addresses and + * interfaces and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_iface(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _DC; + next_hop.u64[1].u64++; + iface++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF off-link entries with different prefixes + * of the same length and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_pfx(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + for (int i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + pfx.u16[0].u16++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes of the same + * length and different next-hop addresses and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_pfx(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + next_hop.u64[1].u64++; + pfx.u16[0].u16++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes of the same + * length and different interfaces and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_iface_pfx(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + iface++; + pfx.u16[0].u16++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes of the same + * length, different interfaces, and different next hop addresses and then + * tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_iface_pfx(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + next_hop.u64[1].u64++; + iface++; + pfx.u16[0].u16++; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, GLOBAL_PREFIX_LEN)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF off-link entries with different prefix + * lengths and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_pfx_len(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN; + + for (int i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + _nib_offl_entry_t *dst; + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _PL; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes and then tries to + * add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_pfx_len(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _PL; + next_hop.u64[1].u64++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefix lengths and + * interfaces and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_iface_pfx_len(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _PL; + iface++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefix lengths, + * interfaces, and next hop addresses and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_iface_pfx_len(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _PL; + next_hop.u64[1].u64++; + iface++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, pfx_len)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF off-link entries with different prefixes + * and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_pfx_pfx_len(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN; + + for (int i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + pfx.u16[0].u16++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes and different + * next-hop addresses and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_pfx_pfx_len(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + next_hop.u64[1].u64++; + pfx.u16[0].u16++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, IFACE, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes and different + * interfaces and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_iface_pfx_pfx_len(void) +{ + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + iface++; + pfx.u16[0].u16++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes, different + * interfaces, and different next hop addresses and then tries to add another. + * Expected result: should return NULL + */ +static void test_nib_offl_alloc__no_space_left_diff_next_hop_iface_pfx_pfx_len(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + next_hop.u64[1].u64++; + iface++; + pfx.u16[0].u16++; + pfx_len--; + } + TEST_ASSERT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, pfx_len)); +} + +/* + * Creates MAX_NUMOF off-link entries with different prefixes, different + * interfaces, and different next hop addresses and then tries to add another + * equal to the last. + * Expected result: should return not NULL (the last) + */ +static void test_nib_offl_alloc__success_duplicate(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (int i = 0; i < MAX_NUMOF; i++) { + _nib_offl_entry_t *dst; + + next_hop.u64[1].u64++; + iface++; + pfx.u16[0].u16++; + pfx_len--; + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, iface, &pfx, + pfx_len))); + TEST_ASSERT_NOT_NULL(dst->next_hop); + dst->mode |= _FT; + } + TEST_ASSERT_NOT_NULL(_nib_offl_alloc(&next_hop, iface, &pfx, pfx_len)); +} + +/* + * Creates an off-link entry with no next hop address and then adds another + * with equal prefix and interface to the last, but with a next hop address + * Expected result: the first entry should be equal to the second and both + * have the same next hop address + */ +static void test_nib_offl_alloc__success_overwrite_unspecified(void) +{ + _nib_offl_entry_t *dst1, *dst2; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst1 = _nib_offl_alloc(NULL, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst1->mode |= _PL; + TEST_ASSERT_NOT_NULL((dst2 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL(dst1->next_hop); + TEST_ASSERT_EQUAL_INT(_PL, dst1->mode); + TEST_ASSERT(dst1 == dst2); + TEST_ASSERT(ipv6_addr_equal(&next_hop, &dst1->next_hop->ipv6)); +} + +/* + * Creates an off-link entry. + * Expected result: new entry should contain the given prefix, address and + * interface + */ +static void test_nib_offl_alloc__success(void) +{ + _nib_offl_entry_t *dst; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, dst->pfx_len); + TEST_ASSERT(GLOBAL_PREFIX_LEN <= ipv6_addr_match_prefix(&pfx, &dst->pfx)); + TEST_ASSERT_NOT_NULL(dst->next_hop); + TEST_ASSERT_EQUAL_INT(_DST, dst->next_hop->mode); + TEST_ASSERT(ipv6_addr_equal(&next_hop, &dst->next_hop->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(dst->next_hop)); +} + +/* + * Creates an off-link entry, sets a neighbor cache flag, and tries to remove + * it. + * Expected result: The off-link entry is removed, but the on-link entry should + * still exist + */ +static void test_nib_offl_clear__uncleared(void) +{ + _nib_offl_entry_t *dst; + _nib_onl_entry_t *node; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + node = dst->next_hop; + node->mode |= _NC; + _nib_offl_clear(dst); + TEST_ASSERT_NULL(_nib_offl_iter(NULL)); + TEST_ASSERT(node == _nib_onl_iter(NULL)); +} + +/* + * Creates two off-link entry off-link entries and tries to remove one of them. + * Expected result: The NIB should only contain the one removed, the on-link + * entry should still exist + */ +static void test_nib_offl_clear__same_next_hop(void) +{ + _nib_offl_entry_t *dst1, *dst2, *res; + _nib_onl_entry_t *node; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst1 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst1->mode |= _FT; + pfx.u16[0].u16++; + TEST_ASSERT_NOT_NULL((dst2 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT(dst1->next_hop == dst2->next_hop); + node = dst2->next_hop; + TEST_ASSERT_NOT_NULL(node); + _nib_offl_clear(dst2); + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(NULL))); + TEST_ASSERT(dst1 == res); + TEST_ASSERT_NULL(_nib_offl_iter(res)); + TEST_ASSERT(node == _nib_onl_iter(NULL)); +} + +/* + * Creates an off-link entry and tries to remove it. + * Expected result: The NIB should be empty + */ +static void test_nib_offl_clear__cleared(void) +{ + _nib_offl_entry_t *dst; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + _nib_offl_clear(dst); + TEST_ASSERT_NULL(_nib_offl_iter(NULL)); + TEST_ASSERT_NULL(_nib_onl_iter(NULL)); +} + +/* + * Iterates over empty off-link entries + * Expected result: _nib_drl_iter returns NULL + */ +static void test_nib_offl_iter__empty(void) +{ + TEST_ASSERT_NULL(_nib_offl_iter(NULL)); +} + +/* + * Iterates over off-link entries with one element + * Expected result: _nib_offl_iter returns element with NULL, and with that + * element NULL. + */ +static void test_nib_offl_iter__one_elem(void) +{ + _nib_offl_entry_t *dst, *res; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst->mode |= _FT; + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(NULL))); + TEST_ASSERT(res == dst); + TEST_ASSERT_NULL(_nib_offl_iter(res)); +} + +/* + * Iterates over off-link entries with three element + * Expected result: _nib_offl_iter returns element with NULL, with that element + * another, with that element yet another and with the last NULL. + */ +static void test_nib_offl_iter__three_elem(void) +{ + _nib_offl_entry_t *dst1, *dst2, *dst3, *res; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst1 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst1->mode |= _FT; + pfx.u16[0].u16++; + TEST_ASSERT_NOT_NULL((dst2 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst2->mode |= _FT; + pfx.u16[0].u16++; + TEST_ASSERT_NOT_NULL((dst3 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst3->mode |= _FT; + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(NULL))); + TEST_ASSERT(res == dst1); + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(res))); + TEST_ASSERT(res == dst2); + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(res))); + TEST_ASSERT(res == dst3); + TEST_ASSERT_NULL(_nib_offl_iter(res)); +} + +/* + * Iterates over off-link entries with two elements, where there is a whole in + * the internal array + * Expected result: _nib_offl_iter returns element with NULL, with that element + * another, and with the last NULL. + */ +static void test_nib_offl_iter__three_elem_middle_removed(void) +{ + _nib_offl_entry_t *dst1, *dst2, *dst3, *res; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst1 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst1->mode |= _FT; + pfx.u16[0].u16++; + TEST_ASSERT_NOT_NULL((dst2 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst2->mode |= _FT; + pfx.u16[0].u16++; + TEST_ASSERT_NOT_NULL((dst3 = _nib_offl_alloc(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + dst3->mode |= _FT; + dst2->mode = _EMPTY; + _nib_offl_clear(dst2); + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(NULL))); + TEST_ASSERT(res == dst1); + TEST_ASSERT_NOT_NULL((res = _nib_offl_iter(res))); + TEST_ASSERT(res == dst3); + TEST_ASSERT_NULL(_nib_offl_iter(res)); +} + +#if GNRC_IPV6_NIB_CONF_DC +/* + * Creates a destination cache entry. + * Expected result: new entry should contain the given address and interface + */ +static void test_nib_dc_add__success(void) +{ + _nib_offl_entry_t *dst; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t dst_addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_dc_add(&next_hop, IFACE, &dst_addr))); + TEST_ASSERT(dst->mode & _DC); + TEST_ASSERT_EQUAL_INT(IPV6_ADDR_BIT_LEN, dst->pfx_len); + TEST_ASSERT(ipv6_addr_equal(&dst_addr, &dst->pfx)); + TEST_ASSERT_NOT_NULL(dst->next_hop); + TEST_ASSERT_EQUAL_INT(_DST, dst->next_hop->mode); + TEST_ASSERT(ipv6_addr_equal(&next_hop, &dst->next_hop->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(dst->next_hop)); +} + +/* + * Creates a destination cache entry and removes it. + * Expected result: The destination cache should be empty + */ +static void test_nib_dc_remove(void) +{ + + _nib_offl_entry_t *dst; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t dst_addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_dc_add(&next_hop, IFACE, &dst_addr))); + _nib_dc_remove(dst); + TEST_ASSERT_NULL(_nib_offl_iter(NULL)); +} +#endif + +/* + * Creates a prefix list entry. + * Expected result: new entry should contain the given address and interface + */ +static void test_nib_pl_add__success(void) +{ + _nib_offl_entry_t *dst; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, &pfx, GLOBAL_PREFIX_LEN))); + TEST_ASSERT(dst->mode & _PL); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, dst->pfx_len); + TEST_ASSERT(GLOBAL_PREFIX_LEN <= ipv6_addr_match_prefix(&pfx, &dst->pfx)); + TEST_ASSERT_NOT_NULL(dst->next_hop); + TEST_ASSERT_EQUAL_INT(_DST, dst->next_hop->mode); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(dst->next_hop)); +} + +/* + * Creates a prefix list entry and removes it. + * Expected result: The prefix list should be empty + */ +static void test_nib_pl_remove(void) +{ + + _nib_offl_entry_t *dst; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, &pfx, GLOBAL_PREFIX_LEN))); + _nib_pl_remove(dst); + TEST_ASSERT_NULL(_nib_offl_iter(NULL)); +} + +/* + * Creates a forwarding table entry. + * Expected result: new entry should contain the given address and interface + */ +static void test_nib_ft_add__success(void) +{ + _nib_offl_entry_t *dst; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_ft_add(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + TEST_ASSERT(dst->mode & _FT); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, dst->pfx_len); + TEST_ASSERT(GLOBAL_PREFIX_LEN <= ipv6_addr_match_prefix(&pfx, &dst->pfx)); + TEST_ASSERT_NOT_NULL(dst->next_hop); + TEST_ASSERT_EQUAL_INT(_DST, dst->next_hop->mode); + TEST_ASSERT(ipv6_addr_equal(&next_hop, &dst->next_hop->ipv6)); + TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(dst->next_hop)); +} + +/* + * Creates a forwarding table entry and removes it. + * Expected result: The forwarding table should be empty + */ +static void test_nib_ft_remove(void) +{ + + _nib_offl_entry_t *dst; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_NOT_NULL((dst = _nib_ft_add(&next_hop, IFACE, &pfx, + GLOBAL_PREFIX_LEN))); + _nib_ft_remove(dst); + TEST_ASSERT_NULL(_nib_offl_iter(NULL)); +} + /* * Creates GNRC_NETIF_NUMOF interfaces and then tries to add another. * Expected result: should return NULL @@ -998,6 +1745,7 @@ Test *tests_gnrc_ipv6_nib_internal_tests(void) 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_noaddr_override), new_TestFixture(test_nib_alloc__success), new_TestFixture(test_nib_clear__persistent), new_TestFixture(test_nib_clear__non_persistent_but_content), @@ -1041,6 +1789,39 @@ Test *tests_gnrc_ipv6_nib_internal_tests(void) 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_offl_alloc__no_space_left_diff_next_hop), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_iface), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_iface), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_pfx), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_pfx), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_iface_pfx), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_iface_pfx), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_iface_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_iface_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_pfx_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_pfx_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_iface_pfx_pfx_len), + new_TestFixture(test_nib_offl_alloc__no_space_left_diff_next_hop_iface_pfx_pfx_len), + new_TestFixture(test_nib_offl_alloc__success_duplicate), + new_TestFixture(test_nib_offl_alloc__success_overwrite_unspecified), + new_TestFixture(test_nib_offl_alloc__success), + new_TestFixture(test_nib_offl_clear__uncleared), + new_TestFixture(test_nib_offl_clear__same_next_hop), + new_TestFixture(test_nib_offl_clear__cleared), + new_TestFixture(test_nib_offl_iter__empty), + new_TestFixture(test_nib_offl_iter__one_elem), + new_TestFixture(test_nib_offl_iter__three_elem), + new_TestFixture(test_nib_offl_iter__three_elem_middle_removed), +#if GNRC_IPV6_NIB_CONF_DC + new_TestFixture(test_nib_dc_add__success), + new_TestFixture(test_nib_dc_remove), +#endif + new_TestFixture(test_nib_pl_add__success), + new_TestFixture(test_nib_pl_remove), + new_TestFixture(test_nib_ft_add__success), + new_TestFixture(test_nib_ft_remove), new_TestFixture(test_nib_iface_get__no_space_left), new_TestFixture(test_nib_iface_get__success), };