util: Introduce helper functions for generating unique netdev name

Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
common helper functions.

Signed-off-by: Shi Lei <shi_lei@massclouds.com>
Reviewed-by: Laine Stump <laine@redhat.com>
This commit is contained in:
Shi Lei 2020-12-14 09:50:32 +08:00 committed by Laine Stump
parent 84dc367e2a
commit 294fd4bd80
8 changed files with 164 additions and 15 deletions

View File

@ -80,10 +80,10 @@ bhyveBuildNetArgStr(const virDomainDef *def,
}
if (!net->ifname ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
strchr(net->ifname, '%')) {
VIR_FREE(net->ifname);
net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
}
if (!dryRun) {

View File

@ -11081,7 +11081,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname &&
(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
(STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
(STRPREFIX(ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
STRPREFIX(ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
STRPREFIX(ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
(prefix && STRPREFIX(ifname, prefix)))) {
@ -25504,7 +25504,7 @@ virDomainNetDefFormat(virBufferPtr buf,
if (def->ifname &&
(def->managed_tap == VIR_TRISTATE_BOOL_NO ||
!((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
(STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
(STRPREFIX(def->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
STRPREFIX(def->ifname, VIR_NET_GENERATED_MACVLAN_PREFIX) ||
(prefix && STRPREFIX(def->ifname, prefix)))))) {

View File

@ -544,7 +544,7 @@ udevBridgeScanDirFilter(const struct dirent *entry)
* vnet%d. Improvements to this check are welcome.
*/
if (strlen(entry->d_name) >= 5) {
if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_TAP_PREFIX) &&
if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_VNET_PREFIX) &&
g_ascii_isdigit(entry->d_name[4]))
return 0;
}

View File

@ -2554,6 +2554,7 @@ virNetDevDelMulti;
virNetDevExists;
virNetDevFeatureTypeFromString;
virNetDevFeatureTypeToString;
virNetDevGenerateName;
virNetDevGetFeatures;
virNetDevGetIndex;
virNetDevGetLinkInfo;
@ -2577,6 +2578,7 @@ virNetDevIfStateTypeToString;
virNetDevIsVirtualFunction;
virNetDevPFGetVF;
virNetDevReadNetConfig;
virNetDevReserveName;
virNetDevRunEthernetScript;
virNetDevRxFilterFree;
virNetDevRxFilterModeTypeFromString;

View File

@ -456,10 +456,10 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def,
}
} else {
if (!net->ifname ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
strchr(net->ifname, '%')) {
VIR_FREE(net->ifname);
net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
/* avoid exposing vnet%d in getXMLDesc or error outputs */
template_ifname = true;
}
@ -560,10 +560,10 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def,
}
if (!net->ifname ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_VNET_PREFIX) ||
strchr(net->ifname, '%')) {
VIR_FREE(net->ifname);
net->ifname = g_strdup(VIR_NET_GENERATED_TAP_PREFIX "%d");
net->ifname = g_strdup(VIR_NET_GENERATED_VNET_PREFIX "%d");
/* avoid exposing vnet%d in getXMLDesc or error outputs */
template_ifname = true;
}

View File

@ -17,6 +17,7 @@
*/
#include <config.h>
#include <math.h>
#include "virnetdev.h"
#include "viralloc.h"
@ -95,6 +96,14 @@ VIR_LOG_INIT("util.netdev");
(FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
#endif
static virNetDevGenName
virNetDevGenNames[VIR_NET_DEV_GEN_NAME_LAST] = {
{-1, VIR_NET_GENERATED_VNET_PREFIX, VIR_MUTEX_INITIALIZER},
{-1, VIR_NET_GENERATED_MACVTAP_PREFIX, VIR_MUTEX_INITIALIZER},
{-1, VIR_NET_GENERATED_MACVLAN_PREFIX, VIR_MUTEX_INITIALIZER},
};
typedef enum {
VIR_MCAST_TYPE_INDEX_TOKEN,
VIR_MCAST_TYPE_NAME_TOKEN,
@ -3516,3 +3525,116 @@ virNetDevSetRootQDisc(const char *ifname,
return 0;
}
/**
* virNetDevReserveName:
* @name: name of an existing network device
*
* Reserve a network device name, so that any new network device
* created with an autogenerated name will use a number higher
* than the number in the given device name.
*
* Returns nothing.
*/
void
virNetDevReserveName(const char *name)
{
unsigned int id;
const char *idstr = NULL;
virNetDevGenNameType type;
if (!name)
return;
if (STRPREFIX(name, VIR_NET_GENERATED_VNET_PREFIX))
type = VIR_NET_DEV_GEN_NAME_VNET;
else if (STRPREFIX(name, VIR_NET_GENERATED_MACVTAP_PREFIX))
type = VIR_NET_DEV_GEN_NAME_MACVTAP;
else if (STRPREFIX(name, VIR_NET_GENERATED_MACVLAN_PREFIX))
type = VIR_NET_DEV_GEN_NAME_MACVLAN;
else
return;
VIR_INFO("marking device in use: '%s'", name);
idstr = name + strlen(virNetDevGenNames[type].prefix);
if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
virMutexLock(&virNetDevGenNames[type].mutex);
if (virNetDevGenNames[type].lastID < (int)id)
virNetDevGenNames[type].lastID = id;
virMutexUnlock(&virNetDevGenNames[type].mutex);
}
}
/**
* virNetDevGenerateName:
* @ifname: pointer to pointer to string which can be a template,
* NULL or user-provided name.
* @type: type of the network device
*
* generate a new (currently unused) name for a new network device based
* on @ifname. If string pointed by @ifname is a template, replace %d
* with the reserved id; if that string is NULL, just generate a new
* name. 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.
*
* Note: if string pointed by @ifname is NOT a template or NULL, leave
* it unchanged and return it directly.
*
* Returns 0 on success, -1 on failure.
*/
int
virNetDevGenerateName(char **ifname, virNetDevGenNameType type)
{
int id;
const char *prefix = virNetDevGenNames[type].prefix;
double maxIDd = pow(10, IFNAMSIZ - 1 - strlen(prefix));
int maxID = INT_MAX;
int attempts = 0;
/* The @ifname is not a template, leave it unchanged. */
if (*ifname &&
(strchr(*ifname, '%') != strrchr(*ifname, '%') ||
strstr(*ifname, "%d") == NULL)) {
return 0;
}
if (maxIDd <= (double)INT_MAX)
maxID = (int)maxIDd;
do {
g_autofree char *try = NULL;
virMutexLock(&virNetDevGenNames[type].mutex);
id = ++virNetDevGenNames[type].lastID;
/* reset before overflow */
if (virNetDevGenNames[type].lastID >= maxID)
virNetDevGenNames[type].lastID = -1;
virMutexUnlock(&virNetDevGenNames[type].mutex);
if (*ifname)
try = g_strdup_printf(*ifname, id);
else
try = g_strdup_printf("%s%d", prefix, id);
if (!virNetDevExists(try)) {
g_free(*ifname);
*ifname = g_steal_pointer(&try);
return 0;
}
} while (++attempts < 10000);
virReportError(VIR_ERR_INTERNAL_ERROR,
_("no unused %s names available"),
prefix);
return -1;
}

View File

@ -38,7 +38,13 @@ typedef void virIfreq;
/* Used for prefix of ifname of any tap device name generated
* dynamically by libvirt, cannot be used for a persistent network name.
*/
#define VIR_NET_GENERATED_TAP_PREFIX "vnet"
#define VIR_NET_GENERATED_VNET_PREFIX "vnet"
/* libvirt will start macvtap/macvlan interface names with one of
* these prefixes when it auto-generates the name
*/
#define VIR_NET_GENERATED_MACVTAP_PREFIX "macvtap"
#define VIR_NET_GENERATED_MACVLAN_PREFIX "macvlan"
typedef enum {
VIR_NETDEV_RX_FILTER_MODE_NONE = 0,
@ -145,6 +151,21 @@ struct _virNetDevCoalesce {
uint32_t rate_sample_interval;
};
typedef enum {
VIR_NET_DEV_GEN_NAME_VNET,
VIR_NET_DEV_GEN_NAME_MACVTAP,
VIR_NET_DEV_GEN_NAME_MACVLAN,
VIR_NET_DEV_GEN_NAME_LAST
} virNetDevGenNameType;
typedef struct _virNetDevGenName virNetDevGenName;
typedef virNetDevGenName *virNetDevGenNamePtr;
struct _virNetDevGenName {
int lastID; /* not "unsigned" because callers use %d */
const char *prefix;
virMutex mutex;
};
int virNetDevSetupControl(const char *ifname,
virIfreq *ifr)
@ -321,3 +342,7 @@ int virNetDevVFInterfaceStats(virPCIDeviceAddressPtr vfAddr,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetDevRxFilter, virNetDevRxFilterFree);
void virNetDevReserveName(const char *name);
int virNetDevGenerateName(char **ifname, virNetDevGenNameType type);

View File

@ -76,11 +76,11 @@ virNetDevTapReserveName(const char *name)
const char *idstr = NULL;
if (STRPREFIX(name, VIR_NET_GENERATED_TAP_PREFIX)) {
if (STRPREFIX(name, VIR_NET_GENERATED_VNET_PREFIX)) {
VIR_INFO("marking device in use: '%s'", name);
idstr = name + strlen(VIR_NET_GENERATED_TAP_PREFIX);
idstr = name + strlen(VIR_NET_GENERATED_VNET_PREFIX);
if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
virMutexLock(&virNetDevTapCreateMutex);
@ -200,7 +200,7 @@ static int
virNetDevTapGenerateName(char **ifname)
{
int id;
double maxIDd = pow(10, IFNAMSIZ - 1 - strlen(VIR_NET_GENERATED_TAP_PREFIX));
double maxIDd = pow(10, IFNAMSIZ - 1 - strlen(VIR_NET_GENERATED_VNET_PREFIX));
int maxID = INT_MAX;
int attempts = 0;
@ -227,7 +227,7 @@ virNetDevTapGenerateName(char **ifname)
virReportError(VIR_ERR_INTERNAL_ERROR,
_("no unused %s names available"),
VIR_NET_GENERATED_TAP_PREFIX);
VIR_NET_GENERATED_VNET_PREFIX);
return -1;
}
@ -270,7 +270,7 @@ int virNetDevTapCreate(char **ifname,
* immediately re-using names that have just been released, which
* can lead to race conditions).
*/
if (STREQ(*ifname, VIR_NET_GENERATED_TAP_PREFIX "%d") &&
if (STREQ(*ifname, VIR_NET_GENERATED_VNET_PREFIX "%d") &&
virNetDevTapGenerateName(ifname) < 0) {
goto cleanup;
}