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