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. */

Reply via email to