内核态默认网卡命名规则
基本流程:
1、首先内核发现网卡设备,去调用设备probe函数去完成接下来的操作;
2、网卡初始化操作:网卡驱动会默认提供一个eth%d的名称供该网口使用,然后调用内核接口register_netdev函数
3、register_netdev流程中,会根据系统中已有的接口情况,找出一个最小的还没有使用的数字编号,分给新注册的网卡。比如,系统中当前有,eth0, eth2, eth3三块网卡。那么新注册的网卡,名字就是eth1了。
strcpy(netdev->name, "eth%d");//此时net_device结构体中的name成员为(eth%d)
err = register_netdev(netdev);
register_netdev() 会调用 dev_get_valid_name(),判断 netdev->name 含有转义字符 %d,因此动态地分配了 index number 用于唯一标识这个网卡。
Note: 若 netdev->name 无转义字符 %d,则其值即为网卡名称,不会动态地分配 index number,此设备也只能存在一个该类型的网卡。
register_netdev函数如何找到合适的%d数值并赋值给新注册的网卡?
最终会调用dev_get_valid_name函数分配完整的设备名称。内核最终调用__dev_alloc_name确定设备的名称。内核可支持的设备数量最大为8*PAGE_SIZE,即一个页面的大小的比特数量(通常为8 * 4K=32k个设备总是)。每一位代表一个设备。当前需要做的就是,在net命名空间中,遍历所有的设备名称,将其索引值在一个空白页面中的应用位置置位。遍历完成之后,只需要在页面中查找第一个未置位的位,作为新设备的索引值。将其替换name格式字符串中的%d,赋值于buf保存新的设备名称。
内核默认命名规则的局限性:
往往不一定准确对应网卡接口的物理顺序,而且每次启动只根据内核发现网卡的顺序进行命名,因此并不固定;所以目前一般情况下会在用户态启用其他的方式去更改网卡名称,原则就是在内核命名ethx后将其在根据用户态的规则rename为其他的名字,这种规则往往是根据网卡的Mac地址以及其他能够唯一代表一块网卡的参数去命名,因此会一一对应;
用户态网卡重命名规则
一致性网络设备命名规范
CentOS 7提供了在网络接口中使用一致且可预期的网络设备命名方法, 目前默认使用的是net.ifnames规则。
-
1、/usr/lib/udev/rules.d/60-net.rules 文件中的规则会让 udev 帮助工具/lib/udev/rename_device 查看所有 /etc/sysconfig/network-scripts/ifcfg-suffix 文
件。如果发现包含 HWADDR 条目的 ifcfg 文件与某个接口的 MAC 地址匹配,它会将该接口重命名为ifcfg 文件中由 DEVICE 指令给出的名称。rename条件:如果网卡的ifconfig文件中未加入HWADDR,则rename脚本并不会根据配置文件去重命名网卡;
ACTION=="add", SUBSYSTEM=="net", DRIVERS=="?*", ATTR{type}=="1", PROGRAM="/lib/udev/rename_device", RESULT=="?*", NAME="$result"
-
2、/usr/lib/udev/rules.d/71-biosdevname.rules 中的规则让 biosdevname 根据其命名策略重命名该接口,即在上一步中没有重命名该接口、安装biosdevname、且在 boot 命令行中将biosdevname=0 作为内核命令给出。(bisodevname规则,从CentOS 7 开始默认不使用,所以该条规则在不配置的情况下失效,直接去执行3;默认在cmdline中bisodevname=0,如果需要启用,则需要设置bisodevname=1)
-
3、/lib/udev/rules.d/75-net-description.rules 中的规则让 udev 通过检查网络接口设备,填写内部 udev 设备属性值 ID_NET_NAME_ONBOARD、ID_NET_NAME_SLOT、ID_NET_NAME_PATH、ID_NET_NAME_MAC。注:有些设备属性可能处于未定义状态。
运行了这条规则之后,会将ID_NET_NAME_ONBOARD、ID_NET_NAME_SLOT、ID_NET_NAME_PATH、ID_NET_NAME_MAC等属性值设置完成,可使用udevadm info查看,待后续规则取用;
#/lib/udev/rules.d/75-net-description.rules
ACTION=="remove", GOTO="net_end"
SUBSYSTEM!="net", GOTO="net_end"
IMPORT{builtin}="net_id"
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}"
SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
LABEL="net_end"
#通过udevadm info查询网口信息如下:
#会提前将所有的命名规则名字均定义好,然后在根据不同的规则去取;如下的信息是在规则75-net-de中去配置好的
#已这个网卡为例,会设置好ID_NET_NAME_SLOT、ID_NET_NAME_PATH、ID_NET_NAME_MAC属性值,这里ID_NET_NAME_ONBOARD值缺失;
P: /devices/pci0000:17/0000:17:02.0/0000:19:00.0/net/ens3f0
E: DEVPATH=/devices/pci0000:17/0000:17:02.0/0000:19:00.0/net/ens3f0
E: ID_NET_DRIVER=ixgbe
E: ID_NET_NAME_MAC=enx7057bff9f52c
E: ID_NET_NAME_PATH=enp25s0f0
E: ID_NET_NAME_SLOT=ens3f0
E: INTERFACE=ens3f0
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/ens3f0
- 4、/usr/lib/udev/rules.d/80-net-name-slot.rules 中的规则让 udev 重命名该接口,优先顺序如下:ID_NET_NAME_ONBOARD、ID_NET_NAME_SLOT、ID_NET_NAME_PATH。并提供如下信息:没有在步骤 1 或 2 中重命名该接口,同时未给出内核参数 net.ifnames=0。如果一个参数未设定,则会按列表的顺序设定下一个。如果没有设定任何参数,则不会重命名该接口;
#/usr/lib/udev/rules.d/80-net-name-slot.rules
#按照ID_NET_NAME_ONBOARD、ID_NET_NAME_SLOT、ID_NET_NAME_PATH的顺序依次寻找,如果找到ID_NET_NAME_ONBOARD即用其命名;已上面的网卡信息为例,缺失ID_NET_NAME_ONBOARD属性,依照顺序找到ID_NET_NAME_SLOT=ens3f0,则最终网卡名字命名为ens3f0;
#同时可以将其他的属性值加入到规则中,例如ID_NET_NAME_MAC(默认不会使用该属性值)。或者自己更改优先顺序;
IMPORT{cmdline}="net.ifnames"//net.ifnames命名规则,默认为1,可在cmdline中取配置是否使用该规则,若配置为net.ifnames=0,则不执行该规则;
ENV{net.ifnames}=="0", GOTO="net_name_slot_end"
NAME=="", ENV{ID_NET_NAME_ONBOARD}!="", NAME="$env{ID_NET_NAME_ONBOARD}"#on-board板载网卡
NAME=="", ENV{ID_NET_NAME_SLOT}!="", NAME="$env{ID_NET_NAME_SLOT}"
NAME=="", ENV{ID_NET_NAME_PATH}!="", NAME="$env{ID_NET_NAME_PATH}"
net.ifnames命名规范
格式 | 描述 |
---|---|
o | 板载设备索引号 |
s【f】【d<dev_port>】 | 热插拔插槽索引号 |
x | MAC 地址 |
ps【f】【d<dev_port>】 | 【d<dev_id>】 |
-
所有多功能 PCI 设备都在其设备名称中包含 [f] 号,其中包括 function 0 设备。
具体命名函数为:systemd源码函数dev_pci_slot
“%x:%x:%x.%u”, &domain, &bus, &slot, &func 0000:19:00.1
address:0000:19:00
例如ens3f1是如何识别的:
根据address去遍历/sys/bus/pci/slots寻找该设备的slots号,
[root@localhost slots]# ls
1 10 2 3 9
[root@localhost ~]# ls /sys/bus/pci/slots/3/
address cur_bus_speed max_bus_speed
[root@localhost ~]# cat /sys/bus/pci/slots/3/address
0000:19:00
那么就能确定该网卡的slots为3,同时func为1,因此命名为ens3f1;
//systemd代码
* PCI ethernet card with firmware index "1":
* ID_NET_NAME_ONBOARD=eno1
* ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
*
* PCI ethernet card in hotplug slot with firmware index number:
* /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
* ID_NET_NAME_MAC=enx000000000466
* ID_NET_NAME_PATH=enp5s0
* ID_NET_NAME_SLOT=ens1
*
* PCI ethernet multi-function card with 2 ports://dev_pci_slot
* /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
* ID_NET_NAME_MAC=enx78e7d1ea46da
* ID_NET_NAME_PATH=enp2s0f0
* /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
* ID_NET_NAME_MAC=enx78e7d1ea46dc
* ID_NET_NAME_PATH=enp2s0f1
/* retrieve on-board index number and label from firmware */
static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
const char *index;
int idx;
/* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
/* SMBIOS type 41 -- Onboard Devices Extended Information */
if (!index)
index = udev_device_get_sysattr_value(names->pcidev, "index");
if (!index)
return -ENOENT;
idx = strtoul(index, NULL, 0);
if (idx <= 0)
return -EINVAL;
snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
return 0;
}
后续需要继续确认的流程
1、网卡配置文件是如何产生的?
参考网址
https://blog.youkuaiyun.com/tiantao2012/article/details/76177363
https://vvl.me/2021/06/netdev-name-in-linux/
https://www.codenong.com/p12092731/