]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-net_id.c
udev: add emacs header line
[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 * ccw<name> -- CCW bus group name
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 "string-util.h"
106 #include "udev.h"
107
108 enum netname_type{
109 NET_UNDEF,
110 NET_PCI,
111 NET_USB,
112 NET_BCMA,
113 NET_VIRTIO,
114 NET_CCWGROUP,
115 };
116
117 struct netnames {
118 enum netname_type type;
119
120 uint8_t mac[6];
121 bool mac_valid;
122
123 struct udev_device *pcidev;
124 char pci_slot[IFNAMSIZ];
125 char pci_path[IFNAMSIZ];
126 char pci_onboard[IFNAMSIZ];
127 const char *pci_onboard_label;
128
129 char usb_ports[IFNAMSIZ];
130 char bcma_core[IFNAMSIZ];
131 char ccw_group[IFNAMSIZ];
132 };
133
134 /* retrieve on-board index number and label from firmware */
135 static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
136 unsigned dev_port = 0;
137 size_t l;
138 char *s;
139 const char *attr;
140 int idx;
141
142 /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
143 attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
144 /* SMBIOS type 41 -- Onboard Devices Extended Information */
145 if (!attr)
146 attr = udev_device_get_sysattr_value(names->pcidev, "index");
147 if (!attr)
148 return -ENOENT;
149
150 idx = strtoul(attr, NULL, 0);
151 if (idx <= 0)
152 return -EINVAL;
153
154 /* kernel provided port index for multiple ports on a single PCI function */
155 attr = udev_device_get_sysattr_value(dev, "dev_port");
156 if (attr)
157 dev_port = strtol(attr, NULL, 10);
158
159 s = names->pci_onboard;
160 l = sizeof(names->pci_onboard);
161 l = strpcpyf(&s, l, "o%d", idx);
162 if (dev_port > 0)
163 l = strpcpyf(&s, l, "d%d", dev_port);
164 if (l == 0)
165 names->pci_onboard[0] = '\0';
166
167 names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
168
169 return 0;
170 }
171
172 /* read the 256 bytes PCI configuration space to check the multi-function bit */
173 static bool is_pci_multifunction(struct udev_device *dev) {
174 _cleanup_close_ int fd = -1;
175 const char *filename;
176 uint8_t config[64];
177
178 filename = strjoina(udev_device_get_syspath(dev), "/config");
179 fd = open(filename, O_RDONLY | O_CLOEXEC);
180 if (fd < 0)
181 return false;
182 if (read(fd, &config, sizeof(config)) != sizeof(config))
183 return false;
184
185 /* bit 0-6 header type, bit 7 multi/single function device */
186 if ((config[PCI_HEADER_TYPE] & 0x80) != 0)
187 return true;
188
189 return false;
190 }
191
192 static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
193 struct udev *udev = udev_device_get_udev(names->pcidev);
194 unsigned domain, bus, slot, func, dev_port = 0;
195 size_t l;
196 char *s;
197 const char *attr;
198 struct udev_device *pci = NULL;
199 char slots[256], str[256];
200 _cleanup_closedir_ DIR *dir = NULL;
201 struct dirent *dent;
202 int hotplug_slot = 0, err = 0;
203
204 if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
205 return -ENOENT;
206
207 /* kernel provided port index for multiple ports on a single PCI function */
208 attr = udev_device_get_sysattr_value(dev, "dev_port");
209 if (attr)
210 dev_port = strtol(attr, NULL, 10);
211
212 /* compose a name based on the raw kernel's PCI bus, slot numbers */
213 s = names->pci_path;
214 l = sizeof(names->pci_path);
215 if (domain > 0)
216 l = strpcpyf(&s, l, "P%u", domain);
217 l = strpcpyf(&s, l, "p%us%u", bus, slot);
218 if (func > 0 || is_pci_multifunction(names->pcidev))
219 l = strpcpyf(&s, l, "f%u", func);
220 if (dev_port > 0)
221 l = strpcpyf(&s, l, "d%u", dev_port);
222 if (l == 0)
223 names->pci_path[0] = '\0';
224
225 /* ACPI _SUN -- slot user number */
226 pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
227 if (!pci) {
228 err = -ENOENT;
229 goto out;
230 }
231 snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
232 dir = opendir(slots);
233 if (!dir) {
234 err = -errno;
235 goto out;
236 }
237
238 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
239 int i;
240 char *rest;
241 char *address;
242
243 if (dent->d_name[0] == '.')
244 continue;
245 i = strtol(dent->d_name, &rest, 10);
246 if (rest[0] != '\0')
247 continue;
248 if (i < 1)
249 continue;
250 snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
251 if (read_one_line_file(str, &address) >= 0) {
252 /* match slot address with device by stripping the function */
253 if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
254 hotplug_slot = i;
255 free(address);
256 }
257
258 if (hotplug_slot > 0)
259 break;
260 }
261
262 if (hotplug_slot > 0) {
263 s = names->pci_slot;
264 l = sizeof(names->pci_slot);
265 if (domain > 0)
266 l = strpcpyf(&s, l, "P%d", domain);
267 l = strpcpyf(&s, l, "s%d", hotplug_slot);
268 if (func > 0 || is_pci_multifunction(names->pcidev))
269 l = strpcpyf(&s, l, "f%d", func);
270 if (dev_port > 0)
271 l = strpcpyf(&s, l, "d%d", dev_port);
272 if (l == 0)
273 names->pci_slot[0] = '\0';
274 }
275 out:
276 udev_device_unref(pci);
277 return err;
278 }
279
280 static int names_pci(struct udev_device *dev, struct netnames *names) {
281 struct udev_device *parent;
282
283 assert(dev);
284 assert(names);
285
286 parent = udev_device_get_parent(dev);
287
288 /* there can only ever be one virtio bus per parent device, so we can
289 safely ignore any virtio buses. see
290 <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
291 while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
292 parent = udev_device_get_parent(parent);
293
294 if (!parent)
295 return -ENOENT;
296
297 /* check if our direct parent is a PCI device with no other bus in-between */
298 if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
299 names->type = NET_PCI;
300 names->pcidev = parent;
301 } else {
302 names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
303 if (!names->pcidev)
304 return -ENOENT;
305 }
306 dev_pci_onboard(dev, names);
307 dev_pci_slot(dev, names);
308 return 0;
309 }
310
311 static int names_usb(struct udev_device *dev, struct netnames *names) {
312 struct udev_device *usbdev;
313 char name[256];
314 char *ports;
315 char *config;
316 char *interf;
317 size_t l;
318 char *s;
319
320 assert(dev);
321 assert(names);
322
323 usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
324 if (!usbdev)
325 return -ENOENT;
326
327 /* get USB port number chain, configuration, interface */
328 strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
329 s = strchr(name, '-');
330 if (!s)
331 return -EINVAL;
332 ports = s+1;
333
334 s = strchr(ports, ':');
335 if (!s)
336 return -EINVAL;
337 s[0] = '\0';
338 config = s+1;
339
340 s = strchr(config, '.');
341 if (!s)
342 return -EINVAL;
343 s[0] = '\0';
344 interf = s+1;
345
346 /* prefix every port number in the chain with "u" */
347 s = ports;
348 while ((s = strchr(s, '.')))
349 s[0] = 'u';
350 s = names->usb_ports;
351 l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
352
353 /* append USB config number, suppress the common config == 1 */
354 if (!streq(config, "1"))
355 l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
356
357 /* append USB interface number, suppress the interface == 0 */
358 if (!streq(interf, "0"))
359 l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
360 if (l == 0)
361 return -ENAMETOOLONG;
362
363 names->type = NET_USB;
364 return 0;
365 }
366
367 static int names_bcma(struct udev_device *dev, struct netnames *names) {
368 struct udev_device *bcmadev;
369 unsigned int core;
370
371 assert(dev);
372 assert(names);
373
374 bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
375 if (!bcmadev)
376 return -ENOENT;
377
378 /* bus num:core num */
379 if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1)
380 return -EINVAL;
381 /* suppress the common core == 0 */
382 if (core > 0)
383 snprintf(names->bcma_core, sizeof(names->bcma_core), "b%u", core);
384
385 names->type = NET_BCMA;
386 return 0;
387 }
388
389 static int names_ccw(struct udev_device *dev, struct netnames *names) {
390 struct udev_device *cdev;
391 const char *bus_id;
392 size_t bus_id_len;
393 int rc;
394
395 assert(dev);
396 assert(names);
397
398 /* Retrieve the associated CCW device */
399 cdev = udev_device_get_parent(dev);
400 if (!cdev)
401 return -ENOENT;
402
403 /* Network devices are always grouped CCW devices */
404 if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev)))
405 return -ENOENT;
406
407 /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely
408 * identifies the network device on the Linux on System z channel
409 * subsystem. Note that the bus-ID contains lowercase characters.
410 */
411 bus_id = udev_device_get_sysname(cdev);
412 if (!bus_id)
413 return -ENOENT;
414
415 /* Check the length of the bus-ID. Rely on that the kernel provides
416 * a correct bus-ID; alternatively, improve this check and parse and
417 * verify each bus-ID part...
418 */
419 bus_id_len = strlen(bus_id);
420 if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9)
421 return -EINVAL;
422
423 /* Store the CCW bus-ID for use as network device name */
424 rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "ccw%s", bus_id);
425 if (rc >= 0 && rc < (int)sizeof(names->ccw_group))
426 names->type = NET_CCWGROUP;
427 return 0;
428 }
429
430 static int names_mac(struct udev_device *dev, struct netnames *names) {
431 const char *s;
432 unsigned int i;
433 unsigned int a1, a2, a3, a4, a5, a6;
434
435 /* check for NET_ADDR_PERM, skip random MAC addresses */
436 s = udev_device_get_sysattr_value(dev, "addr_assign_type");
437 if (!s)
438 return EXIT_FAILURE;
439 i = strtoul(s, NULL, 0);
440 if (i != 0)
441 return 0;
442
443 s = udev_device_get_sysattr_value(dev, "address");
444 if (!s)
445 return -ENOENT;
446 if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
447 return -EINVAL;
448
449 /* skip empty MAC addresses */
450 if (a1 + a2 + a3 + a4 + a5 + a6 == 0)
451 return -EINVAL;
452
453 names->mac[0] = a1;
454 names->mac[1] = a2;
455 names->mac[2] = a3;
456 names->mac[3] = a4;
457 names->mac[4] = a5;
458 names->mac[5] = a6;
459 names->mac_valid = true;
460 return 0;
461 }
462
463 /* IEEE Organizationally Unique Identifier vendor string */
464 static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
465 char str[32];
466
467 if (!names->mac_valid)
468 return -ENOENT;
469 /* skip commonly misused 00:00:00 (Xerox) prefix */
470 if (memcmp(names->mac, "\0\0\0", 3) == 0)
471 return -EINVAL;
472 snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
473 names->mac[0], names->mac[1], names->mac[2],
474 names->mac[3], names->mac[4], names->mac[5]);
475 udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
476 return 0;
477 }
478
479 static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
480 const char *s;
481 const char *p;
482 unsigned int i;
483 const char *devtype;
484 const char *prefix = "en";
485 struct netnames names = {};
486 int err;
487
488 /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */
489 s = udev_device_get_sysattr_value(dev, "type");
490 if (!s)
491 return EXIT_FAILURE;
492 i = strtoul(s, NULL, 0);
493 switch (i) {
494 case ARPHRD_ETHER:
495 prefix = "en";
496 break;
497 case ARPHRD_SLIP:
498 prefix = "sl";
499 break;
500 default:
501 return 0;
502 }
503
504 /* skip stacked devices, like VLANs, ... */
505 s = udev_device_get_sysattr_value(dev, "ifindex");
506 if (!s)
507 return EXIT_FAILURE;
508 p = udev_device_get_sysattr_value(dev, "iflink");
509 if (!p)
510 return EXIT_FAILURE;
511 if (!streq(s, p))
512 return 0;
513
514 devtype = udev_device_get_devtype(dev);
515 if (devtype) {
516 if (streq("wlan", devtype))
517 prefix = "wl";
518 else if (streq("wwan", devtype))
519 prefix = "ww";
520 }
521
522 err = names_mac(dev, &names);
523 if (err >= 0 && names.mac_valid) {
524 char str[IFNAMSIZ];
525
526 snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
527 names.mac[0], names.mac[1], names.mac[2],
528 names.mac[3], names.mac[4], names.mac[5]);
529 udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
530
531 ieee_oui(dev, &names, test);
532 }
533
534 /* get path names for Linux on System z network devices */
535 err = names_ccw(dev, &names);
536 if (err >= 0 && names.type == NET_CCWGROUP) {
537 char str[IFNAMSIZ];
538
539 if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str))
540 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
541 goto out;
542 }
543
544 /* get PCI based path names, we compose only PCI based paths */
545 err = names_pci(dev, &names);
546 if (err < 0)
547 goto out;
548
549 /* plain PCI device */
550 if (names.type == NET_PCI) {
551 char str[IFNAMSIZ];
552
553 if (names.pci_onboard[0])
554 if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str))
555 udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
556
557 if (names.pci_onboard_label)
558 if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str))
559 udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
560
561 if (names.pci_path[0])
562 if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str))
563 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
564
565 if (names.pci_slot[0])
566 if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str))
567 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
568 goto out;
569 }
570
571 /* USB device */
572 err = names_usb(dev, &names);
573 if (err >= 0 && names.type == NET_USB) {
574 char str[IFNAMSIZ];
575
576 if (names.pci_path[0])
577 if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str))
578 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
579
580 if (names.pci_slot[0])
581 if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
582 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
583 goto out;
584 }
585
586 /* Broadcom bus */
587 err = names_bcma(dev, &names);
588 if (err >= 0 && names.type == NET_BCMA) {
589 char str[IFNAMSIZ];
590
591 if (names.pci_path[0])
592 if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str))
593 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
594
595 if (names.pci_slot[0])
596 if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str))
597 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
598 goto out;
599 }
600 out:
601 return EXIT_SUCCESS;
602 }
603
604 const struct udev_builtin udev_builtin_net_id = {
605 .name = "net_id",
606 .cmd = builtin_net_id,
607 .help = "Network device properties",
608 };