The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=9e792f7ef7298080c058fbc2d36a4e60e596dae9
commit 9e792f7ef7298080c058fbc2d36a4e60e596dae9 Author: Reid Linnemann <rlinnem...@netgate.com> AuthorDate: 2025-09-05 19:57:44 +0000 Commit: Kristof Provost <k...@freebsd.org> CommitDate: 2025-09-05 21:48:48 +0000 sys/netinet6: Fix SLAAC for interfaces with no /64 LL address 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. Reviewed by kp Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D51778 --- sys/netinet6/in6_ifattach.c | 7 +++--- sys/netinet6/in6_ifattach.h | 1 + sys/netinet6/nd6_rtr.c | 54 ++++++++++++++++++++++++++++++++------------- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index f284f7fa5ffc..cc149616006e 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -83,7 +83,6 @@ VNET_DECLARE(struct inpcbinfo, ripcbinfo); #define V_ripcbinfo VNET(ripcbinfo) static int get_rand_ifid(struct ifnet *, struct in6_addr *); -static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *); static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *); static int in6_ifattach_loopback(struct ifnet *); static void in6_purgemaddrs(struct ifnet *); @@ -271,8 +270,8 @@ found: * * altifp - secondary EUI64 source */ -static int -get_ifid(struct ifnet *ifp0, struct ifnet *altifp, +int +in6_get_ifid(struct ifnet *ifp0, struct ifnet *altifp, struct in6_addr *in6) { struct ifnet *ifp; @@ -356,7 +355,7 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp) ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1); } else { NET_EPOCH_ENTER(et); - error = get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr); + error = in6_get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr); NET_EPOCH_EXIT(et); if (error != 0) { nd6log((LOG_ERR, diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index 897926e90078..fd52422b10be 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -41,6 +41,7 @@ void in6_ifdetach(struct ifnet *); void in6_ifdetach_destroy(struct ifnet *); void in6_tmpaddrtimer(void *); int in6_get_hw_ifid(struct ifnet *, struct in6_addr *); +int in6_get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *); int in6_nigroup(struct ifnet *, const char *, int, struct in6_addr *); int in6_nigroup_oldmcprefix(struct ifnet *, const char *, int, struct in6_addr *); #endif /* _KERNEL */ diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index b9af0a78a584..6fe78083df23 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1182,9 +1182,9 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) struct ifnet *ifp = pr->ndpr_ifp; struct ifaddr *ifa; struct in6_aliasreq ifra; - struct in6_ifaddr *ia, *ib; + struct in6_ifaddr *ia = NULL, *ib = NULL; int error, plen0; - struct in6_addr mask; + struct in6_addr *ifid_addr = NULL, mask; int prefixlen = pr->ndpr_plen; int updateflags; char ip6buf[INET6_ADDRSTRLEN]; @@ -1212,18 +1212,42 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) * with different interface identifiers. */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */ - if (ifa) + if (ifa) { ib = (struct in6_ifaddr *)ifa; - else - return NULL; + ifid_addr = &ib->ia_addr.sin6_addr; + + /* prefixlen + ifidlen must be equal to 128 */ + plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); + if (prefixlen != plen0) { + ifa_free(ifa); + ifid_addr = NULL; + nd6log((LOG_DEBUG, + "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n", + __func__, if_name(ifp), prefixlen, 128 - plen0)); + } + } - /* prefixlen + ifidlen must be equal to 128 */ - plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); - if (prefixlen != plen0) { - ifa_free(ifa); + /* No suitable LL address, get the ifid directly */ + if (ifid_addr == NULL) { + struct in6_addr taddr; + ifa = ifa_alloc(sizeof(taddr), M_WAITOK); + if (ifa) { + ib = (struct in6_ifaddr *)ifa; + ifid_addr = &ib->ia_addr.sin6_addr; + if(in6_get_ifid(ifp, NULL, ifid_addr) != 0) { + nd6log((LOG_DEBUG, + "%s: failed to get ifid for %s\n", + __func__, if_name(ifp))); + ifa_free(ifa); + ifid_addr = NULL; + } + } + } + + if (ifid_addr == NULL) { nd6log((LOG_INFO, - "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n", - __func__, if_name(ifp), prefixlen, 128 - plen0)); + "%s: could not determine ifid for %s\n", + __func__, if_name(ifp))); return NULL; } @@ -1233,13 +1257,13 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask); /* interface ID */ ifra.ifra_addr.sin6_addr.s6_addr32[0] |= - (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); + (ifid_addr->s6_addr32[0] & ~mask.s6_addr32[0]); ifra.ifra_addr.sin6_addr.s6_addr32[1] |= - (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); + (ifid_addr->s6_addr32[1] & ~mask.s6_addr32[1]); ifra.ifra_addr.sin6_addr.s6_addr32[2] |= - (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); + (ifid_addr->s6_addr32[2] & ~mask.s6_addr32[2]); ifra.ifra_addr.sin6_addr.s6_addr32[3] |= - (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); + (ifid_addr->s6_addr32[3] & ~mask.s6_addr32[3]); ifa_free(ifa); /* lifetimes. */