Is it safe to set net.ifnames=0 in RHEL7, RHEL8 and RHEL9?
Environment
- Red Hat Enterprise Linux 9
- Red Hat Enterprise Linux 8
- Red Hat Enterprise Linux 7
Issue
- Is it safe to disable Consistent Device Naming by setting net.ifnames=0 on the kernel command line in RHEL7, RHEL8 and RHEL9?
- Why can I no longer use the 70-persistent-net.rules file to force persistent ethX names like I could in earlier versions of Red Hat Enterprise Linux?
Resolution
-
No. Red Hat strongly recommends that the new RHEL7, RHEL8 and RHEL9 naming conventions are used.
-
It is safe to set
net.ifnames=0only under a few specific circumstances:-
If the alternate naming scheme, such as
biosdevname, is enabled and able to identify the needed interface properties.biosdevnameis enabled by default only on systems running RHEL7 on Dell hardware. In all other cases it must be enabled by setting biosdevname=1 and ensuring thebiosdevnamepackage is installed. Non-Dell hardware may not provide the necessary information needed for biosdevname to work. -
If the system only has a single interface and will never have more than a single interface.
-
KVM guests (libvirt only, not RHV or OpenStack) exclusively using virtio-net type interfaces can safely set
net.ifnames=0. -
If the system is configured to use unique, non ethX style names using either udev rules or relying on the functionality of the udev properties in the
/usr/lib/udev/rules.d/60-net.rulesrule file.
-
Root Cause
-
Red Hat Enterprise Linux 7 is the first release of RHEL which assigns network interface names using
systemd's Predictable Network Interface naming scheme. This is described in detail in:- the Consistent network device naming section in the RHEL7 Networking Guide
- the Consistent network interface device naming section in the RHEL8 Configuring and managing networking guide
-
The freedesktop.org website has further details about the new naming behaviour and the move away from ethX device naming:
How are interface names assigned?
-
The kernel itself is inherently stateless and so by itself has no functionality to ensure drivers or network interfaces are enumerated in some predictable order at boot or whenever they appear. At boot time (or whenever a network driver is loaded) ethernet interfaces are always assigned an ethX style name by the kernel, where X is the lowest currently unused number starting with 0 (zero).
-
As the kernel is inherently stateless and its ethX assignments are not predictable or consistent, some userspace component is involved so that after a specific network interface appears it will be renamed as needed to some consistent name. Typically some stable property such as MAC address or PCI bus address is consulted and some renaming decision is made. In RHEL 5 and RHEL 6 that userspace component is
udev; in RHEL 7+it issystemd-udev.
Overview of RHEL 6 naming behaviour
- In RHEL 6, the typical
udevbehaviour is to compare the MAC address of interfaces as they appear against a rule file that specifies which ethX name to assign for an interface with the given MAC address. If the kernel-assigned name does not match what is listed in the rule file, thenudevrenames the interface to match. As long as an interface's MAC address does not change and the associations in the udev rule file are not altered,udevshould ensure naming consistency across reboots.
Overview of RHEL 7+ naming behaviour
- In RHEL 7 and above, the default
systemd-udevbehaviour is significantly different than prior releases.systemd-udevno longer considers the MAC address of an interface nor does it maintain a rule file with MAC-to-name associations. Instead,udevrenames all interfaces in a predictable way based upon stable properties such as PCI Slot Number or PCI bus address. As long as these properties do not change for the interface,systemd-udevshould always derive the same name across reboots. Deliberately, the new names thatudevassigns do not follow the kernel's ethX pattern.systemdrefers to this new scheme as itsPredictable Network Interfacefeature.
Why is disabling the Predictable Network Interface feature not recommended (setting net.ifnames=0)?
- In RHEL 7 and above, setting the kernel command line parameter
net.ifnames=0disables thePredictable Network Interfacerenaming behaviour. If no alternate method is employed to rename interfaces then all network interfaces will remain with their original ethX kernel-assigned names which, as discussed above, are inherently unreliable. This can cause any number of problems.
Why does renaming to ethX sometimes fail in RHEL 7+?
-
In RHEL 7+ renaming will fail if the new name is already in use by some other interface. If the new name to be assigned is some ethX name then the likelihood of it already being in use is high, especially at boot time. For a more technical explanation please see the below Diagnostic Steps section of this article.
-
Because of the above changes, the upstream developers of systemd/udev have removed the 70-persistent-net.rules functionality and replaced it with the default naming scheme present in Red Hat Enterprise Linux 7 and above.
Why only RHEL KVM, and not RHV or OpenStack?
RHEL KVM persists virtio device address on the virtual PCI bus. We believe that KVM virtualization operates in such a way that the virtual hardware bus is enumerated the same way each time, resulting in a persistent presentation of devices to the kernel for naming with the ethX naming scheme.
The bus location is stored in the <address> parameter of the VM XML as follows, where # are unique parameters per VM:
<interface type='bridge'>
<mac address='52:54:00:##:##:##'/>
<source bridge='br0'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x0#' function='0x0'/>
</interface>
RHV and OpenStack do not store any such device information. The VM XML is built dynamically each time the VM starts, and the bus location may change with hardware changes or updates to the VM management software. There is no method to persist device order, hence no way to ensure persistent device enumeration or kernel ordering.
Diagnostic Steps
- With the move to systemd, providing consistent ethX style naming is no longer a non-trivial task for the user. Older versions of udev include a much more complicated rename_netif() function which tried to handle collisions during an interface name change. That is to say, if a request is made to rename interface A to eth2 while interface B is already using the name eth2, udev would detect the issue and rename interface B to some other name (renameX where X was the netdev index number) and then rename interface A to eth2. This happened inside the udev function rename_netif(), note the section highlighted in red:
static int rename_netif(struct udev_event *event)
{
struct udev_device *dev = event->dev;
int sk;
struct ifreq ifr;
int err;
info(event->udev, "changing net interface name from '%s' to '%s'\n",
udev_device_get_sysname(dev), event->name);
sk = socket(PF_INET, SOCK_DGRAM, 0);
if (sk < 0) {
err(event->udev, "error opening socket: %m\n");
return -1;
}
memset(&ifr, 0x00, sizeof(struct ifreq));
util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev));
util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name);
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err == 0)
rename_netif_kernel_log(ifr);
else {
int loop;
/* see if the destination interface name already exists */
if (errno != EEXIST) {
err(event->udev, "error changing netif name '%s' to '%s': %m\n",
ifr.ifr_name, ifr.ifr_newname);
goto exit;
}
/* free our own name, another process may wait for us */
snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%s", udev_device_get_sysattr_value(dev, "ifindex"));
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err != 0) {
err(event->udev, "error changing netif name '%s' to '%s': %m\n",
ifr.ifr_name, ifr.ifr_newname);
goto exit;
}
rename_netif_kernel_log(ifr);
/* wait 90 seconds for our target to become available */
util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname);
util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name);
loop = 90 * 20;
while (loop--) {
const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 };
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err == 0) {
rename_netif_kernel_log(ifr);
break;
}
if (errno != EEXIST) {
err(event->udev, "error changing net interface name '%s' to '%s': %m\n",
ifr.ifr_name, ifr.ifr_newname);
break;
}
dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n",
event->name, (90 * 20) - loop);
nanosleep(&duration, NULL);
}
}
exit:
close(sk);
return err;
}
- With RHEL 7+ and systemd, the rename_netif() function has been greatly simplified. It no longer handles naming collisions so if there is an attempt to rename an interface to a name which is already in use, it will simply fail.
static int rename_netif(struct udev_event *event) {
struct udev_device *dev = event->dev;
char name[IFNAMSIZ];
const char *oldname;
int r;
oldname = udev_device_get_sysname(dev);
strscpy(name, IFNAMSIZ, event->name);
r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name);
if (r < 0)
return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name);
log_debug("renamed network interface '%s' to '%s'", oldname, name);
return 0;
}
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.