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 0d42695336..8b9ed0e23f 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -84,8 +84,11 @@ void _nib_release(void) 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)); + if (addr == NULL) { + return ipv6_addr_is_unspecified(&node->ipv6); + } else { + return ipv6_addr_equal(addr, &node->ipv6); + } } _nib_onl_entry_t *_nib_onl_alloc(const ipv6_addr_t *addr, unsigned iface) @@ -501,28 +504,34 @@ _nib_offl_entry_t *_nib_offl_alloc(const ipv6_addr_t *next_hop, unsigned iface, _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)); + if (tmp->mode == _EMPTY) { + if (dst == NULL) { + dst = tmp; } - tmp->next_hop->mode |= _DST; - return tmp; + continue; } - if ((dst == NULL) && (tmp_node == NULL)) { - dst = tmp; + + /* else: offlink entry not empty, potential match */ + if (tmp->pfx_len == pfx_len && ipv6_addr_match_prefix(&tmp->pfx, pfx) >= pfx_len) { + /* prefix matches */ + assert(tmp_node); + if (_nib_onl_get_if(tmp_node) == iface && (ipv6_addr_is_unspecified(&tmp_node->ipv6) + || _addr_equals(next_hop, tmp_node))) { + /* next hop matches or is unspecified */ + DEBUG(" %p is an exact match\n", (void *)tmp); + if (next_hop != NULL) { + /* sets next_hop if it was previously unspecified */ + memcpy(&tmp_node->ipv6, next_hop, sizeof(tmp_node->ipv6)); + } + /*mark that this NCE is used by an offl_entry*/ + tmp->next_hop->mode |= _DST; + return tmp; + } } } if (dst != NULL) { DEBUG(" using %p\n", (void *)dst); - dst->next_hop = _nib_onl_alloc(next_hop, iface); - - if (dst->next_hop == NULL) { + if (!dst->next_hop && !(dst->next_hop = _nib_onl_alloc(next_hop, iface))) { memset(dst, 0, sizeof(_nib_offl_entry_t)); return NULL; } 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 988f755a62..8f0f856433 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 @@ -1449,6 +1449,60 @@ static void test_nib_offl_alloc__success_overwrite_unspecified(void) TEST_ASSERT(ipv6_addr_equal(&next_hop, &dst1->next_hop->ipv6)); } +/* + * Creates an off-link entry (to a next hop) and an on-link entry on the same interface. + * Then proceeds to delete the off-link entries to this next hop + * by only comparing the next hop, not checking the _PFX_ON_LINK flag. + * + * Expected results: Only off-link entries are deleted. + * On-link entries on the same interface are unaffected by the deletion. + */ +static void test_nib_offl_alloc__next_hop_indicates_whether_onl(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 } } }; + + /*are in practice different prefixes actually*/ + static const ipv6_addr_t *onl_pfx = &pfx; + static const ipv6_addr_t *offl_pfx = &pfx; + + /* Add off-link entry */ + _nib_offl_entry_t *dst1; + TEST_ASSERT_NOT_NULL((dst1 = _nib_ft_add(&next_hop, IFACE, offl_pfx, GLOBAL_PREFIX_LEN))); + + /* Add on-link entry */ + const unsigned pfx_len = GLOBAL_PREFIX_LEN; /* arbitrary */ + + /* (calls _nib_offl_alloc) */ + _nib_offl_entry_t *dst; + TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, onl_pfx, pfx_len, UINT32_MAX, UINT32_MAX))); + TEST_ASSERT(ipv6_addr_is_unspecified(&dst->next_hop->ipv6)); + /* would normally be set by PIO flags in Router Advertisement */ + dst->flags |= _PFX_ON_LINK; + + /* Delete all off-link entries to next_hop */ + _nib_offl_entry_t *route = NULL; + while ((route = _nib_offl_iter(route))) { + if ((_nib_onl_get_if(route->next_hop) == IFACE) && + (route->next_hop != NULL) && + ipv6_addr_equal(&route->next_hop->ipv6, &next_hop) /*off-link, to this next hop*/ + /*should not need to be checked for when next hop is already checked:*/ + /*&& !(route->flags & _PFX_ON_LINK)*/ + ) { + _nib_ft_remove(route); + } + } + + /* Expected result: On-link entries remain unaffected */ + gnrc_ipv6_nib_pl_t prefix; + void *state = NULL; + TEST_ASSERT_MESSAGE(gnrc_ipv6_nib_pl_iter(IFACE, &state, &prefix), + "No prefix list entry found"); + TEST_ASSERT_MESSAGE(ipv6_addr_match_prefix(onl_pfx, &prefix.pfx) >= pfx_len, + "Unexpected prefix configured"); +} + /* * Creates an off-link entry. * Expected result: new entry should contain the given prefix, address and @@ -2069,6 +2123,7 @@ Test *tests_gnrc_ipv6_nib_internal_tests(void) 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__next_hop_indicates_whether_onl), new_TestFixture(test_nib_offl_alloc__success), new_TestFixture(test_nib_offl_clear__uncleared), new_TestFixture(test_nib_offl_clear__same_next_hop),