udev-builtin-net_id.c revision 16f948cb208f1db9a1665f07ac9b22e416dc19d4
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers/***
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers This file is part of systemd.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers Copyright 2012 Kay Sievers <kay@vrfy.org>
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
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
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
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/>.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers***/
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers/*
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * Predictable network interface device names based on:
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * - firmware/bios-provided index numbers for on-board devices
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * - firmware-provided pci-express hotplug slot index number
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * - physical/geographical location of the hardware
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * - the interface's MAC address
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers *
de892aea1c486b59e04884268b612081d1660514Kay Sievers * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
de892aea1c486b59e04884268b612081d1660514Kay Sievers *
de892aea1c486b59e04884268b612081d1660514Kay Sievers * Two character prefixes based on the type of interface:
de892aea1c486b59e04884268b612081d1660514Kay Sievers * en -- ethernet
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * sl -- serial line IP (slip)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * wl -- wlan
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ww -- wwan
de892aea1c486b59e04884268b612081d1660514Kay Sievers *
de892aea1c486b59e04884268b612081d1660514Kay Sievers * Type of names:
de892aea1c486b59e04884268b612081d1660514Kay Sievers * b<number> -- BCMA bus core number
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ccw<name> -- CCW bus group name
de892aea1c486b59e04884268b612081d1660514Kay Sievers * o<index> -- on-board device index number
de892aea1c486b59e04884268b612081d1660514Kay Sievers * s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
de892aea1c486b59e04884268b612081d1660514Kay Sievers * x<MAC> -- MAC address
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * -- PCI geographical location
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * -- USB port number chain
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers *
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * All multi-function PCI devices will carry the [f<function>] number in the
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * device name, including the function 0 device.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers *
de892aea1c486b59e04884268b612081d1660514Kay Sievers * When using PCI geography, The PCI domain is only prepended when it is not 0.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers *
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * For USB devices the full chain of port numbers of hubs is composed. If the
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * name gets longer than the maximum number of 15 characters, the name is not
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * exported.
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers * The usual USB configuration == 1 and interface == 0 values are suppressed.
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers *
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * PCI ethernet card with firmware index "1":
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_ONBOARD=eno1
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers *
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * PCI ethernet card in hotplug slot with firmware index number:
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers * ID_NET_NAME_MAC=enx000000000466
01d183ddae6fb3445c4519cf1d90c6575f17292eKay Sievers * ID_NET_NAME_PATH=enp5s0
01d183ddae6fb3445c4519cf1d90c6575f17292eKay Sievers * ID_NET_NAME_SLOT=ens1
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers *
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * PCI ethernet multi-function card with 2 ports:
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_MAC=enx78e7d1ea46da
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_PATH=enp2s0f0
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_MAC=enx78e7d1ea46dc
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_PATH=enp2s0f1
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers *
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * PCI wlan card:
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers * ID_NET_NAME_MAC=wlx0024d7e31130
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_PATH=wlp3s0
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers *
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * USB built-in 3G modem:
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_MAC=wwx028037ec0200
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers * ID_NET_NAME_PATH=wwp0s29u1u4i6
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers *
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers * USB Android phone:
de892aea1c486b59e04884268b612081d1660514Kay Sievers * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
de892aea1c486b59e04884268b612081d1660514Kay Sievers * ID_NET_NAME_MAC=enxd626b3450fb5
de892aea1c486b59e04884268b612081d1660514Kay Sievers * ID_NET_NAME_PATH=enp0s29u1u2
de892aea1c486b59e04884268b612081d1660514Kay Sievers */
de892aea1c486b59e04884268b612081d1660514Kay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <stdio.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <stdlib.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <stdarg.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <unistd.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <string.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <errno.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <net/if.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <net/if_arp.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include <linux/pci_regs.h>
de892aea1c486b59e04884268b612081d1660514Kay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include "udev.h"
de892aea1c486b59e04884268b612081d1660514Kay Sievers#include "fileio.h"
de892aea1c486b59e04884268b612081d1660514Kay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sieversenum netname_type{
de892aea1c486b59e04884268b612081d1660514Kay Sievers NET_UNDEF,
de892aea1c486b59e04884268b612081d1660514Kay Sievers NET_PCI,
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers NET_USB,
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers NET_BCMA,
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers NET_VIRTIO,
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers NET_CCWGROUP,
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers};
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversstruct netnames {
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers enum netname_type type;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers uint8_t mac[6];
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers bool mac_valid;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers struct udev_device *pcidev;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers char pci_slot[IFNAMSIZ];
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers char pci_path[IFNAMSIZ];
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers char pci_onboard[IFNAMSIZ];
de892aea1c486b59e04884268b612081d1660514Kay Sievers const char *pci_onboard_label;
de892aea1c486b59e04884268b612081d1660514Kay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sievers char usb_ports[IFNAMSIZ];
de892aea1c486b59e04884268b612081d1660514Kay Sievers char bcma_core[IFNAMSIZ];
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers char ccw_group[IFNAMSIZ];
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers};
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers/* retrieve on-board index number and label from firmware */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversstatic int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers const char *index;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers int idx;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* SMBIOS type 41 -- Onboard Devices Extended Information */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (!index)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers index = udev_device_get_sysattr_value(names->pcidev, "index");
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (!index)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return -ENOENT;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers idx = strtoul(index, NULL, 0);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (idx <= 0)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return -EINVAL;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return 0;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers}
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers/* read the 256 bytes PCI configuration space to check the multi-function bit */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversstatic bool is_pci_multifunction(struct udev_device *dev) {
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers _cleanup_fclose_ FILE *f = NULL;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers const char *filename;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers uint8_t config[64];
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers filename = strjoina(udev_device_get_syspath(dev), "/config");
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers f = fopen(filename, "re");
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers if (!f)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return false;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (fread(&config, sizeof(config), 1, f) != 1)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return false;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* bit 0-6 header type, bit 7 multi/single function device */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if ((config[PCI_HEADER_TYPE] & 0x80) != 0)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return true;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers return false;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers}
de892aea1c486b59e04884268b612081d1660514Kay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sieversstatic int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
de892aea1c486b59e04884268b612081d1660514Kay Sievers struct udev *udev = udev_device_get_udev(names->pcidev);
de892aea1c486b59e04884268b612081d1660514Kay Sievers unsigned domain, bus, slot, func, dev_port = 0;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers size_t l;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers char *s;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers const char *attr;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers struct udev_device *pci = NULL;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers char slots[256], str[256];
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers _cleanup_closedir_ DIR *dir = NULL;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers struct dirent *dent;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers int hotplug_slot = 0, err = 0;
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return -ENOENT;
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers /* kernel provided multi-device index */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers attr = udev_device_get_sysattr_value(dev, "dev_port");
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers if (attr)
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers dev_port = strtol(attr, NULL, 10);
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers /* compose a name based on the raw kernel's PCI bus, slot numbers */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers s = names->pci_path;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = sizeof(names->pci_path);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (domain > 0)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "P%u", domain);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "p%us%u", bus, slot);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (func > 0 || is_pci_multifunction(names->pcidev))
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "f%u", func);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (dev_port > 0)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "d%u", dev_port);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (l == 0)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers names->pci_path[0] = '\0';
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers /* ACPI _SUN -- slot user number */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (!pci) {
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers err = -ENOENT;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers goto out;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers }
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers dir = opendir(slots);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (!dir) {
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers err = -errno;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers goto out;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers }
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers int i;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers char *rest;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers char *address;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers if (dent->d_name[0] == '.')
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers continue;
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers i = strtol(dent->d_name, &rest, 10);
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers if (rest[0] != '\0')
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers continue;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (i < 1)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers continue;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (read_one_line_file(str, &address) >= 0) {
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers /* match slot address with device by stripping the function */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers hotplug_slot = i;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers free(address);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers }
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (hotplug_slot > 0)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers break;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers }
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (hotplug_slot > 0) {
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers s = names->pci_slot;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = sizeof(names->pci_slot);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (domain > 0)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "P%d", domain);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "s%d", hotplug_slot);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (func > 0 || is_pci_multifunction(names->pcidev))
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers l = strpcpyf(&s, l, "f%d", func);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers if (dev_port > 0)
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers l = strpcpyf(&s, l, "d%d", dev_port);
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers if (l == 0)
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers names->pci_slot[0] = '\0';
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers }
a660c63c551b88136ac6176855b5907cc533e848Kay Sieversout:
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers udev_device_unref(pci);
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers return err;
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers}
static int names_pci(struct udev_device *dev, struct netnames *names) {
struct udev_device *parent;
parent = udev_device_get_parent(dev);
if (!parent)
return -ENOENT;
/* check if our direct parent is a PCI device with no other bus in-between */
if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
names->type = NET_PCI;
names->pcidev = parent;
} else {
names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
if (!names->pcidev)
return -ENOENT;
}
dev_pci_onboard(dev, names);
dev_pci_slot(dev, names);
return 0;
}
static int names_usb(struct udev_device *dev, struct netnames *names) {
struct udev_device *usbdev;
char name[256];
char *ports;
char *config;
char *interf;
size_t l;
char *s;
usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
if (!usbdev)
return -ENOENT;
/* get USB port number chain, configuration, interface */
strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
s = strchr(name, '-');
if (!s)
return -EINVAL;
ports = s+1;
s = strchr(ports, ':');
if (!s)
return -EINVAL;
s[0] = '\0';
config = s+1;
s = strchr(config, '.');
if (!s)
return -EINVAL;
s[0] = '\0';
interf = s+1;
/* prefix every port number in the chain with "u" */
s = ports;
while ((s = strchr(s, '.')))
s[0] = 'u';
s = names->usb_ports;
l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
/* append USB config number, suppress the common config == 1 */
if (!streq(config, "1"))
l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
/* append USB interface number, suppress the interface == 0 */
if (!streq(interf, "0"))
l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
if (l == 0)
return -ENAMETOOLONG;
names->type = NET_USB;
return 0;
}
static int names_bcma(struct udev_device *dev, struct netnames *names) {
struct udev_device *bcmadev;
unsigned int core;
bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
if (!bcmadev)
return -ENOENT;
/* bus num:core num */
if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1)
return -EINVAL;
/* suppress the common core == 0 */
if (core > 0)
snprintf(names->bcma_core, sizeof(names->bcma_core), "b%u", core);
names->type = NET_BCMA;
return 0;
}
static int names_ccw(struct udev_device *dev, struct netnames *names) {
struct udev_device *cdev;
const char *bus_id;
size_t bus_id_len;
int rc;
/* Retrieve the associated CCW device */
cdev = udev_device_get_parent(dev);
if (!cdev)
return -ENOENT;
/* Network devices are always grouped CCW devices */
if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev)))
return -ENOENT;
/* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely
* identifies the network device on the Linux on System z channel
* subsystem. Note that the bus-ID contains lowercase characters.
*/
bus_id = udev_device_get_sysname(cdev);
if (!bus_id)
return -ENOENT;
/* Check the length of the bus-ID. Rely on that the kernel provides
* a correct bus-ID; alternatively, improve this check and parse and
* verify each bus-ID part...
*/
bus_id_len = strlen(bus_id);
if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9)
return -EINVAL;
/* Store the CCW bus-ID for use as network device name */
rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "ccw%s", bus_id);
if (rc >= 0 && rc < (int)sizeof(names->ccw_group))
names->type = NET_CCWGROUP;
return 0;
}
static int names_mac(struct udev_device *dev, struct netnames *names) {
const char *s;
unsigned int i;
unsigned int a1, a2, a3, a4, a5, a6;
/* check for NET_ADDR_PERM, skip random MAC addresses */
s = udev_device_get_sysattr_value(dev, "addr_assign_type");
if (!s)
return EXIT_FAILURE;
i = strtoul(s, NULL, 0);
if (i != 0)
return 0;
s = udev_device_get_sysattr_value(dev, "address");
if (!s)
return -ENOENT;
if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
return -EINVAL;
/* skip empty MAC addresses */
if (a1 + a2 + a3 + a4 + a5 + a6 == 0)
return -EINVAL;
names->mac[0] = a1;
names->mac[1] = a2;
names->mac[2] = a3;
names->mac[3] = a4;
names->mac[4] = a5;
names->mac[5] = a6;
names->mac_valid = true;
return 0;
}
/* IEEE Organizationally Unique Identifier vendor string */
static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
char str[32];
if (!names->mac_valid)
return -ENOENT;
/* skip commonly misused 00:00:00 (Xerox) prefix */
if (memcmp(names->mac, "\0\0\0", 3) == 0)
return -EINVAL;
snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
names->mac[0], names->mac[1], names->mac[2],
names->mac[3], names->mac[4], names->mac[5]);
udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
return 0;
}
static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
const char *s;
const char *p;
unsigned int i;
const char *devtype;
const char *prefix = "en";
struct netnames names = {};
int err;
/* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */
s = udev_device_get_sysattr_value(dev, "type");
if (!s)
return EXIT_FAILURE;
i = strtoul(s, NULL, 0);
switch (i) {
case ARPHRD_ETHER:
prefix = "en";
break;
case ARPHRD_SLIP:
prefix = "sl";
break;
default:
return 0;
}
/* skip stacked devices, like VLANs, ... */
s = udev_device_get_sysattr_value(dev, "ifindex");
if (!s)
return EXIT_FAILURE;
p = udev_device_get_sysattr_value(dev, "iflink");
if (!p)
return EXIT_FAILURE;
if (!streq(s, p))
return 0;
devtype = udev_device_get_devtype(dev);
if (devtype) {
if (streq("wlan", devtype))
prefix = "wl";
else if (streq("wwan", devtype))
prefix = "ww";
}
err = names_mac(dev, &names);
if (err >= 0 && names.mac_valid) {
char str[IFNAMSIZ];
snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
names.mac[0], names.mac[1], names.mac[2],
names.mac[3], names.mac[4], names.mac[5]);
udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
ieee_oui(dev, &names, test);
}
/* get path names for Linux on System z network devices */
err = names_ccw(dev, &names);
if (err >= 0 && names.type == NET_CCWGROUP) {
char str[IFNAMSIZ];
if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
goto out;
}
/* get PCI based path names, we compose only PCI based paths */
err = names_pci(dev, &names);
if (err < 0)
goto out;
/* plain PCI device */
if (names.type == NET_PCI) {
char str[IFNAMSIZ];
if (names.pci_onboard[0])
if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
if (names.pci_onboard_label)
if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
if (names.pci_path[0])
if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
if (names.pci_slot[0])
if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
goto out;
}
/* USB device */
err = names_usb(dev, &names);
if (err >= 0 && names.type == NET_USB) {
char str[IFNAMSIZ];
if (names.pci_path[0])
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
if (names.pci_slot[0])
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
goto out;
}
/* Broadcom bus */
err = names_bcma(dev, &names);
if (err >= 0 && names.type == NET_BCMA) {
char str[IFNAMSIZ];
if (names.pci_path[0])
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
if (names.pci_slot[0])
if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
goto out;
}
out:
return EXIT_SUCCESS;
}
const struct udev_builtin udev_builtin_net_id = {
.name = "net_id",
.cmd = builtin_net_id,
.help = "Network device properties",
};