render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
7548c0
From 37b1acb1c820421d62b1416d90138bae7961bfb7 Mon Sep 17 00:00:00 2001
7548c0
Message-Id: <37b1acb1c820421d62b1416d90138bae7961bfb7@dist-git>
7548c0
From: Laine Stump <laine@redhat.com>
7548c0
Date: Sat, 12 Dec 2020 22:04:52 -0500
7548c0
Subject: [PATCH] util: assign tap device names using a monotonically
7548c0
 increasing integer
7548c0
7548c0
When creating a standard tap device, if provided with an ifname that
7548c0
contains "%d", rather than taking that literally as the name to use
7548c0
for the new device, the kernel will instead use that string as a
7548c0
template, and search for the lowest number that could be put in place
7548c0
of %d and produce an otherwise unused and unique name for the new
7548c0
device. For example, if there is no tap device name given in the XML,
7548c0
libvirt will always send "vnet%d" as the device name, and the kernel
7548c0
will create new devices named "vnet0", "vnet1", etc. If one of those
7548c0
devices is deleted, creating a "hole" in the name list, the kernel
7548c0
will always attempt to reuse the name in the hole first before using a
7548c0
name with a higher number (i.e. it finds the lowest possible unused
7548c0
number).
7548c0
7548c0
The problem with this, as described in the previous patch dealing with
7548c0
macvtap device naming, is that it makes "immediate reuse" of a newly
7548c0
freed tap device name *much* more common, and in the aftermath of
7548c0
deleting a tap device, there is some other necessary cleanup of things
7548c0
which are named based on the device name (nwfilter rules, bandwidth
7548c0
rules, OVS switch ports, to name a few) that could end up stomping
7548c0
over the top of the setup of a new device of the same name for a
7548c0
different guest.
7548c0
7548c0
Since the kernel "create a name based on a template" functionality for
7548c0
tap devices doesn't exist for macvtap, this patch for standard tap
7548c0
devices is a bit different from the previous patch for macvtap - in
7548c0
particular there was no previous "bitmap ID reservation system" or
7548c0
overly-complex retry loop that needed to be removed. We simply find
7548c0
and unused name, and pass that name on to the kernel instead of
7548c0
"vnet%d".
7548c0
7548c0
This counter is also wrapped when either it gets to INT_MAX or if the
7548c0
full name would overflow IFNAMSIZ-1 characters. In the case of
7548c0
"vnet%d" and a 32 bit int, we would reach INT_MAX first, but possibly
7548c0
someday someone will change the name from vnet to something else.
7548c0
7548c0
(NB: It is still possible for a user to provide their own
7548c0
parameterized template name (e.g. "mytap%d") in the XML, and libvirt
7548c0
will just pass that through to the kernel as it always has.)
7548c0
7548c0
Signed-off-by: Laine Stump <laine@redhat.com>
7548c0
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
7548c0
(cherry picked from commit 95089f481e003d971fe0a082018216c58c1b80e5)
7548c0
7548c0
https://bugzilla.redhat.com/1874304
7548c0
Signed-off-by: Laine Stump <laine@redhat.com>
7548c0
Message-Id: <20201213030453.48851-3-laine@redhat.com>
7548c0
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
7548c0
---
7548c0
 src/libvirt_private.syms |   1 +
7548c0
 src/qemu/qemu_process.c  |  20 +++++++-
7548c0
 src/util/virnetdevtap.c  | 108 ++++++++++++++++++++++++++++++++++++++-
7548c0
 src/util/virnetdevtap.h  |   4 ++
7548c0
 4 files changed, 130 insertions(+), 3 deletions(-)
7548c0
7548c0
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
7548c0
index 1c66c40f86..d6598c2514 100644
7548c0
--- a/src/libvirt_private.syms
7548c0
+++ b/src/libvirt_private.syms
7548c0
@@ -2638,6 +2638,7 @@ virNetDevTapGetName;
7548c0
 virNetDevTapGetRealDeviceName;
7548c0
 virNetDevTapInterfaceStats;
7548c0
 virNetDevTapReattachBridge;
7548c0
+virNetDevTapReserveName;
7548c0
 
7548c0
 
7548c0
 # util/virnetdevveth.h
7548c0
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
7548c0
index b49a463c02..f90096e68d 100644
7548c0
--- a/src/qemu/qemu_process.c
7548c0
+++ b/src/qemu/qemu_process.c
7548c0
@@ -3287,8 +3287,26 @@ qemuProcessNotifyNets(virDomainDefPtr def)
7548c0
          * domain to be unceremoniously killed, which would be *very*
7548c0
          * impolite.
7548c0
          */
7548c0
-        if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
7548c0
+        switch (virDomainNetGetActualType(net)) {
7548c0
+        case VIR_DOMAIN_NET_TYPE_DIRECT:
7548c0
             virNetDevMacVLanReserveName(net->ifname);
7548c0
+            break;
7548c0
+        case VIR_DOMAIN_NET_TYPE_BRIDGE:
7548c0
+        case VIR_DOMAIN_NET_TYPE_NETWORK:
7548c0
+        case VIR_DOMAIN_NET_TYPE_ETHERNET:
7548c0
+            virNetDevTapReserveName(net->ifname);
7548c0
+            break;
7548c0
+        case VIR_DOMAIN_NET_TYPE_USER:
7548c0
+        case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
7548c0
+        case VIR_DOMAIN_NET_TYPE_SERVER:
7548c0
+        case VIR_DOMAIN_NET_TYPE_CLIENT:
7548c0
+        case VIR_DOMAIN_NET_TYPE_MCAST:
7548c0
+        case VIR_DOMAIN_NET_TYPE_INTERNAL:
7548c0
+        case VIR_DOMAIN_NET_TYPE_HOSTDEV:
7548c0
+        case VIR_DOMAIN_NET_TYPE_UDP:
7548c0
+        case VIR_DOMAIN_NET_TYPE_LAST:
7548c0
+            break;
7548c0
+        }
7548c0
 
7548c0
         if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
7548c0
             if (!conn && !(conn = virGetConnectNetwork()))
7548c0
diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c
7548c0
index 6a16b58d60..fd4b70df30 100644
7548c0
--- a/src/util/virnetdevtap.c
7548c0
+++ b/src/util/virnetdevtap.c
7548c0
@@ -45,11 +45,51 @@
7548c0
 #if defined(HAVE_GETIFADDRS) && defined(AF_LINK)
7548c0
 # include <ifaddrs.h>
7548c0
 #endif
7548c0
+#include <math.h>
7548c0
 
7548c0
 #define VIR_FROM_THIS VIR_FROM_NONE
7548c0
 
7548c0
 VIR_LOG_INIT("util.netdevtap");
7548c0
 
