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