chiark / gitweb /
udev link-config: add asserts to silence scan-build
[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                 free(link->alias);
128
129                 free(link);
130         }
131 }
132
133 void link_config_ctx_free(link_config_ctx *ctx) {
134         if (!ctx)
135                 return;
136
137         if (ctx->ethtool_fd >= 0)
138                 close_nointr_nofail(ctx->ethtool_fd);
139
140         sd_rtnl_unref(ctx->rtnl);
141
142         strv_free(ctx->link_dirs);
143         link_configs_free(ctx);
144
145         free(ctx);
146
147         return;
148 }
149
150 static int load_link(link_config_ctx *ctx, const char *filename) {
151         link_config *link;
152         _cleanup_fclose_ FILE *file;
153         int r;
154
155         assert(ctx);
156         assert(filename);
157
158         file = fopen(filename, "re");
159         if (!file) {
160                 if (errno == ENOENT)
161                         return 0;
162                 else
163                         return errno;
164         }
165
166         link = new0(link_config, 1);
167         if (!link) {
168                 r = log_oom();
169                 goto failure;
170         }
171
172         link->mac_policy = _MACPOLICY_INVALID;
173         link->wol = _WOL_INVALID;
174         link->duplex = _DUP_INVALID;
175
176         r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
177                          (void*) link_config_gperf_lookup, false, false, link);
178         if (r < 0) {
179                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
180                 goto failure;
181         } else
182                 log_debug("Parsed configuration file %s", filename);
183
184         link->filename = strdup(filename);
185
186         LIST_PREPEND(links, ctx->links, link);
187
188         return 0;
189
190 failure:
191         free(link);
192         return r;
193 }
194
195 static bool enable_name_policy(void) {
196         _cleanup_free_ char *line;
197         char *w, *state;
198         int r;
199         size_t l;
200
201         r = proc_cmdline(&line);
202         if (r < 0)
203                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
204         if (r <= 0)
205                 return true;
206
207         FOREACH_WORD_QUOTED(w, l, line, state)
208                 if (strneq(w, "net.ifnames=0", l))
209                         return false;
210
211         return true;
212 }
213
214 int link_config_load(link_config_ctx *ctx) {
215         int r;
216         char **files, **f;
217
218         link_configs_free(ctx);
219
220         if (!enable_name_policy()) {
221                 ctx->enable_name_policy = false;
222                 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
223         }
224
225         /* update timestamp */
226         paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, true);
227
228         r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
229         if (r < 0) {
230                 log_error("failed to enumerate link files: %s", strerror(-r));
231                 return r;
232         }
233
234         STRV_FOREACH_BACKWARDS(f, files) {
235                 r = load_link(ctx, *f);
236                 if (r < 0)
237                         return r;
238         }
239
240         return 0;
241 }
242
243 bool link_config_should_reload(link_config_ctx *ctx) {
244         return paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, false);
245 }
246
247 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
248         link_config *link;
249
250         LIST_FOREACH(links, link, ctx->links) {
251
252                 if (net_match_config(link->match_mac, link->match_path,
253                                      link->match_driver, link->match_type, NULL,
254                                      udev_device_get_sysattr_value(device, "address"),
255                                      udev_device_get_property_value(device, "ID_PATH"),
256                                      udev_device_get_driver(device),
257                                      udev_device_get_devtype(device),
258                                      NULL)) {
259                         log_debug("Config file %s applies to device %s",
260                                   link->filename,
261                                   udev_device_get_sysname(device));
262                         *ret = link;
263                         return 0;
264                 }
265         }
266
267         *ret = NULL;
268
269         return -ENOENT;
270 }
271
272 static bool mac_is_random(struct udev_device *device) {
273         const char *s;
274         unsigned type;
275         int r;
276
277         s = udev_device_get_sysattr_value(device, "addr_assign_type");
278         if (!s)
279                 return false; /* if we don't know, assume it is not random */
280         r = safe_atou(s, &type);
281         if (r < 0)
282                 return false;
283
284         /* check for NET_ADDR_RANDOM */
285         return type == 1;
286 }
287
288 static bool mac_is_permanent(struct udev_device *device) {
289         const char *s;
290         unsigned type;
291         int r;
292
293         s = udev_device_get_sysattr_value(device, "addr_assign_type");
294         if (!s)
295                 return true; /* if we don't know, assume it is permanent */
296         r = safe_atou(s, &type);
297         if (r < 0)
298                 return true;
299
300         /* check for NET_ADDR_PERM */
301         return type == 0;
302 }
303
304 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
305         unsigned int seed;
306         int r, i;
307
308         if (want_random)
309                 seed = random_u();
310         else {
311                 const char *name;
312                 sd_id128_t machine;
313                 char machineid_buf[33];
314                 const char *seed_str;
315
316                 /* fetch some persistent data unique (on this machine) to this device */
317                 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
318                 if (!name) {
319                         name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
320                         if (!name) {
321                                 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
322                                 if (!name)
323                                         return -ENOENT;
324                         }
325                 }
326                 /* fetch some persistent data unique to this machine */
327                 r = sd_id128_get_machine(&machine);
328                 if (r < 0)
329                         return r;
330
331                 /* combine the data */
332                 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
333
334                 /* hash to get seed */
335                 seed = string_hash_func(seed_str);
336         }
337
338         srandom(seed);
339
340         for (i = 0; i < ETH_ALEN; i++) {
341                 mac->ether_addr_octet[i] = random();
342         }
343
344         /* see eth_random_addr in the kernel */
345         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
346         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
347
348         return 0;
349 }
350
351 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
352         const char *old_name;
353         const char *new_name = NULL;
354         struct ether_addr generated_mac;
355         struct ether_addr *mac = NULL;
356         int r, ifindex;
357
358         assert(ctx);
359         assert(config);
360         assert(device);
361         assert(name);
362
363         r = link_config_ctx_connect(ctx);
364         if (r < 0)
365                 return r;
366
367         old_name = udev_device_get_sysname(device);
368         if (!old_name)
369                 return -EINVAL;
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, config->alias, mac, config->mtu);
439         if (r < 0) {
440                 log_warning("Could not set Alias, 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");