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
This commit is contained in:
Pouria Mousavizadeh Tehrani
2026-03-06 15:18:23 +03:30
parent e85f221def
commit 0951901814
7 changed files with 205 additions and 64 deletions
+46
View File
@@ -89,6 +89,7 @@ static int action_show_prefix(struct prefix *);
static int action_show_rtinfo(struct rtinfo *); static int action_show_rtinfo(struct rtinfo *);
static int action_show_rdnss(void *); static int action_show_rdnss(void *);
static int action_show_dnssl(void *); static int action_show_dnssl(void *);
static void action_show_pref64(void *);
static int csock_client_open(struct sockinfo *); static int csock_client_open(struct sockinfo *);
static size_t dname_labeldec(char *, size_t, const char *); 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_ifi_ra_timer[IFNAMSIZ + sizeof(":ifi_ra_timer=")];
char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")]; char argv_rdnss[IFNAMSIZ + sizeof(":rdnss=")];
char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")]; char argv_dnssl[IFNAMSIZ + sizeof(":dnssl=")];
char argv_pref64[IFNAMSIZ + sizeof(":pref64=")];
char ssbuf[SSBUFLEN]; char ssbuf[SSBUFLEN];
struct timespec now, ts0, ts; struct timespec now, ts0, ts;
@@ -691,6 +693,21 @@ action_show(int argc, char **argv)
action_show_dnssl(cp.cp_val); 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) if (vflag < LOG_NOTICE)
continue; continue;
@@ -896,6 +913,35 @@ action_show_dnssl(void *msg)
return (0); 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 */ /* Decode domain name label encoding in RFC 1035 Section 3.1 */
static size_t static size_t
dname_labeldec(char *dst, size_t dlen, const char *src) dname_labeldec(char *dst, size_t dlen, const char *src)
+70 -52
View File
@@ -291,6 +291,7 @@ rm_rainfo(struct rainfo *rai)
struct rdnss *rdn; struct rdnss *rdn;
struct rdnss_addr *rdna; struct rdnss_addr *rdna;
struct dnssl *dns; struct dnssl *dns;
struct pref64 *prf64;
struct rtinfo *rti; struct rtinfo *rti;
syslog(LOG_DEBUG, "<%s>: enter", __func__); syslog(LOG_DEBUG, "<%s>: enter", __func__);
@@ -325,6 +326,10 @@ rm_rainfo(struct rainfo *rai)
TAILQ_REMOVE(&rai->rai_route, rti, rti_next); TAILQ_REMOVE(&rai->rai_route, rti, rti_next);
free(rti); free(rti);
} }
while ((prf64 = TAILQ_FIRST(&rai->rai_pref64)) != NULL) {
TAILQ_REMOVE(&rai->rai_pref64, prf64, p64_next);
free(prf64);
}
free(rai); free(rai);
syslog(LOG_DEBUG, "<%s>: leave", __func__); syslog(LOG_DEBUG, "<%s>: leave", __func__);
@@ -369,6 +374,7 @@ getconfig(struct ifinfo *ifi)
TAILQ_INIT(&rai->rai_route); TAILQ_INIT(&rai->rai_route);
TAILQ_INIT(&rai->rai_rdnss); TAILQ_INIT(&rai->rai_rdnss);
TAILQ_INIT(&rai->rai_dnssl); TAILQ_INIT(&rai->rai_dnssl);
TAILQ_INIT(&rai->rai_pref64);
TAILQ_INIT(&rai->rai_soliciter); TAILQ_INIT(&rai->rai_soliciter);
rai->rai_ifinfo = ifi; rai->rai_ifinfo = ifi;
@@ -916,52 +922,62 @@ getconfig(struct ifinfo *ifi)
/* /*
* handle pref64 * handle pref64
*/ */
rai->rai_pref64.p64_enabled = false; for (i = -1; i < MAXPREF64 ; i++) {
struct pref64 *prf64;
if ((addr = (char *)agetstr("pref64", &bp))) { makeentry(entbuf, sizeof(entbuf), i, "pref64");
if (inet_pton(AF_INET6, addr, &rai->rai_pref64.p64_prefix) != 1) { addr = (char *)agetstr(entbuf, &bp);
if (addr == NULL)
continue;
ELM_MALLOC(prf64, exit(1));
if (inet_pton(AF_INET6, addr, &prf64->p64_prefix) != 1) {
syslog(LOG_ERR, "<%s> inet_pton failed for %s", syslog(LOG_ERR, "<%s> inet_pton failed for %s",
__func__, addr); __func__, addr);
} else { goto getconfig_free_prf64;
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;
} }
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. */ /* This logic is from RFC 8781 section 4.1. */
val64 = agetnum("pref64lifetime");
if (val64 == -1)
val64 = rai->rai_lifetime * 3;
if (val64 > 65528) if (val64 > 65528)
val64 = 65528; val64 = 65528;
val64 = (val64 + 7) / 8; val64 = (val64 + 7) / 8;
rai->rai_pref64.p64_sl = (uint16_t) (uint64_t) val64; 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 */ /* construct the sending packet */
@@ -1386,6 +1402,7 @@ make_packet(struct rainfo *rai)
struct rdnss *rdn; struct rdnss *rdn;
struct nd_opt_dnssl *ndopt_dnssl; struct nd_opt_dnssl *ndopt_dnssl;
struct dnssl *dns; struct dnssl *dns;
struct pref64 *prf64;
struct nd_opt_pref64 *ndopt_pref64; struct nd_opt_pref64 *ndopt_pref64;
size_t len; size_t len;
struct prefix *pfx; struct prefix *pfx;
@@ -1408,8 +1425,6 @@ make_packet(struct rainfo *rai)
packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs; packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs;
if (rai->rai_linkmtu) if (rai->rai_linkmtu)
packlen += sizeof(struct nd_opt_mtu); 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) TAILQ_FOREACH(rti, &rai->rai_route, rti_next)
packlen += sizeof(struct nd_opt_route_info) + packlen += sizeof(struct nd_opt_route_info) +
@@ -1436,6 +1451,9 @@ make_packet(struct rainfo *rai)
packlen += len; packlen += len;
} }
TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next)
packlen += sizeof(struct nd_opt_pref64);
/* allocate memory for the packet */ /* allocate memory for the packet */
if ((buf = malloc(packlen)) == NULL) { if ((buf = malloc(packlen)) == NULL) {
syslog(LOG_ERR, syslog(LOG_ERR,
@@ -1490,19 +1508,6 @@ make_packet(struct rainfo *rai)
buf += sizeof(struct nd_opt_mtu); 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) { TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
uint32_t vltime, pltime; uint32_t vltime, pltime;
struct timespec now; struct timespec now;
@@ -1616,4 +1621,17 @@ make_packet(struct rainfo *rai)
syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__, syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__,
ndopt_dnssl->nd_opt_dnssl_len); 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);
}
} }
+1
View File
@@ -52,3 +52,4 @@ extern void get_prefix(struct rainfo *);
#define MAXROUTE 100 #define MAXROUTE 100
#define MAXRDNSSENT 100 #define MAXRDNSSENT 100
#define MAXDNSSLENT 100 #define MAXDNSSLENT 100
#define MAXPREF64 100
+56
View File
@@ -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_pfx(struct ctrl_msg_pl *);
static int cm_getprop_rdnss(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_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_getprop_rti(struct ctrl_msg_pl *);
static int cm_setprop_reload(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(pfx),
DEF_PL_HANDLER(rdnss), DEF_PL_HANDLER(rdnss),
DEF_PL_HANDLER(dnssl), DEF_PL_HANDLER(dnssl),
DEF_PL_HANDLER(pref64),
}; };
static int static int
@@ -516,6 +518,60 @@ cm_getprop_dnssl(struct ctrl_msg_pl *cp)
return (0); 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 int
cm_getprop(struct ctrl_msg_pl *cp) cm_getprop(struct ctrl_msg_pl *cp)
{ {
+6 -2
View File
@@ -137,6 +137,7 @@ union nd_opt {
#define NDOPT_FLAG_MTU (1 << 4) #define NDOPT_FLAG_MTU (1 << 4)
#define NDOPT_FLAG_RDNSS (1 << 5) #define NDOPT_FLAG_RDNSS (1 << 5)
#define NDOPT_FLAG_DNSSL (1 << 6) #define NDOPT_FLAG_DNSSL (1 << 6)
#define NDOPT_FLAG_PREF64 (1 << 7)
static uint32_t ndopt_flags[] = { static uint32_t ndopt_flags[] = {
[ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR,
@@ -146,6 +147,7 @@ static uint32_t ndopt_flags[] = {
[ND_OPT_MTU] = NDOPT_FLAG_MTU, [ND_OPT_MTU] = NDOPT_FLAG_MTU,
[ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS,
[ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL,
[ND_OPT_PREF64] = NDOPT_FLAG_PREF64,
}; };
static void rtadvd_shutdown(void); 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), error = nd6_options((struct nd_opt_hdr *)(nra + 1),
len - sizeof(struct nd_router_advert), &ndopts, len - sizeof(struct nd_router_advert), &ndopts,
NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU | 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) { if (error) {
syslog(LOG_INFO, syslog(LOG_INFO,
"<%s> ND option check failed for an RA from %s on %s", "<%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 && if (hdr->nd_opt_type > ND_OPT_MTU &&
hdr->nd_opt_type != ND_OPT_RDNSS && 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)", syslog(LOG_INFO, "<%s> unknown ND option(type %d)",
__func__, hdr->nd_opt_type); __func__, hdr->nd_opt_type);
continue; continue;
@@ -1473,6 +1476,7 @@ nd6_options(struct nd_opt_hdr *hdr, int limit,
case ND_OPT_REDIRECTED_HEADER: case ND_OPT_REDIRECTED_HEADER:
case ND_OPT_RDNSS: case ND_OPT_RDNSS:
case ND_OPT_DNSSL: case ND_OPT_DNSSL:
case ND_OPT_PREF64:
break; /* we don't care about these options */ break; /* we don't care about these options */
case ND_OPT_SOURCE_LINKADDR: case ND_OPT_SOURCE_LINKADDR:
case ND_OPT_MTU: case ND_OPT_MTU:
+20 -3
View File
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd June 4, 2011 .Dd January 14, 2026
.Dt RTADVD.CONF 5 .Dt RTADVD.CONF 5
.Os .Os
.Sh NAME .Sh NAME
@@ -435,6 +435,21 @@ These items are optional.
.Bl -tag -width indent .Bl -tag -width indent
.It Cm \&pref64 .It Cm \&pref64
(str) The prefix to advertise in the PREF64 option. (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 .It Cm \&pref64len
(num) The length of the PREF64 prefix. (num) The length of the PREF64 prefix.
This must be 96, 64, 56, 48, 40, or 32. This must be 96, 64, 56, 48, 40, or 32.
@@ -484,13 +499,15 @@ ef0:\\
.Pp .Pp
The following example configures the The following example configures the
.Li wlan0 .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. using the default option lifetime values.
.Bd -literal -offset indent .Bd -literal -offset indent
wlan0:\\ wlan0:\\
:addr="2001:db8:ffff:1000::":prefixlen#64:\\ :addr="2001:db8:ffff:1000::":prefixlen#64:\\
:rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\ :rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\
:dnssl="example.com": :dnssl="example.com":\\
:pref64="64:ff9b::":
.Ed .Ed
.Pp .Pp
The following example presents the default values in an explicit manner. The following example presents the default values in an explicit manner.
+1 -2
View File
@@ -152,7 +152,6 @@ struct rdnss {
struct pref64 { struct pref64 {
TAILQ_ENTRY(pref64) p64_next; TAILQ_ENTRY(pref64) p64_next;
bool p64_enabled;
uint16_t p64_plc; /* prefix length code */ uint16_t p64_plc; /* prefix length code */
uint16_t p64_sl; /* scaled lifetime */ uint16_t p64_sl; /* scaled lifetime */
struct in6_addr p64_prefix; struct in6_addr p64_prefix;
@@ -227,7 +226,7 @@ struct rainfo {
/* actual RA packet data and its length */ /* actual RA packet data and its length */
size_t rai_ra_datalen; size_t rai_ra_datalen;
char *rai_ra_data; char *rai_ra_data;
struct pref64 rai_pref64; /* PREF64 option */ TAILQ_HEAD(, pref64) rai_pref64; /* PREF64 option */
/* info about soliciter */ /* info about soliciter */
TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */ TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */