chiark / gitweb /
94fefc5f9d961bd67c9cd61c2b991512d59e490c
[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  *
38  * All multi-function devices will carry the [f<function>] number in the
39  * device name, including the function 0 device.
40  *
41  * examples:
42  *   ID_NET_NAME_ONBOARD=eno1
43  *   ID_NET_NAME_SLOT=ens1
44  *   ID_NET_NAME_SLOT=ens2f0
45  *   ID_NET_NAME_SLOT=ens2f1
46  *   ID_NET_NAME_MAC=enxf0def180d479
47  *   ID_NET_NAME_PATH=enp0s25
48  *   ID_NET_NAME_PATH=enp19s3f0
49  *   ID_NET_NAME_PATH=enp19s3f1
50  */
51
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <stdarg.h>
55 #include <unistd.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <net/if.h>
59 #include <linux/pci_regs.h>
60
61 #include "udev.h"
62
63 enum netname_type{
64         NET_UNDEF,
65         NET_PCI,
66         NET_USB,
67 };
68
69 struct netnames {
70         enum netname_type type;
71
72         uint8_t mac[6];
73         bool mac_valid;
74
75         struct udev_device *pcidev;
76         char pci_slot[IFNAMSIZ];
77         char pci_path[IFNAMSIZ];
78         char pci_onboard[IFNAMSIZ];
79         const char *pci_onboard_label;
80
81         struct udev_device *usbdev;
82         char usb_ports[IFNAMSIZ];
83 };
84
85 /* retrieve on-board index number and label from firmware */
86 static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
87         const char *index;
88         int idx;
89
90         /* ACPI _DSM  -- device specific method for naming a PCI or PCI Express device */
91         index = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
92         /* SMBIOS type 41 -- Onboard Devices Extended Information */
93         if (!index)
94                 index = udev_device_get_sysattr_value(names->pcidev, "index");
95         if (!index)
96                 return -ENOENT;
97         idx = strtoul(index, NULL, 0);
98         if (idx <= 0)
99                 return -EINVAL;
100         snprintf(names->pci_onboard, sizeof(names->pci_onboard), "o%d", idx);
101
102         names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
103         return 0;
104 }
105
106 /* read the 256 bytes PCI configuration space to check the multi-function bit */
107 static bool is_pci_singlefunction(struct udev_device *dev) {
108         char filename[256];
109         FILE *f;
110         char config[64];
111         bool single = false;
112
113         snprintf(filename, sizeof(filename), "%s/config", udev_device_get_syspath(dev));
114         f = fopen(filename, "re");
115         if (!f)
116                 goto out;
117         if (fread(&config, sizeof(config), 1, f) != 1)
118                 goto out;
119
120         /* bit 0-6 header type, bit 7 multi/single function device */
121         if ((config[PCI_HEADER_TYPE] & 0x80) == 0)
122                 single = true;
123 out:
124         fclose(f);
125         return single;
126 }
127
128 static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
129         struct udev *udev = udev_device_get_udev(names->pcidev);
130         unsigned int bus;
131         unsigned int slot;
132         unsigned int func;
133         struct udev_device *pci = NULL;
134         char slots[256];
135         DIR *dir;
136         struct dirent *dent;
137         char str[256];
138         int hotplug_slot = 0;
139         int err = 0;
140
141         /* compose a name based on the raw kernel's PCI bus, slot numbers */
142         if (sscanf(udev_device_get_sysname(names->pcidev), "0000:%x:%x.%d", &bus, &slot, &func) != 3)
143                 return -ENOENT;
144         if (func == 0 && is_pci_singlefunction(names->pcidev))
145                 snprintf(names->pci_path, sizeof(names->pci_path), "p%ds%d", bus, slot);
146         else
147                 snprintf(names->pci_path, sizeof(names->pci_path), "p%ds%df%d", bus, slot, func);
148
149         /* ACPI _SUN  -- slot user number */
150         pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
151         if (!pci) {
152                 err = -ENOENT;
153                 goto out;
154         }
155         snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
156         dir = opendir(slots);
157         if (!dir) {
158                 err = -errno;
159                 goto out;
160         }
161
162         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
163                 int i;
164                 char *rest;
165                 char *address;
166
167                 if (dent->d_name[0] == '.')
168                         continue;
169                 i = strtol(dent->d_name, &rest, 10);
170                 if (rest[0] != '\0')
171                         continue;
172                 if (i < 1)
173                         continue;
174                 snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
175                 if (read_one_line_file(str, &address) >= 0) {
176                         /* match slot address with device by stripping the function */
177                         if (strncmp(address, udev_device_get_sysname(names->pcidev), strlen(address)) == 0)
178                                 hotplug_slot = i;
179                         free(address);
180                 }
181
182                 if (hotplug_slot > 0)
183                         break;
184         }
185         closedir(dir);
186
187         if (hotplug_slot > 0) {
188                 if (func == 0 && is_pci_singlefunction(names->pcidev))
189                         snprintf(names->pci_slot, sizeof(names->pci_slot), "s%d", hotplug_slot);
190                 else
191                         snprintf(names->pci_slot, sizeof(names->pci_slot), "s%df%d", hotplug_slot, func);
192         }
193 out:
194         udev_device_unref(pci);
195         return err;
196 }
197
198 static int names_pci(struct udev_device *dev, struct netnames *names) {
199         struct udev_device *parent;
200
201         parent = udev_device_get_parent(dev);
202         if (!parent)
203                 return -ENOENT;
204         /* check if our direct parent is a PCI device with no other bus in-between */
205         if (streq("pci", udev_device_get_subsystem(parent))) {
206                 names->type = NET_PCI;
207                 names->pcidev = parent;
208         } else {
209                 names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
210                 if (!names->pcidev)
211                         return -ENOENT;
212         }
213         dev_pci_onboard(dev, names);
214         dev_pci_slot(dev, names);
215         return 0;
216 }
217
218 static int names_usb(struct udev_device *dev, struct netnames *names) {
219         char name[256];
220         char *ports;
221         char *config;
222         char *interf;
223         size_t l;
224         char *s;
225
226         names->usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
227         if (!names->usbdev)
228                 return -ENOENT;
229
230         /* get USB port number chain, configuration, interface */
231         util_strscpy(name, sizeof(name), udev_device_get_sysname(names->usbdev));
232         s = strchr(name, '-');
233         if (!s)
234                 return -EINVAL;
235         ports = s+1;
236
237         s = strchr(ports, ':');
238         if (!s)
239                 return -EINVAL;
240         s[0] = '\0';
241         config = s+1;
242
243         s = strchr(config, '.');
244         if (!s)
245                 return -EINVAL;
246         s[0] = '\0';
247         interf = s+1;
248
249         /* prefix every port number in the chain with "u"*/
250         s = ports;
251         while ((s = strchr(s, '.')))
252                 s[0] = 'u';
253         s = names->usb_ports;
254         l = util_strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL);
255
256         /* append USB config number, suppress the common config == 1 */
257         if (!streq(config, "1"))
258                 l = util_strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL);
259
260         /* append USB interface number, suppress the interface == 0 */
261         if (!streq(interf, "0"))
262                 l = util_strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL);
263         if (l == 0)
264                 return -ENAMETOOLONG;
265
266         names->type = NET_USB;
267         return 0;
268 }
269
270 static int names_mac(struct udev_device *dev, struct netnames *names) {
271         const char *s;
272         unsigned int i;
273         unsigned int a1, a2, a3, a4, a5, a6;
274
275         /* check for NET_ADDR_PERM, skip random MAC addresses */
276         s = udev_device_get_sysattr_value(dev, "addr_assign_type");
277         if (!s)
278                 return EXIT_FAILURE;
279         i = strtoul(s, NULL, 0);
280         if (i != 0)
281                 return 0;
282
283         s = udev_device_get_sysattr_value(dev, "address");
284         if (!s)
285                 return -ENOENT;
286         if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
287                 return -EINVAL;
288
289         /* skip empty MAC addresses */
290         if (a1 + a2 + a3 + a4 + a5 + a6 == 0)
291                 return -EINVAL;
292
293         names->mac[0] = a1;
294         names->mac[1] = a2;
295         names->mac[2] = a3;
296         names->mac[3] = a4;
297         names->mac[4] = a5;
298         names->mac[5] = a6;
299         names->mac_valid = true;
300         return 0;
301 }
302
303 /* IEEE Organizationally Unique Identifier vendor string */
304 static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
305         char str[IFNAMSIZ];
306
307         if (names->mac_valid)
308                 return -ENOENT;
309         /* skip commonly misused 00:00:00 (Xerox) prefix */
310         if (memcmp(names->mac, "\0\0\0", 3) == 0)
311                 return -EINVAL;
312         snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
313                  names->mac[0], names->mac[1], names->mac[2],
314                  names->mac[3], names->mac[4], names->mac[5]);
315         udev_builtin_hwdb_lookup(dev, str, test);
316         return 0;
317 }
318
319 static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
320         const char *s;
321         unsigned int i;
322         const char *devtype;
323         const char *prefix = "en";
324         struct netnames names;
325         int err;
326
327         /* handle only ARPHRD_ETHER devices */
328         s = udev_device_get_sysattr_value(dev, "type");
329         if (!s)
330                 return EXIT_FAILURE;
331         i = strtoul(s, NULL, 0);
332         if (i != 1)
333                 return 0;
334
335         devtype = udev_device_get_devtype(dev);
336         if (devtype) {
337                 if (streq("wlan", devtype))
338                         prefix = "wl";
339                 else if (streq("wwan", devtype))
340                         prefix = "ww";
341         }
342
343         zero(names);
344         err = names_mac(dev, &names);
345         if (err >= 0 && names.mac_valid) {
346                 char str[IFNAMSIZ];
347
348                 snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
349                          names.mac[0], names.mac[1], names.mac[2],
350                          names.mac[3], names.mac[4], names.mac[5]);
351                 udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
352
353                 ieee_oui(dev, &names, test);
354         }
355
356         /* get PCI based path names, we compose only PCI based paths */
357         err = names_pci(dev, &names);
358         if (err < 0)
359                 goto out;
360
361         /* plain PCI device */
362         if (names.type == NET_PCI) {
363                 char str[IFNAMSIZ];
364
365                 if (names.pci_onboard[0])
366                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str))
367                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
368
369                 if (names.pci_onboard_label)
370                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str))
371                                 udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
372
373                 if (names.pci_path[0])
374                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str))
375                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
376
377                 if (names.pci_slot[0])
378                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str))
379                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
380                 goto out;
381         }
382
383         /* USB device */
384         err = names_usb(dev, &names);
385         if (err >= 0 && names.type == NET_USB) {
386                 char str[IFNAMSIZ];
387
388                 if (names.pci_path[0])
389                         if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str))
390                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
391
392                 if (names.pci_slot[0])
393                         if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str))
394                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
395         }
396 out:
397         return EXIT_SUCCESS;
398 }
399
400 const struct udev_builtin udev_builtin_net_id = {
401         .name = "net_id",
402         .cmd = builtin_net_id,
403         .help = "network device properties",
404 };