Page MenuHomeFreeBSD

ndp: don't send unsolicited NA for multicast address
ClosedPublic

Authored by pouria on Mon, Mar 16, 9:00 PM.

Details

Summary

During link-layer address change event, don't send unsolicited
NA for multicast and linklocal addresses.

Test Plan

Run:

kyua test -k /usr/tests/Kyuafile sys/netinet6

or simply change mac address of your interface:

ifconfig vtnet0 ether 11:22:33:44:55:66

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

This change also fixes a panic produced in main from kyua tests.

[root@ftsr1] [~] # kyua --config /usr/local/etc/kyua.conf test -k /usr/tests/Kyuafile sys/net
Results file id is usr_tests.20260316-214508-051017
Results saved to /root/.kyua/store/results.usr_tests.20260316-214508-051017.db

139/147 passed (0 broken, 2 failed, 6 skipped)
[root@ftsr1] [~] # kyua --config /usr/local/etc/kyua.conf test -k /usr/tests/Kyuafile sys/netinet6
Results file id is usr_tests.20260316-222610-373655
Results saved to /root/.kyua/store/results.usr_tests.20260316-222610-373655.db

83/84 passed (0 broken, 0 failed, 1 skipped)
This revision is now accepted and ready to land.Tue, Mar 17, 1:56 AM
zlei added inline comments.
sys/netinet6/nd6.c
243

I think it is right to not send unsolicited NA for multicast address, but I expect the opposite for linklocal address.

My local test show no unsolicited NA sent for linklocal address, hence the peer get stale ndp entry and can not reach after the ether address changed. That appears wrong to me.

In addition to comments:
If you check the pcap during grand, you'll see the grand itself will send its NA using link-local addresses.
Therefore, without GRAND, it should hint our NUD to update its NCE.
That is another reason for why our NUD is the problem not the GRAND.

Until I fix our NUD, I'll remove the link-local check for now.

sys/netinet6/nd6.c
243

I think it is right to not send unsolicited NA for multicast address, but I expect the opposite for linklocal address.

Thank you for your test.
This is actually also valid for link-local addresses, as defined in RFC9131:
It should be noted that the mechanism discussed in this document allows hosts to proactively inform their routers about global IPv6 addresses existing on-link.

My local test show no unsolicited NA sent for linklocal address, hence the peer get stale ndp entry and can not reach after the ether address changed. That appears wrong to me.

Ensuring whether the ndp cache is still valid or not is the job of NUD and as it's outlined in rfc9898, section 3.9:
GRAND eliminates the router forwarding delay, but it does not solve other Router-NCE-on-Demand issues.

So, the primarily issue is forwarding delay from receive path.
The GRAND by nature is a performance optimization mostly targeted on global scoped addresses.

However, as you have already found out through your tests, our NUD implementation is not robust enough to detect such changes and may remain stuck in STALE for a long time.
Therefore, I'll remove this link-local check for now, but after optimizing the NUD implementation, we may want to avoid sending grand for link-local addresses eventually.

This revision now requires review to proceed.Tue, Mar 17, 7:11 AM

The link-local check here is also redundant. You have already checked the scope of the address in nd6_grand_start(). So if the address is global one, it must not be a link-local one.

nd6_grand_start(struct ifaddr *ifa, uint32_t flags)
{
...
        /* Check if new address is global */
        if ((flags & ND6_QUEUE_FLAG_NEWGUA) != 0 &&
            in6_addrscope(IFA_IN6(ifa)) != IPV6_ADDR_SCOPE_GLOBAL)
                return;
...
}
This revision is now accepted and ready to land.Tue, Mar 17, 8:43 AM

The link-local check here is also redundant. You have already checked the scope of the address in nd6_grand_start(). So if the address is global one, it must not be a link-local one.

nd6_grand_start(struct ifaddr *ifa, uint32_t flags)
{
...
        /* Check if new address is global */
        if ((flags & ND6_QUEUE_FLAG_NEWGUA) != 0 &&
            in6_addrscope(IFA_IN6(ifa)) != IPV6_ADDR_SCOPE_GLOBAL)
                return;
...
}

You're right! what a brain glitch!
Thank you!

The link-local check here is also redundant. You have already checked the scope of the address in nd6_grand_start(). So if the address is global one, it must not be a link-local one.

nd6_grand_start(struct ifaddr *ifa, uint32_t flags)
{
...
        /* Check if new address is global */
        if ((flags & ND6_QUEUE_FLAG_NEWGUA) != 0 &&
            in6_addrscope(IFA_IN6(ifa)) != IPV6_ADDR_SCOPE_GLOBAL)
                return;
...
}

You're right! what a brain glitch!
Thank you!

Oh no, I reacted too fast, this shows I don't trust my own memory :)
This check only applies for new address event, not link-layer address change.

The link-local check here is also redundant. You have already checked the scope of the address in nd6_grand_start(). So if the address is global one, it must not be a link-local one.

nd6_grand_start(struct ifaddr *ifa, uint32_t flags)
{
...
        /* Check if new address is global */
        if ((flags & ND6_QUEUE_FLAG_NEWGUA) != 0 &&
            in6_addrscope(IFA_IN6(ifa)) != IPV6_ADDR_SCOPE_GLOBAL)
                return;
...
}

You're right! what a brain glitch!
Thank you!

Oh no, I reacted too fast, this shows I don't trust my own memory :)
This check only applies for new address event, not link-layer address change.

Obviously I missed ND6_QUEUE_FLAG_NEWGUA ;)