]> xenbits.xensource.com Git - libvirt.git/commitdiff
conf: new "managed" attribute for target dev of <interface type='ethernet'>
authorLaine Stump <laine@redhat.com>
Wed, 21 Aug 2019 20:42:41 +0000 (16:42 -0400)
committerLaine Stump <laine@redhat.com>
Mon, 9 Sep 2019 18:35:54 +0000 (14:35 -0400)
Although <interface type='ethernet'> has always been able to use an
existing tap device, this is just a coincidence due to the fact that
the same ioctl is used to create a new tap device or get a handle to
an existing device.

Even then, once we have the handle to the device, we still insist on
doing extra setup to it (setting the MAC address and IFF_UP).  That
*might* be okay if libvirtd is running as a privileged process, but if
libvirtd is running as an unprivileged user, those attempted
modifications to the tap device will fail (yes, even if the tap is set
to be owned by the user running libvirtd). We could avoid this if we
knew that the device already existed, but as stated above, an existing
device and new device are both accessed in the same manner, and
anyway, we need to preserve existing behavior for those who are
already using pre-existing devices with privileged libvirtd (and
allowing/expecting libvirt to configure the pre-existing device).

In order to cleanly support the idea of using a pre-existing and
pre-configured tap device, this patch introduces a new optional
attribute "managed" for the interface <target> element. This
attribute is only valid for <interface type='ethernet'> (since all
other interface types have mandatory config that doesn't apply in the
case where we expect the tap device to be setup before we
get it). The syntax would look something like this:

   <interface type='ethernet'>
      <target dev='mytap0' managed='no'/>
      ...
   </interface>

This patch just adds managed to the grammar and parser for <target>,
but has no functionality behind it.

(NB: when managed='no' (the default when not specified is 'yes'), the
target dev is always a name explicitly provided, so we don't
auto-remove it from the config just because it starts with "vnet"
(VIR_NET_GENERATED_TAP_PREFIX); this makes it possible to use the
same pattern of names that libvirt itself uses when it automatically
creates the tap devices.)

Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
docs/formatdomain.html.in
docs/schemas/domaincommon.rng
src/conf/domain_conf.c
src/conf/domain_conf.h
tests/qemuxml2argvdata/net-eth-unmanaged-tap.xml [new file with mode: 0644]
tests/qemuxml2xmloutdata/net-eth-unmanaged-tap.xml [new file with mode: 0644]
tests/qemuxml2xmltest.c

index fcb7c59c00d8aa815ff30a7a931bfb32f89d5858..86a5261e47e17d9b7b0a9500734f586921e3eca4 100644 (file)
     <h5><a id="elementsNICSEthernet">Generic ethernet connection</a></h5>
 
     <p>
-      Provides a means for the administrator to execute an arbitrary script
-      to connect the guest's network to the LAN. The guest will have a tun
-      device created with a name of vnetN, which can also be overridden with the
-      &lt;target&gt; element. After creating the tun device a shell script will
-      be run which is expected to do whatever host network integration is
-      required. By default this script is called /etc/qemu-ifup but can be
-      overridden.
+      Provides a means to use a new or existing tap device (or veth
+      device pair, depening on the needs of the hypervisor driver)
+      that is partially or wholly setup external to libvirt (either
+      prior to the guest starting, or while the guest is being started
+      via an optional script specified in the config).
+    </p>
+    <p>
+      The name of the tap device can optionally be specified with
+      the <code>dev</code> attribute of the
+      <code>&lt;target&gt;</code> element. If no target dev is
+      specified, libvirt will create a new standard tap device with a
+      name of the pattern "vnetN", where "N" is replaced with a
+      number. If a target dev is specified and that device doesn't
+      exist, then a new standard tap device will be created with the
+      exact dev name given. If the specified target dev does exist,
+      then that existing device will be used. Usually some basic setup
+      of the device is done by libvirt, including setting a MAC
+      address, and the IFF_UP flag, but if the <code>dev</code> is a
+      pre-existing device, and the <code>managed</code> attribute of
+      the <code>target</code> element is also set to "no" (the default
+      value is "yes"), even this basic setup will not be performed -
+      libvirt will simply pass the device on to the hypervisor with no
+      setup at all. <span class="since">Since 5.7.0</span> Using
+      managed='no' with a pre-created tap device is useful because
+      it permits a virtual machine managed by an unprivileged libvirtd
+      to have emulated network devices based on tap devices.
+    </p>
+    <p>
+      After creating/opening the tap device, an optional shell script
+      (given in the <code>path</code> attribute of
+      the <code>&lt;script&gt;</code> element) will be run; this can
+      be used to do whatever extra host network integration is
+      required.
     </p>
 
 <pre>
 ...
 &lt;devices&gt;
