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