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