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