X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fudev%2Fudev-builtin-net_id.c;h=803c33fea3bc0c51233436857e4863eb809af6fa;hb=53e1b683907c2f12330f00feb9630150196f064d;hp=e83b8b1c12b7538a0946d06adc269a853f036ffc;hpb=d9a090b9957b04ec34a145a0a40f41abafe73917;p=thirdparty%2Fsystemd.git diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index e83b8b1c12b..803c33fea3b 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -1,5 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -29,21 +28,26 @@ * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames * * Two character prefixes based on the type of interface: - * en -- Ethernet - * sl -- serial line IP (slip) - * wl -- wlan - * ww -- wwan + * en — Ethernet + * sl — serial line IP (slip) + * wl — wlan + * ww — wwan * * Type of names: - * b -- BCMA bus core number - * ccw -- CCW bus group name - * o[d] -- on-board device index number - * s[f][d] -- hotplug slot index number - * x -- MAC address - * [P]ps[f][d] - * -- PCI geographical location + * b — BCMA bus core number + * c — bus id of a grouped CCW or CCW device, + * with all leading zeros stripped [s390] + * o[n|d] + * — on-board device index number + * s[f][n|d] + * — hotplug slot index number + * x — MAC address + * [P]ps[f][n|d] + * — PCI geographical location * [P]ps[f][u][..][c][i] - * -- USB port number chain + * — USB port number chain + * v - VIO slot number (IBM PowerVM) + * ai — Platform bus ACPI instance id * * All multi-function PCI devices will carry the [f] number in the * device name, including the function 0 device. @@ -87,6 +91,11 @@ * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 * ID_NET_NAME_MAC=enxd626b3450fb5 * ID_NET_NAME_PATH=enp0s29u1u2 + * + * s390 grouped CCW interface: + * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 + * ID_NET_NAME_MAC=enx026d3c00000a + * ID_NET_NAME_PATH=encf5f0 */ #include @@ -100,19 +109,24 @@ #include #include +#include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "stdio-util.h" #include "string-util.h" #include "udev.h" +#define ONBOARD_INDEX_MAX (16*1024-1) + enum netname_type{ NET_UNDEF, NET_PCI, NET_USB, NET_BCMA, NET_VIRTIO, - NET_CCWGROUP, + NET_CCW, + NET_VIO, + NET_PLATFORM, }; struct netnames { @@ -129,20 +143,34 @@ struct netnames { char usb_ports[IFNAMSIZ]; char bcma_core[IFNAMSIZ]; - char ccw_group[IFNAMSIZ]; + char ccw_busid[IFNAMSIZ]; + char vio_slot[IFNAMSIZ]; + char platform_path[IFNAMSIZ]; }; +/* skip intermediate virtio devices */ +static struct udev_device *skip_virtio(struct udev_device *dev) { + struct udev_device *parent = dev; + + /* there can only ever be one virtio bus per parent device, so we can + safely ignore any virtio buses. see + */ + while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) + parent = udev_device_get_parent(parent); + return parent; +} + /* retrieve on-board index number and label from firmware */ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { unsigned dev_port = 0; size_t l; char *s; - const char *attr; + const char *attr, *port_name; int idx; - /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */ + /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index"); - /* SMBIOS type 41 -- Onboard Devices Extended Information */ + /* SMBIOS type 41 — Onboard Devices Extended Information */ if (!attr) attr = udev_device_get_sysattr_value(names->pcidev, "index"); if (!attr) @@ -152,15 +180,27 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { if (idx <= 0) return -EINVAL; + /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for + * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary + * cut-off, which is somewhere beyond the realistic number of physical network interface a system might + * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */ + if (idx > ONBOARD_INDEX_MAX) + return -ENOENT; + /* kernel provided port index for multiple ports on a single PCI function */ attr = udev_device_get_sysattr_value(dev, "dev_port"); if (attr) dev_port = strtol(attr, NULL, 10); + /* kernel provided front panel port name for multiple port PCI device */ + port_name = udev_device_get_sysattr_value(dev, "phys_port_name"); + s = names->pci_onboard; l = sizeof(names->pci_onboard); l = strpcpyf(&s, l, "o%d", idx); - if (dev_port > 0) + if (port_name) + l = strpcpyf(&s, l, "n%s", port_name); + else if (dev_port > 0) l = strpcpyf(&s, l, "d%d", dev_port); if (l == 0) names->pci_onboard[0] = '\0'; @@ -195,9 +235,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { unsigned domain, bus, slot, func, dev_port = 0; size_t l; char *s; - const char *attr; + const char *attr, *port_name; struct udev_device *pci = NULL; - char slots[256], str[256]; + char slots[PATH_MAX]; _cleanup_closedir_ DIR *dir = NULL; struct dirent *dent; int hotplug_slot = 0, err = 0; @@ -210,6 +250,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { if (attr) dev_port = strtol(attr, NULL, 10); + /* kernel provided front panel port name for multiple port PCI device */ + port_name = udev_device_get_sysattr_value(dev, "phys_port_name"); + /* compose a name based on the raw kernel's PCI bus, slot numbers */ s = names->pci_path; l = sizeof(names->pci_path); @@ -218,28 +261,30 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { l = strpcpyf(&s, l, "p%us%u", bus, slot); if (func > 0 || is_pci_multifunction(names->pcidev)) l = strpcpyf(&s, l, "f%u", func); - if (dev_port > 0) + if (port_name) + l = strpcpyf(&s, l, "n%s", port_name); + else if (dev_port > 0) l = strpcpyf(&s, l, "d%u", dev_port); if (l == 0) names->pci_path[0] = '\0'; - /* ACPI _SUN -- slot user number */ + /* ACPI _SUN — slot user number */ pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); if (!pci) { err = -ENOENT; goto out; } - xsprintf(slots, "%s/slots", udev_device_get_syspath(pci)); + + snprintf(slots, sizeof slots, "%s/slots", udev_device_get_syspath(pci)); dir = opendir(slots); if (!dir) { err = -errno; goto out; } - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + FOREACH_DIRENT_ALL(dent, dir, break) { int i; - char *rest; - char *address; + char *rest, *address, str[PATH_MAX]; if (dent->d_name[0] == '.') continue; @@ -248,7 +293,8 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { continue; if (i < 1) continue; - xsprintf(str, "%s/%s/address", slots, dent->d_name); + + snprintf(str, sizeof str, "%s/%s/address", slots, dent->d_name); if (read_one_line_file(str, &address) >= 0) { /* match slot address with device by stripping the function */ if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address))) @@ -268,7 +314,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { l = strpcpyf(&s, l, "s%d", hotplug_slot); if (func > 0 || is_pci_multifunction(names->pcidev)) l = strpcpyf(&s, l, "f%d", func); - if (dev_port > 0) + if (port_name) + l = strpcpyf(&s, l, "n%s", port_name); + else if (dev_port > 0) l = strpcpyf(&s, l, "d%d", dev_port); if (l == 0) names->pci_slot[0] = '\0'; @@ -278,6 +326,87 @@ out: return err; } +static int names_vio(struct udev_device *dev, struct netnames *names) { + struct udev_device *parent; + unsigned busid, slotid, ethid; + const char *syspath; + + /* check if our direct parent is a VIO device with no other bus in-between */ + parent = udev_device_get_parent(dev); + if (!parent) + return -ENOENT; + + if (!streq_ptr("vio", udev_device_get_subsystem(parent))) + return -ENOENT; + + /* The devices' $DEVPATH number is tied to (virtual) hardware (slot id + * selected in the HMC), thus this provides a reliable naming (e.g. + * "/devices/vio/30000002/net/eth1"); we ignore the bus number, as + * there should only ever be one bus, and then remove leading zeros. */ + syspath = udev_device_get_syspath(dev); + + if (sscanf(syspath, "/sys/devices/vio/%4x%4x/net/eth%u", &busid, &slotid, ðid) != 3) + return -EINVAL; + + xsprintf(names->vio_slot, "v%u", slotid); + names->type = NET_VIO; + return 0; +} + +#define _PLATFORM_TEST "/sys/devices/platform/vvvvPPPP" +#define _PLATFORM_PATTERN4 "/sys/devices/platform/%4s%4x:%2x/net/eth%u" +#define _PLATFORM_PATTERN3 "/sys/devices/platform/%3s%4x:%2x/net/eth%u" + +static int names_platform(struct udev_device *dev, struct netnames *names, bool test) { + struct udev_device *parent; + char vendor[5]; + unsigned model, instance, ethid; + const char *syspath, *pattern, *validchars; + + /* check if our direct parent is a platform device with no other bus in-between */ + parent = udev_device_get_parent(dev); + if (!parent) + return -ENOENT; + + if (!streq_ptr("platform", udev_device_get_subsystem(parent))) + return -ENOENT; + + syspath = udev_device_get_syspath(dev); + + /* syspath is too short, to have a valid ACPI instance */ + if (strlen(syspath) < sizeof _PLATFORM_TEST) + return -EINVAL; + + /* Vendor ID can be either PNP ID (3 chars A-Z) or ACPI ID (4 chars A-Z and numerals) */ + if (syspath[sizeof _PLATFORM_TEST - 1] == ':') { + pattern = _PLATFORM_PATTERN4; + validchars = UPPERCASE_LETTERS DIGITS; + } else { + pattern = _PLATFORM_PATTERN3; + validchars = UPPERCASE_LETTERS; + } + + /* Platform devices are named after ACPI table match, and instance id + * eg. "/sys/devices/platform/HISI00C2:00"); + * The Vendor (3 or 4 char), followed by hexdecimal model number : instance id. + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + if (sscanf(syspath, pattern, vendor, &model, &instance, ðid) != 4) + return -EINVAL; +#pragma GCC diagnostic pop + + if (!in_charset(vendor, validchars)) + return -ENOENT; + + ascii_strlower(vendor); + + xsprintf(names->platform_path, "a%s%xi%u", vendor, model, instance); + names->type = NET_PLATFORM; + return 0; +} + static int names_pci(struct udev_device *dev, struct netnames *names) { struct udev_device *parent; @@ -285,12 +414,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) { assert(names); parent = udev_device_get_parent(dev); - - /* there can only ever be one virtio bus per parent device, so we can - safely ignore any virtio buses. see - */ - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); + /* skip virtio subsystem if present */ + parent = skip_virtio(parent); if (!parent) return -ENOENT; @@ -389,8 +514,9 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) { static int names_ccw(struct udev_device *dev, struct netnames *names) { struct udev_device *cdev; - const char *bus_id; + const char *bus_id, *subsys; size_t bus_id_len; + size_t bus_id_start; int rc; assert(dev); @@ -398,14 +524,17 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) { /* Retrieve the associated CCW device */ cdev = udev_device_get_parent(dev); + /* skip virtio subsystem if present */ + cdev = skip_virtio(cdev); if (!cdev) return -ENOENT; - /* Network devices are always grouped CCW devices */ - if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev))) + /* Network devices are either single or grouped CCW devices */ + subsys = udev_device_get_subsystem(cdev); + if (!STRPTR_IN_SET(subsys, "ccwgroup", "ccw")) return -ENOENT; - /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely + /* Retrieve bus-ID of the 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. */ @@ -421,10 +550,18 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) { if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9) return -EINVAL; + /* Strip leading zeros from the bus id for aesthetic purposes. This + * keeps the ccw names stable, yet much shorter in general case of + * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is + * not prepended when it is zero. Preserve the last 0 for 0.0.0000. + */ + bus_id_start = strspn(bus_id, ".0"); + bus_id += bus_id_start < bus_id_len ? bus_id_start : bus_id_len - 1; + /* 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; + rc = snprintf(names->ccw_busid, sizeof(names->ccw_busid), "c%s", bus_id); + if (rc >= 0 && rc < (int)sizeof(names->ccw_busid)) + names->type = NET_CCW; return 0; } @@ -534,10 +671,30 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool /* get path names for Linux on System z network devices */ err = names_ccw(dev, &names); - if (err >= 0 && names.type == NET_CCWGROUP) { + if (err >= 0 && names.type == NET_CCW) { + char str[IFNAMSIZ]; + + if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_busid) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); + goto out; + } + + /* get ibmveth/ibmvnic slot-based names. */ + err = names_vio(dev, &names); + if (err >= 0 && names.type == NET_VIO) { + char str[IFNAMSIZ]; + + if (snprintf(str, sizeof(str), "%s%s", prefix, names.vio_slot) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); + goto out; + } + + /* get ACPI path names for ARM64 platform devices */ + err = names_platform(dev, &names, test); + if (err >= 0 && names.type == NET_PLATFORM) { char str[IFNAMSIZ]; - if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str)) + if (snprintf(str, sizeof(str), "%s%s", prefix, names.platform_path) < (int)sizeof(str)) udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); goto out; }