Dave Voutila writes:

> Dave Voutila writes:
>
>> tech@,
>>
>> While migrating an existing -current vm to use dhcpleased(8), one of the
>> issues discovered was the dhcp/bootp handling built into vmd(8) for
>> local interfaces was improperly missing packets sent to the ethernet
>> address of the host-side tap(4) device. (vmd(8) was only looking for
>> broadcast packets.)
>>
>> The following diff:
>> - includes the host-side tap(4) lladdr in the packet filtering logic for
>>   intercepting vio(4) dhcp/bootp communication
>> - removes a conditional check (dhcpsz == 0) pointed out by deraadt@ that
>>   could contribute to missed packets while iterating through the vionet
>>   tx ring
>
> The conditional check (dhcpsz == 0) was committed. The diff below is
> updated to apply to the latest copy of the src tree.
>
>>
>> Because of vmd(8)'s priv-sep design, the approach taken is to pass a
>> duplicate of the opened tap(4) fd to the "priv" process that is
>> unpledged and responsible for setting up networking. A response imsg
>> travels between processes, being forwarded until it gets to the intended
>> vm process.
>>
>> For those looking to test the diff, some steps to follow are:
>>
>> 0. Don't apply the patch yet.
>> 1. Use an OpenBSD guest running a recent snapshot that has the latest
>> dhcpleased(8)[1] and dhcpleasectl(8)[2] changes committed by florian@.
>> 2. Configure the guest for using dhcpleased(8), ideally via
>> /etc/hostname.vio0 containing:
>>
>>   inet autoconf
>>   up
>>
>> 3. Ensure you get an IP assigned from vmd(8) after boot.
>> 4. Check the lease with `dhcpleasectl show interface vio0`. It should
>>    report [Bound] and have a _very_ long lease time.
>> 5. Force a renewal with `dhcpleasectl send request vio0`.
>> 6. Check again like in step 4. It will be "stuck" in a [Renewing] state.
>> 7. Shut down the guest, vmd(8), and apply the patch.
>> 8. Repeat steps 4-6, but the guest should no longer be "stuck" in
>>    [Renewing] and should report [Bound] after forced renewal.
>>
>> You can also turn up debug logging for vmd(8) and should see
>> corresponding messages of:
>>
>>   vionet: dhcp request, local response size 342
>>
>> If you do not, the packet was not intercepted by vmd(8).
>>
>> I have tested this diff with a variety of conditions, including multiple
>> emulated nics per guest. Specifically 1 nic using dhcp/bootp resolved by
>> vmd(8) and the other nic connected to a virtual switch using veb(4) and
>> an instance of dhcpd(8) running on the host. Also ran a snapshot upgrade
>> with no issues.
>
> I haven't received any issue reports as of yet.
>

No issue reports. mlarkin@ asked for a change the function signature of
vionet_set_hostmac (change array to pointer).

OK?

>>
>> [1] https://marc.info/?l=openbsd-cvs&m=161643049815177&w=2
>> [2] https://marc.info/?l=openbsd-cvs&m=161652157122736&w=2
>>


Index: usr.sbin/vmd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/config.c,v
retrieving revision 1.60
diff -u -p -r1.60 config.c
--- usr.sbin/vmd/config.c       19 Mar 2021 09:29:33 -0000      1.60
+++ usr.sbin/vmd/config.c       29 Mar 2021 21:19:09 -0000
@@ -227,6 +227,7 @@ config_setvm(struct privsep *ps, struct
        char                     base[PATH_MAX];
        unsigned int             unit;
        struct timeval           tv, rate, since_last;
+       struct vmop_addr_req     var;

        errno = 0;

@@ -499,6 +500,12 @@ config_setvm(struct privsep *ps, struct
                proc_compose_imsg(ps, PROC_VMM, -1,
                    IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i],
                    &i, sizeof(i));
+
+               memset(&var, 0, sizeof(var));
+               var.var_vmid = vm->vm_vmid;
+               var.var_nic_idx = i;
+               proc_compose_imsg(ps, PROC_PRIV, -1, IMSG_VMDOP_PRIV_GET_ADDR,
+                   vm->vm_vmid, dup(tapfds[i]), &var, sizeof(var));
        }

        if (!(vm->vm_state & VM_STATE_RECEIVED))
