воскресенье, 22 ноября 2015 г.

Имя сетевого устройства

Старое название интерфейсов, такое как к примеру eth0, кануло в лету (ну почти, иногда старые имена можно встретить). В RHEL7 используются следующие варианты схем наименования сетевого интерфейса, в порядке применения:
  1. Имя, предоставляемое аппаратным обеспечением (Firmware или BIOS), включающее порядковый номер, для устройств, расположенных на материнской плате, к примеру eno1. Если выбор имени не произведен, используется схема 2.
  2. Имя, предоставляемое аппаратным обеспечением (Firmware или BIOS), включающее порядковый номер слота PCI Express, к примеру ens1. Если выбор не произведен, используется схема 3.
  3. Имя формируется из физического положения точки подключения устройства, к примеру enp2s0. Иначе используется схема 5.
  4. Имя включает MAC устройства, к примеру enx94de80a44f0c. Эта схема по умолчанию не используется.
  5. Старая схема, где имя присваивается ядром в порядке обнаружения интерфейса, к примеру eth0 и т.д.
Это поведение по умолчанию.

Новые имена сами по себе содержат полезную информацию. Принятое соглашение о наименовании следующее: первые два символа отражают тип интерфейса:
  1. en — Ethernet.
  2. wl — беспроводная локальная сеть, WLAN.
  3. ww — беспроводная глобальная сеть, WWAN.
Далее следует тип, отражающий схему наименования, описанную выше:
  1. o<index> индекс для устройств расположенных на материнской плате.
  2. s<slot>[f<function>][d<dev_id>] сначала идет номер PCI Express слота, далее f<function> - номер функции у многофункционального PCI устройства (я не встречал таких плат) и последним идет идентификатор устройства.
  3. p<bus>s<slot>[f<function>][d<dev_id>] расположение устройства на PCI шине. Домен PCI (p<bus>) указывается только если он отличен от нулевого (это можно увидеть в больших системах). Номер функции и номер устройства аналогично предыдущему примеру.
  4. p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>] расположение устройства на USB шине, причем учитывается вся цепочка портов.
  5. x<MAC> MAC адрес.
Нужно отметить, что имя не может быть более 15 символов, это ограничение ядра.

В случае использования VLAN добавляется следующее соглашение о имени интерфейса:
  1. vlan<vlanid> — имя с полным номером vlanid, к примеру vlan0012 или имя с сокращенным vlanid — vlan12
  2. device_name.<vlanid> - имя интерфейса и VLAN ID в полном или сокращенном виде, к примеру ens3.0012 или ens3.12
Как бы глупо это не звучало, но помимо описанных соглашений о наименовании, есть еще одно, используемое при помощи утилиты udev — biosdevname (насколько я понял, возникло это соглашение по инициативе DELL и используется на их серверах). Оно имеет силу только при наличии установленного пакета biosdevname и при указании biosdevname=1 при загрузке системы. Переименования будут производится только для встроенных интерфейсов. При этом:
  1. Встроенные интерфейсы будут иметь имя типа em[123...]
  2. Интерфейсы, подключенные по шине PCI — p<slot>p<ethernet port>, к примеру p3p4
Чтобы окончательно не запутаться в теоретической части, рассмотрим более глубоко процесс присваивания имени интерфейсу по шагам. Ниже пример того что происходит, когда в системе появляется новый интерфейс:

# udevadm monitor
...
KERNEL[27783.869667] add /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.4/1-7.4:1.0/net/eth0 (net)
...
KERNEL[27783.879674] move /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.4/1-7.4:1.0/net/enp0s20u7u4 (net)
UDEV [27783.892038] add /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.4/1-7.4:1.0/net/enp0s20u7u4 (net)
...

Т.е. сначала интерфейс появился со старым, знакомым именем eth0, а далее стал enp0s20u7u4. Нетрудно заметить, что новое имя сразу отображает что это адаптер Ethernet (en), подключенный к USB шине через usb хаб (в имени два usb порта u7u4). Достаточно удобно.

Посмотрим, что выполняет udev над интерфейсом:

  • Шаг первый:
# udevadm test /sys/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.4/1-7.4:1.0/net/enp0s20u7u4
...
PROGRAM '/lib/udev/rename_device' /usr/lib/udev/rules.d/60-net.rules:1
starting '/lib/udev/rename_device'

Что происходит: правило udev /usr/lib/udev/rules.d/60-net.rules при помощи /lib/udev/rename_device, просматривает конфигурационные файлы сетевых интерфейсов - /etc/sysconfig/network-scripts/ifcfg-*, и если есть соответствие MAC адреса добавляемого интерфейса и адреса в директиве HWADDR, то имя интерфейса устанавливается в соответствии с директивой DEVICE.
Сейчас воспользуемся небольшой хитростью, чтобы процесс применения правил udev был более наглядным. Создадим конфигурационный файл ifcfg-tstif0 с именем tstif0 для нашего адаптера и вставим адаптер. Интерфейс в системе станет tstif0. Теперь удалим конфигурационный файл, и посмотрим на обработку правил еще раз, но уже для интерфейса tstif0, в конечном итоге мы должны будем получить имя enp0s20u7u4.
Запустим еще раз, первое правило не отработает, т.к. конфигурационный файл мы удалили:

