The branch main has been updated by pouria:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=0951901814d1def721ac9a4fc3657af5c9694228

commit 0951901814d1def721ac9a4fc3657af5c9694228
Author:     Pouria Mousavizadeh Tehrani <[email protected]>
AuthorDate: 2026-03-06 11:48:23 +0000
Commit:     Pouria Mousavizadeh Tehrani <[email protected]>
CommitDate: 2026-03-06 12:35:09 +0000

    rtadvd: add multi pref64 support
    
    Add support for multi pref64 in rtadvd and rtadvctl
    
    Reviewed By: zlei, bz
    Differential Revision: https://reviews.freebsd.org/D54636
---
 usr.sbin/rtadvctl/rtadvctl.c     |  46 ++++++++++++++
 usr.sbin/rtadvd/config.c         | 130 ++++++++++++++++++++++-----------------
 usr.sbin/rtadvd/config.h         |   1 +
 usr.sbin/rtadvd/control_server.c |  56 +++++++++++++++++
 usr.sbin/rtadvd/rtadvd.c         |   8 ++-
 usr.sbin/rtadvd/rtadvd.conf.5    |  23 ++++++-
 usr.sbin/rtadvd/rtadvd.h         |   3 +-
 7 files changed, 204 insertions(+), 63 deletions(-)

diff --git a/usr.sbin/rtadvctl/rtadvctl.c b/usr.sbin/rtadvctl/rtadvctl.c
index 9fdb643cef10..201993ab06df 100644
--- a/usr.sbin/rtadvctl/rtadvctl.c
+++ b/usr.sbin/rtadvctl/rtadvctl.c
@@ -89,6 +89,7 @@ static int    action_show_prefix(struct prefix *);
 static int     action_show_rtinfo(struct rtinfo *);
 static int     action_show_rdnss(void *);
 static int     action_show_dnssl(void *);
+static void    action_show_pref64(void *);
 
 static int     csock_client_open(struct sockinfo *);
 static size_t  dname_labeldec(char *, size_t, const char *);
@@ -414,6 +415,7 @@ action_show(int argc, char **argv)
        char argv_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")];
        char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")];
        char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")];
+       char argv_pref64[IFNAMSIZ + sizeof(":pref64=")];
        char ssbuf[SSBUFLEN];
 
        struct timespec now, ts0, ts;
@@ -691,6 +693,21 @@ action_show(int argc, char **argv)
                        action_show_dnssl(cp.cp_val);
                }
 
+               /* PREF64 information */
+               sprintf(argv_pref64, "%s:pref64=", ifi->ifi_ifname);
+               action_argv = argv_pref64;
+
+               error = action_propget(action_argv, &cp);
+               if (error)
+                       continue;
+
+               len = *((uint16_t *)cp.cp_val);
+
+               if (len > 0) {
+                       printf("\tPREF64:\n");
+                       action_show_pref64(cp.cp_val);
+               }
+
                if (vflag < LOG_NOTICE)
                        continue;
 
@@ -896,6 +913,35 @@ action_show_dnssl(void *msg)
        return (0);
 }
 
+static void
+action_show_pref64(void *msg)
+{
+       struct pref64 *prf64;
+       uint16_t *prf64_cnt;
+       char ntopbuf[INET6_ADDRSTRLEN];
+       char ssbuf[SSBUFLEN];
+       char *p;
+       int i;
+       uint16_t prf64len;
+
+       p = msg;
+       prf64_cnt = (uint16_t *)p;
+       p += sizeof(*prf64_cnt);
+
+       for (i = 0; i < *prf64_cnt; i++) {
+               prf64 = (struct pref64 *)p;
+
+               /* RFC 8781 Section 4: Map PLC values to prefix lengths */
+               prf64len = (prf64->p64_plc == 0) ? 96 : 72 - (8 * 
prf64->p64_plc);
+               printf("\t  %s/%d (ltime: %s)\n",
+                   inet_ntop(AF_INET6, &prf64->p64_prefix,
+                       ntopbuf, sizeof(ntopbuf)),
+                   prf64len, sec2str(prf64->p64_sl, ssbuf));
+
+               p += sizeof(*prf64);
+       }
+}
+
 /* Decode domain name label encoding in RFC 1035 Section 3.1 */
 static size_t
 dname_labeldec(char *dst, size_t dlen, const char *src)
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
index 1b37d53c8b91..83b2efb68303 100644
--- a/usr.sbin/rtadvd/config.c
+++ b/usr.sbin/rtadvd/config.c
@@ -291,6 +291,7 @@ rm_rainfo(struct rainfo *rai)
        struct rdnss *rdn;
        struct rdnss_addr *rdna;
        struct dnssl *dns;
