chiark / gitweb /
udev: link-config - let udevd set the ifname
[elogind.git] / src / udev / net / link-config.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4  This file is part of systemd.
5
6  Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
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 #include <netinet/ether.h>
23 #include <net/if.h>
24
25 #include "sd-id128.h"
26
27 #include "link-config.h"
28 #include "ethtool-util.h"
29
30 #include "libudev-private.h"
31 #include "sd-rtnl.h"
32 #include "util.h"
33 #include "log.h"
34 #include "strv.h"
35 #include "path-util.h"
36 #include "conf-parser.h"
37 #include "conf-files.h"
38 #include "fileio.h"
39 #include "hashmap.h"
40 #include "rtnl-util.h"
41
42 struct link_config_ctx {
43         LIST_HEAD(link_config, links);
44
45         int ethtool_fd;
46
47         sd_rtnl *rtnl;
48
49         char **link_dirs;
50         usec_t link_dirs_ts_usec;
51 };
52
53 DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
54 #define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
55
56 int link_config_ctx_new(link_config_ctx **ret) {
57         _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
58
59         if (!ret)
60                 return -EINVAL;
61
62         ctx = new0(link_config_ctx, 1);
63         if (!ctx)
64                 return -ENOMEM;
65
66         LIST_HEAD_INIT(ctx->links);
67
68         ctx->ethtool_fd = -1;
69
70         ctx->link_dirs = strv_new("/etc/systemd/network",
71                                   "/run/systemd/network",
72                                   "/usr/lib/systemd/network",
73                                   NULL);
74         if (!ctx->link_dirs) {
75                 log_error("failed to build link config directory array");
76                 return -ENOMEM;
77         }
78
79         if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
80                 log_error("failed to canonicalize link config directories\n");
81                 return -ENOMEM;
82         }
83
84         *ret = ctx;
85         ctx = NULL;
86
87         return 0;
88 }
89
90 static int link_config_ctx_connect(link_config_ctx *ctx) {
91         int r;
92
93         if (ctx->ethtool_fd >= 0 && ctx->rtnl)
94                 return 0;
95
96         r = ethtool_connect(&ctx->ethtool_fd);
97         if (r < 0)
98                 return r;
99
100         r = sd_rtnl_open(0, &ctx->rtnl);
101         if (r < 0)
102                 return r;
103
104         return 0;
105 }
106
107 static void link_configs_free(link_config_ctx *ctx) {
108         link_config *link, *link_next;
109
110         if (!ctx)
111                 return;
112
113         LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
114                 free(link->filename);
115                 free(link->match_path);
116                 free(link->match_driver);
117                 free(link->match_type);
118                 free(link->description);
119
120                 free(link);
121         }
122 }
123
124 void link_config_ctx_free(link_config_ctx *ctx) {
125         if (!ctx)
126                 return;
127
128         if (ctx->ethtool_fd >= 0)
129                 close_nointr_nofail(ctx->ethtool_fd);
130
131         sd_rtnl_unref(ctx->rtnl);
132
133         strv_free(ctx->link_dirs);
134         link_configs_free(ctx);
135
136         free(ctx);
137
138         return;
139 }
140
141 static int load_link(link_config_ctx *ctx, const char *filename) {
142         link_config *link;
143         FILE *file;
144         int r;
145
146         file = fopen(filename, "re");
147         if (!file) {
148                 if (errno == ENOENT)
149                         return 0;
150                 else
151                         return errno;
152         }
153
154         link = new0(link_config, 1);
155         if (!link) {
156                 r = log_oom();
157                 goto failure;
158         }
159
160         link->mac_policy = _MACPOLICY_INVALID;
161         link->wol = _WOL_INVALID;
162         link->duplex = _DUP_INVALID;
163
164
165         r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
166                          (void*) link_config_gperf_lookup, false, false, link);
167         if (r < 0) {
168                 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
169                 goto failure;
170         } else
171                 log_info("Parsed configuration file %s", filename);
172
173         link->filename = strdup(filename);
174
175         LIST_PREPEND(links, ctx->links, link);
176
177         return 0;
178
179 failure:
180         free(link);
181         return r;
182 }
183
184 int link_config_load(link_config_ctx *ctx) {
185         int r;
186         char **files, **f;
187
188         link_configs_free(ctx);
189
190         /* update timestamp */
191         paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, true);
192
193         r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
194         if (r < 0) {
195                 log_error("failed to enumerate link files: %s", strerror(-r));
196                 return r;
197         }
198
199         STRV_FOREACH_BACKWARDS(f, files) {
200                 r = load_link(ctx, *f);
201                 if (r < 0)
202                         return r;
203         }
204
205         return 0;
206 }
207
208 bool link_config_should_reload(link_config_ctx *ctx) {
209         return paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, false);
210 }
211
212 static bool match_config(link_config *match, struct udev_device *device) {
213         const char *property;
214
215         if (match->match_mac) {
216                 property = udev_device_get_sysattr_value(device, "address");
217                 if (!property || memcmp(match->match_mac, ether_aton(property), ETH_ALEN)) {
218                         log_debug("Device MAC address (%s) did not match MACAddress=%s",
219                                   property, ether_ntoa(match->match_mac));
220                         return 0;
221                 }
222         }
223
224         if (match->match_path) {
225                 property = udev_device_get_property_value(device, "ID_PATH");
226                 if (!streq_ptr(match->match_path, property)) {
227                         log_debug("Device's persistent path (%s) did not match Path=%s",
228                                   property, match->match_path);
229                         return 0;
230                 }
231         }
232
233         if (match->match_driver) {
234                 property = udev_device_get_driver(device);
235                 if (!streq_ptr(match->match_driver, property)) {
236                         log_debug("Device driver (%s) did not match Driver=%s",
237                                   property, match->match_driver);
238                         return 0;
239                 }
240         }
241
242         if (match->match_type) {
243                 property = udev_device_get_devtype(device);
244                 if (!streq_ptr(match->match_type, property)) {
245                         log_debug("Device type (%s) did not match Type=%s",
246                                   property, match->match_type);
247                         return 0;
248                 }
249         }
250
251         return 1;
252 }
253
254 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
255         link_config *link;
256
257         LIST_FOREACH(links, link, ctx->links) {
258                 if (!match_config(link, device)) {
259                         log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
260                 } else {
261                         log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
262                         *ret = link;
263                         return 0;
264                 }
265         }
266
267         return -ENOENT;
268 }
269
270 static bool enable_name_policy(void) {
271         _cleanup_free_ char *line;
272         char *w, *state;
273         int r;
274         size_t l;
275
276         r = read_one_line_file("/proc/cmdline", &line);
277         if (r < 0) {
278                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
279                 return true; /* something is very wrong, let's not make it worse */
280         }
281
282         FOREACH_WORD_QUOTED(w, l, line, state)
283                 if (strneq(w, "net.ifnames=0", l)) {
284                         log_info("Link name policy disabled on kernel commandline, ignoring.");
285                         return false;
286                 }
287
288         return true;
289 }
290
291 static bool mac_is_random(struct udev_device *device) {
292         const char *s;
293         unsigned type;
294         int r;
295
296         s = udev_device_get_sysattr_value(device, "addr_assign_type");
297         if (!s)
298                 return false; /* if we don't know, assume it is not random */
299         r = safe_atou(s, &type);
300         if (r < 0)
301                 return false;
302
303         /* check for NET_ADDR_RANDOM */
304         return type == 1;
305 }
306
307 static bool mac_is_permanent(struct udev_device *device) {
308         const char *s;
309         unsigned type;
310         int r;
311
312         s = udev_device_get_sysattr_value(device, "addr_assign_type");
313         if (!s)
314                 return true; /* if we don't know, assume it is permanent */
315         r = safe_atou(s, &type);
316         if (r < 0)
317                 return true;
318
319         /* check for NET_ADDR_PERM */
320         return type == 0;
321 }
322
323 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
324         unsigned int seed;
325         int r, i;
326
327         if (want_random)
328                 seed = random_u();
329         else {
330                 const char *name;
331                 sd_id128_t machine;
332                 char machineid_buf[33];
333                 const char *seed_str;
334
335                 /* fetch some persistent data unique (on this machine) to this device */
336                 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
337                 if (!name) {
338                         name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
339                         if (!name) {
340                                 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
341                                 if (!name)
342                                         return -ENOENT;
343                         }
344                 }
345                 /* fetch some persistent data unique to this machine */
346                 r = sd_id128_get_machine(&machine);
347                 if (r < 0)
348                         return r;
349
350                 /* combine the data */
351                 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
352
353                 /* hash to get seed */
354                 seed = string_hash_func(seed_str);
355         }
356
357         srandom(seed);
358
359         for(i = 0; i < ETH_ALEN; i++) {
360                 mac->ether_addr_octet[i] = random();
361         }
362
363         /* see eth_random_addr in the kernel */
364         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
365         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
366
367         return 0;
368 }
369
370 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
371         const char *old_name;
372         const char *new_name = NULL;
373         struct ether_addr generated_mac;
374         struct ether_addr *mac = NULL;
375         int r, ifindex;
376
377         assert(ctx);
378         assert(config);
379         assert(device);
380         assert(name);
381
382         r = link_config_ctx_connect(ctx);
383         if (r < 0)
384                 return r;
385
386         old_name = udev_device_get_sysname(device);
387         if (!old_name)
388                 return -EINVAL;
389
390         log_info("Configuring %s", old_name);
391
392         if (config->description) {
393                 r = udev_device_set_sysattr_value(device, "ifalias",
394                                                   config->description);
395                 if (r < 0)
396                         log_warning("Could not set description of %s to '%s': %s",
397                                     old_name, config->description, strerror(-r));
398         }
399
400         r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed, config->duplex);
401         if (r < 0)
402                 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
403                              old_name, config->speed, duplex_to_string(config->duplex), strerror(-r));
404
405         r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
406         if (r < 0)
407                 log_warning("Could not set WakeOnLan of %s to %s: %s",
408                             old_name, wol_to_string(config->wol), strerror(-r));
409
410         ifindex = udev_device_get_ifindex(device);
411         if (ifindex <= 0) {
412                 log_warning("Could not find ifindex");
413                 return -ENODEV;
414         }
415
416         if (config->name_policy && enable_name_policy()) {
417                 NamePolicy *policy;
418
419                 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
420                         switch (*policy) {
421                                 case NAMEPOLICY_ONBOARD:
422                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
423                                         break;
424                                 case NAMEPOLICY_SLOT:
425                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
426                                         break;
427                                 case NAMEPOLICY_PATH:
428                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
429                                         break;
430                                 case NAMEPOLICY_MAC:
431                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
432                                         break;
433                                 default:
434                                         break;
435                         }
436                 }
437         }
438
439         if (new_name)
440                 *name = new_name; /* a name was set by a policy */
441         else if (config->name)
442                 *name = config->name; /* a name was set manually in the config */
443         else
444                 *name = NULL;
445
446         switch (config->mac_policy) {
447                 case MACPOLICY_PERSISTENT:
448                         if (!mac_is_permanent(device)) {
449                                 r = get_mac(device, false, &generated_mac);
450                                 if (r < 0)
451                                         return r;
452                                 mac = &generated_mac;
453                         }
454                         break;
455                 case MACPOLICY_RANDOM:
456                         if (!mac_is_random(device)) {
457                                 r = get_mac(device, true, &generated_mac);
458                                 if (r < 0)
459                                         return r;
460                                 mac = &generated_mac;
461                         }
462                         break;
463                 default:
464                         mac = config->mac;
465         }
466
467         r = rtnl_set_link_properties(ctx->rtnl, ifindex, mac, config->mtu);
468         if (r < 0) {
469                 log_warning("Could not set MACAddress or MTU on %s: %s", old_name, strerror(-r));
470                 return r;
471         }
472
473         return 0;
474 }