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);

Reply via email to