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