+       struct pref64 *prf64;
        struct rtinfo *rti;
 
        syslog(LOG_DEBUG, "<%s>: enter",  __func__);
@@ -325,6 +326,10 @@ rm_rainfo(struct rainfo *rai)
                TAILQ_REMOVE(&rai->rai_route, rti, rti_next);
                free(rti);
        }
+       while ((prf64 = TAILQ_FIRST(&rai->rai_pref64)) != NULL) {
+               TAILQ_REMOVE(&rai->rai_pref64, prf64, p64_next);
+               free(prf64);
+       }
        free(rai);
        syslog(LOG_DEBUG, "<%s>: leave",  __func__);
 
@@ -369,6 +374,7 @@ getconfig(struct ifinfo *ifi)
        TAILQ_INIT(&rai->rai_route);
        TAILQ_INIT(&rai->rai_rdnss);
        TAILQ_INIT(&rai->rai_dnssl);
+       TAILQ_INIT(&rai->rai_pref64);
        TAILQ_INIT(&rai->rai_soliciter);
        rai->rai_ifinfo = ifi;
 
@@ -916,52 +922,62 @@ getconfig_free_dns:
        /*
         * handle pref64
         */
-       rai->rai_pref64.p64_enabled = false;
+       for (i = -1; i < MAXPREF64 ; i++) {
+               struct pref64 *prf64;
+
+               makeentry(entbuf, sizeof(entbuf), i, "pref64");
+               addr = (char *)agetstr(entbuf, &bp);
+               if (addr == NULL)
+                       continue;
+               ELM_MALLOC(prf64, exit(1));
 
-       if ((addr = (char *)agetstr("pref64", &bp))) {
-               if (inet_pton(AF_INET6, addr, &rai->rai_pref64.p64_prefix) != 
1) {
+               if (inet_pton(AF_INET6, addr, &prf64->p64_prefix) != 1) {
                        syslog(LOG_ERR, "<%s> inet_pton failed for %s",
                            __func__, addr);
-               } else {
-                       rai->rai_pref64.p64_enabled = true;
-
-                       switch (val64 = agetnum("pref64len")) {
-                       case -1:
-                       case 96:
-                               rai->rai_pref64.p64_plc = 0;
-                               break;
-                       case 64:
-                               rai->rai_pref64.p64_plc = 1;
-                               break;
-                       case 56:
-                               rai->rai_pref64.p64_plc = 2;
-                               break;
-                       case 48:
-                               rai->rai_pref64.p64_plc = 3;
-                               break;
-                       case 40:
-                               rai->rai_pref64.p64_plc = 4;
-                               break;
-                       case 32:
-                               rai->rai_pref64.p64_plc = 5;
-                               break;
-                       default:
-                               syslog(LOG_ERR, "prefix length %" PRIi64
-                                      "on %s is invalid; disabling PREF64",
-                                      val64, ifi->ifi_ifname);
-                               rai->rai_pref64.p64_enabled = 0;
-                               break;
-                       }
+                       goto getconfig_free_prf64;
+               }
 
-                       /* This logic is from RFC 8781 section 4.1. */
-                       val64 = agetnum("pref64lifetime");
-                       if (val64 == -1)
-                               val64 = rai->rai_lifetime * 3;
-                       if (val64 > 65528)
-                               val64 = 65528;
-                       val64 = (val64 + 7) / 8;
-                       rai->rai_pref64.p64_sl = (uint16_t) (uint64_t) val64;
+               makeentry(entbuf, sizeof(entbuf), i, "pref64len");
+               MAYHAVE(val64, entbuf, 96);
+               switch (val64) {
+               case 96:
+                       prf64->p64_plc = 0;
+                       break;
+               case 64:
+                       prf64->p64_plc = 1;
+                       break;
+               case 56:
+                       prf64->p64_plc = 2;
+                       break;
+               case 48:
+                       prf64->p64_plc = 3;
+                       break;
+               case 40:
+                       prf64->p64_plc = 4;
+                       break;
+               case 32:
+                       prf64->p64_plc = 5;
+                       break;
+               default:
+                       syslog(LOG_ERR, "PREF64 prefix length %" PRIi64
+                              "on %s is invalid; skipping",
+                              val64, ifi->ifi_ifname);
+                       goto getconfig_free_prf64;
                }
+
+               makeentry(entbuf, sizeof(entbuf), i, "pref64lifetime");
+               MAYHAVE(val64, entbuf, (rai->rai_lifetime * 3));
+               /* This logic is from RFC 8781 section 4.1. */
+               if (val64 > 65528)
+                       val64 = 65528;
+               val64 = (val64 + 7) / 8;
+               prf64->p64_sl = (uint16_t)val64;
+
+               /* link into chain */
+               TAILQ_INSERT_TAIL(&rai->rai_pref64, prf64, p64_next);
+               continue;
+getconfig_free_prf64:
+               free(prf64);
        }
 
        /* construct the sending packet */
@@ -1386,6 +1402,7 @@ make_packet(struct rainfo *rai)
        struct rdnss *rdn;
        struct nd_opt_dnssl *ndopt_dnssl;
        struct dnssl *dns;
+       struct pref64 *prf64;
        struct nd_opt_pref64 *ndopt_pref64;
        size_t len;
        struct prefix *pfx;
@@ -1408,8 +1425,6 @@ make_packet(struct rainfo *rai)
                packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs;
        if (rai->rai_linkmtu)
                packlen += sizeof(struct nd_opt_mtu);
-       if (rai->rai_pref64.p64_enabled)
-               packlen += sizeof(struct nd_opt_pref64);
 
        TAILQ_FOREACH(rti, &rai->rai_route, rti_next)
                packlen += sizeof(struct nd_opt_route_info) +
@@ -1436,6 +1451,9 @@ make_packet(struct rainfo *rai)
 
                packlen += len;
        }
+       TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next)
+               packlen += sizeof(struct nd_opt_pref64);
+
        /* allocate memory for the packet */
        if ((buf = malloc(packlen)) == NULL) {
                syslog(LOG_ERR,
@@ -1490,19 +1508,6 @@ make_packet(struct rainfo *rai)
                buf += sizeof(struct nd_opt_mtu);
        }
 
-       if (rai->rai_pref64.p64_enabled) {
-               ndopt_pref64 = (struct nd_opt_pref64 *)buf;
-               ndopt_pref64->nd_opt_pref64_type = ND_OPT_PREF64;
-               ndopt_pref64->nd_opt_pref64_len = 2;
-               ndopt_pref64->nd_opt_pref64_sl_plc =
-                       (htons(rai->rai_pref64.p64_sl << 3)) |
-                       htons((rai->rai_pref64.p64_plc & 0x7));
-               memcpy(&ndopt_pref64->nd_opt_prefix[0],
-                      &rai->rai_pref64.p64_prefix,
-                      sizeof(ndopt_pref64->nd_opt_prefix));
-               buf += sizeof(struct nd_opt_pref64);
-       }
-
        TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
                uint32_t vltime, pltime;
                struct timespec now;
@@ -1616,4 +1621,17 @@ make_packet(struct rainfo *rai)
                syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__,
                    ndopt_dnssl->nd_opt_dnssl_len);
        }
