net: add a vnet_hdr=on|off parameter

This allows people to disable the IFF_VNET_HDR flag, e.g. for debugging
purposes or if they know they may migrate the guest to a machine without
IFF_VNET_HDR support.

It also allows making the lack of IFF_VNET_HDR support an error
condition, e.g. in the case where a guest is being migrated from a host
which does support it.

Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/net.c b/net.c
index 8d35b91..3d94260 100644
--- a/net.c
+++ b/net.c
@@ -1494,7 +1494,8 @@
 }
 
 #if defined (CONFIG_BSD) || defined (__FreeBSD_kernel__)
-static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
+static int tap_open(char *ifname, int ifname_size,
+                    int *vnet_hdr, int vnet_hdr_required)
 {
     int fd;
     char *dev;
@@ -1636,7 +1637,8 @@
     return tap_fd;
 }
 
-static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
+static int tap_open(char *ifname, int ifname_size,
+                    int *vnet_hdr, int vnet_hdr_required)
 {
     char  dev[10]="";
     int fd;
@@ -1649,13 +1651,15 @@
     return fd;
 }
 #elif defined (_AIX)
-static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
+static int tap_open(char *ifname, int ifname_size,
+                    int *vnet_hdr, int vnet_hdr_required)
 {
     fprintf (stderr, "no tap on AIX\n");
     return -1;
 }
 #else
-static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
+static int tap_open(char *ifname, int ifname_size,
+                    int *vnet_hdr, int vnet_hdr_required)
 {
     struct ifreq ifr;
     int fd, ret;
@@ -1668,7 +1672,7 @@
     memset(&ifr, 0, sizeof(ifr));
     ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 
-    {
+    if (*vnet_hdr) {
         unsigned int features;
 
         if (ioctl(fd, TUNGETFEATURES, &features) == 0 &&
@@ -1676,6 +1680,13 @@
             *vnet_hdr = 1;
             ifr.ifr_flags |= IFF_VNET_HDR;
         }
+
+        if (vnet_hdr_required && !*vnet_hdr) {
+            qemu_error("vnet_hdr=1 requested, but no kernel "
+                       "support for IFF_VNET_HDR available");
+            close(fd);
+            return -1;
+        }
     }
 
     if (ifname[0] != '\0')
@@ -1740,7 +1751,7 @@
 
 static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
 {
-    int fd;
+    int fd, vnet_hdr_required;
     char ifname[128] = {0,};
     const char *setup_script;
 
@@ -1748,8 +1759,14 @@
         pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
     }
 
-    *vnet_hdr = 0;
-    TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr));
+    *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
+    if (qemu_opt_get(opts, "vnet_hdr")) {
+        vnet_hdr_required = *vnet_hdr;
+    } else {
+        vnet_hdr_required = 0;
+    }
+
+    TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required));
     if (fd < 0) {
         return -1;
     }
@@ -2698,8 +2715,9 @@
     if (qemu_opt_get(opts, "fd")) {
         if (qemu_opt_get(opts, "ifname") ||
             qemu_opt_get(opts, "script") ||
-            qemu_opt_get(opts, "downscript")) {
-            qemu_error("ifname=, script= and downscript= is invalid with fd=\n");
+            qemu_opt_get(opts, "downscript") ||
+            qemu_opt_get(opts, "vnet_hdr")) {
+            qemu_error("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=\n");
             return -1;
         }
 
@@ -3055,6 +3073,10 @@
                 .name = "sndbuf",
                 .type = QEMU_OPT_SIZE,
                 .help = "send buffer limit"
+            }, {
+                .name = "vnet_hdr",
+                .type = QEMU_OPT_BOOL,
+                .help = "enable the IFF_VNET_HDR flag on the tap interface"
             },
             { /* end of list */ }
         },