-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gnrc_ipv6_nib: Force unspecified next hop addresses #20371
Conversation
Now some tests are failing, is this intended? |
I would guess not. I can only tell so far that we fail because
dst->next_hop = _nib_onl_alloc(next_hop, iface);
if (dst->next_hop == NULL) {
memset(dst, 0, sizeof(_nib_offl_entry_t));
return NULL;
} |
Maybe no neighbor cache entry should be allocated at all if no next hop address is known.
If there is no IPv6 address for a neighbor and neighbors are keyed by their address, why would we want to add a neighbor cache entry at all? |
No, but turns out they test in a wrong way. :D 🤯 |
It feels like the test was working by coincidence. I think it should not be an NCE without an address just to store an interface number for a prefix. |
Conceptionally the DRL and PL are two different data structures. |
If this causes a test to fail that was testing the wrong thing, you also need to add the fix to the test in this PR, otherwise there is no way to merge this. |
40f4b86
to
0a9f611
Compare
Thanks for the heads up. The erroneous test (#20377) is already fixed in master. Therefore I rebased. |
0a9f611
to
3ab7896
Compare
Squashed your suggested change. |
CI finds that |
But currently a new offlink entry with the next hop IPv6 address is allocated and thus You could update the code like this or similar to change that an offlink entry matches by interface + prefix + next hop IPv6 if not unspecified, if that works for you. 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 7a33add7d1..a011244a45 100644
--- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
+++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
@@ -504,18 +504,21 @@ _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) {
+ /* offlink entry not empty */
+ if (tmp->pfx_len == pfx_len && ipv6_addr_match_prefix(&tmp->pfx, pfx) >= pfx_len) {
+ /* prefix matches */
+ 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) {
+ memcpy(&tmp_node->ipv6, next_hop, sizeof(tmp_node->ipv6));
+ }
+ tmp->next_hop->mode |= _DST;
+ return tmp;
+ }
}
- tmp->next_hop->mode |= _DST;
- return tmp;
}
if ((dst == NULL) && (tmp_node == NULL)) {
dst = tmp;
@@ -523,9 +526,7 @@ _nib_offl_entry_t *_nib_offl_alloc(const ipv6_addr_t *next_hop, unsigned iface,
}
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;
} |
Just a note in detail on the severity of this: Like you said:
The IPv6 address of the neighbor ( The NCE will never be accidentally deleted while in use, because of this safeguard
And _nib_offl_alloc has _DST mode set as long as the NCE is used:
Still, it might be confusing to see an NCE that should have been deleted, but is instead kept merely because an on-link prefix used it to specify an interface. |
Thank you, @fabian18, I did not know of this behavior. |
CI doesn't like the commit
since it assumes it to be a fixup commit. |
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would that work?
if (tmp->mode != _EMPTY) { | |
if (tmp->mode == _EMPTY) { | |
dst = tmp; | |
continue; | |
} |
But tbh I don't know what's the difference between tmp->next_hop == NULL
and tmp->mode == _EMPTY
, I would have thought tmp->next_hop == NULL
could also happen on a non-empty entry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even for an on-link prefix, tmp->next_hop
points to an NCE in order to refer to an interface, so I wouldn't know a case where tmp->next_hop == NULL
while tmp->mode != _EMPTY
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your suggested change would use the last free entry instead of the first one, when there is no exact matching entry. I think, while it wouldn't cause errors, it's an unusual allocation policy (compared to all other allocations in GNRC).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (tmp->mode == _EMPTY) {
if (dst == NULL) {
dst = tmp;
}
continue;
}
would also work, at the expense of one more branch that might be a bit unececary.
What I'm getting at was reducing the level of indentation if you return / continue early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think every offlink entry has an onlink entry in our implementation.
Even if it only stores an interface ID and has an unspecified next hop..
In _nib_offl_alloc
, if dst
was NULL
this means no offlink entry could be allocated and none matched.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be safe, an assert(tmp_node)
when the prefix matches does not harm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if dst was NULL this means no offlink entry could be allocated and none matched
That is true only after all entries were iterated through. The change suggested by @benpicco is however in the loop still, in which case dst == NULL
means no candidate has been found so far (in previous iterations).
Co-Authored-By: fabian18 <15147337+fabian18@users.noreply.github.com>
61e7ea9
to
0a8403a
Compare
Too me it looks good. |
tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c
Outdated
Show resolved
Hide resolved
499d2c8
to
c6d5112
Compare
Then you can call 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 8d7dd5968c..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
@@ -1459,8 +1459,6 @@ static void test_nib_offl_alloc__success_overwrite_unspecified(void)
*/
static void test_nib_offl_alloc__next_hop_indicates_whether_onl(void)
{
- _nib_offl_entry_t *dst1;
- void *state = NULL;
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 } } };
@@ -1470,17 +1468,17 @@ static void test_nib_offl_alloc__next_hop_indicates_whether_onl(void)
static const ipv6_addr_t *offl_pfx = &pfx;
/* Add off-link entry */
- TEST_ASSERT_NOT_NULL((dst1 = _nib_ft_add(&next_hop, IFACE, offl_pfx,
- GLOBAL_PREFIX_LEN)));
+ _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 = _nib_pl_add(IFACE, onl_pfx, pfx_len, UINT32_MAX, UINT32_MAX);
+ _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));
-
- TEST_ASSERT_NOT_NULL(dst);
+ /* would normally be set by PIO flags in Router Advertisement */
dst->flags |= _PFX_ON_LINK;
/* Delete all off-link entries to next_hop */
@@ -1492,18 +1490,16 @@ static void test_nib_offl_alloc__next_hop_indicates_whether_onl(void)
/*should not need to be checked for when next hop is already checked:*/
/*&& !(route->flags & _PFX_ON_LINK)*/
) {
- route->mode &= ~_PL;
- route->next_hop->mode &= ~_DST;
- _nib_offl_clear(route);
+ _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,
+ "No prefix list entry found");
+ TEST_ASSERT_MESSAGE(ipv6_addr_match_prefix(onl_pfx, &prefix.pfx) >= pfx_len,
"Unexpected prefix configured");
} |
Invert condition, add assert
c6d5112
to
8b73628
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2024-08-31 12:18:03,285 # main(): This is RIOT! (Version: 2024.10-devel-25-g8b736-nib-next-hop-addr)
2024-08-31 12:18:03,285 # Help: Press s to start test, r to print it is ready
s
2024-08-31 12:18:04,190 # START
2024-08-31 12:18:04,192 # ........................................................................................................................................................
2024-08-31 12:18:04,192 # OK (152 tests)
2024-08-31 12:18:04,192 #
As the ta_pfx will have an NCE with unspecified next hop, 1 NCE is needed for that.
As the ta_pfx will have an NCE with unspecified next hop, 1 NCE is needed for that.
As the ta_pfx will have an NCE with unspecified next hop, 1 NCE is needed for that.
Reserve unspecified address in NCEs for on-link prefixes, to have
_PFX_ON_LINK => ipv6_addr_is_unspecified(dst->next_hop)
be trueContribution description
In nib_[onl|offl]_entry_t,
a next_hop of NULL (as used by prefix list)
shouldn't be interpreted as "don't care"
but as an explicit "none, not applicable",
because the next_hop value is used for association with a router at
RIOT/sys/net/gnrc/network_layer/ipv6/nib/nib.c
Line 1469 in 270aa70
The code intends to delete all off-link prefixes where next hop is the specified router [1], but (without this PR) it actually also deletes on-link prefixes on the same interface [2] because they falsely use the same next hop. (And are only distinguished as on-link through
_PFX_ON_LINK
flag, which however is not checked for in this place.)(Note that on-link prefixes are added again when processing a RA Prefix Information Option, which can be in the same Router Advertisement that caused
_handle_rtr_timeout
and thereby their accidental deletion. This seems to be common and makes this deletion hard to notice.)[1] which is the wrong thing to do in the first place, and was therefore removed in 9648000, thereby making this bug theoretical only
[2] such as "the /128 prefix used for managing a temporary address" in #20369 (affected)
Testing procedure
Tests: #20372