Index: usr.sbin/vmd/dhcp.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/dhcp.c,v
retrieving revision 1.8
diff -u -p -r1.8 dhcp.c
--- usr.sbin/vmd/dhcp.c 27 Dec 2018 19:51:30 -0000      1.8
+++ usr.sbin/vmd/dhcp.c 29 Mar 2021 21:19:09 -0000
@@ -57,8 +57,11 @@ dhcp_request(struct vionet_dev *dev, cha
        if ((offset = decode_hw_header(buf, buflen, 0, &pc, HTYPE_ETHER)) < 0)
                return (-1);

-       if (memcmp(pc.pc_smac, dev->mac, ETHER_ADDR_LEN) != 0 ||
-           memcmp(pc.pc_dmac, broadcast, ETHER_ADDR_LEN) != 0)
+       if (memcmp(pc.pc_dmac, broadcast, ETHER_ADDR_LEN) != 0 &&
+           memcmp(pc.pc_dmac, dev->hostmac, ETHER_ADDR_LEN) != 0)
+               return (-1);
+
+       if (memcmp(pc.pc_smac, dev->mac, ETHER_ADDR_LEN) != 0)
                return (-1);

        if ((offset = decode_udp_ip_header(buf, buflen, offset, &pc)) < 0)
Index: usr.sbin/vmd/priv.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/priv.c,v
retrieving revision 1.16
diff -u -p -r1.16 priv.c
--- usr.sbin/vmd/priv.c 28 Feb 2021 22:56:09 -0000      1.16
+++ usr.sbin/vmd/priv.c 29 Mar 2021 21:19:09 -0000
@@ -92,6 +92,8 @@ priv_dispatch_parent(int fd, struct priv
        struct ifaliasreq        ifra;
        struct in6_aliasreq      in6_ifra;
        struct if_afreq          ifar;
+       struct vmop_addr_req     vareq;
+       struct vmop_addr_result  varesult;
        char                     type[IF_NAMESIZE];

        switch (imsg->hdr.type) {
@@ -115,6 +117,7 @@ priv_dispatch_parent(int fd, struct priv
                break;
        case IMSG_VMDOP_CONFIG:
        case IMSG_CTL_RESET:
+       case IMSG_VMDOP_PRIV_GET_ADDR:
                break;
        default:
                return (-1);
@@ -244,6 +247,22 @@ priv_dispatch_parent(int fd, struct priv

                if (ioctl(env->vmd_fd6, SIOCAIFADDR_IN6, &in6_ifra) == -1)
                        log_warn("SIOCAIFADDR_IN6");
+               break;
+       case IMSG_VMDOP_PRIV_GET_ADDR:
+               IMSG_SIZE_CHECK(imsg, &vareq);
+               memcpy(&vareq, imsg->data, sizeof(vareq));
+
+               varesult.var_vmid = vareq.var_vmid;
+               varesult.var_nic_idx = vareq.var_nic_idx;
+
+               /* resolve lladdr for the tap(4) and send back to parent */
+               if (ioctl(imsg->fd, SIOCGIFADDR, &varesult.var_addr) != 0)
+                       log_warn("SIOCGIFADDR");
+               else
+                       proc_compose_imsg(ps, PROC_PARENT, -1,
+                           IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE, imsg->hdr.peerid,
+                           -1, &varesult, sizeof(varesult));
+               close(imsg->fd);
                break;
        case IMSG_VMDOP_CONFIG:
                config_getconfig(env, imsg);
Index: usr.sbin/vmd/virtio.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v
retrieving revision 1.83
diff -u -p -r1.83 virtio.c
--- usr.sbin/vmd/virtio.c       26 Mar 2021 17:40:03 -0000      1.83
+++ usr.sbin/vmd/virtio.c       29 Mar 2021 21:19:09 -0000
@@ -2034,6 +2034,31 @@ virtio_init(struct vmd_vm *vm, int child
        evtimer_set(&vmmci.timeout, vmmci_timeout, NULL);
 }

+/*
+ * vionet_set_hostmac
+ *
+ * Sets the hardware address for the host-side tap(4) on a vionet_dev.
+ *
+ * This should only be called from the event-loop thread
+ *
+ * vm: pointer to the current vmd_vm instance
+ * idx: index into the array of vionet_dev's for the target vionet_dev
+ * addr: ethernet address to set
+ */
+void
+vionet_set_hostmac(struct vmd_vm *vm, unsigned int idx, uint8_t *addr)
+{
+       struct vmop_create_params *vmc = &vm->vm_params;
+       struct vm_create_params   *vcp = &vmc->vmc_params;
+       struct vionet_dev         *dev;
+
+       if (idx > vcp->vcp_nnics)
+               fatalx("vionet_set_hostmac");
+
+       dev = &vionet[idx];
+       memcpy(dev->hostmac, addr, sizeof(dev->hostmac));
+}
+
 void
 virtio_shutdown(struct vmd_vm *vm)
 {
Index: usr.sbin/vmd/virtio.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.h,v
retrieving revision 1.36
diff -u -p -r1.36 virtio.h
--- usr.sbin/vmd/virtio.h       7 Jan 2021 17:11:38 -0000       1.36
+++ usr.sbin/vmd/virtio.h       29 Mar 2021 21:19:09 -0000
@@ -208,6 +208,7 @@ struct vionet_dev {
        uint32_t vm_vmid;
        int irq;
        uint8_t mac[6];
+       uint8_t hostmac[6];

        int idx;
        int lockedmac;
@@ -298,6 +299,7 @@ void vionet_notify_rx(struct vionet_dev
 int vionet_notify_tx(struct vionet_dev *);
 void vionet_process_rx(uint32_t);
 int vionet_enq_rx(struct vionet_dev *, char *, ssize_t, int *);
+void vionet_set_hostmac(struct vmd_vm *, unsigned int, uint8_t *);

 int vmmci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
 int vmmci_dump(int);
Index: usr.sbin/vmd/vm.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vm.c,v
retrieving revision 1.60
diff -u -p -r1.60 vm.c
--- usr.sbin/vmd/vm.c   19 Mar 2021 09:29:33 -0000      1.60
+++ usr.sbin/vmd/vm.c   29 Mar 2021 21:19:09 -0000
@@ -394,6 +394,7 @@ vm_dispatch_vmm(int fd, short event, voi
 {
        struct vmd_vm           *vm = arg;
        struct vmop_result       vmr;
+       struct vmop_addr_result  var;
        struct imsgev           *iev = &vm->vm_iev;
        struct imsgbuf          *ibuf = &iev->ibuf;
        struct imsg              imsg;
@@ -470,6 +471,16 @@ vm_dispatch_vmm(int fd, short event, voi
                                imsg_flush(&current_vm->vm_iev.ibuf);
                                _exit(0);
                        }
+                       break;
+               case IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE:
+                       IMSG_SIZE_CHECK(&imsg, &var);
+                       memcpy(&var, imsg.data, sizeof(var));
+
+                       log_debug("%s: received tap addr %s for nic %d",
+                           vm->vm_params.vmc_params.vcp_name,
+                           ether_ntoa((void *)var.var_addr), var.var_nic_idx);
+
+                       vionet_set_hostmac(vm, var.var_nic_idx, var.var_addr);
                        break;
                default:
                        fatalx("%s: got invalid imsg %d from %s",
Index: usr.sbin/vmd/vmd.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.c,v
retrieving revision 1.120
diff -u -p -r1.120 vmd.c
--- usr.sbin/vmd/vmd.c  27 Jan 2021 07:21:54 -0000      1.120
+++ usr.sbin/vmd/vmd.c  29 Mar 2021 21:19:09 -0000
@@ -58,6 +58,7 @@ void   vmd_shutdown(void);
 int     vmd_control_run(void);
 int     vmd_dispatch_control(int, struct privsep_proc *, struct imsg *);
 int     vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *);
+int     vmd_dispatch_priv(int, struct privsep_proc *, struct imsg *);
 int     vmd_check_vmh(struct vm_dump_header *);

 int     vm_instance(struct privsep *, struct vmd_vm **,
@@ -70,7 +71,7 @@ struct vmd    *env;

 static struct privsep_proc procs[] = {
        /* Keep "priv" on top as procs[0] */
-       { "priv",       PROC_PRIV,      NULL, priv },
+       { "priv",       PROC_PRIV,      vmd_dispatch_priv, priv },
        { "control",    PROC_CONTROL,   vmd_dispatch_control, control },
        { "vmm",        PROC_VMM,       vmd_dispatch_vmm, vmm, vmm_shutdown },
 };
@@ -542,6 +543,24 @@ vmd_dispatch_vmm(int fd, struct privsep_
                }
                IMSG_SIZE_CHECK(imsg, &res);
                proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
+               break;
+       default:
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+vmd_dispatch_priv(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+       struct vmop_addr_result  var;
+
+       switch (imsg->hdr.type) {
+       case IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE:
+               IMSG_SIZE_CHECK(imsg, &var);
+               memcpy(&var, imsg->data, sizeof(var));
+               proc_forward_imsg(p->p_ps, imsg, PROC_VMM, -1);
                break;
        default:
                return (-1);
Index: usr.sbin/vmd/vmd.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.h,v
retrieving revision 1.102
diff -u -p -r1.102 vmd.h
--- usr.sbin/vmd/vmd.h  19 Mar 2021 09:29:33 -0000      1.102
+++ usr.sbin/vmd/vmd.h  29 Mar 2021 21:19:09 -0000
@@ -120,6 +120,8 @@ enum imsg_type {
        IMSG_VMDOP_PRIV_IFADDR,
        IMSG_VMDOP_PRIV_IFADDR6,
        IMSG_VMDOP_PRIV_IFRDOMAIN,
+       IMSG_VMDOP_PRIV_GET_ADDR,
+       IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE,
        IMSG_VMDOP_VM_SHUTDOWN,
        IMSG_VMDOP_VM_REBOOT,
        IMSG_VMDOP_CONFIG,
@@ -156,6 +158,17 @@ struct vmop_ifreq {
        char                             vfr_value[VM_NAME_MAX];
        struct sockaddr_storage          vfr_addr;
        struct sockaddr_storage          vfr_mask;
+};
+
+struct vmop_addr_req {
+       uint32_t                 var_vmid;
+       unsigned int             var_nic_idx;
+};
+
+struct vmop_addr_result {
+       uint32_t                 var_vmid;
+       unsigned int             var_nic_idx;
+       uint8_t                  var_addr[ETHER_ADDR_LEN];
 };

 struct vmop_owner {
Index: usr.sbin/vmd/vmm.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmm.c,v
retrieving revision 1.97
diff -u -p -r1.97 vmm.c
--- usr.sbin/vmd/vmm.c  2 Mar 2021 02:56:22 -0000       1.97
+++ usr.sbin/vmd/vmm.c  29 Mar 2021 21:19:09 -0000
@@ -109,6 +109,7 @@ vmm_dispatch_parent(int fd, struct privs
        struct vmop_id           vid;
        struct vmop_result       vmr;
        struct vmop_create_params vmc;
+       struct vmop_addr_result  var;
        uint32_t                 id = 0, peerid = imsg->hdr.peerid;
        pid_t                    pid = 0;
        unsigned int             mode, flags;
@@ -331,6 +332,18 @@ vmm_dispatch_parent(int fd, struct privs
                if ((id = vm_id2vmid(id, NULL)) == 0)
                        res = ENOENT;
                cmd = IMSG_VMDOP_START_VM_RESPONSE;
+               break;
+       case IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE:
+               IMSG_SIZE_CHECK(imsg, &var);
+               memcpy(&var, imsg->data, sizeof(var));
+               if ((vm = vm_getbyvmid(var.var_vmid)) == NULL) {
+                       res = ENOENT;
+                       break;
+               }
+               /* Forward hardware address details to the guest vm */
+               imsg_compose_event(&vm->vm_iev,
+                   imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
+                   imsg->fd, &var, sizeof(var));
                break;
        default:
                return (-1);

Reply via email to