Page MenuHomeFreeBSD

sys/netinet6: Fix SLAAC for interfaces with no /64 LL address
ClosedPublic

Authored by linnemannr_gmail.com on Aug 6 2025, 10:30 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Sep 28, 12:32 PM
Unknown Object (File)
Thu, Sep 25, 1:03 PM
Unknown Object (File)
Tue, Sep 23, 9:14 PM
Unknown Object (File)
Wed, Sep 17, 6:09 AM
Unknown Object (File)
Wed, Sep 17, 12:56 AM
Unknown Object (File)
Sun, Sep 7, 12:15 AM
Unknown Object (File)
Sep 2 2025, 12:02 PM
Unknown Object (File)
Sep 1 2025, 7:12 AM

Details

Summary

in6_ifadd() asserts that an interface has an existing LL address with a /64
prefix from which to extract the ifid for SLAAC address selection (even though
the comments suggest that an ifid will be generated if one does not exist). This
is adequate for most generic cases, however to support PPP links with /128 LL
addresses we must be able to fall back on another source for the ifid since we
cannot assume the /128 LL has a unique ifid in the lower 64 bits.

To do this, the static function get_ifid() in in6_ifattach.c is renamed to
non-static in6_get_ifid(), and this is used in lieu of a proper /64 LL address
to attempt to obtain a valid ifid.

Sponsored by: Rubicon Communications, LLC ("Netgate")

Diff Detail

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

Event Timeline

I'm a little confused by PPP links with /128 LL addresses ? Can you elaborate how you setup the PPP links ?

I'm a little confused by PPP links with /128 LL addresses ? Can you elaborate how you setup the PPP links ?

