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 andww
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.