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