-  &lt;interface type='ethernet'/&gt;
-  ...
   &lt;interface type='ethernet'&gt;
-    &lt;target dev='vnet7'/&gt;
     &lt;script path='/etc/qemu-ifup-mynet'/&gt;
   &lt;/interface&gt;
+  ...
+  &lt;interface type='ethernet'&gt;
+    &lt;target dev='mytap1' managed='no'/&gt;
+    &lt;model type='virtio'/&gt;
+  &lt;/interface&gt;
 &lt;/devices&gt;
 ...</pre>
 
index c48f8c4f56f44149cf5f2d7219837fe2b5c81a08..cae3be639e9dc7774f17c494df6a78a6bd1f6bcd 100644 (file)
           <attribute name="dev">
             <ref name="deviceName"/>
           </attribute>
+          <optional>
+            <attribute name="managed">
+              <ref name="virYesNo"/>
+            </attribute>
+          </optional>
           <empty/>
         </element>
       </optional>
index ddc72d3cf992aaf7bd86ea5fbe77673f4950c07c..7f49c8253f35824e52ccf56188bb9f001f2f269e 100644 (file)
@@ -6112,6 +6112,14 @@ virDomainNetDefValidate(const virDomainNetDef *net)
                        virDomainNetTypeToString(net->type));
         return -1;
     }
+    if (net->managed_tap == VIR_TRISTATE_BOOL_NO &&
+        net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("unmanaged target dev is not supported on "
+                         "interfaces of type '%s'"),
+                       virDomainNetTypeToString(net->type));
+        return -1;
+    }
     return 0;
 }
 
@@ -11421,6 +11429,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
     VIR_AUTOFREE(char *) bridge = NULL;
     VIR_AUTOFREE(char *) dev = NULL;
     VIR_AUTOFREE(char *) ifname = NULL;
+    VIR_AUTOFREE(char *) managed_tap = NULL;
     VIR_AUTOFREE(char *) ifname_guest = NULL;
     VIR_AUTOFREE(char *) ifname_guest_actual = NULL;
     VIR_AUTOFREE(char *) script = NULL;
@@ -11583,13 +11592,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
             } else if (!ifname &&
                        virXMLNodeNameEqual(cur, "target")) {
                 ifname = virXMLPropString(cur, "dev");
-                if (ifname &&
-                    (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
-                    (STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
-                     (prefix && STRPREFIX(ifname, prefix)))) {
-                    /* An auto-generated target name, blank it out */
-                    VIR_FREE(ifname);
-                }
+                managed_tap = virXMLPropString(cur, "managed");
             } else if ((!ifname_guest || !ifname_guest_actual) &&
                        virXMLNodeNameEqual(cur, "guest")) {
                 ifname_guest = virXMLPropString(cur, "dev");
@@ -11923,6 +11926,27 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
                                    ctxt, &def->guestIP) < 0)
         goto error;
 
+    if (managed_tap) {
+        if (STREQ(managed_tap, "no")) {
+            def->managed_tap = VIR_TRISTATE_BOOL_NO;
+        } else if (STREQ(managed_tap, "yes")) {
+            def->managed_tap = VIR_TRISTATE_BOOL_YES;
+        } else {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("invalid 'managed' value '%s'"),
+                           managed_tap);
+            goto error;
+        }
+    }
+
+    if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname &&
+        (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
+        (STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+         (prefix && STRPREFIX(ifname, prefix)))) {
+        /* An auto-generated target name, blank it out */
+        VIR_FREE(ifname);
+    }
+
     if (script != NULL)
         VIR_STEAL_PTR(def->script, script);
     if (domain_name != NULL)
