Linux网络设备命名规则简介

几年前, Linux内核为网络接口分配名称采用的是一种简单和直观的方式:一个固定的前缀和一个递增的序号。比如,内核使用eth0名称以标识启动后第一个加载的网络设备,第二个加载的设备名称是eth1,第三个是eth2,以此类推。。。如果用户想要在系统启动后添加一个新的网卡,那么内核也会按这个规则为它分配新的设备名称。

不过,内核分配的网卡名称有一个隐患:每次系统启动时网络设备的加载顺序是不固定的(多数发生在为系统增加网卡,否则加载顺序基本固定),当系统重启时,内核可能会为因加载顺序为同一个网络设备分配一个与之前不同的名称,原本名称为eth0的网卡,可能经过一次系统重启后就变成了eth1。这样就会对部分涉及到网卡数据采集的应用程序产生影响,因此经开发者们商讨后,决定采用一致性网络设备命名规则。

一致性网络设备命名规则采用udev设备管理器来实现对网络设备的重命名,该方式通过获取网卡的固件、结构、未知等信息,从而固定一张网卡的名称,这样做带来了如下几点好处:

  1. 设备名称完全可以预测;
  2. 由于不依赖加载顺序分配名称,不论是添加还是移除设备,设备名称都是固定的,不会被改变;
  3. 当设备出现故障时,能够无感知地使用新设备代替。

接下来本文将详细介绍该网络设备命名规则。

基础概念

udev

udev是Linux上的一种设备管理器,它用于在设备发生事件时,管理设备文件的权限,或者在/dev目录下创建额外的软链接,也可以重命名网络设备。内核往往通过设备发现顺序来分配无法预测的设备名称,而udev则基于设备属性或配置生成软链接或网络设备名称,提供了识别这些设备的可靠方式。

systemd-udevd.service是udev的守护进程,它会直接从内核监听设备的事件,包括添加、移除、状态改变。当udev获取到一个设备事件时,它会从一系列的规则文件中读取匹配到的规则,并应用对应的动作,比如说保存额外信息到udev数据库中,或者创建设备软链接等等。

所有udev处理的设备信息都会保存到udev数据库中,这些数据可以通过libudev库访问,也可以通过udevadm info <devpath>命令读取。

更多udev的信息,比方说udev规则文件的语法,可以通过man udev命令查看。

一些常用的udev命令:

# 获取udev数据库中记录的指定设备信息
# udevadm info /sys/class/net/eno1
P: /devices/pci0000:00/0000:00:1c.4/0000:09:00.0/net/eno1
E: DEVPATH=/devices/pci0000:00/0000:00:1c.4/0000:09:00.0/net/eno1
...
E: INTERFACE=eno1
E: SUBSYSTEM=net

# 调试指定设备的添加/移除事件
# udevadm test -a add /sys/class/net/eno1
calling: test
version 239 (239-76.el8)
...
Unload module index
Unloaded link configuration context.

# 调试udev内置程序
# udevadm test-builtin net_setup_link /sys/class/net/eno1
calling: test-builtin
Load module index
...
Unload module index
Unloaded link configuration context.

udev在执行规则文件时,会事先添加一些基本的环境变量,主要如下:

DEVPATH=/devices/pci0000:00/0000:00:1c.4/0000:09:00.0/net/eno1
INTERFACE=eno1
IFINDEX=2
ACTION=add
SUBSYSTEM=net
LC_CTYPE=C.UTF-8

一致性网络设备命名规则

当启用一致性网络设备命名规则时,udev设备管理器会根据一系列的规范来创建设备的名称,根据网络设备的类型,设备名称会以如下规则生成一个两字母前缀:

  • en表示以太网设备
  • wl表示无线局域网设备(WLAN)
  • ww表示无线广域网设备(WWAN)