+
+       TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next) {
+               ndopt_pref64 = (struct nd_opt_pref64 *)buf;
+               ndopt_pref64->nd_opt_pref64_type = ND_OPT_PREF64;
+               ndopt_pref64->nd_opt_pref64_len = 2;
+               ndopt_pref64->nd_opt_pref64_sl_plc =
+                       (htons(prf64->p64_sl << 3)) |
+                       htons((prf64->p64_plc & 0x7));
+               memcpy(&ndopt_pref64->nd_opt_prefix[0],
+                      &prf64->p64_prefix,
+                      sizeof(ndopt_pref64->nd_opt_prefix));
+               buf += sizeof(struct nd_opt_pref64);
+       }
 }
diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h
index cfea1821ca5e..d795aab066cd 100644
--- a/usr.sbin/rtadvd/config.h
+++ b/usr.sbin/rtadvd/config.h
@@ -52,3 +52,4 @@ extern void get_prefix(struct rainfo *);
 #define MAXROUTE       100
 #define MAXRDNSSENT    100
 #define MAXDNSSLENT    100
+#define MAXPREF64      100
diff --git a/usr.sbin/rtadvd/control_server.c b/usr.sbin/rtadvd/control_server.c
index 60fdc5ca2ec0..e38045b0d574 100644
--- a/usr.sbin/rtadvd/control_server.c
+++ b/usr.sbin/rtadvd/control_server.c
@@ -80,6 +80,7 @@ static int cm_getprop_rai(struct ctrl_msg_pl *);
 static int cm_getprop_pfx(struct ctrl_msg_pl *);
 static int cm_getprop_rdnss(struct ctrl_msg_pl *);
 static int cm_getprop_dnssl(struct ctrl_msg_pl *);
