udev-builtin-net_id.c revision 54683f0f9b97a8f88aaf4fbb45b4d729057b101c
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen This file is part of systemd.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Copyright 2012 Kay Sievers <kay@vrfy.org>
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen systemd is free software; you can redistribute it and/or modify it
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen under the terms of the GNU Lesser General Public License as published by
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen (at your option) any later version.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen systemd is distributed in the hope that it will be useful, but
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Lesser General Public License for more details.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen You should have received a copy of the GNU Lesser General Public License
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * Predictable network interface device names based on:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * - firmware/bios-provided index numbers for on-board devices
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * - firmware-provided pci-express hotplug slot index number
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * - physical/geographical location of the hardware
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * - the interface's MAC address
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * Two character prefixes based on the type of interface:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * en -- ethernet
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * sl -- serial line IP (slip)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * Type of names:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * b<number> -- BCMA bus core number
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ccw<name> -- CCW bus group name
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * o<index>[d<dev_port>] -- on-board device index number
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * x<MAC> -- MAC address
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * -- PCI geographical location
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * -- USB port number chain
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * All multi-function PCI devices will carry the [f<function>] number in the
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * device name, including the function 0 device.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * When using PCI geography, The PCI domain is only prepended when it is not 0.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * For USB devices the full chain of port numbers of hubs is composed. If the
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * name gets longer than the maximum number of 15 characters, the name is not
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * The usual USB configuration == 1 and interface == 0 values are suppressed.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * PCI ethernet card with firmware index "1":
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_ONBOARD=eno1
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * PCI ethernet card in hotplug slot with firmware index number:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_MAC=enx000000000466
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_PATH=enp5s0
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_SLOT=ens1
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * PCI ethernet multi-function card with 2 ports:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_MAC=enx78e7d1ea46da
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_PATH=enp2s0f0
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_MAC=enx78e7d1ea46dc
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_PATH=enp2s0f1
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * PCI wlan card:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_MAC=wlx0024d7e31130
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_PATH=wlp3s0
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * USB built-in 3G modem:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_MAC=wwx028037ec0200
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering * ID_NET_NAME_PATH=wwp0s29u1u4i6
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * USB Android phone:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_MAC=enxd626b3450fb5
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * ID_NET_NAME_PATH=enp0s29u1u2
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen/* retrieve on-board index number and label from firmware */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* SMBIOS type 41 -- Onboard Devices Extended Information */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen attr = udev_device_get_sysattr_value(names->pcidev, "index");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* kernel provided port index for multiple ports on a single PCI function */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen attr = udev_device_get_sysattr_value(dev, "dev_port");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen/* read the 256 bytes PCI configuration space to check the multi-function bit */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic bool is_pci_multifunction(struct udev_device *dev) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen filename = strjoina(udev_device_get_syspath(dev), "/config");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return false;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (read(fd, &config, sizeof(config)) != sizeof(config))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return false;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* bit 0-6 header type, bit 7 multi/single function device */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return false;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen struct udev *udev = udev_device_get_udev(names->pcidev);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned domain, bus, slot, func, dev_port = 0;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* kernel provided port index for multiple ports on a single PCI function */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen attr = udev_device_get_sysattr_value(dev, "dev_port");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* compose a name based on the raw kernel's PCI bus, slot numbers */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (func > 0 || is_pci_multifunction(names->pcidev))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* ACPI _SUN -- slot user number */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* match slot address with device by stripping the function */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (func > 0 || is_pci_multifunction(names->pcidev))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int names_pci(struct udev_device *dev, struct netnames *names) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* there can only ever be one virtio bus per parent device, so we can
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen safely ignore any virtio buses. see
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* check if our direct parent is a PCI device with no other bus in-between */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int names_usb(struct udev_device *dev, struct netnames *names) {
7e518afab9fb55b8052f68888210927259275560Thomas Hindoe Paaboel Andersen usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* get USB port number chain, configuration, interface */
1fa2f38f0f011010bf57522b42fcc168856a7003Zbigniew Jędrzejewski-Szmek strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* prefix every port number in the chain with "u" */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* append USB config number, suppress the common config == 1 */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* append USB interface number, suppress the interface == 0 */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int names_bcma(struct udev_device *dev, struct netnames *names) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned int core;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* bus num:core num */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* suppress the common core == 0 */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen snprintf(names->bcma_core, sizeof(names->bcma_core), "b%u", core);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int names_ccw(struct udev_device *dev, struct netnames *names) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Retrieve the associated CCW device */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Network devices are always grouped CCW devices */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev)))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * identifies the network device on the Linux on System z channel
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * subsystem. Note that the bus-ID contains lowercase characters.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Check the length of the bus-ID. Rely on that the kernel provides
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * a correct bus-ID; alternatively, improve this check and parse and
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * verify each bus-ID part...
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Store the CCW bus-ID for use as network device name */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "ccw%s", bus_id);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (rc >= 0 && rc < (int)sizeof(names->ccw_group))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int names_mac(struct udev_device *dev, struct netnames *names) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen const char *s;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned int i;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* check for NET_ADDR_PERM, skip random MAC addresses */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen s = udev_device_get_sysattr_value(dev, "addr_assign_type");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen s = udev_device_get_sysattr_value(dev, "address");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* skip empty MAC addresses */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen/* IEEE Organizationally Unique Identifier vendor string */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* skip commonly misused 00:00:00 (Xerox) prefix */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
const char *devtype;
int err;
return EXIT_FAILURE;
case ARPHRD_ETHER:
case ARPHRD_SLIP:
return EXIT_FAILURE;
return EXIT_FAILURE;
if (!streq(s, p))
if (devtype) {
goto out;
if (err < 0)
goto out;
goto out;
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str))
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
goto out;
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str))
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str))
goto out;
out:
return EXIT_SUCCESS;