chiark / gitweb /
ebada80e1bd205ceff1d3233aa14c9aa4adebf6a
[elogind.git] / src / udev / udev-builtin-net_id.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2012 Kay Sievers <kay@vrfy.org>
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 /*
21  * predictable network interface device names based on:
22  *  - firmware/bios-provided index numbers for on-board devices
23  *  - firmware-provided pci-express hotplug slot index number
24  *  - physical/geographical location of the hardware
25  *  - the interface's MAC address
26  *
27  * two character prefixes based on the type of interface:
28  *   en -- ethernet
29  *   wl -- wlan
30  *   ww -- wwan
31  *
32  * type of names:
33  *   o<index>                   -- on-board device index number
34  *   s<slot>[f<function>]       -- hotplug slot index number
35  *   x<MAC>                     -- MAC address
36  *   p<bus>s<slot>[f<function>] -- PCI geographical location
37  *   p<bus>s<slot>[f<function>][u<port>][u<port>][c<config>][i<interface>]
38  *                              -- USB port number chain
39  *
40  * All multi-function devices will carry the [f<function>] number in the
41  * device name, including the function 0 device.
42  *
43  * PCI card with firmware index
44  *   ID_NET_NAME_ONBOARD=eno1
45  *   ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
46  *
47  * PCI card
48  *   /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
49  *   ID_NET_NAME_MAC=enx000000000466
50  *   ID_NET_NAME_PATH=enp5s0
51  *   ID_NET_NAME_SLOT=ens1
52  *
53  * PCI card in hotplug slot with firmware index number:
54  *   /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
55  *   ID_NET_NAME_MAC=enx000000000466
56  *   ID_NET_NAME_PATH=enp5s0
57  *   ID_NET_NAME_SLOT=ens1
58  *
59  * PCI wlan card:
60  *   /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
61  *   ID_NET_NAME_MAC=wlx0024d7e31130
62  *   ID_NET_NAME_PATH=wlp3s0
63  *
64  * USB built-in 3G modem:
65  *   /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
66  *   ID_NET_NAME_MAC=wwx028037ec0200
67  *   ID_NET_NAME_PATH=wwp0s29u1u4i6
68  *
69  * USB Android phone:
70  *   /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
71  *   ID_NET_NAME_MAC=enxd626b3450fb5
72  *   ID_NET_NAME_PATH=enp0s29u1u2
73  */
74
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <stdarg.h>
78 #include <unistd.h>
79 #include <string.h>
80 #include <errno.h>
81 #include <net/if.h>
82 #include <linux/pci_regs.h>
83
84 #include "udev.h"
85
86 enum netname_type{
87         NET_UNDEF,
88         NET_PCI,
89         NET_USB,
90 };
91
92 struct netnames {
93         enum netname_type type;
94
95         uint8_t mac[6];
96         bool mac_valid;
97
98         struct udev_device *pcidev;
99         char pci_slot[IFNAMSIZ];
100         char pci_path[IFNAMSIZ];
101         char pci_onboard[IFNAMSIZ];
102         const char *pci_onboard_label;
103
104         struct udev_device *usbdev;
105         char usb_ports[IFNAMSIZ];
106 };
107
108 /* retrieve on-board index number and label from firmware */
109 static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
110         const char *index;
111         int idx;
112
113         /* ACPI _DSM  -- device specific method for naming a PCI or PCI Express device */
114         index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
115         /* SMBIOS type 41 -- Onboard Devices Extended Information */
116         if (!index)
117                 index = udev_device_get_sysattr_value(names->pcidev, "index");
118         if (!index)
119                 return -ENOENT;
120         idx = strtoul(index, NULL, 0);
121         if (idx <= 0)
122                 return -EINVAL;
123         snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
124
125         names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
126         return 0;
127 }
128
129 /* read the 256 bytes PCI configuration space to check the multi-function bit */
130 static bool is_pci_singlefunction(struct udev_device *dev) {
131         char filename[256];
132         FILE *f;
133         char config[64];
134         bool single = false;
135
136         snprintf(filename, sizeof(filename), "%s/config", udev_device_get_syspath(dev));
137         f = fopen(filename, "re");
138         if (!f)
139                 goto out;
140         if (fread(&config, sizeof(config), 1, f) != 1)
141                 goto out;
142
143         /* bit 0-6 header type, bit 7 multi/single function device */
144         if ((config[PCI_HEADER_TYPE] & 0x80) == 0)
145                 single = true;
146 out:
147         fclose(f);
148         return single;
149 }
150
151 static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
152         struct udev *udev = udev_device_get_udev(names->pcidev);
153         unsigned int bus;
154         unsigned int slot;
155         unsigned int func;
156         struct udev_device *pci = NULL;
157         char slots[256];
158         DIR *dir;
159         struct dirent *dent;
160         char str[256];
161         int hotplug_slot = 0;
162         int err = 0;
163
164         /* compose a name based on the raw kernel's PCI bus, slot numbers */
165         if (sscanf(udev_device_get_sysname(names->pcidev), "0000:%x:%x.%d", &bus, &slot, &func) != 3)
166                 return -ENOENT;
167         if (func == 0 && is_pci_singlefunction(names->pcidev))
168                 snprintf(names->pci_path, sizeof(names->pci_path), "p%ds%d", bus, slot);
169         else
170                 snprintf(names->pci_path, sizeof(names->pci_path), "p%ds%df%d", bus, slot, func);
171
172         /* ACPI _SUN  -- slot user number */
173         pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
174         if (!pci) {
175                 err = -ENOENT;
176                 goto out;
177         }
178         snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
179         dir = opendir(slots);
180         if (!dir) {
181                 err = -errno;
182                 goto out;
183         }
184
185         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
186                 int i;
187                 char *rest;
188                 char *address;
189
190                 if (dent->d_name[0] == '.')
191                         continue;
192                 i = strtol(dent->d_name, &rest, 10);
193                 if (rest[0] != '\0')
194                         continue;
195                 if (i < 1)
196                         continue;
197                 snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
198                 if (read_one_line_file(str, &address) >= 0) {
199                         /* match slot address with device by stripping the function */
200                         if (strncmp(address, udev_device_get_sysname(names->pcidev), strlen(address)) == 0)
201                                 hotplug_slot = i;
202                         free(address);
203                 }
204
205                 if (hotplug_slot > 0)
206                         break;
207         }
208         closedir(dir);
209
210         if (hotplug_slot > 0) {
211                 if (func == 0 && is_pci_singlefunction(names->pcidev))
212                         snprintf(names->pci_slot, sizeof(names->pci_slot), "s%d", hotplug_slot);
213                 else
214                         snprintf(names->pci_slot, sizeof(names->pci_slot), "s%df%d", hotplug_slot, func);
215         }
216 out:
217         udev_device_unref(pci);
218         return err;
219 }
220
221 static int names_pci(struct udev_device *dev, struct netnames *names) {
222         struct udev_device *parent;
223
224         parent = udev_device_get_parent(dev);
225         if (!parent)
226                 return -ENOENT;
227         /* check if our direct parent is a PCI device with no other bus in-between */
228         if (streq("pci", udev_device_get_subsystem(parent))) {
229                 names->type = NET_PCI;
230                 names->pcidev = parent;
231         } else {
232                 names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
233                 if (!names->pcidev)
234                         return -ENOENT;
235         }
236         dev_pci_onboard(dev, names);
237         dev_pci_slot(dev, names);
238         return 0;
239 }
240
241 static int names_usb(struct udev_device *dev, struct netnames *names) {
242         char name[256];
243         char *ports;
244         char *config;
245         char *interf;
246         size_t l;
247         char *s;
248
249         names->usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
250         if (!names->usbdev)
251                 return -ENOENT;
252
253         /* get USB port number chain, configuration, interface */
254         util_strscpy(name, sizeof(name), udev_device_get_sysname(names->usbdev));
255         s = strchr(name, '-');
256         if (!s)
257                 return -EINVAL;
258         ports = s+1;
259
260         s = strchr(ports, ':');
261         if (!s)
262                 return -EINVAL;
263         s[0] = '\0';
264         config = s+1;
265
266         s = strchr(config, '.');
267         if (!s)
268                 return -EINVAL;
269         s[0] = '\0';
270         interf = s+1;
271
272         /* prefix every port number in the chain with "u"*/
273         s = ports;
274         while ((s = strchr(s, '.')))
275                 s[0] = 'u';
276         s = names->usb_ports;
277         l = util_strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
278
279         /* append USB config number, suppress the common config == 1 */
280         if (!streq(config, "1"))
281                 l = util_strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
282
283         /* append USB interface number, suppress the interface == 0 */
284         if (!streq(interf, "0"))
285                 l = util_strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
286         if (l == 0)
287                 return -ENAMETOOLONG;
288
289         names->type = NET_USB;
290         return 0;
291 }
292
293 static int names_mac(struct udev_device *dev, struct netnames *names) {
294         const char *s;
295         unsigned int i;
296         unsigned int a1, a2, a3, a4, a5, a6;
297
298         /* check for NET_ADDR_PERM, skip random MAC addresses */
299         s = udev_device_get_sysattr_value(dev, "addr_assign_type");
300         if (!s)
301                 return EXIT_FAILURE;
302         i = strtoul(s, NULL, 0);
303         if (i != 0)
304                 return 0;
305
306         s = udev_device_get_sysattr_value(dev, "address");
307         if (!s)
308                 return -ENOENT;
309         if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
310                 return -EINVAL;
311
312         /* skip empty MAC addresses */
313         if (a1 + a2 + a3 + a4 + a5 + a6 == 0)
314                 return -EINVAL;
315
316         names->mac[0] = a1;
317         names->mac[1] = a2;
318         names->mac[2] = a3;
319         names->mac[3] = a4;
320         names->mac[4] = a5;
321         names->mac[5] = a6;
322         names->mac_valid = true;
323         return 0;
324 }
325
326 /* IEEE Organizationally Unique Identifier vendor string */
327 static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
328         char str[IFNAMSIZ];
329
330         if (names->mac_valid)
331                 return -ENOENT;
332         /* skip commonly misused 00:00:00 (Xerox) prefix */
333         if (memcmp(names->mac, "\0\0\0", 3) == 0)
334                 return -EINVAL;
335         snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
336                  names->mac[0], names->mac[1], names->mac[2],
337                  names->mac[3], names->mac[4], names->mac[5]);
338         udev_builtin_hwdb_lookup(dev, str, test);
339         return 0;
340 }
341
342 static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
343         const char *s;
344         unsigned int i;
345         const char *devtype;
346         const char *prefix = "en";
347         struct netnames names;
348         int err;
349
350         /* handle only ARPHRD_ETHER devices */
351         s = udev_device_get_sysattr_value(dev, "type");
352         if (!s)
353                 return EXIT_FAILURE;
354         i = strtoul(s, NULL, 0);
355         if (i != 1)
356                 return 0;
357
358         devtype = udev_device_get_devtype(dev);
359         if (devtype) {
360                 if (streq("wlan", devtype))
361                         prefix = "wl";
362                 else if (streq("wwan", devtype))
363                         prefix = "ww";
364         }
365
366         zero(names);
367         err = names_mac(dev, &names);
368         if (err >= 0 && names.mac_valid) {
369                 char str[IFNAMSIZ];
370
371                 snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
372                          names.mac[0], names.mac[1], names.mac[2],
373                          names.mac[3], names.mac[4], names.mac[5]);
374                 udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
375
376                 ieee_oui(dev, &names, test);
377         }
378
379         /* get PCI based path names, we compose only PCI based paths */
380         err = names_pci(dev, &names);
381         if (err < 0)
382                 goto out;
383
384         /* plain PCI device */
385         if (names.type == NET_PCI) {
386                 char str[IFNAMSIZ];
387
388                 if (names.pci_onboard[0])
389                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str))
390                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
391
392                 if (names.pci_onboard_label)
393                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str))
394                                 udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
395
396                 if (names.pci_path[0])
397                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str))
398                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
399
400                 if (names.pci_slot[0])
401                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str))
402                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
403                 goto out;
404         }
405
406         /* USB device */
407         err = names_usb(dev, &names);
408         if (err >= 0 && names.type == NET_USB) {
409                 char str[IFNAMSIZ];
410
411                 if (names.pci_path[0])
412                         if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str))
413                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
414
415                 if (names.pci_slot[0])
416                         if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
417                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
418         }
419 out:
420         return EXIT_SUCCESS;
421 }
422
423 const struct udev_builtin udev_builtin_net_id = {
424         .name = "net_id",
425         .cmd = builtin_net_id,
426         .help = "network device properties",
427 };