接着,udev会根据如下格式为以生成的网络设备名称后追加内容:

  • 板载设备:o<板载索引号>
  • PCIe设备:s<PCIe热插拔索引号>[f<function号>][d<device号>]
  • 其他PCI设备:[P<PCI域位置>]p<bus号>s<slot号>[f<function号>][d<device号>]
  • 有MAC地址的设备:x<MAC地址>
  • USB设备:[P<PCI域位置>]p<bus号>s<slot号>[f<function号>][u<usb端口>][…][c<config>][i<interface>]

原理

一致性网络设备命名规则采用udev来实现对网络设备的重命名,因此重命名的规则也都记录在udev的配置文件中,udev使用如下的顺序处理网络设备命名规则。

/usr/lib/udev/rules.d/60-net.rules

60-net.rules的作用是通过网络接口配置文件更改网络设备命名,它获取匹配网络设备MAC地址的配置文件,并将该配置文件中的设备名作为新设备名。

文件内容如下,当添加设备时,对于net子系统中任意非空设备驱动且type属性为1的设备,执行/lib/udev/rename_device程序,如果程序输出不为空,那么将设备名设置为程序输出。

ACTION=="add", SUBSYSTEM=="net", DRIVERS=="?*", ATTR{type}=="1", PROGRAM="/lib/udev/rename_device", RESULT=="?*", NAME="$result"

/lib/udev/rename_device是一个协助处理程序,它从INTERFACE环境变量中读取网络设备名称,随后通过/sys/class/net/%s/address获取MAC地址,如果/etc/sysconfig/network-scripts/ifcfg-*中有文件的HWADDR参数匹配到对应MAC地址,那么将输出该文件中DEVICE参数的值,否则输出空。

/usr/lib/udev/rules.d/71-biosdevname.rules

71-biosdevname.rules一般在需额外安装的biosdevname软件包中,该规则的作用是通过从bios获取的设备文件名更改网络设备命名,它只会在biosdevname内核参数为1时才生效。

文件内容如下,当添加设备时,对于net子系统中任意名称为空(未重命名过),type属性为1且DEVTYPE环境变量为空的设备,如果biosdevname内核参数为1,那么执行/sbin/biosdevname --smbios 2.6 --nopirq --policy physical -i %k程序(%k是设备的内核名称),将其输出作为新名称。

SUBSYSTEM!="net", GOTO="netdevicename_end"
ACTION!="add",    GOTO="netdevicename_end"
NAME=="?*",       GOTO="netdevicename_end"
ATTR{type}!="1",  GOTO="netdevicename_end"
ENV{DEVTYPE}=="?*", GOTO="netdevicename_end"

# kernel command line "biosdevname={0|1}" can turn off/on biosdevname
IMPORT{cmdline}="biosdevname"
ENV{biosdevname}=="?*", ENV{UDEV_BIOSDEVNAME}="$env{biosdevname}"
# ENV{UDEV_BIOSDEVNAME} can be used for blacklist/whitelist
# but will be overwritten by the kernel command line argument
ENV{UDEV_BIOSDEVNAME}=="0", GOTO="netdevicename_end"
ENV{UDEV_BIOSDEVNAME}=="1", GOTO="netdevicename_start"

# off by default
GOTO="netdevicename_end"

LABEL="netdevicename_start"

# using NAME= instead of setting INTERFACE_NAME, so that persistent
# names aren't generated for these devices, they are "named" on each boot.
SUBSYSTEMS=="pci", PROGRAM="/sbin/biosdevname --smbios 2.6 --nopirq --policy physical -i %k", NAME="%c"  OPTIONS+="string_escape=replace"

LABEL="netdevicename_end"

/usr/lib/udev/rules.d/75-net-description.rules

75-net-description.rules的作用是通过一些udev内置程序获取网络设备的信息并设置为环境变量,用于后续步骤。

文件内容如下,当添加网络设备时,调用net_id内置程序获取设备信息并设置内部环境变量,对于usb类型的网络设备,调用usb_id以及hwdb内置程序获取设备信息并设置内部环境变量,对于pci类型的网络设备,根据一些内核中的设备属性以及hwdb内置程序设置内部环境变量。

ACTION=="remove", GOTO="net_end"
SUBSYSTEM!="net", GOTO="net_end"

