From 2272e5863df41314f4e3e6aab7cf4aa3b69f2fb8 Mon Sep 17 00:00:00 2001 From: dv Date: Thu, 13 Jul 2023 18:31:59 +0000 Subject: [PATCH] vmd(8): pull validation into local prefix parser. 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 | 12 ++- usr.sbin/vmd/dhcp.c | 10 +-- usr.sbin/vmd/parse.y | 179 +++++++++++++++++++++++++++++++----------- usr.sbin/vmd/priv.c | 61 ++++++-------- usr.sbin/vmd/virtio.c | 6 +- usr.sbin/vmd/virtio.h | 3 +- usr.sbin/vmd/vm.c | 10 ++- usr.sbin/vmd/vmd.h | 24 +++--- usr.sbin/vmd/vmm.c | 12 ++- 9 files changed, 203 insertions(+), 114 deletions(-) diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c index b538d40be1a..3bb246a0092 100644 --- a/usr.sbin/vmd/config.c +++ b/usr.sbin/vmd/config.c @@ -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 @@ -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); diff --git a/usr.sbin/vmd/dhcp.c b/usr.sbin/vmd/dhcp.c index 3b8744ffe50..67ab546b442 100644 --- a/usr.sbin/vmd/dhcp.c +++ b/usr.sbin/vmd/dhcp.c @@ -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 @@ -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, diff --git a/usr.sbin/vmd/parse.y b/usr.sbin/vmd/parse.y index 09468e3fe2c..2ee98897290 100644 --- a/usr.sbin/vmd/parse.y +++ b/usr.sbin/vmd/parse.y @@ -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 @@ -30,6 +30,7 @@ #include +#include #include #include #include @@ -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); } diff --git a/usr.sbin/vmd/priv.c b/usr.sbin/vmd/priv.c index a7a7a2bc228..bfb15085cf1 100644 --- a/usr.sbin/vmd/priv.c +++ b/usr.sbin/vmd/priv.c @@ -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 @@ -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); } diff --git a/usr.sbin/vmd/virtio.c b/usr.sbin/vmd/virtio.c index d29b9e7b883..6167a7764cb 100644 --- a/usr.sbin/vmd/virtio.c +++ b/usr.sbin/vmd/virtio.c @@ -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 @@ -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), diff --git a/usr.sbin/vmd/virtio.h b/usr.sbin/vmd/virtio.h index 285c11630a2..24fd0097a1c 100644 --- a/usr.sbin/vmd/virtio.h +++ b/usr.sbin/vmd/virtio.h @@ -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 @@ -257,6 +257,7 @@ struct vionet_dev { int lockedmac; int local; int pxeboot; + struct local_prefix local_prefix; unsigned int idx; }; diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c index 8ec69d6056e..5f598bcc14a 100644 --- a/usr.sbin/vmd/vm.c +++ b/usr.sbin/vmd/vm.c @@ -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 @@ -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. diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h index 9c25b0c92ad..744b8d19574 100644 --- a/usr.sbin/vmd/vmd.h +++ b/usr.sbin/vmd/vmd.h @@ -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 @@ -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 *); diff --git a/usr.sbin/vmd/vmm.c b/usr.sbin/vmd/vmm.c index 7f307f99c9c..541222e0272 100644 --- a/usr.sbin/vmd/vmm.c +++ b/usr.sbin/vmd/vmm.c @@ -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 @@ -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)) { -- 2.20.1