On 2022-10-15 13:00 +02, Florian Obser <flor...@openbsd.org> 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.