@@ -25550,12 +25574,17 @@ virDomainNetDefFormat(virBufferPtr buf,
     virBufferEscapeString(buf, "<backenddomain name='%s'/>\n", def->domain_name);
 
     if (def->ifname &&
-        !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
-          (STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
-           (prefix && STRPREFIX(def->ifname, prefix))))) {
+        (def->managed_tap == VIR_TRISTATE_BOOL_NO ||
+         !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
+           (STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
+            (prefix && STRPREFIX(def->ifname, prefix)))))) {
         /* Skip auto-generated target names for inactive config. */
         virBufferEscapeString(&attrBuf, " dev='%s'", def->ifname);
     }
+    if (def->managed_tap != VIR_TRISTATE_BOOL_ABSENT) {
+        virBufferAsprintf(&attrBuf, " managed='%s'",
+                          virTristateBoolTypeToString(def->managed_tap));
+    }
 
     if (virXMLFormatElement(buf, "target", &attrBuf, NULL) < 0)
         return -1;
index c00d2b4953ee5a09bb2a8e73a0262fe0258e38a9..af80c2b7abe7fec6e1e145e4abf6a1b1c03d0843 100644 (file)
@@ -1004,6 +1004,7 @@ struct _virDomainNetDef {
     char *script;
     char *domain_name; /* backend domain name */
     char *ifname; /* interface name on the host (<target dev='x'/>) */
+    int managed_tap; /* enum virTristateBool - ABSENT == YES */
     virNetDevIPInfo hostIP;
     char *ifname_guest_actual;
     char *ifname_guest;
diff --git a/tests/qemuxml2argvdata/net-eth-unmanaged-tap.xml b/tests/qemuxml2argvdata/net-eth-unmanaged-tap.xml
new file mode 100644 (file)
index 0000000..7f5a0c2
--- /dev/null
@@ -0,0 +1,35 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219100</memory>
+  <currentMemory unit='KiB'>219100</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='i686' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-i686</emulator>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/HostVG/QEMUGuest1'/>
+      <target dev='hda' bus='ide'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+    </disk>
+    <controller type='usb' index='0'/>
+    <controller type='ide' index='0'/>
+    <controller type='pci' index='0' model='pci-root'/>
+    <interface type='ethernet'>
+      <mac address='fe:11:22:33:44:55'/>
+      <target dev='mytap0' managed='no'/>
+      <model type='virtio'/>
+    </interface>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/net-eth-unmanaged-tap.xml b/tests/qemuxml2xmloutdata/net-eth-unmanaged-tap.xml
new file mode 100644 (file)
index 0000000..cdff179
--- /dev/null
@@ -0,0 +1,40 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219100</memory>
+  <currentMemory unit='KiB'>219100</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='i686' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-i686</emulator>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/HostVG/QEMUGuest1'/>
+      <target dev='hda' bus='ide'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+    </disk>
+    <controller type='usb' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+    </controller>
+    <controller type='ide' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+    </controller>
+    <controller type='pci' index='0' model='pci-root'/>
+    <interface type='ethernet'>
+      <mac address='fe:11:22:33:44:55'/>
+      <target dev='mytap0' managed='no'/>
+      <model type='virtio'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </interface>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='none'/>
+  </devices>
+</domain>
index c4bc05e6795e0aaa9fc41f835605b5af40bd1611..d5c66d8791ae4895b47bc352b7137e2efeb089b4 100644 (file)
@@ -408,6 +408,7 @@ mymain(void)
     DO_TEST("net-eth", NONE);
     DO_TEST("net-eth-ifname", NONE);
     DO_TEST("net-eth-hostip", NONE);
+    DO_TEST("net-eth-unmanaged-tap", NONE);
     DO_TEST("net-virtio-network-portgroup", NONE);
     DO_TEST("net-virtio-rxtxqueuesize", NONE);
     DO_TEST("net-hostdev", NONE);