We ran into this with pfSense's if_pppoe.ko, but I believe it's a generic problem.
Essentially we have IPv6 link-local addresses negotiated via IPv6CP, which gives us a local and remote link-local address (and requires /128 as per https://cgit.freebsd.org/src/tree/sys/netinet6/in6.c#n934). Some ISPs still assign GUA's via SLAAC over that connection, and in that case we failed to turn the router advertisement into a GUA address because we failed, in in6_ifadd(), to figure out how to assign a unique local part for the prefix from the RA.

This change really only matters in that specific scenario: we have an RA, but for whatever reason we don't have a /64 link-local address to generate the interface identifier from. So we now try a bit harder to get one.

I'm fairly sure it's possible to construct a similar situation with in-tree network interfaces. That'll need a point-to-point interface so you can assign src-dst/128 link-local addresses and still inject a router advertisement. if_gif is probably suitable, although I hacked up if_epair to add the IFF_POINTTOPOINT flag for my own testing.

I am also curious about why the PPPoE implementation sticks to only /128 LLAs for the link. When creating a pppoe interface as IPv6-capable one, it should get a /64 LLA because of AUTO_LINKLOCAL flag. Does pfSense disable this address assignment?

In D51778#1185229, @hrs wrote:

I am also curious about why the PPPoE implementation sticks to only /128 LLAs for the link. When creating a pppoe interface as IPv6-capable one, it should get a /64 LLA because of AUTO_LINKLOCAL flag. Does pfSense disable this address assignment?

We assign an initial LLA based on the IPV6CP, which results in an address with with a destination address. Just like we get for IPv4.
Configuring a point-to-point address seems appropriate for a point-to-point link.
That destination address means we have no choice but to use /128 (see https://cgit.freebsd.org/src/tree/sys/netinet6/in6.c#n934), and that in turns means the router advertisement handling code ends up not being able to figure out what unique address bits to use. Not without this patch, anyway.

In D51778#1185473, @kp wrote:
In D51778#1185229, @hrs wrote:

I am also curious about why the PPPoE implementation sticks to only /128 LLAs for the link. When creating a pppoe interface as IPv6-capable one, it should get a /64 LLA because of AUTO_LINKLOCAL flag. Does pfSense disable this address assignment?

We assign an initial LLA based on the IPV6CP, which results in an address with with a destination address. Just like we get for IPv4.
Configuring a point-to-point address seems appropriate for a point-to-point link.
That destination address means we have no choice but to use /128 (see https://cgit.freebsd.org/src/tree/sys/netinet6/in6.c#n934), and that in turns means the router advertisement handling code ends up not being able to figure out what unique address bits to use. Not without this patch, anyway.

Hmm, the IPv6 link-local scope guarantees that the communication over the link will not be forwarded, so having /64 LLAs on the endpoints of the PPP link is totally valid. RFC 5072 clearly says that the IID option is for SLAAC, and this assumes that you simply use an IID from IPV6CP to compose fe80::IID/64 on the local end of the PPP link and perform SLAAC safely. The uniqueness of fe80::IID/64 is also guaranteed. IIRC, PPPoE on MPD5 follows it (but I may misremeber).

Configuring a point-to-point link with a /128 link-local prefix is not a scenario the specification expects, and it just makes things complicated. While I do not strongly object to this change because it is not harmful, if the motivation stems from /128 LLA configurations, I would say it can always be replaced with /64 LLAs. There is a valid reason to use a longer prefix only when using GUAs, such as a /127 prefix for a point-to-point link between two routers, as documented in RFC 6164.

In D51778#1185537, @hrs wrote:

Hmm, the IPv6 link-local scope guarantees that the communication over the link will not be forwarded, so having /64 LLAs on the endpoints of the PPP link is totally valid. RFC 5072 clearly says that the IID option is for SLAAC, and this assumes that you simply use an IID from IPV6CP to compose fe80::IID/64 on the local end of the PPP link and perform SLAAC safely. The uniqueness of fe80::IID/64 is also guaranteed. IIRC, PPPoE on MPD5 follows it (but I may misremeber).

Configuring a point-to-point link with a /128 link-local prefix is not a scenario the specification expects, and it just makes things complicated. While I do not strongly object to this change because it is not harmful, if the motivation stems from /128 LLA configurations, I would say it can always be replaced with /64 LLAs. There is a valid reason to use a longer prefix only when using GUAs, such as a /127 prefix for a point-to-point link between two routers, as documented in RFC 6164.

The issue is not so much that of forwarding as it is what the address describes. From my perspective, the PPPoE link does legitimately only have two addresses on any encapsulated protocol - the self and the peer, so the host address is best described by a p2p ifa with a /128 prefix and a destination analogous to what is done with IPv4 PPP links. Absent the dstaddr there is no property of the ifa that identifies the address of the PPP peer, and IPv6 ifas are forbidden to have a dstaddr unless the source addresses has a /128 prefix.

Using a /64 prefix and no dstaddr deviates from the established practice of IPv4 PPPoE which does utilize the dstaddr and a full /32 mask. To expand on what I previously stated about the PPP peer, absent a dstaddr ifa_is_p2p() cannot determine that an ifa is in fact a p2p address and in6_notify_ifa() will not install the route to the peer when the address is added. It is entirely up to the PPPoE client/server software to install the route, which further deviates from existing IPv4 PPPoE practice. In the IPv4 case, in_aifaddr_iotcl() calls in_handle_ifaddr_route() to install a route to the network prefix which, for PPP links, is the ifa's dstaddr as returned by ia_getrtprefix(). In addition to the route not being added, the ifconfig output does not provide any indication that the LLA is in fact the established PPP p2p address as is the case with IPv4.

I'm also looking to linux as an established example of how IPv6 is implemented on PPPoE. The /128 LLA paired with a destination address appears to be the precedent for PPPoE links in that ecosystem, and ipconfig output for PPPoE links shows the IPv6 LLA as a p2p address with its peer as I'd expect. This doesn't mean it's strictly correct (I honestly think the RFCs don't provide adequate guidance), but it is an established precedent with wide use.

The argument could be made that we should retain the /64 prefix, and in that case the alternative to this patch would require changing in6_validate_ifra() to allow /64 prefix for ifas with a dstaddr. I seem to recall trying this but it was accompanied by other problems (most likely failure to add the route), and in light of all of the information above I pursued the /128 prefix instead.

Regardless of the chosen LLA prefix length, in6_ifadd() currently doesn't look to any other source for an interface id except for the LLA, and as the comments state a LLA is not strictly required to get the interface id. I think at least the change to in6_ifadd() would also apply to the more general case where SLAAC is used to configure networks with a prefixlen that does not match that of the LLA, as described in rfc4862 section 5.5.3.

This revision was not accepted when it landed; it landed in state Needs Review.Fri, Sep 5, 9:53 PM
This revision was automatically updated to reflect the committed changes.