udev-builtin-net_id.c revision 984c4348ff14d29c526d3d372daa82e278eeb5b4
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers This file is part of systemd.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers Copyright 2012 Kay Sievers <kay@vrfy.org>
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers systemd is free software; you can redistribute it and/or modify it
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers under the terms of the GNU Lesser General Public License as published by
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers the Free Software Foundation; either version 2.1 of the License, or
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers (at your option) any later version.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers systemd is distributed in the hope that it will be useful, but
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers WITHOUT ANY WARRANTY; without even the implied warranty of
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers Lesser General Public License for more details.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers You should have received a copy of the GNU Lesser General Public License
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers along with systemd; If not, see <http://www.gnu.org/licenses/>.
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers * Predictable network interface device names based on:
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers * - firmware/bios-provided index numbers for on-board devices
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers * - firmware-provided pci-express hotplug slot index number
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers * - physical/geographical location of the hardware
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers * - the interface's MAC address
25da63b9dac8f166ebf390ca92d1de18fbfc9d11Kay Sievers * Two character prefixes based on the type of interface:
25da63b9dac8f166ebf390ca92d1de18fbfc9d11Kay Sievers * en -- ethernet
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers * wl -- wlan
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ww -- wwan
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * Type of names:
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * o<index> -- on-board device index number
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * s<slot>[f<function>][d<dev_id>] -- hotplug slot index number
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers * x<MAC> -- MAC address
d4b687c96adf207f0878aebf3ce3371f6160687fKay Sievers * p<bus>s<slot>[f<function>][d<dev_id>] -- PCI geographical location
d4b687c96adf207f0878aebf3ce3371f6160687fKay Sievers * p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen * -- USB port number chain
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers * All multi-function PCI devices will carry the [f<function>] number in the
3058e017fced6d5c8712e10c8c1477421bc1e960Thadeu Lima de Souza Cascardo * device name, including the function 0 device.
214daa72cb0c72ea78d1eccd5ffe630a1e04b2f7Sean McGovern * For USB devices the full chain of port numbers of hubs is composed. If the
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers * name gets longer than the maximum number of 15 characters, the name is not
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers * The usual USB configuration == 1 and interface == 0 values are suppressed.
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * PCI ethernet card with firmware index "1":
214daa72cb0c72ea78d1eccd5ffe630a1e04b2f7Sean McGovern * ID_NET_NAME_ONBOARD=eno1
214daa72cb0c72ea78d1eccd5ffe630a1e04b2f7Sean McGovern * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers * PCI ethernet card in hotplug slot with firmware index number:
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers * ID_NET_NAME_MAC=enx000000000466
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers * ID_NET_NAME_PATH=enp5s0
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * ID_NET_NAME_SLOT=ens1
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * PCI ethernet multi-function card with 2 ports:
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers * ID_NET_NAME_MAC=enx78e7d1ea46da
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * ID_NET_NAME_PATH=enp2s0f0
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * ID_NET_NAME_MAC=enx78e7d1ea46dc
de892aea1c486b59e04884268b612081d1660514Kay Sievers * ID_NET_NAME_PATH=enp2s0f1
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * PCI wlan card:
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * ID_NET_NAME_MAC=wlx0024d7e31130
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * ID_NET_NAME_PATH=wlp3s0
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * USB built-in 3G modem:
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers * ID_NET_NAME_MAC=wwx028037ec0200
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * ID_NET_NAME_PATH=wwp0s29u1u4i6
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * USB Android phone:
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * ID_NET_NAME_MAC=enxd626b3450fb5
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers * ID_NET_NAME_PATH=enp0s29u1u2
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers/* retrieve on-board index number and label from firmware */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sieversstatic int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers const char *index;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* SMBIOS type 41 -- Onboard Devices Extended Information */
d4b687c96adf207f0878aebf3ce3371f6160687fKay Sievers index = udev_device_get_sysattr_value(names->pcidev, "index");
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen/* read the 256 bytes PCI configuration space to check the multi-function bit */
01d183ddae6fb3445c4519cf1d90c6575f17292eKay Sieversstatic bool is_pci_multifunction(struct udev_device *dev) {
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers bool multi = false;
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen snprintf(filename, sizeof(filename), "%s/config", udev_device_get_syspath(dev));
309b578d313b363974b99e48f0e378111cc1fa91Tom Gundersen if (fread(&config, sizeof(config), 1, f) != 1)
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen /* bit 0-6 header type, bit 7 multi/single function device */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sieversstatic int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers struct udev *udev = udev_device_get_udev(names->pcidev);
c0a43734ca84a3c9170d4a2d7f4d329b9ef47abcTom Gundersen unsigned int bus;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers unsigned int slot;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers unsigned int func;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers unsigned int dev_id = 0;
1cb5d1f31909c731d93568eb4838cb86e033d783Lennart Poettering if (sscanf(udev_device_get_sysname(names->pcidev), "0000:%x:%x.%d", &bus, &slot, &func) != 3)
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers /* kernel provided multi-device index */
1cb5d1f31909c731d93568eb4838cb86e033d783Lennart Poettering attr = udev_device_get_sysattr_value(dev, "dev_id");
de892aea1c486b59e04884268b612081d1660514Kay Sievers /* compose a name based on the raw kernel's PCI bus, slot numbers */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers l = strpcpyf(&s, sizeof(names->pci_path), "p%ds%d", bus, slot);
3058e017fced6d5c8712e10c8c1477421bc1e960Thadeu Lima de Souza Cascardo if (func > 0 || is_pci_multifunction(names->pcidev))
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* ACPI _SUN -- slot user number */
b5dd8148730db080b48b874c214f8f74ae787d6bZbigniew Jędrzejewski-Szmek pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
309b578d313b363974b99e48f0e378111cc1fa91Tom Gundersen snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
214daa72cb0c72ea78d1eccd5ffe630a1e04b2f7Sean McGovern for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
1fa2f38f0f011010bf57522b42fcc168856a7003Zbigniew Jędrzejewski-Szmek i = strtol(dent->d_name, &rest, 10);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* match slot address with device by stripping the function */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (strncmp(address, udev_device_get_sysname(names->pcidev), strlen(address)) == 0)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers l = strpcpyf(&s, sizeof(names->pci_slot), "s%d", hotplug_slot);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (func > 0 || is_pci_multifunction(names->pcidev))
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversstatic int names_pci(struct udev_device *dev, struct netnames *names) {
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* check if our direct parent is a PCI device with no other bus in-between */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (streq("pci", udev_device_get_subsystem(parent))) {
214daa72cb0c72ea78d1eccd5ffe630a1e04b2f7Sean McGovern names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sieversstatic int names_usb(struct udev_device *dev, struct netnames *names) {
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
bb26309dd042c79de907f103d83f398b9436cde0Rob Clark /* get USB port number chain, configuration, interface */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers s[0] = '\0';
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers s[0] = '\0';
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* prefix every port number in the chain with "u"*/
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* append USB config number, suppress the common config == 1 */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* append USB interface number, suppress the interface == 0 */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sieversstatic int names_bcma(struct udev_device *dev, struct netnames *names) {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers unsigned int core;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* bus num, core num */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*d:%d", &core) != 1)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers snprintf(names->bcma_core, sizeof(names->bcma_core), "b%d", core);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sieversstatic int names_mac(struct udev_device *dev, struct netnames *names) {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers const char *s;
d5a89d7dc17a5ba5cf4fc71f82963c5c94a31c3dKay Sievers unsigned int i;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* check for NET_ADDR_PERM, skip random MAC addresses */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers s = udev_device_get_sysattr_value(dev, "addr_assign_type");
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers s = udev_device_get_sysattr_value(dev, "address");
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
b5dd8148730db080b48b874c214f8f74ae787d6bZbigniew Jędrzejewski-Szmek /* skip empty MAC addresses */
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner/* IEEE Organizationally Unique Identifier vendor string */
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Bruecknerstatic int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner /* skip commonly misused 00:00:00 (Xerox) prefix */
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner names->mac[0], names->mac[1], names->mac[2],
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner names->mac[3], names->mac[4], names->mac[5]);
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Bruecknerstatic int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner const char *s;
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner const char *p;
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner unsigned int i;
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner /* handle only ARPHRD_ETHER devices */
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner s = udev_device_get_sysattr_value(dev, "type");
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner /* skip stacked devices, like VLANs, ... */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers s = udev_device_get_sysattr_value(dev, "ifindex");
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers p = udev_device_get_sysattr_value(dev, "iflink");
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (strcmp(s, p) != 0)
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* get PCI based path names, we compose only PCI based paths */
971e7fb62548f2c9c4e32684bb13409e6579dc6aKay Sievers /* plain PCI device */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str))
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str))
a4bbef099209d4e3bccd913cd30da536f8971064Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str))
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str))
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers /* USB device */
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str))
19aa72f74e41045510b4af3f1415b419d42ff20bTom Gundersen udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
19aa72f74e41045510b4af3f1415b419d42ff20bTom Gundersen if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
e0d4a0ac06afb856c9370c5c256f0f7bb7efdc8eHendrik Brueckner /* Broadcom bus */
72bc96f07868d532596477604b6fb41633ebd124Kay Sievers if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str))
72bc96f07868d532596477604b6fb41633ebd124Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
72bc96f07868d532596477604b6fb41633ebd124Kay Sievers if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str))
72bc96f07868d532596477604b6fb41633ebd124Kay Sievers udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);