rtadvd(8): support PREF64 (RFC 8781)

PREF64 allows a router to advertise the network's NAT64 prefix, allowing
clients to auto-configure CLAT.  This makes it possible to deploy
IPv6-only or IPv6-mostly client access networks without the need for
DNS64.

Reviewed by: imp, glebius (prior suggetions done)
Pull Request: https://github.com/freebsd/freebsd-src/pull/1206
This commit is contained in:
Lexi Winter 2024-04-26 22:41:37 +01:00 committed by Warner Losh
parent 1e8eb413f6
commit 77f06c476c
3 changed files with 97 additions and 0 deletions

View File

@ -912,6 +912,58 @@ getconfig_free_dns:
}
free(dns);
}
/*
* handle pref64
*/
rai->rai_pref64.p64_enabled = false;
if ((addr = (char *)agetstr("pref64", &bp))) {
if (inet_pton(AF_INET6, addr, &rai->rai_pref64.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;
}
/* 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;
}
}
/* construct the sending packet */
make_packet(rai);
@ -1334,6 +1386,7 @@ make_packet(struct rainfo *rai)
struct rdnss *rdn;
struct nd_opt_dnssl *ndopt_dnssl;
struct dnssl *dns;
struct nd_opt_pref64 *ndopt_pref64;
size_t len;
struct prefix *pfx;
struct ifinfo *ifi;
@ -1355,6 +1408,8 @@ 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) +
@ -1435,6 +1490,19 @@ 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;

View File

@ -428,6 +428,24 @@ DNS search list entries.
The default value is 3/2 of the interval time.
.El
.Pp
The following items are for PREF64 discovery
.Pq RFC 8781 ,
which will advertise the network's NAT64 prefix to clients.
These items are optional.
.Bl -tag -width indent
.It Cm \&pref64
(str) The prefix to advertise in the PREF64 option.
.It Cm \&pref64len
(num) The length of the PREF64 prefix.
This must be 96, 64, 56, 48, 40, or 32.
If not specified, the default is 96.
.It Cm \&pref64lifetime
(num) The prefix lifetime to advertise in the PREF64 option.
This should be at least as long as the RA lifetime, but cannot be greater
than 65528.
If not specified, the default is the RA lifetime, or 65528, whichever is lower.
.El
.Pp
You can also refer one line from another by using
.Cm tc
capability.

View File

@ -32,6 +32,8 @@
* SUCH DAMAGE.
*/
#include <stdbool.h>
#define ELM_MALLOC(p,error_action) \
do { \
p = malloc(sizeof(*p)); \
@ -148,6 +150,14 @@ struct rdnss {
uint32_t rd_ltime; /* number of seconds valid */
};
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;
};
/*
* The maximum length of a domain name in a DNS search list is calculated
* by a domain name + length fields per 63 octets + a zero octet at
@ -217,6 +227,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 */
/* info about soliciter */
TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */