Blame SOURCES/libvirt-util-assign-tap-device-names-using-a-monotonically-increasing-integer.patch

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