The following diff adds in virtio 1.1's VIRTIO_NET_F_MTU feature support
to vmd(8)'s virtio networking device. This allows for communicating an MTU
to the guest driver and then enforcing it in the emulated device.

When the feature is offered, per Virtio v1.1, 5.1.4.1 [1]:

"The device MUST NOT pass received packets that exceed mtu (plus low
level ethernet header length) size with gso_type NONE or ECN after
VIRTIO_NET_F_MTU has been successfully negotiated."

(GSO is not supported or negotiated, so it's always NONE. This is
primarly because the vmd vionet device also doesn't support or negotiate
checksum offloading.)

The prior logic in place simply checked the packet was of a allowable
size, which meant the largest IP packet (65535) plus an ethernet header.

If testing the diff, you can change the VIONET_MTU definition to
something other than 1500 and check that a non-OpenBSD guest defaults to
using the value and forbids setting it higher. This is easy in an Alpine
or Debian Linux guest using:

a) to view the mtu: ip link
b) to set the mtu: sudo ip link set dev <devname> mtu <mtu value>

For example:

  dave@debian:~$ sudo ip link set dev enp0s2 mtu 1501
  Error: mtu greater than device maximum.

Since the diff lacks context of the goto, it jumps to section that
advances to the next ring

Currently, vio(4) does not negotiate this feature and won't obey it. I'm
working on that separately.

OK? Feedback?

[1] 
https://docs.oasis-open.org/virtio/virtio/v1.1/cs01/virtio-v1.1-cs01.html#x1-2000004

Index: virtio.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v
retrieving revision 1.87
diff -u -p -r1.87 virtio.c
--- virtio.c    18 May 2021 11:06:43 -0000      1.87
+++ virtio.c    24 May 2021 01:31:22 -0000
@@ -60,6 +60,7 @@ int nr_vioblk;

 #define MAXPHYS        (64 * 1024)     /* max raw I/O transfer size */

+#define VIRTIO_NET_F_MTU       (1<<3)
 #define VIRTIO_NET_F_MAC       (1<<5)

 #define VMMCI_F_TIMESYNC       (1<<0)
@@ -1046,6 +1047,26 @@ virtio_net_io(int dir, uint16_t reg, uin
                        *data = dev->mac[reg -
                            VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI];
                        break;
+               case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 10:
+                       if (sz == 2) {
+                               *data = VIONET_MTU;
+                       } else if (sz == 1) {
+                               *data &= 0xFF00;
+                               *data |= (uint32_t)(VIONET_MTU) & 0xFF;
+                       } else {
+                               log_warnx("%s: illegal read of vionet_mtu",
+                                       __progname);
+                       }
+                       break;
+               case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 11:
+                       if (sz == 1) {
+                               *data &= 0xFF00;
+                               *data = (uint32_t)(VIONET_MTU >> 8) & 0xFF;
+                       } else {
+                               log_warnx("%s: illegal read of vionet_mtu",
+                                       __progname);
+                       }
+                       break;
                case VIRTIO_CONFIG_DEVICE_FEATURES:
                        *data = dev->cfg.device_feature;
                        break;
@@ -1437,7 +1458,7 @@ vionet_notify_tx(struct vionet_dev *dev)
        size_t pktsz, chunk_size = 0;
        ssize_t dhcpsz;
        int ret, num_enq, ofs, spc;
-       char *vr, *pkt, *dhcppkt;
+       char *vr, *pkt = NULL, *dhcppkt;
        struct vring_desc *desc, *pkt_desc, *hdr_desc;
        struct vring_avail *avail;
        struct vring_used *used;
@@ -1505,12 +1526,13 @@ vionet_notify_tx(struct vionet_dev *dev)
                /* Remove virtio header descriptor len */
                pktsz -= hdr_desc->len;

-               /* Only allow buffer len < max IP packet + Ethernet header */
-               if (pktsz > IP_MAXPACKET + ETHER_HDR_LEN) {
+               /* Drop frames larger than our MTU + ethernet header */
+               if (pktsz > VIONET_MTU + ETHER_HDR_LEN) {
                        log_warnx("%s: invalid packet size %lu", __func__,
                            pktsz);
-                       goto out;
+                       goto drop_packet;
                }
+
                pkt = malloc(pktsz);
                if (pkt == NULL) {
                        log_warn("malloc error alloc packet buf");
@@ -1590,6 +1612,7 @@ vionet_notify_tx(struct vionet_dev *dev)
                        goto out;
                }

+       drop_packet:
                ret = 1;
                dev->cfg.isr_status = 1;
                used->ring[used->idx & VIONET_QUEUE_MASK].id = hdr_desc_idx;
@@ -1965,6 +1988,9 @@ virtio_init(struct vmd_vm *vm, int child
                                vionet[i].pxeboot = 1;
                        vionet[i].idx = i;
                        vionet[i].pci_id = id;
+
+                       /* Allow drivers to identify the MTU */
+                       vionet[i].cfg.device_feature |= VIRTIO_NET_F_MTU;

                        log_debug("%s: vm \"%s\" vio%u lladdr %s%s%s%s",
                            __func__, vcp->vcp_name, i,
Index: virtio.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.h,v
retrieving revision 1.38
diff -u -p -r1.38 virtio.h
--- virtio.h    21 Apr 2021 18:27:36 -0000      1.38
+++ virtio.h    24 May 2021 01:31:22 -0000
@@ -35,6 +35,7 @@

 #define VIONET_QUEUE_SIZE      256
 #define VIONET_QUEUE_MASK      (VIONET_QUEUE_SIZE - 1)
+#define VIONET_MTU             1500

 /* VMM Control Interface shutdown timeout (in seconds) */
 #define VMMCI_TIMEOUT          3

Reply via email to