+static int cm_getprop_pref64(struct ctrl_msg_pl *);
 static int cm_getprop_rti(struct ctrl_msg_pl *);
 
 static int cm_setprop_reload(struct ctrl_msg_pl *);
@@ -101,6 +102,7 @@ static struct dispatch_table {
        DEF_PL_HANDLER(pfx),
        DEF_PL_HANDLER(rdnss),
        DEF_PL_HANDLER(dnssl),
+       DEF_PL_HANDLER(pref64),
 };
 
 static int
@@ -516,6 +518,60 @@ cm_getprop_dnssl(struct ctrl_msg_pl *cp)
        return (0);
 }
 
+static int
+cm_getprop_pref64(struct ctrl_msg_pl *cp)
+{
+       struct ifinfo *ifi;
+       struct rainfo *rai;
+       struct pref64 *prf64;
+       char *p;
+       size_t len;
+       uint16_t *prf64_cnt;
+
+       syslog(LOG_DEBUG, "<%s> enter", __func__);
+
+       len = 0;
+       TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
+               if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0)
+                       break;
+       }
+       if (ifi == NULL) {
+               syslog(LOG_ERR, "<%s> %s not found", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       if (ifi->ifi_rainfo == NULL) {
+               syslog(LOG_ERR, "<%s> %s has no rainfo", __func__,
+                   cp->cp_ifname);
+               return (1);
+       }
+       rai = ifi->ifi_rainfo;
+
+       len = sizeof(*prf64_cnt);
+       TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next)
+               len += sizeof(*prf64);
+
+       syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len);
+
+       p = malloc(len);
+       if (p == NULL)
+               exit(1);
+       memset(p, 0, len);
+       cp->cp_val = p;
+
+       prf64_cnt = (uint16_t *)cp->cp_val;
+       p += sizeof(*prf64_cnt);
+       TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next) {
+               (*prf64_cnt)++;
+               memcpy(p, prf64, sizeof(*prf64));
+               p += sizeof(*prf64);
+       }
+       cp->cp_val_len = p - cp->cp_val;
+
+       return (0);
+}
+
+
 int
 cm_getprop(struct ctrl_msg_pl *cp)
 {
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
index fa5640afa96c..1eb8f12a7338 100644
--- a/usr.sbin/rtadvd/rtadvd.c
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -137,6 +137,7 @@ union nd_opt {
 #define NDOPT_FLAG_MTU         (1 << 4)
 #define NDOPT_FLAG_RDNSS       (1 << 5)
 #define NDOPT_FLAG_DNSSL       (1 << 6)
+#define NDOPT_FLAG_PREF64      (1 << 7)
 
 static uint32_t ndopt_flags[] = {
        [ND_OPT_SOURCE_LINKADDR]        = NDOPT_FLAG_SRCLINKADDR,
@@ -146,6 +147,7 @@ static uint32_t ndopt_flags[] = {
        [ND_OPT_MTU]                    = NDOPT_FLAG_MTU,
        [ND_OPT_RDNSS]                  = NDOPT_FLAG_RDNSS,
        [ND_OPT_DNSSL]                  = NDOPT_FLAG_DNSSL,
+       [ND_OPT_PREF64]                 = NDOPT_FLAG_PREF64,
 };
 
 static void    rtadvd_shutdown(void);
@@ -1083,7 +1085,7 @@ ra_input(int len, struct nd_router_advert *nra,
        error = nd6_options((struct nd_opt_hdr *)(nra + 1),
            len - sizeof(struct nd_router_advert), &ndopts,
            NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU |
-           NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL);
+           NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL | NDOPT_FLAG_PREF64);
        if (error) {
                syslog(LOG_INFO,
                    "<%s> ND option check failed for an RA from %s on %s",
@@ -1428,7 +1430,8 @@ nd6_options(struct nd_opt_hdr *hdr, int limit,
 
                if (hdr->nd_opt_type > ND_OPT_MTU &&
                    hdr->nd_opt_type != ND_OPT_RDNSS &&
-                   hdr->nd_opt_type != ND_OPT_DNSSL) {
+                   hdr->nd_opt_type != ND_OPT_DNSSL &&
+                   hdr->nd_opt_type != ND_OPT_PREF64) {
                        syslog(LOG_INFO, "<%s> unknown ND option(type %d)",
                            __func__, hdr->nd_opt_type);
                        continue;
@@ -1473,6 +1476,7 @@ skip:
                case ND_OPT_REDIRECTED_HEADER:
                case ND_OPT_RDNSS:
                case ND_OPT_DNSSL:
+               case ND_OPT_PREF64:
                        break;  /* we don't care about these options */
                case ND_OPT_SOURCE_LINKADDR:
                case ND_OPT_MTU:
diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5
index 8158d09f99cf..5af4865885c4 100644
--- a/usr.sbin/rtadvd/rtadvd.conf.5
+++ b/usr.sbin/rtadvd/rtadvd.conf.5
@@ -27,7 +27,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 4, 2011
+.Dd January 14, 2026
 .Dt RTADVD.CONF 5
 .Os
 .Sh NAME
@@ -435,6 +435,21 @@ These items are optional.
 .Bl -tag -width indent
 .It Cm \&pref64
 (str) The prefix to advertise in the PREF64 option.
+Multiple PREF64 prefixes can be specified by seperating entries using
+.Cm pref64 ,
+.Cm pref640 ,
+.Cm pref641 ,
+.Cm pref642 ...
+options with corresponding
+.Cm pref64len ,
+.Cm pref64len0 ,
+.Cm pref64len1 ,
+.Cm pref64len2 ...
+entries.
+This is also true for the
+.Cm pref64lifetime
+option.
+Note that the maximum number of prefixes depends on the receiver side.
 .It Cm \&pref64len
 (num) The length of the PREF64 prefix.
 This must be 96, 64, 56, 48, 40, or 32.
@@ -484,13 +499,15 @@ ef0:\\
 .Pp
 The following example configures the
 .Li wlan0
-interface and adds two DNS servers and a DNS domain search options
+interface and adds two DNS servers, a DNS domain search,
+and a PREF64 prefix,
 using the default option lifetime values.
 .Bd -literal -offset indent
 wlan0:\\
        :addr="2001:db8:ffff:1000::":prefixlen#64:\\
        :rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\
-       :dnssl="example.com":
+       :dnssl="example.com":\\
+       :pref64="64:ff9b::":
 .Ed
 .Pp
 The following example presents the default values in an explicit manner.
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
index 597fb2f47f0d..5ecfd1b56423 100644
--- a/usr.sbin/rtadvd/rtadvd.h
+++ b/usr.sbin/rtadvd/rtadvd.h
@@ -152,7 +152,6 @@ struct rdnss {
 
 struct pref64 {
        TAILQ_ENTRY(pref64) p64_next;
-       bool            p64_enabled;
        uint16_t        p64_plc;        /* prefix length code */
        uint16_t        p64_sl;         /* scaled lifetime */
        struct in6_addr p64_prefix;
@@ -227,7 +226,7 @@ struct      rainfo {
        /* actual RA packet data and its length */
        size_t  rai_ra_datalen;
        char    *rai_ra_data;
-       struct pref64 rai_pref64;       /* PREF64 option */
+       TAILQ_HEAD(, pref64) rai_pref64; /* PREF64 option */
 
        /* info about soliciter */
        TAILQ_HEAD(, soliciter) rai_soliciter;  /* recent solication source */

Reply via email to