-/* $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>
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 */
/* 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);
-/* $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>
(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)
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,
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,
-/* $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>
#include <machine/vmmvar.h>
+#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
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;
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);
}
-/* $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>
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);
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);
}
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);
}
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.
*/
else
addr.s6_addr[15] = 2;
- memcpy(in6_addr, &addr, sizeof(*in6_addr));
+ memcpy(out, &addr, sizeof(*out));
return (0);
}
-/* $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>
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),
-/* $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>
int lockedmac;
int local;
int pxeboot;
+ struct local_prefix local_prefix;
unsigned int idx;
};
-/* $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>
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.
-/* $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>
};
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 {
struct timeval delay;
int parallelism;
- struct address cfg_localprefix;
- struct address cfg_localprefix6;
+ struct local_prefix cfg_localprefix;
struct vmd_agentx cfg_agentx;
};
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 *);
/* 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 *);
-/* $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>
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)) {