7548c0
+virMutex virNetDevTapCreateMutex = VIR_MUTEX_INITIALIZER;
7548c0
+static int virNetDevTapLastID = -1; /* not "unsigned" because callers use %d */
7548c0
+
7548c0
+
7548c0
+/**
7548c0
+ * virNetDevTapReserveName:
7548c0
+ * @name: name of an existing tap device
7548c0
+ *
7548c0
+ * Set the value of virNetDevTapLastID to assure that any new tap
7548c0
+ * device created with an autogenerated name will use a number higher
7548c0
+ * than the number in the given tap device name.
7548c0
+ *
7548c0
+ * Returns nothing.
7548c0
+ */
7548c0
+void
7548c0
+virNetDevTapReserveName(const char *name)
7548c0
+{
7548c0
+    unsigned int id;
7548c0
+    const char *idstr = NULL;
7548c0
+
7548c0
+
7548c0
+    if (STRPREFIX(name, VIR_NET_GENERATED_TAP_PREFIX)) {
7548c0
+
7548c0
+        VIR_INFO("marking device in use: '%s'", name);
7548c0
+
7548c0
+        idstr = name + strlen(VIR_NET_GENERATED_TAP_PREFIX);
7548c0
+
7548c0
+        if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
7548c0
+            virMutexLock(&virNetDevTapCreateMutex);
7548c0
+
7548c0
+            if (virNetDevTapLastID < (int)id)
7548c0
+                virNetDevTapLastID = id;
7548c0
+
7548c0
+            virMutexUnlock(&virNetDevTapCreateMutex);
7548c0
+        }
7548c0
+    }
7548c0
+}
7548c0
+
7548c0
+
7548c0
 /**
7548c0
  * virNetDevTapGetName:
7548c0
  * @tapfd: a tun/tap file descriptor
7548c0
@@ -200,6 +240,55 @@ virNetDevProbeVnetHdr(int tapfd)
7548c0
 
7548c0
 
7548c0
 #ifdef TUNSETIFF
7548c0
+/**
7548c0
+ * virNetDevTapGenerateName:
7548c0
+ * @ifname: pointer to pointer to string containing template
7548c0
+ *
7548c0
+ * generate a new (currently unused) name for a new tap device based
7548c0
+ * on the templace string in @ifname - replace %d with
7548c0
+ * ++virNetDevTapLastID, and keep trying new values until one is found
7548c0
+ * that doesn't already exist, or we've tried 10000 different
7548c0
+ * names. Once a usable name is found, replace the template with the
7548c0
+ * actual name.
7548c0
+ *
7548c0
+ * Returns 0 on success, -1 on failure.
7548c0
+ */
7548c0
+static int
7548c0
+virNetDevTapGenerateName(char **ifname)
7548c0
+{
7548c0
+    int id;
7548c0
+    double maxIDd = pow(10, IFNAMSIZ - 1 - strlen(VIR_NET_GENERATED_TAP_PREFIX));
7548c0
+    int maxID = INT_MAX;
7548c0
+    int attempts = 0;
7548c0
+
7548c0
+    if (maxIDd <= (double)INT_MAX)
7548c0
+        maxID = (int)maxIDd;
7548c0
+
7548c0
+    do {
7548c0
+        g_autofree char *try = NULL;
7548c0
+
7548c0
+        id = ++virNetDevTapLastID;
7548c0
+
7548c0
+        /* reset before overflow */
7548c0
+        if (virNetDevTapLastID >= maxID)
7548c0
+            virNetDevTapLastID = -1;
7548c0
+
7548c0
+        try = g_strdup_printf(*ifname, id);
7548c0
+
7548c0
+        if (!virNetDevExists(try)) {
7548c0
+            g_free(*ifname);
7548c0
+            *ifname = g_steal_pointer(&try;;
7548c0
+            return 0;
7548c0
+        }
7548c0
+    } while (++attempts < 10000);
7548c0
+
7548c0
+    virReportError(VIR_ERR_INTERNAL_ERROR,
7548c0
+                   _("no unused %s names available"),
7548c0
+                   VIR_NET_GENERATED_TAP_PREFIX);
7548c0
+    return -1;
7548c0
+}
7548c0
+
7548c0
+
7548c0
 /**
7548c0
  * virNetDevTapCreate:
7548c0
  * @ifname: the interface name
7548c0
@@ -226,10 +315,22 @@ int virNetDevTapCreate(char **ifname,
7548c0
                        size_t tapfdSize,
7548c0
                        unsigned int flags)
7548c0
 {
7548c0
-    size_t i;
7548c0
+    size_t i = 0;
7548c0
     struct ifreq ifr;
7548c0
     int ret = -1;
7548c0
-    int fd;
7548c0
+    int fd = 0;
7548c0
+
7548c0
+    virMutexLock(&virNetDevTapCreateMutex);
7548c0
+
7548c0
+    /* if ifname is "vnet%d", then auto-generate a name for the new
7548c0
+     * device (the kernel could do this for us, but has a bad habit of
7548c0
+     * immediately re-using names that have just been released, which
7548c0
+     * can lead to race conditions).
7548c0
+     */
7548c0
+    if (STREQ(*ifname, VIR_NET_GENERATED_TAP_PREFIX "%d") &&
7548c0
+        virNetDevTapGenerateName(ifname) < 0) {
7548c0
+        goto cleanup;
7548c0
+    }
7548c0
 
7548c0
     if (!tunpath)
7548c0
         tunpath = "/dev/net/tun";
7548c0
@@ -295,9 +396,11 @@ int virNetDevTapCreate(char **ifname,
7548c0
         tapfd[i] = fd;
7548c0
     }
7548c0
 
7548c0
+    VIR_INFO("created device: '%s'", *ifname);
7548c0
     ret = 0;
7548c0
 
7548c0
  cleanup:
7548c0
+    virMutexUnlock(&virNetDevTapCreateMutex);
7548c0
     if (ret < 0) {
7548c0
         VIR_FORCE_CLOSE(fd);
7548c0
         while (i--)
7548c0
@@ -347,6 +450,7 @@ int virNetDevTapDelete(const char *ifname,
7548c0
         goto cleanup;
7548c0
     }
7548c0
 
7548c0
+    VIR_INFO("delete device: '%s'", ifname);
7548c0
     ret = 0;
7548c0
 
7548c0
  cleanup:
7548c0
diff --git a/src/util/virnetdevtap.h b/src/util/virnetdevtap.h
7548c0
index cae8e61861..2994c9ca71 100644
7548c0
--- a/src/util/virnetdevtap.h
7548c0
+++ b/src/util/virnetdevtap.h
7548c0
@@ -29,6 +29,10 @@
7548c0
 # define VIR_NETDEV_TAP_REQUIRE_MANUAL_CLEANUP 1
7548c0
 #endif
7548c0
 
7548c0
+void
7548c0
+virNetDevTapReserveName(const char *name)
7548c0
+    ATTRIBUTE_NONNULL(1);
7548c0
+
7548c0
 int virNetDevTapCreate(char **ifname,
7548c0
                        const char *tunpath,
7548c0
                        int *tapfd,
7548c0
-- 
7548c0
2.29.2
7548c0