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