vmd(8): pull validation into local prefix parser.
authordv <dv@openbsd.org>
Thu, 13 Jul 2023 18:31:59 +0000 (18:31 +0000)
committerdv <dv@openbsd.org>
Thu, 13 Jul 2023 18:31:59 +0000 (18:31 +0000)
Validation for local prefixes, both inet and inet6, was scattered
around. To make it even more confusing, vmd was using generic address
parsing logic from prior network daemons. vmd doesn't need to parse
addresses other than when parsing the local prefix settings in
vm.conf and no runtime parsing is needed.

This change merges parsing and validation based on vmd's specific
needs for local prefixes (e.g. reserving enough bits for vm id and
network interface id encoding in an ipv4 address). In addition, it
simplifies the struct from a generic address struct to one focused
on just storing the v4 and v6 prefixes and masks. This cleans up an
unused TAILQ struct member that isn't used by vmd and was leftover
copy-pasta from those prior daemons.

The address parsing that vmd uses is also updated to using the
latest logic in bgpd(8).

ok mlarkin@

usr.sbin/vmd/config.c
usr.sbin/vmd/dhcp.c
usr.sbin/vmd/parse.y
usr.sbin/vmd/priv.c
usr.sbin/vmd/virtio.c
usr.sbin/vmd/virtio.h
usr.sbin/vmd/vm.c
usr.sbin/vmd/vmd.h
usr.sbin/vmd/vmm.c

