Some drivers (currently mlx5_core) expose sub-functions (SFs) of a PCI Physical Function as auxiliary devices. Each SF carries a host network interface that sits below the aux device in sysfs: /sys/devices/.../<PF BDF>/mlx5_core.sf.<idx>/net/eth<N> Because the network device's immediate parent is the aux device and not a PCI device, names_pci() bails out and these interfaces fall through to the kernel-assigned eth<N> name, which is not stable across reboots, module reloads or topology changes. The naming applies when the SF network device's direct sysfs parent is the aux device that exposes sfnum, i.e. the kernel driver passes the aux device to SET_NETDEV_DEV(). mlx5_core does so. ice's ice_sf_cfg_netdev() currently passes the parent PF's PCI device, so ice SF network devices sit as siblings of the PF rather than below the aux device and fall outside this precondition; pending a kernel change in ice to mirror mlx5's SET_NETDEV_DEV(netdev, &adev->dev), they continue to receive the kernel-assigned name as they do today. The aux device exposes 'sfnum', the user-defined sub-function number (the value passed to "devlink port add ... sfnum N"), which is stable and unique within its parent PF. The aux device's direct sysfs parent is the PF's PCI device. Treat an SF host network device analogously to an SR-IOV VF host network device: walk to the parent PCI function, derive the base name from there, then append a single-character "S<sfnum>" suffix. Lowercase 's' is already taken (slot) and the existing grammar uses one character per token, so 'S' is the best option. E.g. for an SF whose parent PF is at PCI 0000:c1:00.0 and which was added with "sfnum 88": ID_NET_NAME_PATH=enp193s0f0S88 ID_NET_NAME_SLOT=enp193s0f0S88 This is parallel to how SR-IOV VFs get a "v<N>" suffix on top of the parent PF's name. Gate the new behaviour behind NAMING_SUBFUNC and NAMING_V261. Document the new suffix in both the ID_NET_NAME_SLOT and ID_NET_NAME_PATH grammars in systemd.net-naming-scheme(7) and add a v261 history entry. Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
193 lines
5.8 KiB
C
193 lines
5.8 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "sd-device.h"
|
|
|
|
#include "alloc-util.h"
|
|
#include "device-private.h"
|
|
#include "log.h"
|
|
#include "netif-naming-scheme.h"
|
|
#include "proc-cmdline.h"
|
|
#include "string-table.h"
|
|
#include "string-util.h"
|
|
|
|
static const NamingScheme naming_schemes[] = {
|
|
{ "v238", NAMING_V238 },
|
|
{ "v239", NAMING_V239 },
|
|
{ "v240", NAMING_V240 },
|
|
{ "v241", NAMING_V241 },
|
|
{ "v243", NAMING_V243 },
|
|
{ "v245", NAMING_V245 },
|
|
{ "v247", NAMING_V247 },
|
|
{ "v249", NAMING_V249 },
|
|
{ "v250", NAMING_V250 },
|
|
{ "v251", NAMING_V251 },
|
|
{ "v252", NAMING_V252 },
|
|
{ "v253", NAMING_V253 },
|
|
{ "v254", NAMING_V254 },
|
|
{ "v255", NAMING_V255 },
|
|
{ "v257", NAMING_V257 },
|
|
{ "v258", NAMING_V258 },
|
|
{ "v259", NAMING_V259 },
|
|
{ "v260", NAMING_V260 },
|
|
{ "v261", NAMING_V261 },
|
|
/* … add more schemes here, as the logic to name devices is updated … */
|
|
|
|
EXTRA_NET_NAMING_MAP
|
|
};
|
|
|
|
const NamingScheme* naming_scheme_from_name(const char *name) {
|
|
/* "latest" may either be defined explicitly by the extra map, in which case we will find it in
|
|
* the table like any other name. After iterating through the table, we check for "latest" again,
|
|
* which means that if not mapped explicitly, it maps to the last defined entry, whatever that is. */
|
|
|
|
FOREACH_ELEMENT(scheme, naming_schemes)
|
|
if (streq(scheme->name, name))
|
|
return scheme;
|
|
|
|
if (streq(name, "latest"))
|
|
return naming_schemes + ELEMENTSOF(naming_schemes) - 1;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const NamingScheme* naming_scheme(void) {
|
|
static const NamingScheme *cache = NULL;
|
|
_cleanup_free_ char *buffer = NULL;
|
|
const char *e, *k;
|
|
|
|
if (cache)
|
|
return cache;
|
|
|
|
/* Acquire setting from the kernel command line */
|
|
(void) proc_cmdline_get_key("net.naming_scheme", 0, &buffer);
|
|
|
|
/* Also acquire it from an env var */
|
|
e = getenv("NET_NAMING_SCHEME");
|
|
if (e) {
|
|
if (*e == ':') {
|
|
/* If prefixed with ':' the kernel cmdline takes precedence */
|
|
k = buffer ?: e + 1;
|
|
} else
|
|
k = e; /* Otherwise the env var takes precedence */
|
|
} else
|
|
k = buffer;
|
|
|
|
if (k) {
|
|
cache = naming_scheme_from_name(k);
|
|
if (cache) {
|
|
log_info("Using interface naming scheme '%s'.", cache->name);
|
|
return cache;
|
|
}
|
|
|
|
log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k);
|
|
}
|
|
|
|
cache = naming_scheme_from_name(DEFAULT_NET_NAMING_SCHEME);
|
|
assert(cache);
|
|
log_info("Using default interface naming scheme '%s'.", cache->name);
|
|
|
|
return cache;
|
|
}
|
|
|
|
static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
|
|
[NAMEPOLICY_KERNEL] = "kernel",
|
|
[NAMEPOLICY_KEEP] = "keep",
|
|
[NAMEPOLICY_DATABASE] = "database",
|
|
[NAMEPOLICY_ONBOARD] = "onboard",
|
|
[NAMEPOLICY_SLOT] = "slot",
|
|
[NAMEPOLICY_PATH] = "path",
|
|
[NAMEPOLICY_MAC] = "mac",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
|
|
|
|
static const char* const alternative_names_policy_table[_NAMEPOLICY_MAX] = {
|
|
[NAMEPOLICY_DATABASE] = "database",
|
|
[NAMEPOLICY_ONBOARD] = "onboard",
|
|
[NAMEPOLICY_SLOT] = "slot",
|
|
[NAMEPOLICY_PATH] = "path",
|
|
[NAMEPOLICY_MAC] = "mac",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(alternative_names_policy, NamePolicy);
|
|
|
|
static int naming_sysattr_allowed_by_default(sd_device *dev) {
|
|
int r;
|
|
|
|
assert(dev);
|
|
|
|
r = device_get_property_bool(dev, "ID_NET_NAME_ALLOW");
|
|
if (r == -ENOENT)
|
|
return true;
|
|
|
|
return r;
|
|
}
|
|
|
|
static int naming_sysattr_allowed(sd_device *dev, const char *sysattr) {
|
|
char *sysattr_property;
|
|
int r;
|
|
|
|
assert(dev);
|
|
assert(sysattr);
|
|
|
|
sysattr_property = strjoina("ID_NET_NAME_ALLOW_", sysattr);
|
|
ascii_strupper(sysattr_property);
|
|
|
|
r = device_get_property_bool(dev, sysattr_property);
|
|
if (r == -ENOENT)
|
|
/* If ID_NET_NAME_ALLOW is not set or set to 1 default is to allow */
|
|
return naming_sysattr_allowed_by_default(dev);
|
|
|
|
return r;
|
|
}
|
|
|
|
int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value) {
|
|
int r;
|
|
|
|
r = naming_sysattr_allowed(device, sysattr);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return -ENOENT;
|
|
|
|
return device_get_sysattr_int(device, sysattr, ret_value);
|
|
}
|
|
|
|
int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value) {
|
|
int r;
|
|
|
|
r = naming_sysattr_allowed(device, sysattr);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return -ENOENT;
|
|
|
|
return device_get_sysattr_unsigned(device, sysattr, ret_value);
|
|
}
|
|
|
|
int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr) {
|
|
int r;
|
|
|
|
r = naming_sysattr_allowed(device, sysattr);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return -ENOENT;
|
|
|
|
return device_get_sysattr_bool(device, sysattr);
|
|
}
|
|
|
|
int device_get_sysattr_safe_string_filtered(sd_device *device, const char *sysattr, const char **ret_value) {
|
|
int r;
|
|
|
|
r = naming_sysattr_allowed(device, sysattr);
|
|
if (r < 0)
|
|
return r;
|
|
if (r == 0)
|
|
return -ENOENT;
|
|
|
|
return device_get_sysattr_safe_string(device, sysattr, ret_value);
|
|
}
|