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