Intro

If you are running a recent ubuntu release ( > 15.XX ), you've probably noticed that your net interfaces have been "rebranded". Maybe this noticing came in the form of your sripts being broken....your /etc/network/if-up.d/XXX script or vagrant-libvirt deploy failed because you hard coded an interface name in your scripts (bad practice which im guilty of). It worked previously on your old-release, then you had to upgrade...but I digress.

In this post, I'll try talking about how net devices get their names, the schemes employed and the tech behind the schemes.


The classic naming scheme - eth<0-n>

Not so long ago, without any devops intervention, interface names were decided solely by the Kernel based on the order in which they are enumerated which in turn is based on the order in which their modules/drivers are loaded and then, where multiple NICs catered for by the same driver are present, they'd be named in the order in which they were discovered which in turn is again affected by the order in which the PCI device list is enumerated.

When its all done, you'd have something like this :-

eth<index> = ethernet, wlan<index> = wireless, usb<index> = usbnet - prolly phone-tethered

While the scheme is good enough to tell about the type of connection, there's not much in the way of name predictability and telling about the nature of the hardware that provides a given interface i.e. are eth0 and eth1 provided by different ports on the same NIC? Is eth2 provided by a USB adapter?

Another problem is that if you leave it all up to the powers behind the scheme, the device naming will probably not be the same after kernel, NIC or motherboard upgrades or sometimes even reboots.

It's possible to get around the above mentioned limitations using scripts and udev rules but we live in modern times and techies should not be bothered to jump through those hoops just to sort out something that should be automagic.


biosdevname - em<port>, p<slot>p<port>

One of the efforts at bringing order out of chaos is dell's biosdevname project which according to the project description is a udev helper for naming devices per BIOS names. This helper-utility determines device names based on the intended order of network devices as suggested by the BIOS.

With the default policy, the naming scheme of it goes like so:-

  • On-board net devices get named em<port>[_<virtual instance>]
  • Add-on NICs devices get named p<slot>p<port>[_<virtual instance>]

where:-

  • em = ethernet-on-motherboard,
  • <slot> = the respective PCI slot,
  • <port> the port number...for multi-port NICs.
  • <virtual instance> is the SRIOV and/or NPAR instance index
  • The "p"s in the Add-on card scheme stand for pci slot and port respectively

The above scheme is implemented here:- naming_policy.c

If you have the utility installed, you can try it out by booting your kernel with biosdevname=1 option or just run biosdevname -i eth0 #(swap eth0 with any interface you have) from the terminal. If conditions are right, you should get something along the lines of em1 or p1p1.

With this utility, names are stable, predictable and you can also tell what's embedded and what's slotted in...but it has it's limitations, one of them being that it doesn't cater for a wider range of interface types/devices. It refused to name my usb ethernet dongle.


systemd udev - eno<index>, ens<slot>d<port>

Starting version 197, shortly after udev was absorbed into systemd source tree, native predictable naming was added to the mix.

According to the systemd wiki on the topic, and the systemd-udev sources, the names are generated based on:-

  • firmware/bios-provided for on-board devices - <type>o<index>[d<dev_port>]
  • firmware/bios-provided for pci-express hotplug slot - <type>s<slot>[f<function>][d<dev_port>]
  • physical/geographical location for PCI devices -
    <type>[P<domain>]p<bus>s<slot>[f<function>][[d<dev_port>]|[u<port>][..][c<config>][i<interface>]]
  • the devices's MAC address - <type>x<MAC>

Where:-

  • <type> is a two character prefix that tells the nature of the device/connection:- en for ethernet, sl = Serial Line Internet Protocol, wl = Wireless LAN/Wi-Fi and ww for Wireless WAN (LTE/3g device, probably even WiMAX)
  • <domain> is pci device domain.
  • <function> = SRIOV(or maybe also NPAR?) number for multi-function PCI devices.
  • u<port> is a usb port

Thus for example eno1 is an onboard ethernet device, enp8s0 should be an ethernet interface provided by a device on bus #8 slot #0 with only 1 port and wlp9s0 should be an wlan interface provided by device on bus #9 slot #0.

On trying out a usb ethernet dongle, I got enp0s29f7u1 and going by the location/path scheme we can tell that it's an ethernet interface provided by a usb device connected to a usb controller on PCI bus #0, device #29, with function #7 plugged into usb port #1.

systemd-udev Naming Policy

Net devices may have more than one name generated for them which you can view by querying the udev database (filtering the results):-

udevadm info -q property -p /sys/class/net/ens33 | grep ID_NET_NAME
ID_NET_NAME_MAC=enx112233445566
ID_NET_NAME_PATH=enp2s0
ID_NET_NAME_SLOT=ens33

In the above example, we have names by mac, location and bios provided index. The choice of name is determined by a list if policies defined for NamePolicy in the respective link config file (see man systemd.link). To find out what link file is in use, query udevadm like so:-

udevadm info -q property -p /sys/class/net/ens33 | grep LINK_FILE
ID_NET_LINK_FILE=/lib/systemd/network/99-default.link

I get 99-default.link, contents of which are:-

cat /lib/systemd/network/99-default.link
[Link]
NamePolicy=kernel database onboard slot path
MACAddressPolicy=persistent

Looking at the NamePolicy entry, the precedence is defined by the order in which they appear and the first successful one is used to set the device name. In this case with ens33 being chosen, kernel, database and onboard failed, slot passed and path wasn't considered.

Outro

systemd-udev is now standard on most big name distros (ubuntu included) and has naturally superseded biosdevname where it reigned. There is no need to sweat it out with unpredictable interface names like the techies of yesterday but just in case you want to, you can disable all manner of predictability by passing net.ifnames=0 and biosdevname=0 to the kernel at boot time.

Resources, Refs & google juice:-