On Wed, Mar 01, 2017 at 07:17:10PM +0100, Reyk Floeter wrote: > On Wed, Mar 01, 2017 at 01:04:57PM +0100, Reyk Floeter wrote: > > Hi, > > > > add the new "fixed lladdr" option: when multiple VMs are connected to > > a switch, it is desirable that an individual VM cannot spoof another > > MAC address, especially when using meta-data*. vmd(8) can enforce > > this by comparing the address in the Ethernet header with the > > configured/generated address of the VM interface. > > > > This somewhat resembles the following features from VMware: > > ethernet0.noforgedsrcaddr = "TRUE" > > ethernet0.nopromisc = "TRUE" > > > > The important parts of the diff** are in the two if statements including > > "dev->fixedmac" below, the rest is infrastructure, config, and > > documentation. > > > > I could have used bridge(4) rules or switch(4) OpenFlow actions, but I > > decided to implement it in vmd(8) directly to make it easier and to > > work in all cases independent from the switch type. > > > > *) https://github.com/reyk/meta-data > > **) this diff conflicts with the previous vmm.c split. Whatever goes > > in first, I can update and resend it accordingly. > > > > OK? > > > > Reyk > > > > Add "fixed lladdr" option to prevent VMs from spoofing MAC addresses. > > > > This is especially useful when multiple VMs share a switch, the > > implementation is independent from the underlying switch or bridge. > > > > After some discussions, this is the updated diff: > > - change "fixed" to "locked", adjust manpage > - sync with latest vmm.c split > > Reyk >
no objections > Index: usr.sbin/vmd/config.c > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/config.c,v > retrieving revision 1.25 > diff -u -p -u -p -r1.25 config.c > --- usr.sbin/vmd/config.c 1 Mar 2017 07:43:33 -0000 1.25 > +++ usr.sbin/vmd/config.c 1 Mar 2017 18:16:20 -0000 > @@ -236,7 +236,8 @@ config_setvm(struct privsep *ps, struct > } > > /* Set the interface status */ > - vif->vif_flags = vmc->vmc_ifflags[i] & IFF_UP; > + vif->vif_flags = > + vmc->vmc_ifflags[i] & (VMIFF_UP|VMIFF_OPTMASK); > } > > /* Open TTY */ > Index: usr.sbin/vmd/parse.y > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/parse.y,v > retrieving revision 1.21 > diff -u -p -u -p -r1.21 parse.y > --- usr.sbin/vmd/parse.y 1 Mar 2017 07:43:33 -0000 1.21 > +++ usr.sbin/vmd/parse.y 1 Mar 2017 18:16:20 -0000 > @@ -116,10 +116,11 @@ typedef struct { > > %token INCLUDE ERROR > %token ADD DISK DOWN GROUP INTERFACE NIFS PATH SIZE SWITCH UP VMID > -%token ENABLE DISABLE VM KERNEL LLADDR MEMORY OWNER > +%token ENABLE DISABLE VM KERNEL LLADDR MEMORY OWNER LOCKED > %token <v.string> STRING > %token <v.number> NUMBER > %type <v.number> disable > +%type <v.number> locked > %type <v.number> updown > %type <v.lladdr> lladdr > %type <v.string> string > @@ -174,7 +175,7 @@ switch : SWITCH string { > > vsw->sw_id = env->vmd_nswitches + 1; > vsw->sw_name = $2; > - vsw->sw_flags = IFF_UP; > + vsw->sw_flags = VMIFF_UP; > snprintf(vsw->sw_ifname, sizeof(vsw->sw_ifname), > "%s%u", vsw_type, vsw_unit++); > TAILQ_INIT(&vsw->sw_ifs); > @@ -241,11 +242,14 @@ switch_opts : disable { > } > free($2); > } > + | LOCKED LLADDR { > + vsw->sw_flags |= VMIFF_LOCKED; > + } > | updown { > if ($1) > - vsw->sw_flags |= IFF_UP; > + vsw->sw_flags |= VMIFF_UP; > else > - vsw->sw_flags &= ~IFF_UP; > + vsw->sw_flags &= ~VMIFF_UP; > } > ; > > @@ -503,14 +507,16 @@ iface_opts : SWITCH string { > sizeof(vmc.vmc_ifgroup[i])); > free($2); > } > - | LLADDR lladdr { > - memcpy(vcp->vcp_macs[vcp_nnics], $2, ETHER_ADDR_LEN); > + | locked LLADDR lladdr { > + if ($1) > + vmc.vmc_ifflags[vcp_nnics] |= VMIFF_LOCKED; > + memcpy(vcp->vcp_macs[vcp_nnics], $3, ETHER_ADDR_LEN); > } > | updown { > if ($1) > - vmc.vmc_ifflags[vcp_nnics] |= IFF_UP; > + vmc.vmc_ifflags[vcp_nnics] |= VMIFF_UP; > else > - vmc.vmc_ifflags[vcp_nnics] &= ~IFF_UP; > + vmc.vmc_ifflags[vcp_nnics] &= ~VMIFF_UP; > } > ; > > @@ -541,6 +547,10 @@ lladdr : STRING { > } > ; > > +locked : /* empty */ { $$ = 0; } > + | LOCKED { $$ = 1; } > + ; > + > updown : UP { $$ = 1; } > | DOWN { $$ = 0; } > ; > @@ -606,6 +616,7 @@ lookup(char *s) > { "interfaces", NIFS }, > { "kernel", KERNEL }, > { "lladdr", LLADDR }, > + { "locked", LOCKED }, > { "memory", MEMORY }, > { "owner", OWNER }, > { "size", SIZE }, > Index: usr.sbin/vmd/priv.c > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/priv.c,v > retrieving revision 1.5 > diff -u -p -u -p -r1.5 priv.c > --- usr.sbin/vmd/priv.c 29 Oct 2016 14:56:05 -0000 1.5 > +++ usr.sbin/vmd/priv.c 1 Mar 2017 18:16:20 -0000 > @@ -295,7 +295,7 @@ vm_priv_ifconfig(struct privsep *ps, str > } > > /* Set the new interface status to up or down */ > - proc_compose(ps, PROC_PRIV, (vif->vif_flags & IFF_UP) ? > + proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ? > IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, > &vfr, sizeof(vfr)); > } > @@ -339,7 +339,7 @@ vm_priv_brconfig(struct privsep *ps, str > } > > /* Set the new interface status to up or down */ > - proc_compose(ps, PROC_PRIV, (vsw->sw_flags & IFF_UP) ? > + proc_compose(ps, PROC_PRIV, (vsw->sw_flags & VMIFF_UP) ? > IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, > &vfr, sizeof(vfr)); > > Index: usr.sbin/vmd/virtio.c > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v > retrieving revision 1.32 > diff -u -p -u -p -r1.32 virtio.c > --- usr.sbin/vmd/virtio.c 13 Feb 2017 18:49:01 -0000 1.32 > +++ usr.sbin/vmd/virtio.c 1 Mar 2017 18:16:20 -0000 > @@ -897,6 +897,7 @@ vionet_rx(struct vionet_dev *dev) > { > char buf[PAGE_SIZE]; > int hasdata, num_enq = 0, spc = 0; > + struct ether_header *eh; > ssize_t sz; > > do { > @@ -909,9 +910,14 @@ vionet_rx(struct vionet_dev *dev) > if (errno != EAGAIN) > log_warn("unexpected read error on vionet " > "device"); > - } else if (sz != 0) > - num_enq += vionet_enq_rx(dev, buf, sz, &spc); > - else if (sz == 0) { > + } else if (sz != 0) { > + eh = (struct ether_header *)buf; > + if (!dev->lockedmac || sz < ETHER_HDR_LEN || > + ETHER_IS_MULTICAST(eh->ether_dhost) || > + memcmp(eh->ether_dhost, dev->mac, > + sizeof(eh->ether_dhost)) == 0) > + num_enq += vionet_enq_rx(dev, buf, sz, &spc); > + } else if (sz == 0) { > log_debug("process_rx: no data"); > hasdata = 0; > break; > @@ -1044,6 +1050,7 @@ vionet_notifyq(struct vionet_dev *dev) > struct vring_desc *desc, *pkt_desc, *hdr_desc; > struct vring_avail *avail; > struct vring_used *used; > + struct ether_header *eh; > > vr = pkt = NULL; > ret = 0; > @@ -1152,8 +1159,16 @@ vionet_notifyq(struct vionet_dev *dev) > goto out; > } > > + /* reject other source addresses */ > + if (dev->lockedmac && pktsz >= ETHER_HDR_LEN && > + (eh = (struct ether_header *)pkt) && > + memcmp(eh->ether_shost, dev->mac, > + sizeof(eh->ether_shost)) != 0) > + log_debug("vionet: wrong source address %s for vm %d", > + ether_ntoa((struct ether_addr *) > + eh->ether_shost), dev->vm_id); > /* XXX signed vs unsigned here, funky cast */ > - if (write(dev->fd, pkt, pktsz) != (int)pktsz) { > + else if (write(dev->fd, pkt, pktsz) != (int)pktsz) { > log_warnx("vionet: tx failed writing to tap: " > "%d", errno); > goto out; > @@ -1298,8 +1313,9 @@ vmmci_io(int dir, uint16_t reg, uint32_t > } > > void > -virtio_init(struct vm_create_params *vcp, int *child_disks, int *child_taps) > +virtio_init(struct vmop_create_params *vmc, int *child_disks, int > *child_taps) > { > + struct vm_create_params *vcp = &vmc->vmc_params; > static const uint8_t zero_mac[6]; > uint8_t id; > uint8_t i; > @@ -1458,10 +1474,13 @@ virtio_init(struct vm_create_params *vcp > vionet[i].mac[4] = rng; > vionet[i].mac[5] = rng >> 8; > } > + vionet[i].lockedmac = > + vmc->vmc_ifflags[i] & VMIFF_LOCKED ? 1 : 0; > > - log_debug("%s: vm \"%s\" vio%u lladdr %s", > + log_debug("%s: vm \"%s\" vio%u lladdr %s%s", > __func__, vcp->vcp_name, i, > - ether_ntoa((void *)vionet[i].mac)); > + ether_ntoa((void *)vionet[i].mac), > + vionet[i].lockedmac ? " (locked)" : ""); > } > } > > Index: usr.sbin/vmd/virtio.h > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/virtio.h,v > retrieving revision 1.9 > diff -u -p -u -p -r1.9 virtio.h > --- usr.sbin/vmd/virtio.h 21 Jan 2017 12:45:41 -0000 1.9 > +++ usr.sbin/vmd/virtio.h 1 Mar 2017 18:16:21 -0000 > @@ -118,6 +118,7 @@ struct vionet_dev { > uint32_t vm_id; > int irq; > uint8_t mac[6]; > + int lockedmac; > }; > > struct virtio_net_hdr { > @@ -150,7 +151,7 @@ struct vmmci_dev { > int irq; > }; > > -void virtio_init(struct vm_create_params *, int *, int *); > +void virtio_init(struct vmop_create_params *, int *, int *); > uint32_t vring_size(uint32_t); > > int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *); > Index: usr.sbin/vmd/vm.c > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/vm.c,v > retrieving revision 1.1 > diff -u -p -u -p -r1.1 vm.c > --- usr.sbin/vmd/vm.c 1 Mar 2017 18:00:50 -0000 1.1 > +++ usr.sbin/vmd/vm.c 1 Mar 2017 18:16:21 -0000 > @@ -63,7 +63,7 @@ > > io_fn_t ioports_map[MAX_PORTS]; > > -int run_vm(int *, int *, struct vm_create_params *, struct vcpu_reg_state *); > +int run_vm(int *, int *, struct vmop_create_params *, struct vcpu_reg_state > *); > void vm_dispatch_vmm(int, short, void *); > void *event_thread(void *); > void *vcpu_run_loop(void *); > @@ -72,7 +72,7 @@ int vcpu_reset(uint32_t, uint32_t, struc > void create_memory_map(struct vm_create_params *); > int alloc_guest_mem(struct vm_create_params *); > int vmm_create_vm(struct vm_create_params *); > -void init_emulated_hw(struct vm_create_params *, int *, int *); > +void init_emulated_hw(struct vmop_create_params *, int *, int *); > void vcpu_exit_inout(struct vm_run_params *); > uint8_t vcpu_exit_pci(struct vm_run_params *); > int vcpu_pic_intr(uint32_t, uint32_t, uint8_t); > @@ -236,7 +236,7 @@ start_vm(struct vmd_vm *vm, int fd) > fatal("setup vm pipe"); > > /* Execute the vcpu run loop(s) for this VM */ > - ret = run_vm(vm->vm_disks, nicfds, vcp, &vrs); > + ret = run_vm(vm->vm_disks, nicfds, &vm->vm_params, &vrs); > > return (ret); > } > @@ -493,9 +493,10 @@ vmm_create_vm(struct vm_create_params *v > * Initializes the userspace hardware emulation > */ > void > -init_emulated_hw(struct vm_create_params *vcp, int *child_disks, > +init_emulated_hw(struct vmop_create_params *vmc, int *child_disks, > int *child_taps) > { > + struct vm_create_params *vcp = &vmc->vmc_params; > int i; > > /* Reset the IO port map */ > @@ -534,7 +535,7 @@ init_emulated_hw(struct vm_create_params > pci_init(); > > /* Initialize virtio devices */ > - virtio_init(vcp, child_disks, child_taps); > + virtio_init(vmc, child_disks, child_taps); > } > > /* > @@ -545,7 +546,7 @@ init_emulated_hw(struct vm_create_params > * Parameters: > * child_disks: previously-opened child VM disk file file descriptors > * child_taps: previously-opened child tap file descriptors > - * vcp: vm_create_params struct containing the VM's desired creation > + * vmc: vmop_create_params struct containing the VM's desired creation > * configuration > * vrs: VCPU register state to initialize > * > @@ -554,9 +555,10 @@ init_emulated_hw(struct vm_create_params > * !0 : the VM exited abnormally or failed to start > */ > int > -run_vm(int *child_disks, int *child_taps, struct vm_create_params *vcp, > +run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc, > struct vcpu_reg_state *vrs) > { > + struct vm_create_params *vcp = &vmc->vmc_params; > uint8_t evdone = 0; > size_t i; > int ret; > @@ -597,7 +599,7 @@ run_vm(int *child_disks, int *child_taps > log_debug("%s: initializing hardware for vm %s", __func__, > vcp->vcp_name); > > - init_emulated_hw(vcp, child_disks, child_taps); > + init_emulated_hw(vmc, child_disks, child_taps); > > ret = pthread_mutex_init(&threadmutex, NULL); > if (ret) { > Index: usr.sbin/vmd/vm.conf.5 > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v > retrieving revision 1.15 > diff -u -p -u -p -r1.15 vm.conf.5 > --- usr.sbin/vmd/vm.conf.5 1 Mar 2017 12:29:06 -0000 1.15 > +++ usr.sbin/vmd/vm.conf.5 1 Mar 2017 18:16:21 -0000 > @@ -130,11 +130,16 @@ rules for several VM interfaces in the s > The > .Ar group-name > must not end with a digit. > -.It Cm lladdr Ar etheraddr > +.It Oo Cm locked Oc Cm lladdr Op Ar etheraddr > Change the link layer address (MAC address) of the interface on the > VM guest side. > If not specified, a randomized address will be assigned by > .Xr vmd 8 . > +If the > +.Cm locked > +keyword is specified, > +.Xr vmd 8 > +will drop packets from the VM with altered source addresses. > .It Cm switch Ar name > Set the virtual switch > by > @@ -221,6 +226,11 @@ This is the default if neither > nor > .Cm disable > is specified. > +.It Cm locked lladdr > +If this option is specified, > +.Xr vmd 8 > +will drop packets with altered sources addresses that do not match the > +link layer addresses (MAC addresses) of the VM interfaces in this switch. > .It Cm disable > Do not configure this switch. > .It Cm group Ar group-name > Index: usr.sbin/vmd/vmd.c > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/vmd.c,v > retrieving revision 1.52 > diff -u -p -u -p -r1.52 vmd.c > --- usr.sbin/vmd/vmd.c 1 Mar 2017 07:43:33 -0000 1.52 > +++ usr.sbin/vmd/vmd.c 1 Mar 2017 18:16:21 -0000 > @@ -753,6 +753,7 @@ vm_register(struct privsep *ps, struct v > struct vmd_vm *vm = NULL; > struct vm_create_params *vcp = &vmc->vmc_params; > unsigned int i; > + struct vmd_switch *sw; > > errno = 0; > *ret_vm = NULL; > @@ -801,13 +802,20 @@ vm_register(struct privsep *ps, struct v > goto fail; > > memcpy(&vm->vm_params, vmc, sizeof(vm->vm_params)); > + vmc = &vm->vm_params; > vm->vm_pid = -1; > vm->vm_tty = -1; > > for (i = 0; i < vcp->vcp_ndisks; i++) > vm->vm_disks[i] = -1; > - for (i = 0; i < vcp->vcp_nnics; i++) > + for (i = 0; i < vcp->vcp_nnics; i++) { > vm->vm_ifs[i].vif_fd = -1; > + > + if ((sw = switch_getbyname(vmc->vmc_ifswitch[i])) != NULL) { > + /* inherit per-interface flags from the switch */ > + vmc->vmc_ifflags[i] |= (sw->sw_flags & VMIFF_OPTMASK); > + } > + } > vm->vm_kernel = -1; > vm->vm_iev.ibuf.fd = -1; > > Index: usr.sbin/vmd/vmd.h > =================================================================== > RCS file: /cvs/src/usr.sbin/vmd/vmd.h,v > retrieving revision 1.46 > diff -u -p -u -p -r1.46 vmd.h > --- usr.sbin/vmd/vmd.h 1 Mar 2017 18:00:50 -0000 1.46 > +++ usr.sbin/vmd/vmd.h 1 Mar 2017 18:16:21 -0000 > @@ -113,6 +113,9 @@ struct vmop_create_params { > > /* userland-only part of the create params */ > unsigned int vmc_ifflags[VMM_MAX_NICS_PER_VM]; > +#define VMIFF_UP 0x01 > +#define VMIFF_LOCKED 0x02 > +#define VMIFF_OPTMASK VMIFF_LOCKED > char vmc_ifnames[VMM_MAX_NICS_PER_VM][IF_NAMESIZE]; > char vmc_ifswitch[VMM_MAX_NICS_PER_VM][VM_NAME_MAX]; > char vmc_ifgroup[VMM_MAX_NICS_PER_VM][IF_NAMESIZE]; >