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