Implement RFC 8781 PREF64 router advertisement option.
authorflorian <florian@openbsd.org>
Sat, 15 Oct 2022 13:26:15 +0000 (13:26 +0000)
committerflorian <florian@openbsd.org>
Sat, 15 Oct 2022 13:26:15 +0000 (13:26 +0000)
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.

Input & OK kn

usr.sbin/rad/engine.c
usr.sbin/rad/frontend.c
usr.sbin/rad/parse.y
usr.sbin/rad/printconf.c
usr.sbin/rad/rad.8
usr.sbin/rad/rad.c
usr.sbin/rad/rad.conf.5
usr.sbin/rad/rad.h

index dddff2d..ceb11d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: engine.c,v 1.20 2022/03/23 15:26:08 florian Exp $     */
+/*     $OpenBSD: engine.c,v 1.21 2022/10/15 13:26:15 florian Exp $     */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -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 "
index 30bcde2..211a388 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: frontend.c,v 1.40 2022/01/17 18:04:35 naddy Exp $     */
+/*     $OpenBSD: frontend.c,v 1.41 2022/10/15 13:26:15 florian Exp $   */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -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);
index 41b1f0d..2d4812a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.19 2021/10/15 15:01:28 naddy Exp $        */
+/*     $OpenBSD: parse.y,v 1.20 2022/10/15 13:26:15 florian Exp $      */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -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)
+                       err(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)
+               err(1, "%s", __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);
 
index 7809953..f664e58 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: printconf.c,v 1.6 2021/01/19 17:38:41 florian Exp $   */
+/*     $OpenBSD: printconf.c,v 1.7 2022/10/15 13:26:15 florian Exp $   */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -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
index 8e023d9..bc72831 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: rad.8,v 1.6 2019/11/10 20:51:53 landry Exp $
+.\"    $OpenBSD: rad.8,v 1.7 2022/10/15 13:26:15 florian Exp $
 .\"
 .\" Copyright (c) 2018 Florian Obser <florian@openbsd.org>
 .\" Copyright (c) 2016 Kenneth R Westerback <kwesterback@gmail.com>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 10 2019 $
+.Dd $Mdocdate: October 15 2022 $
 .Dt RAD 8
 .Os
 .Sh NAME
@@ -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
index 3bb4851..713fb4a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rad.c,v 1.27 2021/02/27 10:35:20 florian Exp $        */
+/*     $OpenBSD: rad.c,v 1.28 2022/10/15 13:26:15 florian Exp $        */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -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);
 }
index b3ecf8c..065fcd1 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: rad.conf.5,v 1.18 2022/09/23 19:24:08 florian Exp $
+.\"    $OpenBSD: rad.conf.5,v 1.19 2022/10/15 13:26:15 florian Exp $
 .\"
 .\" Copyright (c) 2018 Florian Obser <florian@openbsd.org>
 .\" Copyright (c) 2005 Esben Norby <norby@openbsd.org>
@@ -18,7 +18,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: September 23 2022 $
+.Dd $Mdocdate: October 15 2022 $
 .Dt RAD.CONF 5
 .Os
 .Sh NAME
@@ -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:
index 9b0fedd..3af48dd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rad.h,v 1.22 2022/03/23 15:26:08 florian Exp $        */
+/*     $OpenBSD: rad.h,v 1.23 2022/10/15 13:26:15 florian Exp $        */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -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 */