PROGRAM '/lib/udev/rename_device' /usr/lib/udev/rules.d/60-net.rules:1
starting '/lib/udev/rename_device'
'/lib/udev/rename_device' [5023] exit with return code 0

  • Шаг второй, смотрим дальше:
PROGRAM '/sbin/biosdevname --policy physical -i tstif0' /usr/lib/udev/rules.d/71-biosdevname.rules:22
starting '/sbin/biosdevname --policy physical -i tstif0'
'/sbin/biosdevname --policy physical -i tstif0' [5024] exit with return code 2

Правило /usr/lib/udev/rules.d/71-biosdevname.rules реализует выше рассмотренное соглашение о именах при использовании biosdevname. Уточнение, это правило работает, если в системе установлен biosdevname, при загрузке не был использован параметр biosdevname=0, и если имя не было назначено на предыдущем шаге.

  • Шаг третий:
IMPORT builtin 'net_id' /usr/lib/udev/rules.d/75-net-description.rules:6
IMPORT builtin skip 'usb_id' /usr/lib/udev/rules.d/75-net-description.rules:8
IMPORT builtin 'hwdb' /usr/lib/udev/rules.d/75-net-description.rules:8

На этом этапе правилом /usr/lib/udev/rules.d/75-net-description.rules, заполняются следующие директивы описания устройства: ID_NET_NAME_ONBOARD, ID_NET_NAME_SLOT, ID_NET_NAME_PATH и ID_NET_NAME_MAC. Не все директивы могут быть заполнены, поскольку не все устройства могут иметь требуемую информацию.

  • Шаг четвертый:
IMPORT builtin 'path_id' /usr/lib/udev/rules.d/80-net-setup-link.rules:5
IMPORT builtin 'net_setup_link' /usr/lib/udev/rules.d/80-net-setup-link.rules:9
Config file /usr/lib/systemd/network/99-default.link applies to device tstif0
NAME 'enp0s20u7u4' /usr/lib/udev/rules.d/80-net-setup-link.rules:11

Вот оно правило, которое устанавливает имя для интерфейса в данном случае - /usr/lib/udev/rules.d/80-net-setup-link.rules. Что по этому правило происходит: если интерфейс не был переименован на предыдущих шагах, и если не задан параметр ядра net.ifnames=0, то имя интерфейса устанавливается в соответствии с порядком по приоритету из полученных ранее директив: ID_NET_NAME_ONBOARD, ID_NET_NAME_SLOT, ID_NET_NAME_PATH. Если ни одна из директив не определена, то интерфейс остается не переименованным, т.е. старый вариант именования — eth0.
Для понимания, посмотреть возможные имена можно так:

# udevadm info /sys/class/net/tstif0 | grep ID_NET_NAME
E: ID_NET_NAME=enp0s20u7u4
E: ID_NET_NAME_MAC=enx00101402bca7
E: ID_NET_NAME_PATH=enp0s20u7u4

Можно заметить, что почти все пункты (за исключением 5 — старого именования) из стандартного соглашения о наименовании реализуются в правилах udev: 75-net-description.rules и 80-net-setup-link.rules. Можно возразить, а где наименование по MAC адресам? Включение его можно произвести правкой правила 80-net-setup-link.rules. Скопируйте его:

# cp /usr/lib/udev/rules.d/80-net-name-slot.rules /etc/udev/rules.d

И добавьте строку выделенную курсивом на листинге:

# grep -v "^#\|^$" /etc/udev/rules.d/80-net-name-slot.rules
ACTION!="add", GOTO="net_name_slot_end"
SUBSYSTEM!="net", GOTO="net_name_slot_end"
NAME!="", GOTO="net_name_slot_end"
IMPORT{cmdline}="net.ifnames"
ENV{net.ifnames}=="0", GOTO="net_name_slot_end"
NAME=="", ENV{ID_NET_NAME_MAC}!="", NAME="$env{ID_NET_NAME_MAC}"
NAME=="", ENV{ID_NET_NAME_ONBOARD}!="", NAME="$env{ID_NET_NAME_ONBOARD}"
NAME=="", ENV{ID_NET_NAME_SLOT}!="", NAME="$env{ID_NET_NAME_SLOT}"
NAME=="", ENV{ID_NET_NAME_PATH}!="", NAME="$env{ID_NET_NAME_PATH}"
LABEL="net_name_slot_end"

Не уверен, что данный вариант именования удобен.

Итак, мы обсудили несколько соглашений о наименовании сетевых интерфейсов, рассмотрели по шагам, как происходит выбор имени, попутно узнали, что net.ifnames=0 отключает новую схему наименования, и последнее замечание: не используйте имена из пространства ядра - ethX в схеме с присвоением имен через файлы ifcfg-*.