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