index b538d40..3bb246a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.71 2023/04/28 19:46:42 dv Exp $  */
+/*     $OpenBSD: config.c,v 1.72 2023/07/13 18:31:59 dv Exp $  */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -47,9 +47,7 @@ static int     config_init_localprefix(struct vmd_config *);
 static int
 config_init_localprefix(struct vmd_config *cfg)
 {
-       struct sockaddr_in6     *sin6;
-
-       if (host(VMD_DHCP_PREFIX, &cfg->cfg_localprefix) == -1)
+       if (parse_prefix4(VMD_DHCP_PREFIX, &cfg->cfg_localprefix, NULL) == -1)
                return (-1);
 
        /* IPv6 is disabled by default */
@@ -58,11 +56,11 @@ config_init_localprefix(struct vmd_config *cfg)
        /* Generate random IPv6 prefix only once */
        if (cfg->cfg_flags & VMD_CFG_AUTOINET6)
                return (0);
-       if (host(VMD_ULA_PREFIX, &cfg->cfg_localprefix6) == -1)
+       if (parse_prefix6(VMD_ULA_PREFIX, &cfg->cfg_localprefix, NULL) == -1)
                return (-1);
+
        /* Randomize the 56 bits "Global ID" and "Subnet ID" */
-       sin6 = ss2sin6(&cfg->cfg_localprefix6.ss);
-       arc4random_buf(&sin6->sin6_addr.s6_addr[1], 7);
+       arc4random_buf(&cfg->cfg_localprefix.lp_in6.s6_addr[1], 7);
        cfg->cfg_flags |= VMD_CFG_AUTOINET6;
 
        return (0);
index 3b8744f..67ab546 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dhcp.c,v 1.12 2023/04/27 22:47:27 dv Exp $    */
+/*     $OpenBSD: dhcp.c,v 1.13 2023/07/13 18:31:59 dv Exp $    */
 
 /*
  * Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
@@ -40,7 +40,6 @@
        (1500 - sizeof(struct ip) - sizeof(struct udphdr) - OPTIONS_OFFSET)
 
 static const uint8_t broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-extern struct vmd *env;
 
 ssize_t
 dhcp_request(struct virtio_dev *dev, char *buf, size_t buflen, char **obuf)
@@ -146,8 +145,7 @@ dhcp_request(struct virtio_dev *dev, char *buf, size_t buflen, char **obuf)
                        hostname = vm->vm_params.vmc_params.vcp_name;
        }
 
-       if ((client_addr.s_addr =
-           vm_priv_addr(&env->vmd_cfg,
+       if ((client_addr.s_addr = vm_priv_addr(&vionet->local_prefix,
            dev->vm_vmid, vionet->idx, 1)) == 0)
                return (-1);
        memcpy(&resp.yiaddr, &client_addr,
@@ -156,8 +154,8 @@ dhcp_request(struct virtio_dev *dev, char *buf, size_t buflen, char **obuf)
            sizeof(client_addr));
        ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT);
 
-       if ((server_addr.s_addr = vm_priv_addr(&env->vmd_cfg, dev->vm_vmid,
-           vionet->idx, 0)) == 0)
+       if ((server_addr.s_addr = vm_priv_addr(&vionet->local_prefix,
+           dev->vm_vmid, vionet->idx, 0)) == 0)
                return (-1);
        memcpy(&resp.siaddr, &server_addr, sizeof(server_addr));
        memcpy(&ss2sin(&pc.pc_src)->sin_addr, &server_addr,
index 09468e3..2ee9889 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.67 2023/04/28 21:22:20 dv Exp $   */
+/*     $OpenBSD: parse.y,v 1.68 2023/07/13 18:31:59 dv Exp $   */
 
 /*
  * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
@@ -30,6 +30,7 @@
 
 #include <machine/vmmvar.h>
 
+#include <arpa/inet.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
@@ -189,32 +190,27 @@ main              : LOCAL INET6 {
                        env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
                }
                | LOCAL INET6 PREFIX STRING {
-                       struct address   h;
+                       const char      *err;
 
-                       if (host($4, &h) == -1 ||
-                           h.ss.ss_family != AF_INET6 ||
-                           h.prefixlen > 64 || h.prefixlen < 0) {
-                               yyerror("invalid local inet6 prefix: %s", $4);
-                               free($4);
+                       if (parse_prefix6($4, &env->vmd_cfg.cfg_localprefix,
+                           &err)) {
+                               yyerror("invalid local inet6 prefix: %s", err);
                                YYERROR;
+                       } else {
+                               env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
+                               env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6;
                        }
-
-                       env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
-                       env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6;
-                       memcpy(&env->vmd_cfg.cfg_localprefix6, &h, sizeof(h));
+                       free($4);
                }
                | LOCAL PREFIX STRING {
-                       struct address   h;
+                       const char      *err;
 
-                       if (host($3, &h) == -1 ||
-                           h.ss.ss_family != AF_INET ||
-                           h.prefixlen > 32 || h.prefixlen < 0) {
-                               yyerror("invalid local prefix: %s", $3);
-                               free($3);
+                       if (parse_prefix4($3, &env->vmd_cfg.cfg_localprefix,
+                           &err)) {
+                               yyerror("invalid local prefix: %s", err);
                                YYERROR;
                        }
-
-                       memcpy(&env->vmd_cfg.cfg_localprefix, &h, sizeof(h));
+                       free($3);
                }
                | SOCKET OWNER owner_id {
                        env->vmd_ps.ps_csock.cs_uid = $3.uid;
@@ -1404,42 +1400,133 @@ parse_format(const char *word)
        return (0);
 }
 
+/*
+ * Parse an ipv4 address and prefix for local interfaces and validate
+ * constraints for vmd networking.
+ */
 int
-host(const char *str, struct address *h)
+parse_prefix4(const char *str, struct local_prefix *out, const char **errstr)
 {
-       struct addrinfo          hints, *res;
-       int                      prefixlen;
-       char                    *s, *p;
-       const char              *errstr;
+       struct addrinfo          hints, *res = NULL;
+       struct sockaddr_storage  ss;
+       struct in_addr           addr;
+       int                      mask = 16;
+       char                    *p, *ps;
+
+       if ((ps = strdup(str)) == NULL)
+               fatal("%s: strdup", __func__);
+
+       if ((p = strrchr(ps, '/')) != NULL) {
+               mask = strtonum(p + 1, 1, 16, errstr);
+               if (errstr != NULL && *errstr) {
+                       free(ps);
+                       return (1);
+               }
+               p[0] = '\0';
+       }
 
-       if ((s = strdup(str)) == NULL) {
-               log_warn("%s", __func__);
-               goto fail;
+       /* Attempt to construct an address from the user input. */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_INET;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_flags = AI_NUMERICHOST;
+
+       if (getaddrinfo(ps, NULL, &hints, &res) == 0) {
+               memset(&ss, 0, sizeof(ss));
+               memcpy(&ss, res->ai_addr, res->ai_addrlen);
+               addr.s_addr = ss2sin(&ss)->sin_addr.s_addr;
+               freeaddrinfo(res);
+       } else { /* try 10/8 parsing */
+               memset(&addr, 0, sizeof(addr));
+               if (inet_net_pton(AF_INET, ps, &addr, sizeof(addr)) == -1) {
+                       if (errstr)
+                               *errstr = "invalid format";
+                       free(ps);
+                       return (1);
+               }
        }
+       free(ps);
+
+       /*
+        * Validate the prefix by comparing it with the mask. Since we
+        * constrain the mask length to 16 above, this also validates
+        * we reserve the last 16 bits for use by vmd to assign vm id
+        * and interface id.
+        */
+       if ((addr.s_addr & prefixlen2mask(mask)) != addr.s_addr) {
+               if (errstr)
+                       *errstr = "bad mask";
+               return (1);
+       }
+
+       /* Copy out the local prefix. */
+       out->lp_in.s_addr = addr.s_addr;
+       out->lp_mask.s_addr = prefixlen2mask(mask);
+       return (0);
+}
 
-       if ((p = strrchr(s, '/')) != NULL) {
-               *p++ = '\0';
-               prefixlen = strtonum(p, 0, 128, &errstr);
-               if (errstr) {
-                       log_warnx("prefixlen is %s: %s", errstr, p);
-                       goto fail;
+/*
+ * Parse an ipv6 address and prefix for local interfaces and validate
+ * constraints for vmd networking.
+ */
+int
+parse_prefix6(const char *str, struct local_prefix *out, const char **errstr)
+{
+       struct addrinfo          hints, *res = NULL;
+       struct sockaddr_storage  ss;
+       struct in6_addr          addr6, mask6;
+       size_t                   i;
+       int                      mask = 64, err;
+       char                    *p, *ps;
+
+       if ((ps = strdup(str)) == NULL)
+               fatal("%s: strdup", __func__);
+
+       if ((p = strrchr(ps, '/')) != NULL) {
+               mask = strtonum(p + 1, 0, 64, errstr);
+               if (errstr != NULL && *errstr) {
+                       free(ps);
+                       return (1);
                }
-       } else
-               prefixlen = 128;
+               p[0] = '\0';
+       }
 
+       /* Attempt to construct an address from the user input. */
        memset(&hints, 0, sizeof(hints));
-       hints.ai_family = AF_UNSPEC;
+       hints.ai_family = AF_INET6;
+       hints.ai_socktype = SOCK_DGRAM;
        hints.ai_flags = AI_NUMERICHOST;
-       if (getaddrinfo(s, NULL, &hints, &res) == 0) {
-               memset(h, 0, sizeof(*h));
-               memcpy(&h->ss, res->ai_addr, res->ai_addrlen);
-               h->prefixlen = prefixlen;
-               freeaddrinfo(res);
-               free(s);
-               return (0);
+
+       if ((err = getaddrinfo(ps, NULL, &hints, &res)) != 0) {
+               if (errstr)
+                       *errstr = gai_strerror(err);
+               free(ps);
+               return (1);
+       }
+       free(ps);
+
+       memset(&ss, 0, sizeof(ss));
+       memcpy(&ss, res->ai_addr, res->ai_addrlen);
+       freeaddrinfo(res);
+
+       memcpy(&addr6, (void*)&ss2sin6(&ss)->sin6_addr, sizeof(addr6));
+       prefixlen2mask6(mask, &mask6);
+
+       /*
+        * Validate the prefix by comparing it with the mask. Since we
+        * constrain the mask length to 64 above, this also validates
+        * that we're reserving bits for the encoding of the ipv4
+        * address, the vm id, and interface id. */
+       for (i = 0; i < 16; i++) {
+               if ((addr6.s6_addr[i] & mask6.s6_addr[i]) != addr6.s6_addr[i]) {
+                       if (errstr)
+                               *errstr = "bad mask";
+                       return (1);
+               }
        }
 
- fail:
-       free(s);
-       return (-1);
+       /* Copy out the local prefix. */
+       memcpy(&out->lp_in6, &addr6, sizeof(out->lp_in6));
+       memcpy(&out->lp_mask6, &mask6, sizeof(out->lp_mask6));
+       return (0);
 }
index a7a7a2b..bfb1508 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: priv.c,v 1.22 2023/01/28 14:40:53 dv Exp $    */
+/*     $OpenBSD: priv.c,v 1.23 2023/07/13 18:31:59 dv Exp $    */
 
 /*
  * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
@@ -466,7 +466,7 @@ vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
                        sin4->sin_family = AF_INET;
                        sin4->sin_len = sizeof(*sin4);
                        if ((sin4->sin_addr.s_addr =
-                           vm_priv_addr(&env->vmd_cfg,
+                           vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
                            vm->vm_vmid, i, 0)) == 0)
                                return (-1);
 
@@ -493,7 +493,7 @@ vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
                        sin6 = ss2sin6(&vfr.vfr_addr);
                        sin6->sin6_family = AF_INET6;
                        sin6->sin6_len = sizeof(*sin6);
-                       if (vm_priv_addr6(&env->vmd_cfg,
+                       if (vm_priv_addr6(&env->vmd_cfg.cfg_localprefix,
                            vm->vm_vmid, i, 0, &sin6->sin6_addr) == -1)
                                return (-1);
 
@@ -565,43 +565,33 @@ vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
 }
 
 uint32_t
-vm_priv_addr(struct vmd_config *cfg, uint32_t vmid, int idx, int isvm)
+vm_priv_addr(struct local_prefix *p, uint32_t vmid, int idx, int isvm)
 {
-       struct address          *h = &cfg->cfg_localprefix;
-       in_addr_t                prefix, mask, addr;
+       in_addr_t                addr;
 
-       /*
-        * 1. Set the address prefix and mask, 100.64.0.0/10 by default.
-        */
-       if (h->ss.ss_family != AF_INET ||
-           h->prefixlen < 0 || h->prefixlen > 32)
-               fatal("local prefix");
-       prefix = ss2sin(&h->ss)->sin_addr.s_addr;
-       mask = prefixlen2mask(h->prefixlen);
-
-       /* 2. Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. */
+       /* Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. */
        addr = vmid << 8;
 
        /*
-        * 3. Assign a /31 subnet M per VM interface, 100.64.N.M/31.
+        * Assign a /31 subnet M per VM interface, 100.64.N.M/31.
         * Each subnet contains exactly two IP addresses; skip the
         * first subnet to avoid a gateway address ending with .0.
         */
        addr |= (idx + 1) * 2;
 
-       /* 4. Use the first address for the gateway, the second for the VM. */
+       /* Use the first address for the gateway, the second for the VM. */
        if (isvm)
                addr++;
 
-       /* 5. Convert to network byte order and add the prefix. */
-       addr = htonl(addr) | prefix;
+       /* Convert to network byte order and add the prefix. */
+       addr = htonl(addr) | p->lp_in.s_addr;
 
        /*
         * Validate the results:
         * - the address should not exceed the prefix (eg. VM ID to high).
         * - up to 126 interfaces can be encoded per VM.
         */
-       if (prefix != (addr & mask) || idx >= 0x7f) {
+       if (p->lp_in.s_addr != (addr & p->lp_mask.s_addr) || idx >= 0x7f) {
                log_warnx("%s: dhcp address range exceeded,"
                    " vm id %u interface %d", __func__, vmid, idx);
                return (0);
@@ -611,27 +601,22 @@ vm_priv_addr(struct vmd_config *cfg, uint32_t vmid, int idx, int isvm)
 }
 
 int
-vm_priv_addr6(struct vmd_config *cfg, uint32_t vmid,
-    int idx, int isvm, struct in6_addr *in6_addr)
+vm_priv_addr6(struct local_prefix *p, uint32_t vmid, int idx, int isvm,
+    struct in6_addr *out)
 {
-       struct address          *h = &cfg->cfg_localprefix6;
-       struct in6_addr          addr, mask;
-       uint32_t                 addr4;
-
-       /* 1. Set the address prefix and mask, fd00::/8 by default. */
-       if (h->ss.ss_family != AF_INET6 ||
-           h->prefixlen < 0 || h->prefixlen > 128)
-               fatal("local prefix6");
-       addr = ss2sin6(&h->ss)->sin6_addr;
-       prefixlen2mask6(h->prefixlen, &mask);
-
-       /* 2. Encode the VM IPv4 address as subnet, fd00::NN:NN:0:0/96. */
-       if ((addr4 = vm_priv_addr(cfg, vmid, idx, 1)) == 0)
+       struct in6_addr          addr;
+       in_addr_t                addr4;
+
+       /* Start with the IPv6 prefix. */
+       memcpy(&addr, &p->lp_in6, sizeof(addr));
+
+       /* Encode the VM IPv4 address as subnet, fd00::NN:NN:0:0/96. */
+       if ((addr4 = vm_priv_addr(p, vmid, idx, 1)) == 0)
                return (0);
        memcpy(&addr.s6_addr[8], &addr4, sizeof(addr4));
 
        /*
-        * 3. Set the last octet to 1 (host) or 2 (VM).
+        * Set the last octet to 1 (host) or 2 (VM).
         * The latter is currently not used inside vmd as we don't
         * answer rtsol requests ourselves.
         */
@@ -640,7 +625,7 @@ vm_priv_addr6(struct vmd_config *cfg, uint32_t vmid,
        else
                addr.s6_addr[15] = 2;
 
-       memcpy(in6_addr, &addr, sizeof(*in6_addr));
+       memcpy(out, &addr, sizeof(*out));
 
        return (0);
 }
index d29b9e7..6167a77 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: virtio.c,v 1.103 2023/05/13 23:15:28 dv Exp $ */
+/*     $OpenBSD: virtio.c,v 1.104 2023/07/13 18:31:59 dv Exp $ */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -591,7 +591,9 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
                            vmc->vmc_ifflags[i] & VMIFF_LOCAL ? 1 : 0;
                        if (i == 0 && vmc->vmc_bootdevice & VMBOOTDEV_NET)
                                dev->vionet.pxeboot = 1;
-
+                       memcpy(&dev->vionet.local_prefix,
+                           &env->vmd_cfg.cfg_localprefix,
+                           sizeof(dev->vionet.local_prefix));
                        log_debug("%s: vm \"%s\" vio%u lladdr %s%s%s%s",
                            __func__, vcp->vcp_name, i,
                            ether_ntoa((void *)dev->vionet.mac),
index 285c116..24fd009 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: virtio.h,v 1.45 2023/04/27 22:47:27 dv Exp $  */
+/*     $OpenBSD: virtio.h,v 1.46 2023/07/13 18:31:59 dv Exp $  */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -257,6 +257,7 @@ struct vionet_dev {
        int lockedmac;
        int local;
        int pxeboot;
+       struct local_prefix local_prefix;
 
        unsigned int idx;
 };
index 8ec69d6..5f598bc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vm.c,v 1.89 2023/05/13 23:15:28 dv Exp $      */
+/*     $OpenBSD: vm.c,v 1.90 2023/07/13 18:31:59 dv Exp $      */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -259,6 +259,14 @@ vm_main(int fd, int vmm_fd)
        setproctitle("%s", vcp->vcp_name);
        log_procinit(vcp->vcp_name);
 
+       /* Receive the local prefix settings. */
+       sz = atomicio(read, fd, &env->vmd_cfg.cfg_localprefix,
+           sizeof(env->vmd_cfg.cfg_localprefix));
+       if (sz != sizeof(env->vmd_cfg.cfg_localprefix)) {
+               log_warnx("failed to receive local prefix");
+               _exit(EIO);
+       }
+
        /*
         * We need, at minimum, a vm_kernel fd to boot a vm. This is either a
         * kernel or a BIOS image.
index 9c25b0c..744b8d1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.h,v 1.122 2023/05/13 23:15:28 dv Exp $    */
+/*     $OpenBSD: vmd.h,v 1.123 2023/07/13 18:31:59 dv Exp $    */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -341,12 +341,12 @@ struct name2id {
 };
 TAILQ_HEAD(name2idlist, name2id);
 
-struct address {
-       struct sockaddr_storage  ss;
-       int                      prefixlen;
-       TAILQ_ENTRY(address)     entry;
+struct local_prefix {
+       struct in_addr           lp_in;
+       struct in_addr           lp_mask;
+       struct in6_addr          lp_in6;
+       struct in6_addr          lp_mask6;
 };
-TAILQ_HEAD(addresslist, address);
 
 #define SUN_PATH_LEN           (sizeof(((struct sockaddr_un *)NULL)->sun_path))
 struct vmd_agentx {
@@ -367,8 +367,7 @@ struct vmd_config {
 
        struct timeval           delay;
        int                      parallelism;
-       struct address           cfg_localprefix;
-       struct address           cfg_localprefix6;
+       struct local_prefix      cfg_localprefix;
        struct vmd_agentx        cfg_agentx;
 };
 
@@ -473,9 +472,9 @@ int  priv_findname(const char *, const char **);
 int     priv_validgroup(const char *);
 int     vm_priv_ifconfig(struct privsep *, struct vmd_vm *);
 int     vm_priv_brconfig(struct privsep *, struct vmd_switch *);
-uint32_t vm_priv_addr(struct vmd_config *, uint32_t, int, int);
-int     vm_priv_addr6(struct vmd_config *, uint32_t, int, int,
-           struct in6_addr *);
+uint32_t vm_priv_addr(struct local_prefix *, uint32_t, int, int);
+int     vm_priv_addr6(struct local_prefix *, uint32_t, int, int,
+           struct in6_addr *);
 
 /* vmm.c */
 void    vmm(struct privsep *, struct privsep_proc *);
@@ -518,7 +517,8 @@ void vm_agentx_shutdown(void);
 /* parse.y */
 int     parse_config(const char *);
 int     cmdline_symset(char *);
-int     host(const char *, struct address *);
+int     parse_prefix4(const char *, struct local_prefix *, const char **);
+int     parse_prefix6(const char *, struct local_prefix *, const char **);
 
 /* virtio.c */
 int     virtio_get_base(int, char *, size_t, int, const char *);
index 7f307f9..541222e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm.c,v 1.112 2023/05/13 23:15:28 dv Exp $    */
+/*     $OpenBSD: vmm.c,v 1.113 2023/07/13 18:31:59 dv Exp $    */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -701,6 +701,16 @@ vmm_start_vm(struct imsg *imsg, uint32_t *id, pid_t *pid)
                if (ret == EIO)
                        goto err;
 
+               /* Send the current local prefix configuration. */
+               sz = atomicio(vwrite, fds[0], &env->vmd_cfg.cfg_localprefix,
+                   sizeof(env->vmd_cfg.cfg_localprefix));
+               if (sz != sizeof(env->vmd_cfg.cfg_localprefix)) {
+                       log_warnx("%s: failed to send local prefix for vm '%s'",
+                           __func__, vcp->vcp_name);
+                       ret = EIO;
+                       goto err;
+               }
+
                /* Read back the kernel-generated vm id from the child */
                sz = atomicio(read, fds[0], &vcp->vcp_id, sizeof(vcp->vcp_id));
                if (sz != sizeof(vcp->vcp_id)) {