IMPORT{builtin}="net_id"
# ID_NET_NAMING_SCHEME=rhel-8.0
# ID_NET_NAME_MAC=enxac1f6b848502
# ID_OUI_FROM_DATABASE=Super Micro Computer, Inc.
# ID_NET_NAME_ONBOARD=eno1
# ID_NET_LABEL_ONBOARD=enIntel Ethernet I350AM2 #1
# ID_NET_NAME_PATH=enp9s0f0

SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
SUBSYSTEMS=="usb", GOTO="net_end"

SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
# ID_BUS=pci
# ID_VENDOR_ID=0x8086
# ID_MODEL_ID=0x1521

SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
# ID_PCI_CLASS_FROM_DATABASE=Network controller
# ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
# ID_VENDOR_FROM_DATABASE=Intel Corporation
# ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection

LABEL="net_end"

/usr/lib/udev/rules.d/80-net-setup-link.rules

80-net-setup-link.rules的作用是通过一些udev内置程序获取网络设备的信息并设置为环境变量,用于后续步骤。

文件内容如下,当添加网络设备时,调用path_id内置程序获取设备信息,随后调用net_setup_link内置程序,如果先前没有重命名设备,且内置程序输出的ID_NET_NAME环境变量不为空,那么将该环境变量的值设置为新设备名称。

SUBSYSTEM!="net", GOTO="net_setup_link_end"

IMPORT{builtin}="path_id"
# ID_PATH=pci-0000:09:00.0
# ID_PATH_TAG=pci-0000_09_00_0

ACTION!="add", GOTO="net_setup_link_end"

IMPORT{builtin}="net_setup_link"
# ID_NET_DRIVER=igb
# ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
# ID_NET_NAME=eno1

NAME=="", ENV{ID_NET_NAME}!="", NAME="$env{ID_NET_NAME}"

LABEL="net_setup_link_end"

net_setup_link内置程序会读取/usr/lib/systemd/network/99-default.link配置文件,从而决定最后网络设备应该使用的名称:

[Link]
NamePolicy=kernel database onboard slot path
AlternativeNamesPolicy=database onboard slot path
MACAddressPolicy=persistent

该文件的NamePolicy参数标记了网络设备命名的优先级顺序:内核名称 > udev硬件数据库中记录名称 > onboard名称(ID_NET_NAME_ONBOARD, eno) > slot名称(ID_NET_NAME_SLOT, ens) > path名称(ID_NET_NAME_PATH, enp)。

另外,AlternativeNamesPolicy则记录了网络设备的别名,如果有该配置项,那么应用程序也可以通过别名来访问网络设备。

应用场景

换回原先网络设备命名规则

如果想要换回原先的网络设备命名规则,即ethN命名,需要在内核参数中添加net.ifnames=0以及biosdevname=0参数。

可以在grub引导程序中修改,在/etc/default/grub中的GRUB_CMDLINE_LINUX参数中追加,并通过grub2-mkconfig -o /boot/grub2/grub.cfg命令生成新grub配置文件,重启系统后生效。

除此之外,可能还要修改/etc/sysconfig/network-scripts/ifcfg-*网卡配置文件中的DEVICE参数,防止重启后出现网卡无法拉起或者网络设备名称换回失败的情况。

基于MAC地址固定网络设备名称

通过udev设备管理器,我们可以很方便的更改以及定制网络设备命名规则,比如如果想要基于MAC地址固定某个网络设备的名称,那么可以创建/etc/udev/rules.d70-fix-eno2.rules文件,内容如下:

SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="ac:1f:6b:84:85:03",ATTR{type}=="1",NAME="myfixif"

其中ATTR{address}表示MAC地址,ATTR{type}=="1"表示是Ethernet类型。

修改后重启生效。

参考文档

Chapter 1. Consistent network interface device naming Red Hat Enterprise Linux 8 | Red Hat Customer Portal

Linux┊详解udev - 无双的小宝 - 博客园 (cnblogs.com)

udev(7) - Linux manual page (man7.org)

07-10 19:04