chiark / gitweb /
944e0d5f0ce5723f380894709ab6057fa2b76af5
[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 <linux/pci_regs.h>
59
60 #include "udev.h"
61
62 /* retrieve on-board index number and label from firmware */
63 static int dev_pci_onboard(struct udev_device *dev, struct udev_device *parent, const char *prefix, bool test) {
64         const char *index;
65         int idx;
66         const char *label;
67         char s[16];
68         int err;
69
70         /* ACPI _DSM  -- device specific method for naming a PCI or PCI Express device */
71         index = udev_device_get_sysattr_value(parent, "acpi_index");
72         /* SMBIOS type 41 -- Onboard Devices Extended Information */
73         if (!index)
74                 index = udev_device_get_sysattr_value(parent, "index");
75         if (!index)
76                 return -ENOENT;
77         idx = strtoul(index, NULL, 0);
78         if (idx <= 0)
79                 return -EINVAL;
80         snprintf(s, sizeof(s), "%so%d", prefix, idx);
81         err = udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", s);
82         if (err < 0)
83                 return err;
84
85         label = udev_device_get_sysattr_value(parent, "label");
86         if (label) {
87                 err = udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", label);
88                 if (err < 0)
89                         return err;
90         }
91         return 0;
92 }
93
94 /* read the 256 bytes PCI configuration space to check the multi-function bit */
95 static bool is_pci_singlefunction(struct udev_device *dev) {
96         char filename[256];
97         FILE *f;
98         char config[256];
99         bool single = false;
100
101         snprintf(filename, sizeof(filename), "%s/config", udev_device_get_syspath(dev));
102         f = fopen(filename, "re");
103         if (!f)
104                 goto out;
105         if (fread(&config, sizeof(config), 1, f) != 1)
106                 goto out;
107
108         /* bit 0-6 header type, bit 7 multi/single function device */
109         if ((config[PCI_HEADER_TYPE] & 0x80) == 0)
110                 single = true;
111 out:
112         fclose(f);
113         return single;
114 }
115
116 static int dev_pci_slot(struct udev_device *dev, struct udev_device *parent, const char *prefix, bool test) {
117         struct udev *udev = udev_device_get_udev(dev);
118         unsigned int bus;
119         unsigned int slot;
120         unsigned int func;
121         struct udev_device *pci = NULL;
122         char slots[256];
123         DIR *dir;
124         struct dirent *dent;
125         char str[256];
126         int hotplug_slot = 0;
127         int err = 0;
128
129         /* compose a name based on the raw kernel's PCI bus, slot numbers */
130         if (sscanf(udev_device_get_sysname(parent), "0000:%x:%x.%d", &bus, &slot, &func) != 3)
131                 return -ENOENT;
132         if (func == 0 && is_pci_singlefunction(parent))
133                 snprintf(str, sizeof(str), "%sp%ds%d", prefix, bus, slot);
134         else
135                 snprintf(str, sizeof(str), "%sp%ds%df%d", prefix, bus, slot, func);
136         err = udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
137         if (err < 0)
138                 return err;
139
140         /* ACPI _SUN  -- slot user number */
141         pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
142         if (!pci) {
143                 err = -ENOENT;
144                 goto out;
145         }
146         snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
147         dir = opendir(slots);
148         if (!dir) {
149                 err = -errno;
150                 goto out;
151         }
152
153         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
154                 int i;
155                 char *rest;
156                 char *address;
157
158                 if (dent->d_name[0] == '.')
159                         continue;
160                 i = strtol(dent->d_name, &rest, 10);
161                 if (rest[0] != '\0')
162                         continue;
163                 if (i < 1)
164                         continue;
165                 snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
166                 if (read_one_line_file(str, &address) >= 0) {
167                         /* match slot address with device by stripping the function */
168                         if (strncmp(address, udev_device_get_sysname(parent), strlen(address)) == 0)
169                                 hotplug_slot = i;
170                         free(address);
171                 }
172
173                 if (hotplug_slot > 0)
174                         break;
175         }
176         closedir(dir);
177
178         if (hotplug_slot > 0) {
179                 if (func == 0 && is_pci_singlefunction(parent))
180                         snprintf(str, sizeof(str), "%ss%d", prefix, hotplug_slot);
181                 else
182                         snprintf(str, sizeof(str), "%ss%df%d", prefix, hotplug_slot, func);
183                 err = udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
184         }
185 out:
186         udev_device_unref(pci);
187         return err;
188 }
189
190 static int dev_pci(struct udev_device *dev, const char *prefix, bool test) {
191         struct udev_device *parent;
192
193         /* skip other buses than direct PCI parents */
194         parent = udev_device_get_parent(dev);
195         if (!parent || !streq("pci", udev_device_get_subsystem(parent)))
196                 return -ENOENT;
197
198         dev_pci_onboard(dev, parent, prefix, test);
199         dev_pci_slot(dev, parent, prefix, test);
200         return 0;
201 }
202
203 static int dev_mac(struct udev_device *dev, const char *prefix, bool test) {
204         const char *s;
205         unsigned int i;
206         unsigned int a1, a2, a3, a4, a5, a6;
207         char str[16];
208
209         /* check for NET_ADDR_PERM, skip random MAC addresses */
210         s = udev_device_get_sysattr_value(dev, "addr_assign_type");
211         if (!s)
212                 return EXIT_FAILURE;
213         i = strtoul(s, NULL, 0);
214         if (i != 0)
215                 return 0;
216
217         s = udev_device_get_sysattr_value(dev, "address");
218         if (!s)
219                 return -ENOENT;
220         if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
221                 return -EINVAL;
222
223         /* skip empty MAC addresses */
224         if (a1 + a2 + a3 + a4 + a5 + a6 == 0)
225                 return -EINVAL;
226
227         /*
228          * IEEE Organizationally Unique Identifier vendor string
229          * skip commonly misused 00:00:00 (Xerox) prefix
230          */
231         if (a1 + a2 + a3 > 0) {
232                 snprintf(str, sizeof(str), "OUI:%02X%02X%02X", a1, a2, a3);
233                 udev_builtin_hwdb_lookup(dev, str, test);
234         }
235
236         snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix, a1, a2, a3, a4, a5, a6);
237         return udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
238 }
239
240 static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
241         const char *s;
242         unsigned int i;
243         const char *devtype;
244         const char *prefix = "en";
245
246         /* handle only ARPHRD_ETHER devices */
247         s = udev_device_get_sysattr_value(dev, "type");
248         if (!s)
249                 return EXIT_FAILURE;
250         i = strtoul(s, NULL, 0);
251         if (i != 1)
252                 return 0;
253
254         devtype = udev_device_get_devtype(dev);
255         if (devtype) {
256                 if (streq("wlan", devtype))
257                         prefix = "wl";
258                 else if (streq("wwan", devtype))
259                         prefix = "ww";
260         }
261
262         dev_pci(dev, prefix, test);
263         dev_mac(dev, prefix, test);
264         return EXIT_SUCCESS;
265 }
266
267 const struct udev_builtin udev_builtin_net_id = {
268         .name = "net_id",
269         .cmd = builtin_net_id,
270         .help = "network device properties",
271 };