1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Kay Sievers <kay@vrfy.org>
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.
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.
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/>.
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
28 * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
30 * Two character prefixes based on the type of interface:
32 * sl — serial line IP (slip)
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
52 * All multi-function PCI devices will carry the [f<function>] number in the
53 * device name, including the function 0 device.
55 * When using PCI geography, The PCI domain is only prepended when it is not 0.
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
60 * The usual USB configuration == 1 and interface == 0 values are suppressed.
62 * PCI Ethernet card with firmware index "1":
63 * ID_NET_NAME_ONBOARD=eno1
64 * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
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
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
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
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
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
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
104 #include <net/if_arp.h>
110 #include <linux/pci_regs.h>
112 #include "dirent-util.h"
115 #include "stdio-util.h"
116 #include "string-util.h"
118 #include "udev-util.h"
120 #define ONBOARD_INDEX_MAX (16*1024-1)
134 enum netname_type type
;
139 struct udev_device
*pcidev
;
140 char pci_slot
[IFNAMSIZ
];
141 char pci_path
[IFNAMSIZ
];
142 char pci_onboard
[IFNAMSIZ
];
143 const char *pci_onboard_label
;
145 char usb_ports
[IFNAMSIZ
];
146 char bcma_core
[IFNAMSIZ
];
147 char ccw_busid
[IFNAMSIZ
];
148 char vio_slot
[IFNAMSIZ
];
149 char platform_path
[IFNAMSIZ
];
152 /* skip intermediate virtio devices */
153 static struct udev_device
*skip_virtio(struct udev_device
*dev
) {
154 struct udev_device
*parent
= dev
;
156 /* there can only ever be one virtio bus per parent device, so we can
157 safely ignore any virtio buses. see
158 <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
159 while (parent
&& streq_ptr("virtio", udev_device_get_subsystem(parent
)))
160 parent
= udev_device_get_parent(parent
);
164 /* retrieve on-board index number and label from firmware */
165 static int dev_pci_onboard(struct udev_device
*dev
, struct netnames
*names
) {
166 unsigned dev_port
= 0;
169 const char *attr
, *port_name
;
172 /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
173 attr
= udev_device_get_sysattr_value(names
->pcidev
, "acpi_index");
174 /* SMBIOS type 41 — Onboard Devices Extended Information */
176 attr
= udev_device_get_sysattr_value(names
->pcidev
, "index");
180 idx
= strtoul(attr
, NULL
, 0);
184 /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for
185 * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary
186 * cut-off, which is somewhere beyond the realistic number of physical network interface a system might
187 * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */
188 if (idx
> ONBOARD_INDEX_MAX
)
191 /* kernel provided port index for multiple ports on a single PCI function */
192 attr
= udev_device_get_sysattr_value(dev
, "dev_port");
194 dev_port
= strtol(attr
, NULL
, 10);
196 /* kernel provided front panel port name for multiple port PCI device */
197 port_name
= udev_device_get_sysattr_value(dev
, "phys_port_name");
199 s
= names
->pci_onboard
;
200 l
= sizeof(names
->pci_onboard
);
201 l
= strpcpyf(&s
, l
, "o%d", idx
);
203 l
= strpcpyf(&s
, l
, "n%s", port_name
);
204 else if (dev_port
> 0)
205 l
= strpcpyf(&s
, l
, "d%d", dev_port
);
207 names
->pci_onboard
[0] = '\0';
209 names
->pci_onboard_label
= udev_device_get_sysattr_value(names
->pcidev
, "label");
214 /* read the 256 bytes PCI configuration space to check the multi-function bit */
215 static bool is_pci_multifunction(struct udev_device
*dev
) {
216 _cleanup_close_
int fd
= -1;
217 const char *filename
;
220 filename
= strjoina(udev_device_get_syspath(dev
), "/config");
221 fd
= open(filename
, O_RDONLY
| O_CLOEXEC
);
224 if (read(fd
, &config
, sizeof(config
)) != sizeof(config
))
227 /* bit 0-6 header type, bit 7 multi/single function device */
228 if ((config
[PCI_HEADER_TYPE
] & 0x80) != 0)
234 static int dev_pci_slot(struct udev_device
*dev
, struct netnames
*names
) {
235 struct udev
*udev
= udev_device_get_udev(names
->pcidev
);
236 unsigned domain
, bus
, slot
, func
, dev_port
= 0;
239 const char *attr
, *port_name
;
240 _cleanup_udev_device_unref_
struct udev_device
*pci
= NULL
;
241 char slots
[PATH_MAX
];
242 _cleanup_closedir_
DIR *dir
= NULL
;
244 int hotplug_slot
= 0;
246 if (sscanf(udev_device_get_sysname(names
->pcidev
), "%x:%x:%x.%u", &domain
, &bus
, &slot
, &func
) != 4)
249 /* kernel provided port index for multiple ports on a single PCI function */
250 attr
= udev_device_get_sysattr_value(dev
, "dev_port");
252 dev_port
= strtol(attr
, NULL
, 10);
254 /* kernel provided front panel port name for multiple port PCI device */
255 port_name
= udev_device_get_sysattr_value(dev
, "phys_port_name");
257 /* compose a name based on the raw kernel's PCI bus, slot numbers */
259 l
= sizeof(names
->pci_path
);
261 l
= strpcpyf(&s
, l
, "P%u", domain
);
262 l
= strpcpyf(&s
, l
, "p%us%u", bus
, slot
);
263 if (func
> 0 || is_pci_multifunction(names
->pcidev
))
264 l
= strpcpyf(&s
, l
, "f%u", func
);
266 l
= strpcpyf(&s
, l
, "n%s", port_name
);
267 else if (dev_port
> 0)
268 l
= strpcpyf(&s
, l
, "d%u", dev_port
);
270 names
->pci_path
[0] = '\0';
272 /* ACPI _SUN — slot user number */
273 pci
= udev_device_new_from_subsystem_sysname(udev
, "subsystem", "pci");
277 if (!snprintf_ok(slots
, sizeof slots
, "%s/slots", udev_device_get_syspath(pci
)))
278 return -ENAMETOOLONG
;
280 dir
= opendir(slots
);
284 FOREACH_DIRENT_ALL(dent
, dir
, break) {
286 char *rest
, str
[PATH_MAX
];
287 _cleanup_free_
char *address
= NULL
;
289 if (dent
->d_name
[0] == '.')
291 i
= strtol(dent
->d_name
, &rest
, 10);
297 if (snprintf_ok(str
, sizeof str
, "%s/%s/address", slots
, dent
->d_name
) &&
298 read_one_line_file(str
, &address
) >= 0)
299 /* match slot address with device by stripping the function */
300 if (startswith(udev_device_get_sysname(names
->pcidev
), address
))
303 if (hotplug_slot
> 0)
307 if (hotplug_slot
> 0) {
309 l
= sizeof(names
->pci_slot
);
311 l
= strpcpyf(&s
, l
, "P%d", domain
);
312 l
= strpcpyf(&s
, l
, "s%d", hotplug_slot
);
313 if (func
> 0 || is_pci_multifunction(names
->pcidev
))
314 l
= strpcpyf(&s
, l
, "f%d", func
);
316 l
= strpcpyf(&s
, l
, "n%s", port_name
);
317 else if (dev_port
> 0)
318 l
= strpcpyf(&s
, l
, "d%d", dev_port
);
320 names
->pci_slot
[0] = '\0';
326 static int names_vio(struct udev_device
*dev
, struct netnames
*names
) {
327 struct udev_device
*parent
;
328 unsigned busid
, slotid
, ethid
;
331 /* check if our direct parent is a VIO device with no other bus in-between */
332 parent
= udev_device_get_parent(dev
);
336 if (!streq_ptr("vio", udev_device_get_subsystem(parent
)))
339 /* The devices' $DEVPATH number is tied to (virtual) hardware (slot id
340 * selected in the HMC), thus this provides a reliable naming (e.g.
341 * "/devices/vio/30000002/net/eth1"); we ignore the bus number, as
342 * there should only ever be one bus, and then remove leading zeros. */
343 syspath
= udev_device_get_syspath(dev
);
345 if (sscanf(syspath
, "/sys/devices/vio/%4x%4x/net/eth%u", &busid
, &slotid
, ðid
) != 3)
348 xsprintf(names
->vio_slot
, "v%u", slotid
);
349 names
->type
= NET_VIO
;
353 #define _PLATFORM_TEST "/sys/devices/platform/vvvvPPPP"
354 #define _PLATFORM_PATTERN4 "/sys/devices/platform/%4s%4x:%2x/net/eth%u"
355 #define _PLATFORM_PATTERN3 "/sys/devices/platform/%3s%4x:%2x/net/eth%u"
357 static int names_platform(struct udev_device
*dev
, struct netnames
*names
, bool test
) {
358 struct udev_device
*parent
;
360 unsigned model
, instance
, ethid
;
361 const char *syspath
, *pattern
, *validchars
;
363 /* check if our direct parent is a platform device with no other bus in-between */
364 parent
= udev_device_get_parent(dev
);
368 if (!streq_ptr("platform", udev_device_get_subsystem(parent
)))
371 syspath
= udev_device_get_syspath(dev
);
373 /* syspath is too short, to have a valid ACPI instance */
374 if (strlen(syspath
) < sizeof _PLATFORM_TEST
)
377 /* Vendor ID can be either PNP ID (3 chars A-Z) or ACPI ID (4 chars A-Z and numerals) */
378 if (syspath
[sizeof _PLATFORM_TEST
- 1] == ':') {
379 pattern
= _PLATFORM_PATTERN4
;
380 validchars
= UPPERCASE_LETTERS DIGITS
;
382 pattern
= _PLATFORM_PATTERN3
;
383 validchars
= UPPERCASE_LETTERS
;
386 /* Platform devices are named after ACPI table match, and instance id
387 * eg. "/sys/devices/platform/HISI00C2:00");
388 * The Vendor (3 or 4 char), followed by hexdecimal model number : instance id.
391 #pragma GCC diagnostic push
392 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
393 if (sscanf(syspath
, pattern
, vendor
, &model
, &instance
, ðid
) != 4)
395 #pragma GCC diagnostic pop
397 if (!in_charset(vendor
, validchars
))
400 ascii_strlower(vendor
);
402 xsprintf(names
->platform_path
, "a%s%xi%u", vendor
, model
, instance
);
403 names
->type
= NET_PLATFORM
;
407 static int names_pci(struct udev_device
*dev
, struct netnames
*names
) {
408 struct udev_device
*parent
;
413 parent
= udev_device_get_parent(dev
);
414 /* skip virtio subsystem if present */
415 parent
= skip_virtio(parent
);
420 /* check if our direct parent is a PCI device with no other bus in-between */
421 if (streq_ptr("pci", udev_device_get_subsystem(parent
))) {
422 names
->type
= NET_PCI
;
423 names
->pcidev
= parent
;
425 names
->pcidev
= udev_device_get_parent_with_subsystem_devtype(dev
, "pci", NULL
);
429 dev_pci_onboard(dev
, names
);
430 dev_pci_slot(dev
, names
);
434 static int names_usb(struct udev_device
*dev
, struct netnames
*names
) {
435 struct udev_device
*usbdev
;
446 usbdev
= udev_device_get_parent_with_subsystem_devtype(dev
, "usb", "usb_interface");
450 /* get USB port number chain, configuration, interface */
451 strscpy(name
, sizeof(name
), udev_device_get_sysname(usbdev
));
452 s
= strchr(name
, '-');
457 s
= strchr(ports
, ':');
463 s
= strchr(config
, '.');
469 /* prefix every port number in the chain with "u" */
471 while ((s
= strchr(s
, '.')))
473 s
= names
->usb_ports
;
474 l
= strpcpyl(&s
, sizeof(names
->usb_ports
), "u", ports
, NULL
);
476 /* append USB config number, suppress the common config == 1 */
477 if (!streq(config
, "1"))
478 l
= strpcpyl(&s
, sizeof(names
->usb_ports
), "c", config
, NULL
);
480 /* append USB interface number, suppress the interface == 0 */
481 if (!streq(interf
, "0"))
482 l
= strpcpyl(&s
, sizeof(names
->usb_ports
), "i", interf
, NULL
);
484 return -ENAMETOOLONG
;
486 names
->type
= NET_USB
;
490 static int names_bcma(struct udev_device
*dev
, struct netnames
*names
) {
491 struct udev_device
*bcmadev
;
497 bcmadev
= udev_device_get_parent_with_subsystem_devtype(dev
, "bcma", NULL
);
501 /* bus num:core num */
502 if (sscanf(udev_device_get_sysname(bcmadev
), "bcma%*u:%u", &core
) != 1)
504 /* suppress the common core == 0 */
506 xsprintf(names
->bcma_core
, "b%u", core
);
508 names
->type
= NET_BCMA
;
512 static int names_ccw(struct udev_device
*dev
, struct netnames
*names
) {
513 struct udev_device
*cdev
;
514 const char *bus_id
, *subsys
;
521 /* Retrieve the associated CCW device */
522 cdev
= udev_device_get_parent(dev
);
523 /* skip virtio subsystem if present */
524 cdev
= skip_virtio(cdev
);
528 /* Network devices are either single or grouped CCW devices */
529 subsys
= udev_device_get_subsystem(cdev
);
530 if (!STRPTR_IN_SET(subsys
, "ccwgroup", "ccw"))
533 /* Retrieve bus-ID of the CCW device. The bus-ID uniquely
534 * identifies the network device on the Linux on System z channel
535 * subsystem. Note that the bus-ID contains lowercase characters.
537 bus_id
= udev_device_get_sysname(cdev
);
541 /* Check the length of the bus-ID. Rely on that the kernel provides
542 * a correct bus-ID; alternatively, improve this check and parse and
543 * verify each bus-ID part...
545 bus_id_len
= strlen(bus_id
);
546 if (!IN_SET(bus_id_len
, 8, 9))
549 /* Strip leading zeros from the bus id for aesthetic purposes. This
550 * keeps the ccw names stable, yet much shorter in general case of
551 * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is
552 * not prepended when it is zero. Preserve the last 0 for 0.0.0000.
554 bus_id_start
= strspn(bus_id
, ".0");
555 bus_id
+= bus_id_start
< bus_id_len
? bus_id_start
: bus_id_len
- 1;
557 /* Store the CCW bus-ID for use as network device name */
558 if (snprintf_ok(names
->ccw_busid
, sizeof(names
->ccw_busid
), "c%s", bus_id
))
559 names
->type
= NET_CCW
;
564 static int names_mac(struct udev_device
*dev
, struct netnames
*names
) {
567 unsigned int a1
, a2
, a3
, a4
, a5
, a6
;
569 /* check for NET_ADDR_PERM, skip random MAC addresses */
570 s
= udev_device_get_sysattr_value(dev
, "addr_assign_type");
573 i
= strtoul(s
, NULL
, 0);
577 s
= udev_device_get_sysattr_value(dev
, "address");
580 if (sscanf(s
, "%x:%x:%x:%x:%x:%x", &a1
, &a2
, &a3
, &a4
, &a5
, &a6
) != 6)
583 /* skip empty MAC addresses */
584 if (a1
+ a2
+ a3
+ a4
+ a5
+ a6
== 0)
593 names
->mac_valid
= true;
597 /* IEEE Organizationally Unique Identifier vendor string */
598 static int ieee_oui(struct udev_device
*dev
, struct netnames
*names
, bool test
) {
601 if (!names
->mac_valid
)
603 /* skip commonly misused 00:00:00 (Xerox) prefix */
604 if (memcmp(names
->mac
, "\0\0\0", 3) == 0)
606 xsprintf(str
, "OUI:%02X%02X%02X%02X%02X%02X", names
->mac
[0],
607 names
->mac
[1], names
->mac
[2], names
->mac
[3], names
->mac
[4],
609 udev_builtin_hwdb_lookup(dev
, NULL
, str
, NULL
, test
);
613 static int builtin_net_id(struct udev_device
*dev
, int argc
, char *argv
[], bool test
) {
618 const char *prefix
= "en";
619 struct netnames names
= {};
622 /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */
623 s
= udev_device_get_sysattr_value(dev
, "type");
626 i
= strtoul(s
, NULL
, 0);
638 /* skip stacked devices, like VLANs, ... */
639 s
= udev_device_get_sysattr_value(dev
, "ifindex");
642 p
= udev_device_get_sysattr_value(dev
, "iflink");
648 devtype
= udev_device_get_devtype(dev
);
650 if (streq("wlan", devtype
))
652 else if (streq("wwan", devtype
))
656 err
= names_mac(dev
, &names
);
657 if (err
>= 0 && names
.mac_valid
) {
660 xsprintf(str
, "%sx%02x%02x%02x%02x%02x%02x", prefix
,
661 names
.mac
[0], names
.mac
[1], names
.mac
[2],
662 names
.mac
[3], names
.mac
[4], names
.mac
[5]);
663 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_MAC", str
);
665 ieee_oui(dev
, &names
, test
);
668 /* get path names for Linux on System z network devices */
669 err
= names_ccw(dev
, &names
);
670 if (err
>= 0 && names
.type
== NET_CCW
) {
673 if (snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.ccw_busid
))
674 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_PATH", str
);
678 /* get ibmveth/ibmvnic slot-based names. */
679 err
= names_vio(dev
, &names
);
680 if (err
>= 0 && names
.type
== NET_VIO
) {
683 if (snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.vio_slot
))
684 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_SLOT", str
);
688 /* get ACPI path names for ARM64 platform devices */
689 err
= names_platform(dev
, &names
, test
);
690 if (err
>= 0 && names
.type
== NET_PLATFORM
) {
693 if (snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.platform_path
))
694 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_PATH", str
);
698 /* get PCI based path names, we compose only PCI based paths */
699 err
= names_pci(dev
, &names
);
703 /* plain PCI device */
704 if (names
.type
== NET_PCI
) {
707 if (names
.pci_onboard
[0] &&
708 snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.pci_onboard
))
709 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_ONBOARD", str
);
711 if (names
.pci_onboard_label
&&
712 snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.pci_onboard_label
))
713 udev_builtin_add_property(dev
, test
, "ID_NET_LABEL_ONBOARD", str
);
715 if (names
.pci_path
[0] &&
716 snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.pci_path
))
717 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_PATH", str
);
719 if (names
.pci_slot
[0] &&
720 snprintf_ok(str
, sizeof str
, "%s%s", prefix
, names
.pci_slot
))
721 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_SLOT", str
);
726 err
= names_usb(dev
, &names
);
727 if (err
>= 0 && names
.type
== NET_USB
) {
730 if (names
.pci_path
[0] &&
731 snprintf_ok(str
, sizeof str
, "%s%s%s", prefix
, names
.pci_path
, names
.usb_ports
))
732 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_PATH", str
);
734 if (names
.pci_slot
[0] &&
735 snprintf_ok(str
, sizeof str
, "%s%s%s", prefix
, names
.pci_slot
, names
.usb_ports
))
736 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_SLOT", str
);
741 err
= names_bcma(dev
, &names
);
742 if (err
>= 0 && names
.type
== NET_BCMA
) {
745 if (names
.pci_path
[0] &&
746 snprintf_ok(str
, sizeof str
, "%s%s%s", prefix
, names
.pci_path
, names
.bcma_core
))
747 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_PATH", str
);
749 if (names
.pci_slot
[0] &&
750 snprintf(str
, sizeof str
, "%s%s%s", prefix
, names
.pci_slot
, names
.bcma_core
))
751 udev_builtin_add_property(dev
, test
, "ID_NET_NAME_SLOT", str
);
758 const struct udev_builtin udev_builtin_net_id
= {
760 .cmd
= builtin_net_id
,
761 .help
= "Network device properties",