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