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