On 2022-10-15 13:00 +02, Florian Obser <[email protected]> wrote:
> With this clients can learn the presence and used prefix for Network
> Address and Protocol Translation between IPv6 and IPv4 (NAT64).
>
> Apparently there is support in mobile devices as well as in macOS.
>
> This option, together with the the dhcp "IPv6-only preferred"
> option (108) enables the Customer-side transLATor (CLAT) on macOS so
> IPv4 literals can be used in IPv6-only networks.
>
Better diff, I messed up the default and maximum lifetime seconds.
OK?
diff --git engine.c engine.c
index dddff2d4579..543abffe388 100644
--- engine.c
+++ engine.c
@@ -269,6 +269,7 @@ engine_dispatch_main(int fd, short event, void *bula)
struct ra_prefix_conf *ra_prefix_conf;
struct ra_rdnss_conf *ra_rdnss_conf;
struct ra_dnssl_conf *ra_dnssl_conf;
+ struct ra_pref64_conf *pref64;
ssize_t n;
int shut = 0;
@@ -333,6 +334,7 @@ engine_dispatch_main(int fd, short event, void *bula)
SIMPLEQ_INIT(&nconf->ra_iface_list);
SIMPLEQ_INIT(&nconf->ra_options.ra_rdnss_list);
SIMPLEQ_INIT(&nconf->ra_options.ra_dnssl_list);
+ SIMPLEQ_INIT(&nconf->ra_options.ra_pref64_list);
ra_options = &nconf->ra_options;
break;
case IMSG_RECONF_RA_IFACE:
@@ -349,6 +351,7 @@ engine_dispatch_main(int fd, short event, void *bula)
SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_rdnss_list);
SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_dnssl_list);
+ SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_pref64_list);
SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
ra_iface_conf, entry);
ra_options = &ra_iface_conf->ra_options;
@@ -405,6 +408,18 @@ engine_dispatch_main(int fd, short event, void *bula)
SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
ra_dnssl_conf, entry);
break;
+ case IMSG_RECONF_RA_PREF64:
+ if(IMSG_DATA_SIZE(imsg) != sizeof(struct
+ ra_pref64_conf))
+ fatalx("%s: IMSG_RECONF_RA_PREF64 wrong length:
"
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((pref64 = malloc(sizeof(struct ra_pref64_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(pref64, imsg.data, sizeof(struct
ra_pref64_conf));
+ SIMPLEQ_INSERT_TAIL(&ra_options->ra_pref64_list, pref64,
+ entry);
+ break;
case IMSG_RECONF_END:
if (nconf == NULL)
fatalx("%s: IMSG_RECONF_END without "
diff --git frontend.c frontend.c
index 30bcde218c4..435d6aed8a8 100644
--- frontend.c
+++ frontend.c
@@ -113,6 +113,14 @@ struct ra_iface {
uint8_t data[RA_MAX_SIZE];
};
+#define ND_OPT_PREF64 38
+struct nd_opt_pref64 {
+ u_int8_t nd_opt_pref64_type;
+ u_int8_t nd_opt_pref64_len;
+ u_int16_t nd_opt_pref64_sltime_plc;
+ u_int8_t nd_opt_pref64[12];
+};
+
TAILQ_HEAD(, ra_iface) ra_interfaces;
__dead void frontend_shutdown(void);
@@ -300,6 +308,7 @@ frontend_dispatch_main(int fd, short event, void *bula)
struct ra_prefix_conf *ra_prefix_conf;
struct ra_rdnss_conf *ra_rdnss_conf;
struct ra_dnssl_conf *ra_dnssl_conf;
+ struct ra_pref64_conf *pref64;
int n, shut = 0, icmp6sock, rdomain;
if (event & EV_READ) {
@@ -361,6 +370,7 @@ frontend_dispatch_main(int fd, short event, void *bula)
SIMPLEQ_INIT(&nconf->ra_iface_list);
SIMPLEQ_INIT(&nconf->ra_options.ra_rdnss_list);
SIMPLEQ_INIT(&nconf->ra_options.ra_dnssl_list);
+ SIMPLEQ_INIT(&nconf->ra_options.ra_pref64_list);
ra_options = &nconf->ra_options;
break;
case IMSG_RECONF_RA_IFACE:
@@ -377,6 +387,7 @@ frontend_dispatch_main(int fd, short event, void *bula)
SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_rdnss_list);
SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_dnssl_list);
+ SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_pref64_list);
SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
ra_iface_conf, entry);
ra_options = &ra_iface_conf->ra_options;
@@ -433,6 +444,18 @@ frontend_dispatch_main(int fd, short event, void *bula)
SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
ra_dnssl_conf, entry);
break;
+ case IMSG_RECONF_RA_PREF64:
+ if (IMSG_DATA_SIZE(imsg) != sizeof(struct
+ ra_pref64_conf))
+ fatalx("%s: IMSG_RECONF_RA_PREF64 wrong length:
"
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((pref64 = malloc(sizeof(struct ra_pref64_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(pref64, imsg.data, sizeof(struct
ra_pref64_conf));
+ SIMPLEQ_INSERT_TAIL(&ra_options->ra_pref64_list, pref64,
+ entry);
+ break;
case IMSG_RECONF_END:
if (nconf == NULL)
fatalx("%s: IMSG_RECONF_END without "
@@ -1084,8 +1107,10 @@ build_packet(struct ra_iface *ra_iface)
struct ra_prefix_conf *ra_prefix_conf;
struct nd_opt_rdnss *ndopt_rdnss;
struct nd_opt_dnssl *ndopt_dnssl;
+ struct nd_opt_pref64 *ndopt_pref64;
struct ra_rdnss_conf *ra_rdnss;
struct ra_dnssl_conf *ra_dnssl;
+ struct ra_pref64_conf *pref64;
size_t len, label_len;
uint8_t *p, buf[RA_MAX_SIZE];
char *label_start, *label_end;
@@ -1108,6 +1133,10 @@ build_packet(struct ra_iface *ra_iface)
len += sizeof(*ndopt_dnssl) +
((ra_iface_conf->ra_options.dnssl_len + 7) & ~7);
+ SIMPLEQ_FOREACH(pref64, &ra_iface_conf->ra_options.ra_pref64_list,
+ entry)
+ len += sizeof(struct nd_opt_pref64);
+
if (len > sizeof(ra_iface->data))
fatalx("%s: packet too big", __func__); /* XXX send multiple */
@@ -1207,6 +1236,46 @@ build_packet(struct ra_iface *ra_iface)
*p++ = '\0';
}
+ SIMPLEQ_FOREACH(pref64, &ra_iface_conf->ra_options.ra_pref64_list,
+ entry) {
+ uint16_t sltime_plc;
+
+ /* scaled lifetime in units of 8 seconds */
+ sltime_plc = pref64->ltime / 8;
+ sltime_plc = sltime_plc << 3;
+ /* encode prefix lenght in lower 3 bits */
+ switch (pref64->prefixlen) {
+ case 96:
+ sltime_plc |= 0;
+ break;
+ case 64:
+ sltime_plc |= 1;
+ break;
+ case 56:
+ sltime_plc |= 2;
+ break;
+ case 48:
+ sltime_plc |= 3;
+ break;
+ case 40:
+ sltime_plc |= 4;
+ break;
+ case 32:
+ sltime_plc |= 5;
+ break;
+ default:
+ fatalx("%s: invalid pref64 length: %d", __func__,
+ pref64->prefixlen);
+ }
+ ndopt_pref64 = (struct nd_opt_pref64 *)p;
+ ndopt_pref64->nd_opt_pref64_type = ND_OPT_PREF64;
+ ndopt_pref64->nd_opt_pref64_len = 2;
+ ndopt_pref64->nd_opt_pref64_sltime_plc = htons(sltime_plc);
+ memcpy(ndopt_pref64->nd_opt_pref64, &pref64->prefix,
+ sizeof(ndopt_pref64->nd_opt_pref64));
+ p += sizeof(struct nd_opt_pref64);
+ }
+
if (len != ra_iface->datalen || memcmp(buf, ra_iface->data, len)
!= 0) {
memcpy(ra_iface->data, buf, len);
diff --git parse.y parse.y
index 41b1f0da133..3c5e2d9e18e 100644
--- parse.y
+++ parse.y
@@ -96,11 +96,15 @@ static int errors;
static struct ra_iface_conf *ra_iface_conf;
static struct ra_prefix_conf *ra_prefix_conf;
+static struct ra_pref64_conf *ra_pref64_conf;
struct ra_prefix_conf *conf_get_ra_prefix(struct in6_addr*, int);
+struct ra_pref64_conf *conf_get_ra_pref64(struct in6_addr*, int);
struct ra_iface_conf *conf_get_ra_iface(char *);
void copy_dns_options(const struct ra_options_conf *,
struct ra_options_conf *);
+void copy_pref64_options(const struct ra_options_conf *,
+ struct ra_options_conf *);
typedef struct {
union {
@@ -116,7 +120,7 @@ typedef struct {
%token DEFAULT ROUTER HOP LIMIT MANAGED ADDRESS
%token CONFIGURATION OTHER LIFETIME REACHABLE TIME RETRANS TIMER
%token AUTO PREFIX VALID PREFERRED LIFETIME ONLINK AUTONOMOUS
-%token ADDRESS_CONFIGURATION DNS NAMESERVER SEARCH MTU
+%token ADDRESS_CONFIGURATION DNS NAMESERVER SEARCH MTU NAT64
%token <v.string> STRING
%token <v.number> NUMBER
@@ -215,6 +219,51 @@ ra_opt_block : DEFAULT ROUTER yesno {
| MTU NUMBER {
ra_options->mtu = $2;
}
+ | NAT64 PREFIX STRING {
+ struct in6_addr addr;
+ int prefixlen;
+ char *p;
+ const char *errstr;
+
+ memset(&addr, 0, sizeof(addr));
+ p = strchr($3, '/');
+ if (p != NULL) {
+ *p++ = '\0';
+ prefixlen = strtonum(p, 0, 128, &errstr);
+ if (errstr != NULL) {
+ yyerror("error parsing prefix "
+ "\"%s/%s\"", $3, p);
+ free($3);
+ YYERROR;
+ }
+ } else
+ prefixlen = 96;
+
+ switch (prefixlen) {
+ case 96:
+ case 64:
+ case 56:
+ case 48:
+ case 40:
+ case 32:
+ break;
+ default:
+ yyerror("invalid nat64 prefix length: %d",
+ prefixlen);
+ YYERROR;
+ break;
+ }
+ if(inet_pton(AF_INET6, $3, &addr) == 0) {
+ yyerror("error parsing prefix \"%s/%d\"", $3,
+ prefixlen);
+ free($3);
+ YYERROR;
+ }
+ mask_prefix(&addr, prefixlen);
+ ra_pref64_conf = conf_get_ra_pref64(&addr, prefixlen);
+ } ra_pref64_block {
+ ra_pref64_conf = NULL;
+ }
| DNS dns_block
;
@@ -312,6 +361,26 @@ ra_prefixoptsl : VALID LIFETIME NUMBER {
ra_prefix_conf->aflag = $3;
}
;
+
+ra_pref64_block : '{' optnl ra_pref64opts_l '}'
+ | '{' optnl '}'
+ | /* empty */
+ ;
+
+ra_pref64opts_l : ra_pref64opts_l ra_pref64optsl nl
+ | ra_pref64optsl optnl
+ ;
+
+ra_pref64optsl : LIFETIME NUMBER {
+ if ($2 < 0 || $2 > 65528) {
+ yyerror("Invalid nat64 prefix lifetime: %lld",
+ $2);
+ YYERROR;
+ }
+ ra_pref64_conf->ltime = $2;
+ }
+ ;
+
dns_block : '{' optnl dnsopts_l '}'
| '{' optnl '}'
| /* empty */
@@ -446,6 +515,7 @@ lookup(char *s)
{"managed", MANAGED},
{"mtu", MTU},
{"nameserver", NAMESERVER},
+ {"nat64", NAT64},
{"no", NO},
{"on-link", ONLINK},
{"other", OTHER},
@@ -853,6 +923,12 @@ parse_config(char *filename)
&iface->ra_options);
}
+ if (!SIMPLEQ_EMPTY(&conf->ra_options.ra_pref64_list)) {
+ SIMPLEQ_FOREACH(iface, &conf->ra_iface_list, entry)
+ copy_pref64_options(&conf->ra_options,
+ &iface->ra_options);
+ }
+
return (conf);
}
@@ -884,6 +960,20 @@ copy_dns_options(const struct ra_options_conf *src, struct
ra_options_conf *dst)
}
}
+void
+copy_pref64_options(const struct ra_options_conf *src, struct ra_options_conf
+ *dst)
+{
+ struct ra_pref64_conf *pref64, *npref64;
+
+ SIMPLEQ_FOREACH(pref64, &src->ra_pref64_list, entry) {
+ if ((npref64 = calloc(1, sizeof(*npref64))) == NULL)
+ errx(1, "%s", __func__);
+ memcpy(npref64, pref64, sizeof(*npref64));
+ SIMPLEQ_INSERT_TAIL(&dst->ra_pref64_list, npref64, entry);
+ }
+}
+
int
symset(const char *nam, const char *val, int persist)
{
@@ -991,6 +1081,28 @@ conf_get_ra_prefix(struct in6_addr *addr, int prefixlen)
return (prefix);
}
+struct ra_pref64_conf *
+conf_get_ra_pref64(struct in6_addr *addr, int prefixlen)
+{
+ struct ra_pref64_conf *pref64;
+
+ SIMPLEQ_FOREACH(pref64, &ra_options->ra_pref64_list, entry) {
+ if (pref64->prefixlen == prefixlen && memcmp(addr,
+ &pref64->prefix, sizeof(*addr)) == 0)
+ return (pref64);
+ }
+
+ pref64 = calloc(1, sizeof(*pref64));
+ if (pref64 == NULL)
+ errx(1, "%s: calloc", __func__);
+ pref64->prefixlen = prefixlen;
+ pref64->ltime = ADV_DEFAULT_LIFETIME;
+ pref64->prefix = *addr;
+ SIMPLEQ_INSERT_TAIL(&ra_options->ra_pref64_list, pref64, entry);
+
+ return (pref64);
+}
+
struct ra_iface_conf *
conf_get_ra_iface(char *name)
{
@@ -1017,6 +1129,7 @@ conf_get_ra_iface(char *name)
iface->ra_options.rdnss_count = 0;
SIMPLEQ_INIT(&iface->ra_options.ra_dnssl_list);
iface->ra_options.dnssl_len = 0;
+ SIMPLEQ_INIT(&iface->ra_options.ra_pref64_list);
SIMPLEQ_INSERT_TAIL(&conf->ra_iface_list, iface, entry);
diff --git printconf.c printconf.c
index 78099534812..0204491983e 100644
--- printconf.c
+++ printconf.c
@@ -48,6 +48,7 @@ print_ra_options(const char *indent, const struct
ra_options_conf *ra_options)
{
struct ra_rdnss_conf *ra_rdnss;
struct ra_dnssl_conf *ra_dnssl;
+ struct ra_pref64_conf *pref64;
char buf[INET6_ADDRSTRLEN];
printf("%sdefault router %s\n", indent, yesno(ra_options->dfr));
@@ -84,6 +85,13 @@ print_ra_options(const char *indent, const struct
ra_options_conf *ra_options)
}
printf("%s}\n", indent);
}
+ SIMPLEQ_FOREACH(pref64, &ra_options->ra_pref64_list, entry) {
+ printf("%snat64 prefix %s/%d {\n", indent, inet_ntop(AF_INET6,
+ &pref64->prefix, buf, sizeof(buf)), pref64->prefixlen);
+ printf("%s\tlifetime %u\n", indent, pref64->ltime);
+ printf("%s}\n", indent);
+ }
+
}
void
diff --git rad.8 rad.8
index 8e023d9dbc6..551a010cd94 100644
--- rad.8
+++ rad.8
@@ -132,6 +132,14 @@ socket used for communication with
.%R RFC 8106
.%T IPv6 Router Advertisement Options for DNS Configuration
.Re
+.Pp
+.Rs
+.%A L. Colitti
+.%A J. Linkova
+.%D April 2020
+.%R RFC 8781
+.%T Discovering PREF64 in Router Advertisements
+.Re
.Sh HISTORY
The
.Nm
diff --git rad.c rad.c
index 3bb4851bdb3..2647f06bdf9 100644
--- rad.c
+++ rad.c
@@ -573,6 +573,7 @@ main_imsg_send_config(struct rad_conf *xconf)
struct ra_prefix_conf *ra_prefix_conf;
struct ra_rdnss_conf *ra_rdnss_conf;
struct ra_dnssl_conf *ra_dnssl_conf;
+ struct ra_pref64_conf *pref64;
/* Send fixed part of config to children. */
if (main_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
@@ -592,6 +593,14 @@ main_imsg_send_config(struct rad_conf *xconf)
return (-1);
}
+ /* send global pref64 list to children */
+ SIMPLEQ_FOREACH(pref64, &xconf->ra_options.ra_pref64_list,
+ entry) {
+ if (main_sendboth(IMSG_RECONF_RA_PREF64, pref64,
+ sizeof(*pref64)) == -1)
+ return (-1);
+ }
+
/* Send the interface list to children. */
SIMPLEQ_FOREACH(ra_iface_conf, &xconf->ra_iface_list, entry) {
if (main_sendboth(IMSG_RECONF_RA_IFACE, ra_iface_conf,
@@ -621,6 +630,12 @@ main_imsg_send_config(struct rad_conf *xconf)
sizeof(*ra_dnssl_conf)) == -1)
return (-1);
}
+ SIMPLEQ_FOREACH(pref64,
+ &ra_iface_conf->ra_options.ra_pref64_list, entry) {
+ if (main_sendboth(IMSG_RECONF_RA_PREF64, pref64,
+ sizeof(*pref64)) == -1)
+ return (-1);
+ }
}
/* Tell children the revised config is now complete. */
@@ -644,6 +659,7 @@ void
free_ra_iface_conf(struct ra_iface_conf *ra_iface_conf)
{
struct ra_prefix_conf *prefix;
+ struct ra_pref64_conf *pref64;
if (!ra_iface_conf)
return;
@@ -658,6 +674,13 @@ free_ra_iface_conf(struct ra_iface_conf *ra_iface_conf)
free_dns_options(&ra_iface_conf->ra_options);
+ while ((pref64 =
+ SIMPLEQ_FIRST(&ra_iface_conf->ra_options.ra_pref64_list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&ra_iface_conf->ra_options.ra_pref64_list,
+ entry);
+ free(pref64);
+ }
+
free(ra_iface_conf);
}
@@ -684,6 +707,7 @@ void
merge_config(struct rad_conf *conf, struct rad_conf *xconf)
{
struct ra_iface_conf *ra_iface_conf;
+ struct ra_pref64_conf *pref64;
/* Remove & discard existing interfaces. */
while ((ra_iface_conf = SIMPLEQ_FIRST(&conf->ra_iface_list)) != NULL) {
@@ -692,9 +716,16 @@ merge_config(struct rad_conf *conf, struct rad_conf *xconf)
}
free_dns_options(&conf->ra_options);
+ while ((pref64 = SIMPLEQ_FIRST(&conf->ra_options.ra_pref64_list))
+ != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&conf->ra_options.ra_pref64_list, entry);
+ free(pref64);
+ }
+
conf->ra_options = xconf->ra_options;
SIMPLEQ_INIT(&conf->ra_options.ra_rdnss_list);
SIMPLEQ_INIT(&conf->ra_options.ra_dnssl_list);
+ SIMPLEQ_INIT(&conf->ra_options.ra_pref64_list);
/* Add new interfaces. */
SIMPLEQ_CONCAT(&conf->ra_iface_list, &xconf->ra_iface_list);
@@ -704,6 +735,8 @@ merge_config(struct rad_conf *conf, struct rad_conf *xconf)
&xconf->ra_options.ra_rdnss_list);
SIMPLEQ_CONCAT(&conf->ra_options.ra_dnssl_list,
&xconf->ra_options.ra_dnssl_list);
+ SIMPLEQ_CONCAT(&conf->ra_options.ra_pref64_list,
+ &xconf->ra_options.ra_pref64_list);
free(xconf);
}
@@ -729,6 +762,7 @@ config_new_empty(void)
xconf->ra_options.rdns_lifetime = DEFAULT_RDNS_LIFETIME;
SIMPLEQ_INIT(&xconf->ra_options.ra_rdnss_list);
SIMPLEQ_INIT(&xconf->ra_options.ra_dnssl_list);
+ SIMPLEQ_INIT(&xconf->ra_options.ra_pref64_list);
return (xconf);
}
diff --git rad.conf.5 rad.conf.5
index b3ecf8c11be..622e85e6009 100644
--- rad.conf.5
+++ rad.conf.5
@@ -96,6 +96,23 @@ The MTU option is used in Router Advertisement messages to
ensure that all
nodes on a link use the same MTU value in those cases where the link MTU
is not well known.
The default is 0, meaning unspecified by this router.
+.It Ic nat64 prefix Ar prefix Oc Brq nat64 options
+Add a PREF64 router advertisement option to communicate prefixes used
+for Network Address and Protocol Translation from IPv6 to IPv4 (NAT64).
+If
+.Ar prefix
+is specified without a prefix length, its default is 64.
+.Pp
+.Ic nat64 prefix
+options are as follows:
+.Bl -tag -width Ds
+.It Ic lifetime Ar seconds
+The number of seconds the nat64 prefix option is valid after receiving a router
+advertisement message.
+A value of zero indicates to not use the prefix anymore.
+The maximum is 65528 seconds.
+The default is 1800 seconds.
+.El
.It Ic other configuration Pq Ic yes Ns | Ns Ic no
If set to yes, hosts should consult DHCPv6 for additional configuration
like NTP servers or DNS name servers.
@@ -128,7 +145,7 @@ This can be disabled with
.Ic no auto prefix .
If
.Ar prefix
-is specified without prefixlen, its default is 64.
+is specified without a prefix length, its default is 64.
.Pp
.Ic prefix
options are as follows:
diff --git rad.h rad.h
index 9b0fedd77fb..77ad9b28c2a 100644
--- rad.h
+++ rad.h
@@ -55,6 +55,7 @@ enum imsg_type {
IMSG_RECONF_RA_PREFIX,
IMSG_RECONF_RA_RDNSS,
IMSG_RECONF_RA_DNSSL,
+ IMSG_RECONF_RA_PREF64,
IMSG_RECONF_END,
IMSG_ICMP6SOCK,
IMSG_OPEN_ICMP6SOCK,
@@ -78,6 +79,14 @@ struct ra_dnssl_conf {
char search[MAX_SEARCH];
};
+/* RFC 8781 Section 4 */
+struct ra_pref64_conf {
+ SIMPLEQ_ENTRY(ra_pref64_conf) entry;
+ struct in6_addr prefix; /* prefix */
+ int prefixlen; /* prefix length */
+ uint32_t ltime; /* lifetime */
+};
+
/* RFC 4861 Sections 4.2 and 4.6.4 */
struct ra_options_conf {
int dfr; /* is default router? */
@@ -93,6 +102,7 @@ struct ra_options_conf {
int rdnss_count;
SIMPLEQ_HEAD(, ra_dnssl_conf) ra_dnssl_list;
int dnssl_len;
+ SIMPLEQ_HEAD(, ra_pref64_conf) ra_pref64_list;
};
/* RFC 4861 Section 4.6.2 */
--
I'm not entirely sure you are real.