Hi,
the following diff enhances vmd(8)'s network configuration.
Before, you could only set "interfaces N" or -i N for the number of
desired interfaces.
The diff introduces the concept of virtual switches: bridge(4) or
switch(4) devices that are partially managed by vmd(8) with the
following features:
- Add VM interfaces to virtual switches automatically
- Allow to set the lladdr of each guest interface (zero/random by default)
- Set the interface or switch interface to up or down automatically
The idea is that each VM specifies its interfaces and assigns them to
pre-configured networks via the switches. This pretty much matches
what I would expect from a VMM. And, of course, this might interact
with switchd later.
See the examples and manpage below. It is still compatible to the
previous "interfaces" or -i option, so you can just add interfaces
without any additional configuration like before. All tap(4)
interfaces are now UP by default. vmctl does not support the enhanced
configuration yet, but I will probably add subset to it later.
OK?
Reyk
Index: etc/examples/vm.conf
===================================================================
RCS file: /cvs/src/etc/examples/vm.conf,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 vm.conf
--- etc/examples/vm.conf 6 Jan 2016 09:59:30 -0000 1.4
+++ etc/examples/vm.conf 5 Oct 2016 09:07:34 -0000
@@ -9,6 +9,19 @@ sets="/var/www/htdocs/pub/OpenBSD/snapsh
# Virtual machines
#
+switch "wired" {
+ # This interface will default to bridge0, but switch(4) is supported
+ #interface switch0
+
+ # Add additional members
+ add em0
+ down
+}
+
+switch "wireless" {
+ add iwm0
+}
+
# OpenBSD snapshot install test
vm "openbsd.vm" {
memory 512M
@@ -18,17 +31,23 @@ vm "openbsd.vm" {
disk "/home/vm/OpenBSD.img"
# Second disk from OpenBSD contains the install sets
- disk $sets "install59.fs"
+ disk $sets "install60.fs"
# Interface will show up as tap(4) on the host and as vio(4) in the VM
- interfaces 1
+ interface { switch "wireless" }
+ interface { switch "wired" }
}
# Another VM that is disabled on startup
vm "vm1.example.com" {
disable
memory 1G
- interfaces 2
kernel "/bsd"
disk "/home/vm/vm1-disk.img"
+
+ # Use a specific tap(4) interface with a hardcoded MAC address
+ interface tap3 {
+ lladdr 00:11:22:aa:bb:cc
+ down
+ }
}
Index: usr.sbin/vmd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/config.c,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 config.c
--- usr.sbin/vmd/config.c 4 Oct 2016 17:17:30 -0000 1.13
+++ usr.sbin/vmd/config.c 5 Oct 2016 09:07:34 -0000
@@ -38,6 +38,9 @@
#include "proc.h"
#include "vmd.h"
+/* Supported bridge types */
+const char *vmd_descsw[] = { "switch", "bridge", NULL };
+
int
config_init(struct vmd *env)
{
@@ -56,6 +59,12 @@ config_init(struct vmd *env)
return (-1);
TAILQ_INIT(env->vmd_vms);
}
+ if (what & CONFIG_SWITCHES) {
+ if ((env->vmd_switches = calloc(1,
+ sizeof(*env->vmd_switches))) == NULL)
+ return (-1);
+ TAILQ_INIT(env->vmd_switches);
+ }
return (0);
}
@@ -65,13 +74,19 @@ config_purge(struct vmd *env, unsigned i
{
struct privsep *ps = &env->vmd_ps;
struct vmd_vm *vm;
+ struct vmd_switch *vsw;
unsigned int what;
what = ps->ps_what[privsep_process] & reset;
if (what & CONFIG_VMS && env->vmd_vms != NULL) {
while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL)
vm_remove(vm);
- env->vmd_vmcount = 0;
+ env->vmd_nvm = 0;
+ }
+ if (what & CONFIG_SWITCHES && env->vmd_switches != NULL) {
+ while ((vsw = TAILQ_FIRST(env->vmd_switches)) != NULL)
+ switch_remove(vsw);
+ env->vmd_nswitches = 0;
}
}
@@ -105,17 +120,21 @@ config_getreset(struct vmd *env, struct
}
int
-config_getvm(struct privsep *ps, struct vm_create_params *vcp,
+config_getvm(struct privsep *ps, struct vmop_create_params *vmc,
int kernel_fd, uint32_t peerid)
{
struct vmd *env = ps->ps_env;
struct vmd_vm *vm = NULL;
+ struct vmd_if *vif;
+ struct vm_create_params *vcp = &vmc->vmc_params;
unsigned int i;
int fd, ttys_fd;
int kernfd = -1, *diskfds = NULL, *tapfds = NULL;
int saved_errno = 0;
char ptyname[VM_TTYNAME_MAX];
- char ifname[IF_NAMESIZE];
+ char ifname[IF_NAMESIZE], *s;
+ char path[PATH_MAX];
+ unsigned int unit;
errno = 0;
@@ -146,7 +165,7 @@ config_getvm(struct privsep *ps, struct
for (i = 0; i < vcp->vcp_ndisks; i++)
vm->vm_disks[i] = -1;
for (i = 0; i < vcp->vcp_nnics; i++)
- vm->vm_ifs[i] = -1;
+ vm->vm_ifs[i].vif_fd = -1;
vm->vm_kernel = -1;
vm->vm_vmid = env->vmd_nvm + 1;
@@ -203,19 +222,58 @@ config_getvm(struct privsep *ps, struct
}
}
- /* Open disk network interfaces */
+ /* Open network interfaces */
for (i = 0 ; i < vcp->vcp_nnics; i++) {
- if ((tapfds[i] = opentap(ifname)) == -1) {
+ vif = &vm->vm_ifs[i];
+
+ /* Check if the user has requested a specific tap(4) */
+ s = vmc->vmc_ifnames[i];
+ if (*s != '\0' && strcmp("tap", s) != 0) {
+ if (priv_getiftype(s, ifname, &unit) == -1 ||
+ strcmp(ifname, "tap") != 0) {
+ saved_errno = errno;
+ log_warn("%s: invalid tap name",
+ __func__);
+ goto fail;
+ }
+ } else
+ s = NULL;
+
+ /*
+ * Either open the requested tap(4) device or get
+ * the next available one.
+ */
+ if (s != NULL) {
+ snprintf(path, PATH_MAX, "/dev/%s", s);
+ tapfds[i] = open(path, O_RDWR | O_NONBLOCK);
+ } else {
+ tapfds[i] = opentap(ifname);
+ s = ifname;
+ }
+ if (tapfds[i] == -1) {
saved_errno = errno;
- log_warn("%s: can't open tap", __func__);
+ log_warn("%s: can't open %s", __func__, s);
goto fail;
}
-
- if ((vm->vm_ifnames[i] = strdup(ifname)) == NULL) {
+ if ((vif->vif_name = strdup(s)) == NULL) {
saved_errno = errno;
log_warn("%s: can't save ifname", __func__);
goto fail;
}
+
+ /* Check if the the interface is attached to a switch */
+ s = vmc->vmc_ifswitch[i];
+ if (*s != '\0') {
+ if ((vif->vif_switch = strdup(s)) == NULL) {
+ saved_errno = errno;
+ log_warn("%s: can't save switch",
+ __func__);
+ goto fail;
+ }
+ }
+
+ /* Set the interface status */
+ vif->vif_flags = vmc->vmc_ifflags[i] & IFF_UP;
}
/* Open TTY */
@@ -230,7 +288,7 @@ config_getvm(struct privsep *ps, struct
/* Send VM information */
proc_compose_imsg(ps, PROC_VMM, -1,
IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd,
- vcp, sizeof(*vcp));
+ vmc, sizeof(*vmc));
for (i = 0; i < vcp->vcp_ndisks; i++) {
proc_compose_imsg(ps, PROC_VMM, -1,
IMSG_VMDOP_START_VM_DISK, vm->vm_vmid, diskfds[i],
@@ -317,11 +375,11 @@ config_getif(struct privsep *ps, struct
IMSG_SIZE_CHECK(imsg, &n);
memcpy(&n, imsg->data, sizeof(n));
if (n >= vm->vm_params.vcp_nnics ||
- vm->vm_ifs[n] != -1 || imsg->fd == -1) {
+ vm->vm_ifs[n].vif_fd != -1 || imsg->fd == -1) {
log_debug("invalid interface id");
goto fail;
}
- vm->vm_ifs[n] = imsg->fd;
+ vm->vm_ifs[n].vif_fd = imsg->fd;
return (0);
fail:
@@ -329,5 +387,4 @@ config_getif(struct privsep *ps, struct
close(imsg->fd);
errno = EINVAL;
return (-1);
-
}
Index: usr.sbin/vmd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/parse.y,v
retrieving revision 1.7
diff -u -p -u -p -r1.7 parse.y
--- usr.sbin/vmd/parse.y 21 Jun 2016 21:35:25 -0000 1.7
+++ usr.sbin/vmd/parse.y 5 Oct 2016 09:07:35 -0000
@@ -1,7 +1,7 @@
/* $OpenBSD: parse.y,v 1.7 2016/06/21 21:35:25 benno Exp $ */
/*
- * Copyright (c) 2007-2015 Reyk Floeter <[email protected]>
+ * Copyright (c) 2007-2016 Reyk Floeter <[email protected]>
* Copyright (c) 2004, 2005 Esben Norby <[email protected]>
* Copyright (c) 2004 Ryan McBride <[email protected]>
* Copyright (c) 2002, 2003, 2004 Henning Brauer <[email protected]>
@@ -25,10 +25,15 @@
%{
#include <sys/types.h>
#include <sys/queue.h>
+#include <sys/socket.h>
#include <sys/uio.h>
#include <machine/vmmvar.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
@@ -78,14 +83,21 @@ char *symget(const char *);
ssize_t parse_size(char *, int64_t);
int parse_disk(char *);
-static struct vm_create_params vcp;
-static int vcp_disable = 0;
-static int errors = 0;
-
+static struct vmop_create_params vmc;
+static struct vm_create_params *vcp;
+static struct vmd_switch *vsw;
+static struct vmd_if *vif;
+static unsigned int vsw_unit;
+static char vsw_type[IF_NAMESIZE];
+static int vcp_disable;
+static size_t vcp_nnics;
+static int errors;
extern struct vmd *env;
+extern const char *vmd_descsw[];
typedef struct {
union {
+ u_int8_t lladdr[ETHER_ADDR_LEN];
int64_t number;
char *string;
} v;
@@ -96,12 +108,15 @@ typedef struct {
%token INCLUDE ERROR
-%token DISK NIFS PATH SIZE VMID
-%token ENABLE DISABLE VM KERNEL MEMORY
+%token ADD DISK DOWN INTERFACE NIFS PATH SIZE SWITCH UP VMID
+%token ENABLE DISABLE VM KERNEL LLADDR MEMORY
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> disable
+%type <v.number> updown
+%type <v.lladdr> lladdr
%type <v.string> string
+%type <v.string> optstring
%%
@@ -109,7 +124,8 @@ grammar : /* empty */
| grammar include '\n'
| grammar '\n'
| grammar varset '\n'
- | grammar main '\n'
+ | grammar switch '\n'
+ | grammar vm '\n'
| grammar error '\n' { file->errors++; }
;
@@ -144,41 +160,143 @@ varset : STRING '=' STRING {
}
;
-main : VM string {
- memset(&vcp, 0, sizeof(vcp));
+switch : SWITCH string {
+ if ((vsw = calloc(1, sizeof(*vsw))) == NULL)
+ fatal("could not allocate switch");
+
+ vsw->sw_id = env->vmd_nswitches + 1;
+ vsw->sw_name = $2;
+ vsw->sw_flags = IFF_UP;
+ snprintf(vsw->sw_ifname, sizeof(vsw->sw_ifname),
+ "%s%u", vsw_type, vsw_unit++);
+ TAILQ_INIT(&vsw->sw_ifs);
+
+ vcp_disable = 0;
+ } '{' optnl switch_opts_l '}' {
+ TAILQ_INSERT_TAIL(env->vmd_switches, vsw, sw_entry);
+ env->vmd_nswitches++;
+
+ if (vcp_disable) {
+ log_debug("%s:%d: switch \"%s\""
+ " skipped (disabled)",
+ file->name, yylval.lineno, vsw->sw_name);
+ } else if (!env->vmd_noaction) {
+ /*
+ * XXX Configure the switch right away -
+ * XXX this should be done after parsing
+ * XXX the configuration.
+ */
+ if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) {
+ log_warn("%s:%d: switch \"%s\" failed",
+ file->name, yylval.lineno,
+ vsw->sw_name);
+ YYERROR;
+ } else {
+ log_debug("%s:%d: switch \"%s\""
+ " configured",
+ file->name, yylval.lineno,
+ vsw->sw_name);
+ }
+ }
+ }
+ ;
+
+switch_opts_l : switch_opts_l switch_opts nl
+ | switch_opts optnl
+ ;
+
+switch_opts : disable {
+ vcp_disable = $1;
+ }
+ | ADD string {
+ char type[IF_NAMESIZE];
+
+ if ((vif = calloc(1, sizeof(*vif))) == NULL)
+ fatal("could not allocate interface");
+
+ if (priv_getiftype($2, type, NULL) == -1) {
+ yyerror("invalid interface: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ vif->vif_name = $2;
+
+ TAILQ_INSERT_TAIL(&vsw->sw_ifs, vif, vif_entry);
+ }
+ | INTERFACE string {
+ if (priv_getiftype($2, vsw_type, &vsw_unit) == -1 ||
+ priv_findname(vsw_type, vmd_descsw) == -1) {
+ yyerror("invalid switch interface: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ vsw_unit++;
+
+ if (strlcpy(vsw->sw_ifname, $2,
+ sizeof(vsw->sw_ifname)) >= sizeof(vsw->sw_ifname)) {
+ yyerror("switch interface too long: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | updown {
+ if ($1)
+ vsw->sw_flags |= IFF_UP;
+ else
+ vsw->sw_flags &= ~IFF_UP;
+ }
+ ;
+
+vm : VM string {
+ unsigned int i;
+
+ memset(&vmc, 0, sizeof(vmc));
+ vcp = &vmc.vmc_params;
vcp_disable = 0;
- if (strlcpy(vcp.vcp_name, $2, sizeof(vcp.vcp_name)) >=
- sizeof(vcp.vcp_name)) {
+ vcp_nnics = 0;
+
+ for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
+ /* Set the interface to UP by default */
+ vmc.vmc_ifflags[i] |= IFF_UP;
+ }
+
+ if (strlcpy(vcp->vcp_name, $2, sizeof(vcp->vcp_name)) >=
+ sizeof(vcp->vcp_name)) {
yyerror("vm name too long");
YYERROR;
}
} '{' optnl vm_opts_l '}' {
int ret;
+ /* configured interfaces vs. number of interfaces */
+ if (vcp_nnics > vcp->vcp_nnics)
+ vcp->vcp_nnics = vcp_nnics;
+
if (vcp_disable) {
log_debug("%s:%d: vm \"%s\" skipped (disabled)",
- file->name, yylval.lineno, vcp.vcp_name);
+ file->name, yylval.lineno, vcp->vcp_name);
} else if (!env->vmd_noaction) {
/*
* XXX Start the vm right away -
* XXX this should be done after parsing
* XXX the configuration.
*/
- ret = config_getvm(&env->vmd_ps, &vcp, -1, -1);
+ ret = config_getvm(&env->vmd_ps, &vmc, -1, -1);
if (ret == -1 && errno == EALREADY) {
log_debug("%s:%d: vm \"%s\""
" skipped (running)",
file->name, yylval.lineno,
- vcp.vcp_name);
+ vcp->vcp_name);
} else if (ret == -1) {
log_warn("%s:%d: vm \"%s\" failed",
file->name, yylval.lineno,
- vcp.vcp_name);
+ vcp->vcp_name);
YYERROR;
} else {
log_debug("%s:%d: vm \"%s\" enabled",
file->name, yylval.lineno,
- vcp.vcp_name);
+ vcp->vcp_name);
}
}
}
@@ -199,14 +317,47 @@ vm_opts : disable {
}
free($2);
}
+ | INTERFACE optstring iface_opts_o {
+ unsigned int i;
+ char type[IF_NAMESIZE];
+
+ i = vcp_nnics;
+ if (++vcp_nnics > VMM_MAX_NICS_PER_VM) {
+ yyerror("too many interfaces: %zu", vcp_nnics);
+ free($2);
+ YYERROR;
+ }
+
+ if ($2 != NULL) {
+ if (strcmp($2, "tap") != 0 &&
+ (priv_getiftype($2, type, NULL) == -1 ||
+ strcmp(type, "tap") != 0)) {
+ yyerror("invalid interface: %s", $2);
+ free($2);
+ YYERROR;
+ }
+
+ if (strlcpy(vmc.vmc_ifnames[i], $2,
+ sizeof(vmc.vmc_ifnames[i])) >=
+ sizeof(vmc.vmc_ifnames[i])) {
+ yyerror("interface name too long: %s",
+ $2);
+ free($2);
+ YYERROR;
+ }
+ }
+ free($2);
+ }
| KERNEL string {
- if (vcp.vcp_kernel[0] != '\0') {
+ if (vcp->vcp_kernel[0] != '\0') {
yyerror("kernel specified more than once");
free($2);
YYERROR;
+
}
- if (strlcpy(vcp.vcp_kernel, $2,
- sizeof(vcp.vcp_kernel)) >= sizeof(vcp.vcp_kernel)) {
+ if (strlcpy(vcp->vcp_kernel, $2,
+ sizeof(vcp->vcp_kernel)) >=
+ sizeof(vcp->vcp_kernel)) {
yyerror("kernel name too long");
free($2);
YYERROR;
@@ -214,7 +365,7 @@ vm_opts : disable {
free($2);
}
| NIFS NUMBER {
- if (vcp.vcp_nnics != 0) {
+ if (vcp->vcp_nnics != 0) {
yyerror("interfaces specified more than once");
YYERROR;
}
@@ -222,11 +373,11 @@ vm_opts : disable {
yyerror("too many interfaces: %lld", $2);
YYERROR;
}
- vcp.vcp_nnics = (size_t)$2;
+ vcp->vcp_nnics = (size_t)$2;
}
| MEMORY NUMBER {
ssize_t res;
- if (vcp.vcp_memranges[0].vmr_size != 0) {
+ if (vcp->vcp_memranges[0].vmr_size != 0) {
yyerror("memory specified more than once");
YYERROR;
}
@@ -234,11 +385,11 @@ vm_opts : disable {
yyerror("failed to parse size: %lld", $2);
YYERROR;
}
- vcp.vcp_memranges[0].vmr_size = (size_t)res;
+ vcp->vcp_memranges[0].vmr_size = (size_t)res;
}
| MEMORY STRING {
ssize_t res;
- if (vcp.vcp_memranges[0].vmr_size != 0) {
+ if (vcp->vcp_memranges[0].vmr_size != 0) {
yyerror("argument specified more than once");
free($2);
YYERROR;
@@ -248,11 +399,47 @@ vm_opts : disable {
free($2);
YYERROR;
}
- vcp.vcp_memranges[0].vmr_size = (size_t)res;
+ vcp->vcp_memranges[0].vmr_size = (size_t)res;
+ }
+ ;
+
+iface_opts_o : '{' optnl iface_opts_l '}'
+ | /* empty */
+ ;
+
+iface_opts_l : iface_opts_l iface_opts optnl
+ | iface_opts optnl
+ ;
+
+iface_opts : SWITCH string {
+ unsigned int i = vcp_nnics;
+
+ /* No need to check if the switch exists */
+ if (strlcpy(vmc.vmc_ifswitch[i], $2,
+ sizeof(vmc.vmc_ifswitch[i])) >=
+ sizeof(vmc.vmc_ifswitch[i])) {
+ yyerror("switch name too long: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | LLADDR lladdr {
+ memcpy(vcp->vcp_macs[vcp_nnics], $2, ETHER_ADDR_LEN);
+ }
+ | updown {
+ if ($1)
+ vmc.vmc_ifflags[vcp_nnics] |= IFF_UP;
+ else
+ vmc.vmc_ifflags[vcp_nnics] &= ~IFF_UP;
}
;
-string : STRING string {
+optstring : STRING { $$ = $1; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+string : STRING string {
if (asprintf(&$$, "%s%s", $1, $2) == -1)
fatal("asprintf string");
free($1);
@@ -261,6 +448,24 @@ string : STRING string
{
| STRING
;
+lladdr : STRING {
+ struct ether_addr *ea;
+
+ if ((ea = ether_aton($1)) == NULL) {
+ yyerror("invalid address: %s\n", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+
+ memcpy($$, ea, ETHER_ADDR_LEN);
+ }
+ ;
+
+updown : UP { $$ = 1; }
+ | DOWN { $$ = 0; }
+ ;
+
disable : ENABLE { $$ = 0; }
| DISABLE { $$ = 1; }
;
@@ -306,15 +511,21 @@ lookup(char *s)
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
+ { "add", ADD },
{ "disable", DISABLE },
{ "disk", DISK },
+ { "down", DOWN },
{ "enable", ENABLE },
{ "id", VMID },
{ "include", INCLUDE },
+ { "interface", INTERFACE },
{ "interfaces", NIFS },
{ "kernel", KERNEL },
+ { "lladdr", LLADDR },
{ "memory", MEMORY },
{ "size", SIZE },
+ { "switch", SWITCH },
+ { "up", UP },
{ "vm", VM }
};
const struct keywords *p;
@@ -631,6 +842,9 @@ parse_config(const char *filename)
topfile = file;
setservent(1);
+ /* Set the default switch type */
+ (void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type));
+
yyparse();
errors = file->errors;
popfile();
@@ -760,18 +974,18 @@ parse_size(char *word, int64_t val)
int
parse_disk(char *word)
{
- if (vcp.vcp_ndisks >= VMM_MAX_DISKS_PER_VM) {
+ if (vcp->vcp_ndisks >= VMM_MAX_DISKS_PER_VM) {
log_warnx("too many disks");
return (-1);
}
- if (strlcpy(vcp.vcp_disks[vcp.vcp_ndisks], word,
+ if (strlcpy(vcp->vcp_disks[vcp->vcp_ndisks], word,
VMM_MAX_PATH_DISK) >= VMM_MAX_PATH_DISK) {
log_warnx("disk path too long");
return (-1);
}
- vcp.vcp_ndisks++;
+ vcp->vcp_ndisks++;
return (0);
}
Index: usr.sbin/vmd/priv.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/priv.c,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 priv.c
--- usr.sbin/vmd/priv.c 4 Oct 2016 17:17:30 -0000 1.1
+++ usr.sbin/vmd/priv.c 5 Oct 2016 09:07:35 -0000
@@ -25,6 +25,9 @@
#include <sys/tree.h>
#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <net/if_bridge.h>
#include <errno.h>
#include <event.h>
@@ -41,9 +44,6 @@
int priv_dispatch_parent(int, struct privsep_proc *, struct imsg *);
void priv_run(struct privsep *, struct privsep_proc *, void *);
-int priv_getiftype(char *, char *, unsigned int *);
-int priv_findname(const char *, const char **);
-
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, priv_dispatch_parent }
};
@@ -77,25 +77,72 @@ priv_dispatch_parent(int fd, struct priv
struct vmop_ifreq vfr;
struct vmd *env = ps->ps_env;
struct ifreq ifr;
+ struct ifbreq ifbr;
char type[IF_NAMESIZE];
- unsigned int unit;
switch (imsg->hdr.type) {
case IMSG_VMDOP_PRIV_IFDESCR:
+ case IMSG_VMDOP_PRIV_IFCREATE:
+ case IMSG_VMDOP_PRIV_IFADD:
+ case IMSG_VMDOP_PRIV_IFUP:
+ case IMSG_VMDOP_PRIV_IFDOWN:
IMSG_SIZE_CHECK(imsg, &vfr);
memcpy(&vfr, imsg->data, sizeof(vfr));
/* We should not get malicious requests from the parent */
- if (priv_getiftype(vfr.vfr_name, type, &unit) == -1 ||
+ if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 ||
priv_findname(type, desct) == -1)
fatalx("%s: rejected priv operation on interface: %s",
__func__, vfr.vfr_name);
+ break;
+ default:
+ return (-1);
+ }
+ switch (imsg->hdr.type) {
+ case IMSG_VMDOP_PRIV_IFDESCR:
+ /* Set the interface description */
strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
ifr.ifr_data = (caddr_t)vfr.vfr_value;
if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) < 0)
log_warn("SIOCSIFDESCR");
break;
+ case IMSG_VMDOP_PRIV_IFCREATE:
+ /* Create the bridge if it doesn't exist */
+ strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
+ if (ioctl(env->vmd_fd, SIOCIFCREATE, &ifr) < 0 &&
+ errno != EEXIST)
+ log_warn("SIOCIFCREATE");
+ break;
+ case IMSG_VMDOP_PRIV_IFADD:
+ if (priv_getiftype(vfr.vfr_value, type, NULL) == -1)
+ fatalx("%s: rejected to add interface: %s",
+ __func__, vfr.vfr_value);
+
+ /* Attach the device to the bridge */
+ strlcpy(ifbr.ifbr_name, vfr.vfr_name,
+ sizeof(ifbr.ifbr_name));
+ strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value,
+ sizeof(ifbr.ifbr_ifsname));
+ if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) < 0 &&
+ errno != EEXIST)
+ log_warn("SIOCBRDGADD");
+ break;
+ case IMSG_VMDOP_PRIV_IFUP:
+ case IMSG_VMDOP_PRIV_IFDOWN:
+ /* Set the interface status */
+ strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
+ if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0) {
+ log_warn("SIOCGIFFLAGS");
+ break;
+ }
+ if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) < 0)
+ log_warn("SIOCSIFFLAGS");
+ break;
default:
return (-1);
}
@@ -148,14 +195,18 @@ int
vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
{
struct vm_create_params *vcp = &vm->vm_params;
+ struct vmd_if *vif;
+ struct vmd_switch *vsw;
unsigned int i;
- struct vmop_ifreq vfr;
+ struct vmop_ifreq vfr, vfbr;
for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
- if (vm->vm_ifnames[i] == NULL)
+ vif = &vm->vm_ifs[i];
+
+ if (vif->vif_name == NULL)
break;
- if (strlcpy(vfr.vfr_name, vm->vm_ifnames[i],
+ if (strlcpy(vfr.vfr_name, vif->vif_name,
sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
return (-1);
@@ -163,11 +214,79 @@ vm_priv_ifconfig(struct privsep *ps, str
(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
"vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name);
+ log_debug("%s: interface %s description %s", __func__,
+ vfr.vfr_name, vfr.vfr_value);
+
proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
&vfr, sizeof(vfr));
- /* XXX Add interface to bridge/switch */
+ /* Add interface to bridge/switch */
+ if ((vsw = switch_getbyname(vif->vif_switch)) != NULL) {
+ if (strlcpy(vfbr.vfr_name, vsw->sw_ifname,
+ sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name))
+ return (-1);
+ if (strlcpy(vfbr.vfr_value, vif->vif_name,
+ sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value))
+ return (-1);
+
+ log_debug("%s: interface %s add %s", __func__,
+ vfbr.vfr_name, vfbr.vfr_value);
+
+ proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE,
+ &vfbr, sizeof(vfbr));
+ proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
+ &vfbr, sizeof(vfbr));
+ } else if (vif->vif_switch != NULL)
+ log_warnx("switch %s not found", vif->vif_switch);
+
+ /* Set the new interface status to up or down */
+ proc_compose(ps, PROC_PRIV, (vif->vif_flags & IFF_UP) ?
+ IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
+ &vfr, sizeof(vfr));
+ }
+
+ return (0);
+}
+
+int
+vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
+{
+ struct vmd_if *vif;
+ struct vmop_ifreq vfr;
+
+ if (strlcpy(vfr.vfr_name, vsw->sw_ifname,
+ sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
+ return (-1);
+
+ proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE,
+ &vfr, sizeof(vfr));
+
+ /* Description can be truncated */
+ (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
+ "switch%u-%s", vsw->sw_id, vsw->sw_name);
+
+ log_debug("%s: interface %s description %s", __func__,
+ vfr.vfr_name, vfr.vfr_value);
+
+ proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
+ &vfr, sizeof(vfr));
+
+ TAILQ_FOREACH(vif, &vsw->sw_ifs, vif_entry) {
+ if (strlcpy(vfr.vfr_value, vif->vif_name,
+ sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
+ return (-1);
+
+ log_debug("%s: interface %s add %s", __func__,
+ vfr.vfr_name, vfr.vfr_value);
+
+ proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
+ &vfr, sizeof(vfr));
}
+
+ /* Set the new interface status to up or down */
+ proc_compose(ps, PROC_PRIV, (vsw->sw_flags & IFF_UP) ?
+ IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
+ &vfr, sizeof(vfr));
return (0);
}
Index: usr.sbin/vmd/proc.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/proc.h,v
retrieving revision 1.8
diff -u -p -u -p -r1.8 proc.h
--- usr.sbin/vmd/proc.h 4 Oct 2016 17:17:30 -0000 1.8
+++ usr.sbin/vmd/proc.h 5 Oct 2016 09:07:35 -0000
@@ -48,9 +48,9 @@ struct imsgev {
short events;
};
-#define IMSG_SIZE_CHECK(imsg, p) do { \
- if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \
- fatalx("bad length imsg received"); \
+#define IMSG_SIZE_CHECK(imsg, p) do { \
+ if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \
+ fatalx("bad length imsg received (%s)", #p); \
} while (0)
#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE)
@@ -95,6 +95,7 @@ enum privsep_procid {
#define CONFIG_RELOAD 0x00
#define CONFIG_VMS 0x01
+#define CONFIG_SWITCHES 0x02
#define CONFIG_ALL 0xff
struct privsep_pipes {
Index: usr.sbin/vmd/virtio.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v
retrieving revision 1.20
diff -u -p -u -p -r1.20 virtio.c
--- usr.sbin/vmd/virtio.c 3 Oct 2016 05:59:24 -0000 1.20
+++ usr.sbin/vmd/virtio.c 5 Oct 2016 09:07:35 -0000
@@ -1169,6 +1169,7 @@ out:
void
virtio_init(struct vm_create_params *vcp, int *child_disks, int *child_taps)
{
+ static const uint8_t zero_mac[6];
uint8_t id;
uint8_t i;
int ret;
@@ -1304,11 +1305,12 @@ virtio_init(struct vm_create_params *vcp
return;
}
-#if 0
/* User defined MAC */
- vionet[i].cfg.device_feature = VIRTIO_NET_F_MAC;
- bcopy(&vcp->vcp_macs[i], &vionet[i].mac, 6);
-#endif
+ if (memcmp(zero_mac, &vcp->vcp_macs[i], 6) != 0) {
+ vionet[i].cfg.device_feature =
+ VIRTIO_NET_F_MAC;
+ bcopy(&vcp->vcp_macs[i], &vionet[i].mac, 6);
+ }
}
}
}
Index: usr.sbin/vmd/vm.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v
retrieving revision 1.5
diff -u -p -u -p -r1.5 vm.conf.5
--- usr.sbin/vmd/vm.conf.5 5 May 2016 09:00:44 -0000 1.5
+++ usr.sbin/vmd/vm.conf.5 5 Oct 2016 09:07:35 -0000
@@ -38,6 +38,8 @@ User-defined variables may be defined an
configuration file.
.It Sy VM Configuration
Configuration for each individual virtual machine.
+.It Sy Switch Configuration
+Configuration for virtual switches.
.El
.Pp
Within the sections,
@@ -110,18 +112,133 @@ Disk image file (may be specified multip
Kernel to load when booting the VM.
.It Cm memory Ar bytes
Memory size of the VM, in bytes, rounded to megabytes.
+.It Cm interface Oo name Oc Op Brq ...
+Network interface to add to the VM.
+The optional
+.Ar name
+can be either
+.Sq tap
+to select the next available
+.Xr tap 4
+interface on the VM host side, this is the default, or
+.Ar tapN
+to select a specific one.
+Valid options are:
+.Bl -tag -width Ds
+.It Cm lladdr Ar etheraddr
+Change the link layer address (MAC address) of the interface on the
+VM guest side.
+The
+.Ar etheraddr
+is specified as six colon-separated hex values.
+If not specified, the address will be set to all zeros and eventually
+randomized by the guest OS inside the VM.
+.It Cm switch Ar name
+Set the virtual switch
+by
+.Ar name .
+See the
+.Sx SWITCH CONFIGURATION
+section about virtual switches.
+This option is ignored if a switch with a matching name cannot be found.
+.Sx
+.It Cm up
+Start the interface forwarding packets.
+This is the default.
+.It Cm down
+Stop the interface from forwarding packets.
+.El
.It Cm interfaces Ar count
-Number of network interfaces to add to the VM.
+Optional minimum number of network interfaces to add to the VM.
+If the
+.Ar count
+is greater than the number of
+.Ic interface
+statements, additional default interfaces will be added.
+.El
+.Sh SWITCH CONFIGURATION
+Virtual switches can be configured at any point in the configuration file;
+they allow
+.Nm switchd
+to add network interfaces of VMs to the underlying switch interfaces
+automatically.
+It is possible to pre-configure switch interfaces using
+.Xr hostname.if 5
+or
+.Xr ifconfig 8 ,
+see the sections
+.Sx BRIDGE
+or
+.Sx SWITCH
+in
+.Xr ifconfig 8
+accordingly.
+.Pp
+Each
+.Ic switch
+section starts with a declaration of the virtual switch:
+.Bl -tag -width Ds
+.It Ic switch Ar name Brq ...
+This name can be any string, and is typically a network name.
+.El
+.Pp
+Followed by a block of parameters that is enclosed in curly brackets:
+.Bl -tag -width Ds
+.It Cm add Ar interface
+Add
+.Ar interface
+as a member of the switch.
+Any network interface can be added, typically as an uplink interface,
+but it can be a member of at most one switch.
+.It Cm enable
+Automatically configure the switch.
+This is the default if neither
+.Cm enable
+nor
+.Cm disable
+is specified.
+.It Cm disable
+Do not configure this switch.
+.It Cm interface Ar name
+Set the
+.Xr switch 4
+or
+.Xr bridge 4
+network interface of this switch.
+If not specified,
+.Ar bridge0
+will be used where the interface unit will be incremented for each switch,
+eg.
+.Ar bridge0 , bridge1 , ...
+If the type is changed to
+.Ar switch0 ,
+it will be used for each following switch.
+.It Cm up
+Start the switch forwarding packets.
+This is the default.
+.It Cm down
+Stop the switch from forwarding packets.
.El
.Sh EXAMPLES
-Create a new VM with 512MB memory, 1 network interface, one disk image
-('disk.img') and boot from kernel '/bsd':
+Create a new VM with 512MB memory, 1 network interface connected to
+.Dq uplink ,
+one disk image
+.Sq disk.img
+and boot from kernel
+.Sq /bsd :
.Bd -literal -offset indent
vm "vm2.example.com" {
memory 512M
- interfaces 1
disk "/var/vmm/vm2-disk.img"
kernel "/bsd"
+ interface { switch "uplink" }
+}
+.Ed
+.Pp
+Create the switch "uplink" with an additional physical network interface:
+.Bd -literal -offset indent
+switch "uplink" {
+ add em0
}
.Ed
.Sh SEE ALSO
Index: usr.sbin/vmd/vmd.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.c,v
retrieving revision 1.31
diff -u -p -u -p -r1.31 vmd.c
--- usr.sbin/vmd/vmd.c 4 Oct 2016 17:17:30 -0000 1.31
+++ usr.sbin/vmd/vmd.c 5 Oct 2016 09:07:35 -0000
@@ -65,7 +65,7 @@ vmd_dispatch_control(int fd, struct priv
{
struct privsep *ps = p->p_ps;
int res = 0, cmd = 0, v = 0;
- struct vm_create_params vcp;
+ struct vmop_create_params vmc;
struct vmop_id vid;
struct vm_terminate_params vtp;
struct vmop_result vmr;
@@ -75,9 +75,9 @@ vmd_dispatch_control(int fd, struct priv
switch (imsg->hdr.type) {
case IMSG_VMDOP_START_VM_REQUEST:
- IMSG_SIZE_CHECK(imsg, &vcp);
- memcpy(&vcp, imsg->data, sizeof(vcp));
- res = config_getvm(ps, &vcp, -1, imsg->hdr.peerid);
+ IMSG_SIZE_CHECK(imsg, &vmc);
+ memcpy(&vmc, imsg->data, sizeof(vmc));
+ res = config_getvm(ps, &vmc, -1, imsg->hdr.peerid);
if (res == -1) {
res = errno;
cmd = IMSG_VMDOP_START_VM_RESPONSE;
@@ -500,6 +500,8 @@ vm_getbyname(const char *name)
{
struct vmd_vm *vm;
+ if (name == NULL)
+ return (NULL);
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
if (strcmp(vm->vm_params.vcp_name, name) == 0)
return (vm);
@@ -536,9 +538,10 @@ vm_remove(struct vmd_vm *vm)
close(vm->vm_disks[i]);
}
for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
- if (vm->vm_ifs[i] != -1)
- close(vm->vm_ifs[i]);
- free(vm->vm_ifnames[i]);
+ if (vm->vm_ifs[i].vif_fd != -1)
+ close(vm->vm_ifs[i].vif_fd);
+ free(vm->vm_ifs[i].vif_name);
+ free(vm->vm_ifs[i].vif_switch);
}
if (vm->vm_kernel != -1)
close(vm->vm_kernel);
@@ -547,6 +550,42 @@ vm_remove(struct vmd_vm *vm)
free(vm->vm_ttyname);
free(vm);
+}
+
+void
+switch_remove(struct vmd_switch *vsw)
+{
+ struct vmd_if *vif;
+
+ if (vsw == NULL)
+ return;
+
+ TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry);
+
+ while ((vif = TAILQ_FIRST(&vsw->sw_ifs)) != NULL) {
+ free(vif->vif_name);
+ free(vif->vif_switch);
+ TAILQ_REMOVE(&vsw->sw_ifs, vif, vif_entry);
+ free(vif);
+ }
+
+ free(vsw->sw_name);
+ free(vsw);
+}
+
+struct vmd_switch *
+switch_getbyname(const char *name)
+{
+ struct vmd_switch *vsw;
+
+ if (name == NULL)
+ return (NULL);
+ TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
+ if (strcmp(vsw->sw_name, name) == 0)
+ return (vsw);
+ }
+
+ return (NULL);
}
char *
Index: usr.sbin/vmd/vmd.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.h,v
retrieving revision 1.26
diff -u -p -u -p -r1.26 vmd.h
--- usr.sbin/vmd/vmd.h 4 Oct 2016 17:17:30 -0000 1.26
+++ usr.sbin/vmd/vmd.h 5 Oct 2016 09:07:35 -0000
@@ -40,6 +40,7 @@
#define VM_TTYNAME_MAX 16
#define MAX_TAP 256
#define NR_BACKLOG 5
+#define VMD_SWITCH_TYPE "bridge"
#ifdef VMD_DEBUG
#define dprintf(x...) do { log_debug(x); } while(0)
@@ -61,7 +62,11 @@ enum imsg_type {
IMSG_VMDOP_GET_INFO_VM_END_DATA,
IMSG_VMDOP_LOAD,
IMSG_VMDOP_RELOAD,
- IMSG_VMDOP_PRIV_IFDESCR
+ IMSG_VMDOP_PRIV_IFDESCR,
+ IMSG_VMDOP_PRIV_IFADD,
+ IMSG_VMDOP_PRIV_IFCREATE,
+ IMSG_VMDOP_PRIV_IFUP,
+ IMSG_VMDOP_PRIV_IFDOWN
};
struct vmop_result {
@@ -87,14 +92,41 @@ struct vmop_ifreq {
char vfr_value[VM_NAME_MAX];
};
+struct vmop_create_params {
+ struct vm_create_params vmc_params;
+
+ /* userland-only part of the create params */
+ unsigned int vmc_ifflags[VMM_MAX_NICS_PER_VM];
+ char vmc_ifnames[VMM_MAX_NICS_PER_VM][IF_NAMESIZE];
+ char vmc_ifswitch[VMM_MAX_NICS_PER_VM][VM_NAME_MAX];
+};
+
+struct vmd_if {
+ char *vif_name;
+ char *vif_switch;
+ int vif_fd;
+ unsigned int vif_flags;
+ TAILQ_ENTRY(vmd_if) vif_entry;
+};
+TAILQ_HEAD(viflist, vmd_if);
+
+struct vmd_switch {
+ uint32_t sw_id;
+ char *sw_name;
+ char sw_ifname[IF_NAMESIZE];
+ unsigned int sw_flags;
+ struct viflist sw_ifs;
+ TAILQ_ENTRY(vmd_switch) sw_entry;
+};
+TAILQ_HEAD(switchlist, vmd_switch);
+
struct vmd_vm {
struct vm_create_params vm_params;
pid_t vm_pid;
uint32_t vm_vmid;
int vm_kernel;
int vm_disks[VMM_MAX_DISKS_PER_VM];
- int vm_ifs[VMM_MAX_NICS_PER_VM];
- char *vm_ifnames[VMM_MAX_NICS_PER_VM];
+ struct vmd_if vm_ifs[VMM_MAX_NICS_PER_VM];
char *vm_ttyname;
int vm_tty;
uint32_t vm_peerid;
@@ -109,11 +141,13 @@ struct vmd {
int vmd_debug;
int vmd_verbose;
int vmd_noaction;
- int vmd_vmcount;
uint32_t vmd_nvm;
struct vmlist *vmd_vms;
+ uint32_t vmd_nswitches;
+ struct switchlist *vmd_switches;
+
int vmd_fd;
};
@@ -124,11 +158,16 @@ struct vmd_vm *vm_getbyid(uint32_t);
struct vmd_vm *vm_getbyname(const char *);
struct vmd_vm *vm_getbypid(pid_t);
void vm_remove(struct vmd_vm *);
+void switch_remove(struct vmd_switch *);
+struct vmd_switch *switch_getbyname(const char *);
char *get_string(uint8_t *, size_t);
/* priv.c */
void priv(struct privsep *, struct privsep_proc *);
+int priv_getiftype(char *, char *, unsigned int *);
+int priv_findname(const char *, const char **);
int vm_priv_ifconfig(struct privsep *, struct vmd_vm *);
+int vm_priv_brconfig(struct privsep *, struct vmd_switch *);
/* vmm.c */
void vmm(struct privsep *, struct privsep_proc *);
@@ -144,7 +183,7 @@ int config_init(struct vmd *);
void config_purge(struct vmd *, unsigned int);
int config_setreset(struct vmd *, unsigned int);
int config_getreset(struct vmd *, struct imsg *);
-int config_getvm(struct privsep *, struct vm_create_params *,
+int config_getvm(struct privsep *, struct vmop_create_params *,
int, uint32_t);
int config_getdisk(struct privsep *, struct imsg *);
int config_getif(struct privsep *, struct imsg *);
Index: usr.sbin/vmd/vmm.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmm.c,v
retrieving revision 1.46
diff -u -p -u -p -r1.46 vmm.c
--- usr.sbin/vmd/vmm.c 4 Oct 2016 17:17:30 -0000 1.46
+++ usr.sbin/vmd/vmm.c 5 Oct 2016 09:07:35 -0000
@@ -177,7 +177,7 @@ vmm_dispatch_parent(int fd, struct privs
{
struct privsep *ps = p->p_ps;
int res = 0, cmd = 0;
- struct vm_create_params vcp;
+ struct vmop_create_params vmc;
struct vm_terminate_params vtp;
struct vmop_result vmr;
uint32_t id = 0;
@@ -185,9 +185,9 @@ vmm_dispatch_parent(int fd, struct privs
switch (imsg->hdr.type) {
case IMSG_VMDOP_START_VM_REQUEST:
- IMSG_SIZE_CHECK(imsg, &vcp);
- memcpy(&vcp, imsg->data, sizeof(vcp));
- res = config_getvm(ps, &vcp, imsg->fd, imsg->hdr.peerid);
+ IMSG_SIZE_CHECK(imsg, &vmc);
+ memcpy(&vmc, imsg->data, sizeof(vmc));
+ res = config_getvm(ps, &vmc, imsg->fd, imsg->hdr.peerid);
if (res == -1) {
res = errno;
cmd = IMSG_VMDOP_START_VM_RESPONSE;
@@ -388,6 +388,7 @@ opentap(char *ifname)
int i, fd;
char path[PATH_MAX];
+ strlcpy(ifname, "tap", IF_NAMESIZE);
for (i = 0; i < MAX_TAP; i++) {
snprintf(path, PATH_MAX, "/dev/tap%d", i);
fd = open(path, O_RDWR | O_NONBLOCK);
@@ -432,7 +433,7 @@ start_vm(struct imsg *imsg, uint32_t *id
struct vmd_vm *vm;
size_t i;
int ret = EINVAL;
- int fds[2];
+ int fds[2], nicfds[VMM_MAX_NICS_PER_VM];
struct vcpu_reg_state vrs;
if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
@@ -470,8 +471,8 @@ start_vm(struct imsg *imsg, uint32_t *id
}
for (i = 0 ; i < vcp->vcp_nnics; i++) {
- close(vm->vm_ifs[i]);
- vm->vm_ifs[i] = -1;
+ close(vm->vm_ifs[i].vif_fd);
+ vm->vm_ifs[i].vif_fd = -1;
}
close(vm->vm_kernel);
@@ -549,8 +550,11 @@ start_vm(struct imsg *imsg, uint32_t *id
if (fcntl(con_fd, F_SETFL, O_NONBLOCK) == -1)
fatal("failed to set nonblocking mode on console");
+ for (i = 0; i < VMM_MAX_NICS_PER_VM; i++)
+ nicfds[i] = vm->vm_ifs[i].vif_fd;
+
/* Execute the vcpu run loop(s) for this VM */
- ret = run_vm(vm->vm_disks, vm->vm_ifs, vcp, &vrs);
+ ret = run_vm(vm->vm_disks, nicfds, vcp, &vrs);
_exit(ret != 0);
}
Index: usr.sbin/vmctl/vmctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.c,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 vmctl.c
--- usr.sbin/vmctl/vmctl.c 10 May 2016 11:00:54 -0000 1.15
+++ usr.sbin/vmctl/vmctl.c 5 Oct 2016 09:07:35 -0000
@@ -64,6 +64,7 @@ int
start_vm(const char *name, int memsize, int nnics, int ndisks, char **disks,
char *kernel)
{
+ struct vmop_create_params *vmc;
struct vm_create_params *vcp;
int i;
@@ -80,11 +81,12 @@ start_vm(const char *name, int memsize,
if (nnics == 0)
warnx("starting without network interfaces");
- vcp = malloc(sizeof(struct vm_create_params));
- if (vcp == NULL)
+ vmc = calloc(1, sizeof(struct vmop_create_params));
+ if (vmc == NULL)
return (ENOMEM);
- bzero(vcp, sizeof(struct vm_create_params));
+ /* vcp includes configuration that is shared with the kernel */
+ vcp = &vmc->vmc_params;
/*
* XXX: vmd(8) fills in the actual memory ranges. vmctl(8)
@@ -105,7 +107,7 @@ start_vm(const char *name, int memsize,
vcp->vcp_nnics = nnics;
imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
- vcp, sizeof(struct vm_create_params));
+ vmc, sizeof(struct vmop_create_params));
free(vcp);
return (0);