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