From nobody Mon Feb 9 17:35:35 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) client-ip=63.128.21.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1598419512; cv=none; d=zohomail.com; s=zohoarc; b=e2NzckJSXvjkvhzF2SRU4SjfYJ/u2d3m9eaKC/9csFuHIsdfA34b8KEqRL5836pkXqbHYgEpL2JrTdYyYtNI2cbNNFH4+49IU8hSRabIjSCsExweesbUIpp2i31D1xDD66h6TchK9L27HRz0h5FM/te4fwVBc4r6yC2T6BWB8E8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1598419512; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=2/sBGcrnhrQelWUJA+kJGLKofRJU6CTOhvGKAKgAo3Y=; b=YI8dUWLcVLLlyG/v7mLNDa0BGQqQtnWC+orwDzmkwEPsFHbWU6+ek6Bt4JaKgkMqe6EBjAmDmujBTCkRdDh66lZnA6emO8z1M9Jzc2Fstx5C6C1WwKFj7BskuZcYdgNIlV2tx3HPR0RxmuvgclJH/H9zYslXWCfn/famTXw1E2U= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by mx.zohomail.com with SMTPS id 1598419512469924.589788458206; Tue, 25 Aug 2020 22:25:12 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-246-xo6mTccqOWWRo0zEvtblWQ-1; Wed, 26 Aug 2020 01:25:08 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6C88B1008552; Wed, 26 Aug 2020 05:25:03 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 236465C1C2; Wed, 26 Aug 2020 05:25:03 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id D6F89180B655; Wed, 26 Aug 2020 05:25:02 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 07Q5N6Af006001 for ; Wed, 26 Aug 2020 01:23:06 -0400 Received: by smtp.corp.redhat.com (Postfix) id C7F6C708FF; Wed, 26 Aug 2020 05:23:06 +0000 (UTC) Received: from vhost2.laine.org (ovpn-112-68.phx2.redhat.com [10.3.112.68]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5688974E14; Wed, 26 Aug 2020 05:23:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1598419511; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=2/sBGcrnhrQelWUJA+kJGLKofRJU6CTOhvGKAKgAo3Y=; b=VoKvOlqbqmVlvzoNcYbHqwkY9R+PeQOZVK3tBmMedJZ0WtWgCrfk+ZzKv+8AAuCCCLTfXL Rxtke+cHQcrcWnLq5LRgn/MunJ/MgkH1kxM4qBDoiW65neaMje5HHZPamy5xdHKQu+xaxk yxvjvdY4hwydn1SESm0vw8sCwka/T+s= X-MC-Unique: xo6mTccqOWWRo0zEvtblWQ-1 From: Laine Stump To: libvir-list@redhat.com Subject: [libvirt PATCH v2 2/2] util: assign tap device names using a monotonically increasing integer Date: Wed, 26 Aug 2020 01:22:58 -0400 Message-Id: <20200826052258.109848-3-laine@redhat.com> In-Reply-To: <20200826052258.109848-1-laine@redhat.com> References: <20200826052258.109848-1-laine@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-loop: libvir-list@redhat.com Cc: Bingsong Si , Wei Gong X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0.002 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" When creating a standard tap device, if provided with an ifname that contains "%d", rather than taking that literally as the name to use for the new device, the kernel will instead use that string as a template, and search for the lowest number that could be put in place of %d and produce an otherwise unused and unique name for the new device. For example, if there is no tap device name given in the XML, libvirt will always send "vnet%d" as the device name, and the kernel will create new devices named "vnet0", "vnet1", etc. If one of those devices is deleted, creating a "hole" in the name list, the kernel will always attempt to reuse the name in the hole first before using a name with a higher number (i.e. it finds the lowest possible unused number). The problem with this, as described in the previous patch dealing with macvtap device naming, is that it makes "immediate reuse" of a newly freed tap device name *much* more common, and in the aftermath of deleting a tap device, there is some other necessary cleanup of things which are named based on the device name (nwfilter rules, bandwidth rules, OVS switch ports, to name a few) that could end up stomping over the top of the setup of a new device of the same name for a different guest. Since the kernel "create a name based on a template" functionality for tap devices doesn't exist for macvtap, this patch for standard tap devices is a bit different from the previous patch for macvtap - in particular there was no previous "bitmap ID reservation system" or overly-complex retry loop that needed to be removed. We simply find and unused name, and pass that name on to the kernel instead of "vnet%d". This counter is also wrapped when either it gets to INT_MAX or if the full name would overflow IFNAMSIZ-1 characters. In the case of "vnet%d" and a 32 bit int, we would reach INT_MAX first, but possibly someday someone will change the name from vnet to something else. (NB: It is still possible for a user to provide their own parameterized template name (e.g. "mytap%d") in the XML, and libvirt will just pass that through to the kernel as it always has.) Signed-off-by: Laine Stump --- src/libvirt_private.syms | 1 + src/qemu/qemu_process.c | 20 +++++++- src/util/virnetdevtap.c | 108 ++++++++++++++++++++++++++++++++++++++- src/util/virnetdevtap.h | 4 ++ 4 files changed, 130 insertions(+), 3 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4b155691a8..5736a2dbd3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2676,6 +2676,7 @@ virNetDevTapGetName; virNetDevTapGetRealDeviceName; virNetDevTapInterfaceStats; virNetDevTapReattachBridge; +virNetDevTapReserveName; =20 =20 # util/virnetdevveth.h diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 2a862e6d9e..222a1376c4 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3320,8 +3320,26 @@ qemuProcessNotifyNets(virDomainDefPtr def) * domain to be unceremoniously killed, which would be *very* * impolite. */ - if (virDomainNetGetActualType(net) =3D=3D VIR_DOMAIN_NET_TYPE_DIRE= CT) + switch (virDomainNetGetActualType(net)) { + case VIR_DOMAIN_NET_TYPE_DIRECT: virNetDevMacVLanReserveName(net->ifname); + break; + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_NETWORK: + case VIR_DOMAIN_NET_TYPE_ETHERNET: + virNetDevTapReserveName(net->ifname); + break; + case VIR_DOMAIN_NET_TYPE_USER: + case VIR_DOMAIN_NET_TYPE_VHOSTUSER: + case VIR_DOMAIN_NET_TYPE_SERVER: + case VIR_DOMAIN_NET_TYPE_CLIENT: + case VIR_DOMAIN_NET_TYPE_MCAST: + case VIR_DOMAIN_NET_TYPE_INTERNAL: + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + case VIR_DOMAIN_NET_TYPE_UDP: + case VIR_DOMAIN_NET_TYPE_LAST: + break; + } =20 if (net->type =3D=3D VIR_DOMAIN_NET_TYPE_NETWORK) { if (!conn && !(conn =3D virGetConnectNetwork())) diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c index c0a7c3019e..a46f836da2 100644 --- a/src/util/virnetdevtap.c +++ b/src/util/virnetdevtap.c @@ -49,11 +49,100 @@ #if defined(HAVE_GETIFADDRS) && defined(AF_LINK) # include #endif +#include =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 VIR_LOG_INIT("util.netdevtap"); =20 +virMutex virNetDevTapCreateMutex =3D VIR_MUTEX_INITIALIZER; +static int virNetDevTapLastID =3D -1; /* not "unsigned" because callers us= e %d */ + + +/** + * virNetDevTapReserveName: + * @name: name of an existing tap device + * + * Set the value of virNetDevTapLastID to assure that any new tap + * device created with an autogenerated name will use a number higher + * than the number in the given tap device name. + * + * Returns nothing. + */ +void +virNetDevTapReserveName(const char *name) +{ + unsigned int id; + const char *idstr =3D NULL; + + + if (STRPREFIX(name, VIR_NET_GENERATED_TAP_PREFIX)) { + + VIR_INFO("marking device in use: '%s'", name); + + idstr =3D name + strlen(VIR_NET_GENERATED_TAP_PREFIX); + + if (virStrToLong_ui(idstr, NULL, 10, &id) >=3D 0) { + virMutexLock(&virNetDevTapCreateMutex); + + if (virNetDevTapLastID < (int)id) + virNetDevTapLastID =3D id; + + virMutexUnlock(&virNetDevTapCreateMutex); + } + } +} + + +/** + * virNetDevTapGenerateName: + * @ifname: pointer to pointer to string containing template + * + * generate a new (currently unused) name for a new tap device based + * on the templace string in @ifname - replace %d with + * ++virNetDevTapLastID, and keep trying new values until one is found + * that doesn't already exist, or we've tried 10000 different + * names. Once a usable name is found, replace the template with the + * actual name. + * + * Returns 0 on success, -1 on failure. + */ +static int +virNetDevTapGenerateName(char **ifname) +{ + int id; + double maxIDd =3D pow(10, IFNAMSIZ - 1 - strlen(VIR_NET_GENERATED_TAP_= PREFIX)); + int maxID =3D INT_MAX; + int attempts =3D 0; + + if (maxIDd <=3D (double)INT_MAX) + maxID =3D (int)maxIDd; + + do { + g_autofree char *try =3D NULL; + + id =3D ++virNetDevTapLastID; + + /* reset before overflow */ + if (virNetDevTapLastID >=3D maxID) + virNetDevTapLastID =3D -1; + + try =3D g_strdup_printf(*ifname, id); + + if (!virNetDevExists(try)) { + g_free(*ifname); + *ifname =3D g_steal_pointer(&try); + return 0; + } + } while (++attempts < 10000); + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no unused %s names available"), + VIR_NET_GENERATED_TAP_PREFIX); + return -1; +} + + /** * virNetDevTapGetName: * @tapfd: a tun/tap file descriptor @@ -230,10 +319,22 @@ int virNetDevTapCreate(char **ifname, size_t tapfdSize, unsigned int flags) { - size_t i; + size_t i =3D 0; struct ifreq ifr; int ret =3D -1; - int fd; + int fd =3D 0; + + virMutexLock(&virNetDevTapCreateMutex); + + /* if ifname is "vnet%d", then auto-generate a name for the new + * device (the kernel could do this for us, but has a bad habit of + * immediately re-using names that have just been released, which + * can lead to race conditions). + */ + if (STREQ(*ifname, VIR_NET_GENERATED_TAP_PREFIX "%d") && + virNetDevTapGenerateName(ifname) < 0) { + goto cleanup; + } =20 if (!tunpath) tunpath =3D "/dev/net/tun"; @@ -299,9 +400,11 @@ int virNetDevTapCreate(char **ifname, tapfd[i] =3D fd; } =20 + VIR_INFO("created device: '%s'", *ifname); ret =3D 0; =20 cleanup: + virMutexUnlock(&virNetDevTapCreateMutex); if (ret < 0) { VIR_FORCE_CLOSE(fd); while (i--) @@ -351,6 +454,7 @@ int virNetDevTapDelete(const char *ifname, goto cleanup; } =20 + VIR_INFO("delete device: '%s'", ifname); ret =3D 0; =20 cleanup: diff --git a/src/util/virnetdevtap.h b/src/util/virnetdevtap.h index c6bd9285ba..dea8aec3af 100644 --- a/src/util/virnetdevtap.h +++ b/src/util/virnetdevtap.h @@ -29,6 +29,10 @@ # define VIR_NETDEV_TAP_REQUIRE_MANUAL_CLEANUP 1 #endif =20 +void +virNetDevTapReserveName(const char *name) + ATTRIBUTE_NONNULL(1); + int virNetDevTapCreate(char **ifname, const char *tunpath, int *tapfd, --=20 2.26.2