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
This commit is contained in:
Reid Linnemann
2025-09-05 21:57:44 +02:00
committed by Kristof Provost
parent af60084978
commit 9e792f7ef7
3 changed files with 43 additions and 19 deletions
+3 -4
View File
@@ -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 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
*
* 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,
+1
View File
@@ -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 */
+39 -15
View File
@@ -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);
/* 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));
}
}
/* 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. */