]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udev-builtin-net_id.c
udev: drop unused udev struct
[thirdparty/systemd.git] / src / udev / udev-builtin-net_id.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a660c63c 2
d23965a6 3/*
ad37f393 4 * Predictable network interface device names based on:
472780d8
KS
5 * - firmware/bios-provided index numbers for on-board devices
6 * - firmware-provided pci-express hotplug slot index number
7 * - physical/geographical location of the hardware
8 * - the interface's MAC address
9 *
25da63b9
KS
10 * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
11 *
ad37f393 12 * Two character prefixes based on the type of interface:
ccddd104
ZJS
13 * en — Ethernet
14 * sl — serial line IP (slip)
15 * wl — wlan
16 * ww — wwan
d23965a6 17 *
ad37f393 18 * Type of names:
ccddd104 19 * b<number> — BCMA bus core number
ecc11cf7
VM
20 * c<bus_id> — bus id of a grouped CCW or CCW device,
21 * with all leading zeros stripped [s390]
4887b656
JP
22 * o<index>[n<phys_port_name>|d<dev_port>]
23 * — on-board device index number
24 * s<slot>[f<function>][n<phys_port_name>|d<dev_port>]
25 * — hotplug slot index number
ccddd104 26 * x<MAC> — MAC address
4887b656 27 * [P<domain>]p<bus>s<slot>[f<function>][n<phys_port_name>|d<dev_port>]
ccddd104 28 * — PCI geographical location
214daa72 29 * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
ccddd104 30 * — USB port number chain
765a00b9 31 * v<slot> - VIO slot number (IBM PowerVM)
c20e6de8 32 * a<vendor><model>i<instance> — Platform bus ACPI instance id
472780d8 33 *
ad37f393 34 * All multi-function PCI devices will carry the [f<function>] number in the
472780d8 35 * device name, including the function 0 device.
d23965a6 36 *
eefe36e6
EG
37 * SR-IOV virtual devices are named based on the name of the parent interface,
38 * with a suffix of "v<N>", where <N> is the virtual device number.
39 *
214daa72
SM
40 * When using PCI geography, The PCI domain is only prepended when it is not 0.
41 *
0d6ce923
KS
42 * For USB devices the full chain of port numbers of hubs is composed. If the
43 * name gets longer than the maximum number of 15 characters, the name is not
44 * exported.
45 * The usual USB configuration == 1 and interface == 0 values are suppressed.
ad37f393 46 *
a8eaaee7 47 * PCI Ethernet card with firmware index "1":
0035597a 48 * ID_NET_NAME_ONBOARD=eno1
f610d6de
KS
49 * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
50 *
a8eaaee7 51 * PCI Ethernet card in hotplug slot with firmware index number:
f610d6de
KS
52 * /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
53 * ID_NET_NAME_MAC=enx000000000466
54 * ID_NET_NAME_PATH=enp5s0
de892aea 55 * ID_NET_NAME_SLOT=ens1
f610d6de 56 *
a8eaaee7 57 * PCI Ethernet multi-function card with 2 ports:
decd634e
KS
58 * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
59 * ID_NET_NAME_MAC=enx78e7d1ea46da
60 * ID_NET_NAME_PATH=enp2s0f0
61 * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
62 * ID_NET_NAME_MAC=enx78e7d1ea46dc
63 * ID_NET_NAME_PATH=enp2s0f1
64 *
f610d6de
KS
65 * PCI wlan card:
66 * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
67 * ID_NET_NAME_MAC=wlx0024d7e31130
68 * ID_NET_NAME_PATH=wlp3s0
69 *
70 * USB built-in 3G modem:
71 * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
72 * ID_NET_NAME_MAC=wwx028037ec0200
73 * ID_NET_NAME_PATH=wwp0s29u1u4i6
74 *
75 * USB Android phone:
76 * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
77 * ID_NET_NAME_MAC=enxd626b3450fb5
78 * ID_NET_NAME_PATH=enp0s29u1u2
ecc11cf7
VM
79 *
80 * s390 grouped CCW interface:
81 * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0
82 * ID_NET_NAME_MAC=enx026d3c00000a
83 * ID_NET_NAME_PATH=encf5f0
d23965a6
KS
84 */
85
a660c63c 86#include <errno.h>
07630cea 87#include <fcntl.h>
02609440 88#include <net/if.h>
19aa72f7 89#include <net/if_arp.h>
07630cea
LP
90#include <stdarg.h>
91#include <stdio.h>
92#include <stdlib.h>
93#include <string.h>
94#include <unistd.h>
de892aea 95#include <linux/pci_regs.h>
a660c63c 96
8fb3f009 97#include "dirent-util.h"
3ffd4af2 98#include "fd-util.h"
a5c32cff 99#include "fileio.h"
609948c7 100#include "fs-util.h"
9009d3b5 101#include "parse-util.h"
d054f0a4 102#include "stdio-util.h"
07630cea
LP
103#include "string-util.h"
104#include "udev.h"
a660c63c 105
6c1e69f9
LP
106#define ONBOARD_INDEX_MAX (16*1024-1)
107
02609440
KS
108enum netname_type{
109 NET_UNDEF,
110 NET_PCI,
111 NET_USB,
984c4348 112 NET_BCMA,
e3d56334 113 NET_VIRTIO,
ecc11cf7 114 NET_CCW,
765a00b9 115 NET_VIO,
c20e6de8 116 NET_PLATFORM,
02609440
KS
117};
118
119struct netnames {
120 enum netname_type type;
121
122 uint8_t mac[6];
123 bool mac_valid;
124
125 struct udev_device *pcidev;
126 char pci_slot[IFNAMSIZ];
127 char pci_path[IFNAMSIZ];
128 char pci_onboard[IFNAMSIZ];
129 const char *pci_onboard_label;
130
02609440 131 char usb_ports[IFNAMSIZ];
984c4348 132 char bcma_core[IFNAMSIZ];
ecc11cf7 133 char ccw_busid[IFNAMSIZ];
765a00b9 134 char vio_slot[IFNAMSIZ];
c20e6de8 135 char platform_path[IFNAMSIZ];
02609440
KS
136};
137
609948c7
SH
138struct virtfn_info {
139 struct udev_device *physfn_pcidev;
140 char suffix[IFNAMSIZ];
141};
142
ecc11cf7
VM
143/* skip intermediate virtio devices */
144static struct udev_device *skip_virtio(struct udev_device *dev) {
145 struct udev_device *parent = dev;
146
147 /* there can only ever be one virtio bus per parent device, so we can
148 safely ignore any virtio buses. see
149 <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
150 while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
151 parent = udev_device_get_parent(parent);
152 return parent;
153}
154
609948c7 155static int get_virtfn_info(struct udev_device *dev, struct netnames *names, struct virtfn_info *vf_info) {
609948c7
SH
156 const char *physfn_link_file;
157 _cleanup_free_ char *physfn_pci_syspath = NULL;
158 _cleanup_free_ char *virtfn_pci_syspath = NULL;
159 struct dirent *dent;
160 _cleanup_closedir_ DIR *dir = NULL;
161 struct virtfn_info vf_info_local = {};
162 int r;
163
609948c7
SH
164 /* Check if this is a virtual function. */
165 physfn_link_file = strjoina(udev_device_get_syspath(names->pcidev), "/physfn");
166 r = chase_symlinks(physfn_link_file, NULL, 0, &physfn_pci_syspath);
167 if (r < 0)
168 return r;
169
170 /* Get physical function's pci device. */
2024ed61 171 vf_info_local.physfn_pcidev = udev_device_new_from_syspath(NULL, physfn_pci_syspath);
609948c7
SH
172 if (!vf_info_local.physfn_pcidev)
173 return -ENOENT;
174
175 /* Find the virtual function number by finding the right virtfn link. */
176 dir = opendir(physfn_pci_syspath);
177 if (!dir) {
178 r = -errno;
179 goto out_unref;
180 }
181 FOREACH_DIRENT_ALL(dent, dir, break) {
182 _cleanup_free_ char *virtfn_link_file = NULL;
183 if (!startswith(dent->d_name, "virtfn"))
184 continue;
185 virtfn_link_file = strjoin(physfn_pci_syspath, "/", dent->d_name);
186 if (!virtfn_link_file) {
187 r = -ENOMEM;
188 goto out_unref;
189 }
190 if (chase_symlinks(virtfn_link_file, NULL, 0, &virtfn_pci_syspath) < 0)
191 continue;
192 if (streq(udev_device_get_syspath(names->pcidev), virtfn_pci_syspath)) {
193 if (!snprintf_ok(vf_info_local.suffix, sizeof(vf_info_local.suffix), "v%s", &dent->d_name[6])) {
194 r = -ENOENT;
195 goto out_unref;
196 }
197 break;
198 }
199 }
200 if (isempty(vf_info_local.suffix)) {
201 r = -ENOENT;
202 goto out_unref;
203 }
204 *vf_info = vf_info_local;
205 return 0;
206
207out_unref:
208 udev_device_unref(vf_info_local.physfn_pcidev);
209 return r;
210}
211
0035597a 212/* retrieve on-board index number and label from firmware */
02609440 213static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
c0a43734
TG
214 unsigned dev_port = 0;
215 size_t l;
216 char *s;
4887b656 217 const char *attr, *port_name;
0035597a 218 int idx;
a660c63c 219
ccddd104 220 /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
c0a43734 221 attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
ccddd104 222 /* SMBIOS type 41 — Onboard Devices Extended Information */
c0a43734
TG
223 if (!attr)
224 attr = udev_device_get_sysattr_value(names->pcidev, "index");
225 if (!attr)
0035597a 226 return -ENOENT;
c0a43734
TG
227
228 idx = strtoul(attr, NULL, 0);
0035597a
KS
229 if (idx <= 0)
230 return -EINVAL;
c0a43734 231
6c1e69f9
LP
232 /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for
233 * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary
234 * cut-off, which is somewhere beyond the realistic number of physical network interface a system might
235 * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */
236 if (idx > ONBOARD_INDEX_MAX)
237 return -ENOENT;
238
309b578d 239 /* kernel provided port index for multiple ports on a single PCI function */
c0a43734
TG
240 attr = udev_device_get_sysattr_value(dev, "dev_port");
241 if (attr)
242 dev_port = strtol(attr, NULL, 10);
243
4887b656
JP
244 /* kernel provided front panel port name for multiple port PCI device */
245 port_name = udev_device_get_sysattr_value(dev, "phys_port_name");
246
c0a43734
TG
247 s = names->pci_onboard;
248 l = sizeof(names->pci_onboard);
249 l = strpcpyf(&s, l, "o%d", idx);
4887b656
JP
250 if (port_name)
251 l = strpcpyf(&s, l, "n%s", port_name);
252 else if (dev_port > 0)
c0a43734
TG
253 l = strpcpyf(&s, l, "d%d", dev_port);
254 if (l == 0)
255 names->pci_onboard[0] = '\0';
d23965a6 256
02609440 257 names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
c0a43734 258
0035597a
KS
259 return 0;
260}
d23965a6 261
137661d8 262/* read the 256 bytes PCI configuration space to check the multi-function bit */
1328f66a 263static bool is_pci_multifunction(struct udev_device *dev) {
0454229c 264 _cleanup_close_ int fd = -1;
1cb5d1f3
LP
265 const char *filename;
266 uint8_t config[64];
de892aea 267
63c372cb 268 filename = strjoina(udev_device_get_syspath(dev), "/config");
0454229c
JM
269 fd = open(filename, O_RDONLY | O_CLOEXEC);
270 if (fd < 0)
1cb5d1f3 271 return false;
0454229c 272 if (read(fd, &config, sizeof(config)) != sizeof(config))
1cb5d1f3 273 return false;
de892aea
KS
274
275 /* bit 0-6 header type, bit 7 multi/single function device */
1328f66a 276 if ((config[PCI_HEADER_TYPE] & 0x80) != 0)
1cb5d1f3
LP
277 return true;
278
279 return false;
de892aea
KS
280}
281
6bc04997 282static bool is_pci_ari_enabled(struct udev_device *dev) {
fb702dd7 283 return streq_ptr(udev_device_get_sysattr_value(dev, "ari_enabled"), "1");
6bc04997
SH
284}
285
02609440 286static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
9009d3b5 287 unsigned domain, bus, slot, func, dev_port = 0, hotplug_slot = 0;
1328f66a
KS
288 size_t l;
289 char *s;
4887b656 290 const char *attr, *port_name;
8e766630 291 _cleanup_(udev_device_unrefp) struct udev_device *pci = NULL;
9009d3b5 292 struct udev_device *hotplug_slot_dev;
e68eedbb 293 char slots[PATH_MAX];
b5dd8148 294 _cleanup_closedir_ DIR *dir = NULL;
0035597a 295 struct dirent *dent;
0035597a 296
b5dd8148 297 if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
0035597a 298 return -ENOENT;
6bc04997
SH
299 if (is_pci_ari_enabled(names->pcidev))
300 /* ARI devices support up to 256 functions on a single device ("slot"), and interpret the
301 * traditional 5-bit slot and 3-bit function number as a single 8-bit function number,
302 * where the slot makes up the upper 5 bits. */
303 func += slot * 8;
1328f66a 304
309b578d 305 /* kernel provided port index for multiple ports on a single PCI function */
3058e017 306 attr = udev_device_get_sysattr_value(dev, "dev_port");
1328f66a 307 if (attr)
3058e017 308 dev_port = strtol(attr, NULL, 10);
1328f66a 309
4887b656
JP
310 /* kernel provided front panel port name for multiple port PCI device */
311 port_name = udev_device_get_sysattr_value(dev, "phys_port_name");
312
1328f66a
KS
313 /* compose a name based on the raw kernel's PCI bus, slot numbers */
314 s = names->pci_path;
214daa72
SM
315 l = sizeof(names->pci_path);
316 if (domain > 0)
1fa2f38f
ZJS
317 l = strpcpyf(&s, l, "P%u", domain);
318 l = strpcpyf(&s, l, "p%us%u", bus, slot);
1328f66a 319 if (func > 0 || is_pci_multifunction(names->pcidev))
1fa2f38f 320 l = strpcpyf(&s, l, "f%u", func);
4887b656
JP
321 if (port_name)
322 l = strpcpyf(&s, l, "n%s", port_name);
323 else if (dev_port > 0)
1fa2f38f 324 l = strpcpyf(&s, l, "d%u", dev_port);
1328f66a
KS
325 if (l == 0)
326 names->pci_path[0] = '\0';
d23965a6 327
ccddd104 328 /* ACPI _SUN — slot user number */
2024ed61 329 pci = udev_device_new_from_subsystem_sysname(NULL, "subsystem", "pci");
cc5bbdb2
ZJS
330 if (!pci)
331 return -ENOENT;
e68eedbb 332
73fc96c8
ZJS
333 if (!snprintf_ok(slots, sizeof slots, "%s/slots", udev_device_get_syspath(pci)))
334 return -ENAMETOOLONG;
335
0035597a 336 dir = opendir(slots);
cc5bbdb2
ZJS
337 if (!dir)
338 return -errno;
d23965a6 339
9009d3b5
SH
340 hotplug_slot_dev = names->pcidev;
341 while (hotplug_slot_dev) {
342 FOREACH_DIRENT_ALL(dent, dir, break) {
343 unsigned i;
344 int r;
345 char str[PATH_MAX];
346 _cleanup_free_ char *address = NULL;
347
348 if (dent->d_name[0] == '.')
349 continue;
350 r = safe_atou_full(dent->d_name, 10, &i);
351 if (i < 1 || r < 0)
352 continue;
353
354 if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) &&
355 read_one_line_file(str, &address) >= 0)
356 /* match slot address with device by stripping the function */
357 if (startswith(udev_device_get_sysname(hotplug_slot_dev), address))
358 hotplug_slot = i;
359
360 if (hotplug_slot > 0)
361 break;
362 }
0035597a
KS
363 if (hotplug_slot > 0)
364 break;
9009d3b5
SH
365 rewinddir(dir);
366 hotplug_slot_dev = udev_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL);
0035597a 367 }
d23965a6 368
0035597a 369 if (hotplug_slot > 0) {
1328f66a 370 s = names->pci_slot;
214daa72
SM
371 l = sizeof(names->pci_slot);
372 if (domain > 0)
373 l = strpcpyf(&s, l, "P%d", domain);
374 l = strpcpyf(&s, l, "s%d", hotplug_slot);
1328f66a 375 if (func > 0 || is_pci_multifunction(names->pcidev))
d5a89d7d 376 l = strpcpyf(&s, l, "f%d", func);
4887b656
JP
377 if (port_name)
378 l = strpcpyf(&s, l, "n%s", port_name);
379 else if (dev_port > 0)
3058e017 380 l = strpcpyf(&s, l, "d%d", dev_port);
1328f66a 381 if (l == 0)
16f948cb 382 names->pci_slot[0] = '\0';
d23965a6 383 }
cc5bbdb2
ZJS
384
385 return 0;
0035597a
KS
386}
387
765a00b9
FB
388static int names_vio(struct udev_device *dev, struct netnames *names) {
389 struct udev_device *parent;
390 unsigned busid, slotid, ethid;
391 const char *syspath;
392
393 /* check if our direct parent is a VIO device with no other bus in-between */
394 parent = udev_device_get_parent(dev);
395 if (!parent)
396 return -ENOENT;
397
398 if (!streq_ptr("vio", udev_device_get_subsystem(parent)))
399 return -ENOENT;
400
401 /* The devices' $DEVPATH number is tied to (virtual) hardware (slot id
402 * selected in the HMC), thus this provides a reliable naming (e.g.
403 * "/devices/vio/30000002/net/eth1"); we ignore the bus number, as
404 * there should only ever be one bus, and then remove leading zeros. */
405 syspath = udev_device_get_syspath(dev);
406
407 if (sscanf(syspath, "/sys/devices/vio/%4x%4x/net/eth%u", &busid, &slotid, &ethid) != 3)
408 return -EINVAL;
409
410 xsprintf(names->vio_slot, "v%u", slotid);
411 names->type = NET_VIO;
412 return 0;
413}
414
c20e6de8
DJL
415#define _PLATFORM_TEST "/sys/devices/platform/vvvvPPPP"
416#define _PLATFORM_PATTERN4 "/sys/devices/platform/%4s%4x:%2x/net/eth%u"
417#define _PLATFORM_PATTERN3 "/sys/devices/platform/%3s%4x:%2x/net/eth%u"
418
419static int names_platform(struct udev_device *dev, struct netnames *names, bool test) {
420 struct udev_device *parent;
421 char vendor[5];
422 unsigned model, instance, ethid;
423 const char *syspath, *pattern, *validchars;
424
425 /* check if our direct parent is a platform device with no other bus in-between */
426 parent = udev_device_get_parent(dev);
427 if (!parent)
428 return -ENOENT;
429
430 if (!streq_ptr("platform", udev_device_get_subsystem(parent)))
431 return -ENOENT;
432
433 syspath = udev_device_get_syspath(dev);
434
435 /* syspath is too short, to have a valid ACPI instance */
436 if (strlen(syspath) < sizeof _PLATFORM_TEST)
437 return -EINVAL;
438
439 /* Vendor ID can be either PNP ID (3 chars A-Z) or ACPI ID (4 chars A-Z and numerals) */
440 if (syspath[sizeof _PLATFORM_TEST - 1] == ':') {
441 pattern = _PLATFORM_PATTERN4;
442 validchars = UPPERCASE_LETTERS DIGITS;
443 } else {
444 pattern = _PLATFORM_PATTERN3;
445 validchars = UPPERCASE_LETTERS;
446 }
447
448 /* Platform devices are named after ACPI table match, and instance id
449 * eg. "/sys/devices/platform/HISI00C2:00");
450 * The Vendor (3 or 4 char), followed by hexdecimal model number : instance id.
451 */
86a48fb6
LP
452
453#pragma GCC diagnostic push
454#pragma GCC diagnostic ignored "-Wformat-nonliteral"
c20e6de8
DJL
455 if (sscanf(syspath, pattern, vendor, &model, &instance, &ethid) != 4)
456 return -EINVAL;
86a48fb6 457#pragma GCC diagnostic pop
c20e6de8
DJL
458
459 if (!in_charset(vendor, validchars))
460 return -ENOENT;
461
462 ascii_strlower(vendor);
463
464 xsprintf(names->platform_path, "a%s%xi%u", vendor, model, instance);
465 names->type = NET_PLATFORM;
466 return 0;
467}
468
02609440 469static int names_pci(struct udev_device *dev, struct netnames *names) {
5b8180d3 470 struct udev_device *parent;
609948c7
SH
471 struct netnames vf_names = {};
472 struct virtfn_info vf_info = {};
0035597a 473
3b64e4d4
TG
474 assert(dev);
475 assert(names);
476
5b8180d3 477 parent = udev_device_get_parent(dev);
ecc11cf7
VM
478 /* skip virtio subsystem if present */
479 parent = skip_virtio(parent);
54683f0f 480
02609440
KS
481 if (!parent)
482 return -ENOENT;
54683f0f 483
02609440 484 /* check if our direct parent is a PCI device with no other bus in-between */
bb26309d 485 if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
02609440
KS
486 names->type = NET_PCI;
487 names->pcidev = parent;
488 } else {
489 names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
490 if (!names->pcidev)
491 return -ENOENT;
492 }
609948c7
SH
493
494 if (get_virtfn_info(dev, names, &vf_info) >= 0) {
495 /* If this is an SR-IOV virtual device, get base name using physical device and add virtfn suffix. */
496 vf_names.pcidev = vf_info.physfn_pcidev;
497 dev_pci_onboard(dev, &vf_names);
498 dev_pci_slot(dev, &vf_names);
499 if (vf_names.pci_onboard[0])
500 if (strlen(vf_names.pci_onboard) + strlen(vf_info.suffix) < sizeof(names->pci_onboard))
501 strscpyl(names->pci_onboard, sizeof(names->pci_onboard),
502 vf_names.pci_onboard, vf_info.suffix, NULL);
503 if (vf_names.pci_slot[0])
504 if (strlen(vf_names.pci_slot) + strlen(vf_info.suffix) < sizeof(names->pci_slot))
505 strscpyl(names->pci_slot, sizeof(names->pci_slot),
506 vf_names.pci_slot, vf_info.suffix, NULL);
507 if (vf_names.pci_path[0])
508 if (strlen(vf_names.pci_path) + strlen(vf_info.suffix) < sizeof(names->pci_path))
509 strscpyl(names->pci_path, sizeof(names->pci_path),
510 vf_names.pci_path, vf_info.suffix, NULL);
511 udev_device_unref(vf_info.physfn_pcidev);
512 } else {
513 dev_pci_onboard(dev, names);
514 dev_pci_slot(dev, names);
515 }
02609440
KS
516 return 0;
517}
518
519static int names_usb(struct udev_device *dev, struct netnames *names) {
984c4348 520 struct udev_device *usbdev;
02609440
KS
521 char name[256];
522 char *ports;
523 char *config;
524 char *interf;
525 size_t l;
526 char *s;
527
3b64e4d4
TG
528 assert(dev);
529 assert(names);
530
984c4348
KS
531 usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
532 if (!usbdev)
0035597a
KS
533 return -ENOENT;
534
02609440 535 /* get USB port number chain, configuration, interface */
984c4348 536 strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
02609440
KS
537 s = strchr(name, '-');
538 if (!s)
539 return -EINVAL;
540 ports = s+1;
541
542 s = strchr(ports, ':');
543 if (!s)
544 return -EINVAL;
545 s[0] = '\0';
546 config = s+1;
547
548 s = strchr(config, '.');
549 if (!s)
550 return -EINVAL;
551 s[0] = '\0';
552 interf = s+1;
553
f7340ab2 554 /* prefix every port number in the chain with "u" */
02609440
KS
555 s = ports;
556 while ((s = strchr(s, '.')))
557 s[0] = 'u';
558 s = names->usb_ports;
d5a89d7d 559 l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
02609440
KS
560
561 /* append USB config number, suppress the common config == 1 */
562 if (!streq(config, "1"))
d5a89d7d 563 l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
02609440
KS
564
565 /* append USB interface number, suppress the interface == 0 */
566 if (!streq(interf, "0"))
d5a89d7d 567 l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
02609440
KS
568 if (l == 0)
569 return -ENAMETOOLONG;
570
571 names->type = NET_USB;
d23965a6
KS
572 return 0;
573}
574
984c4348
KS
575static int names_bcma(struct udev_device *dev, struct netnames *names) {
576 struct udev_device *bcmadev;
577 unsigned int core;
578
3b64e4d4
TG
579 assert(dev);
580 assert(names);
581
984c4348
KS
582 bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
583 if (!bcmadev)
584 return -ENOENT;
585
f4ddacbd 586 /* bus num:core num */
b5dd8148 587 if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1)
984c4348 588 return -EINVAL;
f4ddacbd
KS
589 /* suppress the common core == 0 */
590 if (core > 0)
d054f0a4 591 xsprintf(names->bcma_core, "b%u", core);
f4ddacbd 592
984c4348
KS
593 names->type = NET_BCMA;
594 return 0;
595}
596
e0d4a0ac
HB
597static int names_ccw(struct udev_device *dev, struct netnames *names) {
598 struct udev_device *cdev;
3877500c 599 const char *bus_id, *subsys;
e0d4a0ac 600 size_t bus_id_len;
ecc11cf7 601 size_t bus_id_start;
e0d4a0ac 602
3b64e4d4
TG
603 assert(dev);
604 assert(names);
605
e0d4a0ac
HB
606 /* Retrieve the associated CCW device */
607 cdev = udev_device_get_parent(dev);
ecc11cf7
VM
608 /* skip virtio subsystem if present */
609 cdev = skip_virtio(cdev);
e0d4a0ac
HB
610 if (!cdev)
611 return -ENOENT;
612
ecc11cf7
VM
613 /* Network devices are either single or grouped CCW devices */
614 subsys = udev_device_get_subsystem(cdev);
615 if (!STRPTR_IN_SET(subsys, "ccwgroup", "ccw"))
e0d4a0ac
HB
616 return -ENOENT;
617
ecc11cf7 618 /* Retrieve bus-ID of the CCW device. The bus-ID uniquely
e0d4a0ac
HB
619 * identifies the network device on the Linux on System z channel
620 * subsystem. Note that the bus-ID contains lowercase characters.
621 */
622 bus_id = udev_device_get_sysname(cdev);
623 if (!bus_id)
624 return -ENOENT;
625
626 /* Check the length of the bus-ID. Rely on that the kernel provides
627 * a correct bus-ID; alternatively, improve this check and parse and
628 * verify each bus-ID part...
629 */
630 bus_id_len = strlen(bus_id);
977f65f0 631 if (!IN_SET(bus_id_len, 8, 9))
e0d4a0ac
HB
632 return -EINVAL;
633
0037a669
DJL
634 /* Strip leading zeros from the bus id for aesthetic purposes. This
635 * keeps the ccw names stable, yet much shorter in general case of
636 * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is
ecc11cf7 637 * not prepended when it is zero. Preserve the last 0 for 0.0.0000.
0037a669 638 */
ecc11cf7
VM
639 bus_id_start = strspn(bus_id, ".0");
640 bus_id += bus_id_start < bus_id_len ? bus_id_start : bus_id_len - 1;
0037a669 641
e0d4a0ac 642 /* Store the CCW bus-ID for use as network device name */
73fc96c8 643 if (snprintf_ok(names->ccw_busid, sizeof(names->ccw_busid), "c%s", bus_id))
ecc11cf7 644 names->type = NET_CCW;
73fc96c8 645
e0d4a0ac
HB
646 return 0;
647}
648
02609440 649static int names_mac(struct udev_device *dev, struct netnames *names) {
d23965a6
KS
650 const char *s;
651 unsigned int i;
652 unsigned int a1, a2, a3, a4, a5, a6;
d23965a6
KS
653
654 /* check for NET_ADDR_PERM, skip random MAC addresses */
655 s = udev_device_get_sysattr_value(dev, "addr_assign_type");
656 if (!s)
657 return EXIT_FAILURE;
658 i = strtoul(s, NULL, 0);
659 if (i != 0)
660 return 0;
661
662 s = udev_device_get_sysattr_value(dev, "address");
663 if (!s)
664 return -ENOENT;
665 if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
666 return -EINVAL;
667
668 /* skip empty MAC addresses */
669 if (a1 + a2 + a3 + a4 + a5 + a6 == 0)
a660c63c
KS
670 return -EINVAL;
671
02609440
KS
672 names->mac[0] = a1;
673 names->mac[1] = a2;
674 names->mac[2] = a3;
675 names->mac[3] = a4;
676 names->mac[4] = a5;
677 names->mac[5] = a6;
678 names->mac_valid = true;
679 return 0;
680}
d23965a6 681
02609440
KS
682/* IEEE Organizationally Unique Identifier vendor string */
683static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
971e7fb6 684 char str[32];
02609440 685
971e7fb6 686 if (!names->mac_valid)
02609440
KS
687 return -ENOENT;
688 /* skip commonly misused 00:00:00 (Xerox) prefix */
689 if (memcmp(names->mac, "\0\0\0", 3) == 0)
690 return -EINVAL;
d054f0a4
DM
691 xsprintf(str, "OUI:%02X%02X%02X%02X%02X%02X", names->mac[0],
692 names->mac[1], names->mac[2], names->mac[3], names->mac[4],
693 names->mac[5]);
a4bbef09 694 udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
02609440 695 return 0;
a660c63c
KS
696}
697
698static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
d23965a6 699 const char *s;
72bc96f0 700 const char *p;
d23965a6
KS
701 unsigned int i;
702 const char *devtype;
703 const char *prefix = "en";
b92bea5d 704 struct netnames names = {};
02609440 705 int err;
d23965a6 706
e0d4a0ac 707 /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */
d23965a6
KS
708 s = udev_device_get_sysattr_value(dev, "type");
709 if (!s)
710 return EXIT_FAILURE;
711 i = strtoul(s, NULL, 0);
e0d4a0ac 712 switch (i) {
19aa72f7 713 case ARPHRD_ETHER:
e0d4a0ac
HB
714 prefix = "en";
715 break;
19aa72f7 716 case ARPHRD_SLIP:
e0d4a0ac
HB
717 prefix = "sl";
718 break;
719 default:
d23965a6 720 return 0;
e0d4a0ac 721 }
d23965a6 722
72bc96f0
KS
723 /* skip stacked devices, like VLANs, ... */
724 s = udev_device_get_sysattr_value(dev, "ifindex");
725 if (!s)
726 return EXIT_FAILURE;
727 p = udev_device_get_sysattr_value(dev, "iflink");
728 if (!p)
729 return EXIT_FAILURE;
090be865 730 if (!streq(s, p))
72bc96f0
KS
731 return 0;
732
d23965a6
KS
733 devtype = udev_device_get_devtype(dev);
734 if (devtype) {
735 if (streq("wlan", devtype))
736 prefix = "wl";
737 else if (streq("wwan", devtype))
738 prefix = "ww";
739 }
740
02609440
KS
741 err = names_mac(dev, &names);
742 if (err >= 0 && names.mac_valid) {
743 char str[IFNAMSIZ];
744
d054f0a4 745 xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix,
02609440
KS
746 names.mac[0], names.mac[1], names.mac[2],
747 names.mac[3], names.mac[4], names.mac[5]);
748 udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
749
750 ieee_oui(dev, &names, test);
751 }
752
e0d4a0ac
HB
753 /* get path names for Linux on System z network devices */
754 err = names_ccw(dev, &names);
ecc11cf7 755 if (err >= 0 && names.type == NET_CCW) {
e0d4a0ac
HB
756 char str[IFNAMSIZ];
757
73fc96c8 758 if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.ccw_busid))
e0d4a0ac
HB
759 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
760 goto out;
761 }
762
765a00b9
FB
763 /* get ibmveth/ibmvnic slot-based names. */
764 err = names_vio(dev, &names);
765 if (err >= 0 && names.type == NET_VIO) {
766 char str[IFNAMSIZ];
767
73fc96c8 768 if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.vio_slot))
765a00b9
FB
769 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
770 goto out;
771 }
772
c20e6de8
DJL
773 /* get ACPI path names for ARM64 platform devices */
774 err = names_platform(dev, &names, test);
775 if (err >= 0 && names.type == NET_PLATFORM) {
776 char str[IFNAMSIZ];
777
73fc96c8 778 if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.platform_path))
c20e6de8
DJL
779 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
780 goto out;
781 }
782
02609440
KS
783 /* get PCI based path names, we compose only PCI based paths */
784 err = names_pci(dev, &names);
785 if (err < 0)
786 goto out;
787
788 /* plain PCI device */
789 if (names.type == NET_PCI) {
790 char str[IFNAMSIZ];
791
73fc96c8
ZJS
792 if (names.pci_onboard[0] &&
793 snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_onboard))
794 udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
02609440 795
73fc96c8
ZJS
796 if (names.pci_onboard_label &&
797 snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_onboard_label))
798 udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
02609440 799
73fc96c8
ZJS
800 if (names.pci_path[0] &&
801 snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_path))
802 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
02609440 803
73fc96c8
ZJS
804 if (names.pci_slot[0] &&
805 snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_slot))
806 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
02609440
KS
807 goto out;
808 }
809
810 /* USB device */
811 err = names_usb(dev, &names);
812 if (err >= 0 && names.type == NET_USB) {
813 char str[IFNAMSIZ];
814
73fc96c8
ZJS
815 if (names.pci_path[0] &&
816 snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_path, names.usb_ports))
817 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
02609440 818
73fc96c8
ZJS
819 if (names.pci_slot[0] &&
820 snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.usb_ports))
821 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
984c4348
KS
822 goto out;
823 }
824
825 /* Broadcom bus */
826 err = names_bcma(dev, &names);
827 if (err >= 0 && names.type == NET_BCMA) {
828 char str[IFNAMSIZ];
829
73fc96c8
ZJS
830 if (names.pci_path[0] &&
831 snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_path, names.bcma_core))
832 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
984c4348 833
73fc96c8
ZJS
834 if (names.pci_slot[0] &&
835 snprintf(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.bcma_core))
836 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
984c4348 837 goto out;
02609440
KS
838 }
839out:
a660c63c
KS
840 return EXIT_SUCCESS;
841}
842
843const struct udev_builtin udev_builtin_net_id = {
844 .name = "net_id",
845 .cmd = builtin_net_id,
5ac0162c 846 .help = "Network device properties",
a660c63c 847};