chiark / gitweb /
path_check_timestamp: only keep the most recent timestamp
[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 int link_config_ctx_new(link_config_ctx **ret) {
53         link_config_ctx *ctx;
54         int r;
55
56         if (!ret)
57                 return -EINVAL;
58
59         ctx = new0(link_config_ctx, 1);
60         if (!ctx)
61                 return -ENOMEM;
62
63         r = ethtool_connect(&ctx->ethtool_fd);
64         if (r < 0) {
65                 link_config_ctx_free(ctx);
66                 return r;
67         }
68
69         r = sd_rtnl_open(0, &ctx->rtnl);
70         if (r < 0) {
71                 link_config_ctx_free(ctx);
72                 return r;
73         }
74
75         LIST_HEAD_INIT(ctx->links);
76
77         ctx->link_dirs = strv_new("/etc/systemd/network",
78                                   "/run/systemd/network",
79                                   "/usr/lib/systemd/network",
80                                   NULL);
81         if (!ctx->link_dirs) {
82                 log_error("failed to build link config directory array");
83                 link_config_ctx_free(ctx);
84                 return -ENOMEM;
85         }
86         if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
87                 log_error("failed to canonicalize link config directories\n");
88                 link_config_ctx_free(ctx);
89                 return -ENOMEM;
90         }
91
92         *ret = ctx;
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, 5 * USEC_PER_SEC, 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         int type;
326
327         s = udev_device_get_sysattr_value(device, "addr_assign_type");
328         if (!s)
329                 return -EINVAL;
330         type = strtoul(s, NULL, 0);
331
332         /* check for NET_ADDR_RANDOM */
333         return type == 1;
334 }
335
336 static bool mac_is_permanent(struct udev_device *device) {
337         const char *s;
338         int type;
339
340         s = udev_device_get_sysattr_value(device, "addr_assign_type");
341         if (!s)
342                 return -EINVAL;
343         type = strtoul(s, NULL, 0);
344
345         /* check for NET_ADDR_PERM */
346         return type == 0;
347 }
348
349 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
350         unsigned int seed;
351         int r, i;
352
353         if (want_random)
354                 seed = random_u();
355         else {
356                 const char *name;
357                 sd_id128_t machine;
358                 char machineid_buf[33];
359                 const char *seed_str;
360
361                 /* fetch some persistent data unique (on this machine) to this device */
362                 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
363                 if (!name) {
364                         name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
365                         if (!name) {
366                                 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
367                                 if (!name)
368                                         return -1;
369                         }
370                 }
371                 /* fetch some persistent data unique to this machine */
372                 r = sd_id128_get_machine(&machine);
373                 if (r < 0)
374                         return -1;
375
376                 /* combine the data */
377                 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
378
379                 /* hash to get seed */
380                 seed = string_hash_func(seed_str);
381         }
382
383         srandom(seed);
384
385         for(i = 0; i < ETH_ALEN; i++) {
386                 mac->ether_addr_octet[i] = random();
387         }
388
389         /* see eth_random_addr in the kernel */
390         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
391         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
392
393         return 0;
394 }
395
396 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
397         const char *name;
398         const char *new_name = NULL;
399         struct ether_addr generated_mac;
400         struct ether_addr *mac = NULL;
401         int r, ifindex;
402
403         name = udev_device_get_sysname(device);
404         if (!name)
405                 return -EINVAL;
406
407         log_info("Configuring %s", name);
408
409         if (config->description) {
410                 r = udev_device_set_sysattr_value(device, "ifalias",
411                                                   config->description);
412                 if (r < 0)
413                         log_warning("Could not set description of %s to '%s': %s",
414                                     name, config->description, strerror(-r));
415         }
416
417         r = ethtool_set_speed(ctx->ethtool_fd, name, config->speed, config->duplex);
418         if (r < 0)
419                 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
420                              name, config->speed, duplex_to_string(config->duplex), strerror(-r));
421
422         r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
423         if (r < 0)
424                 log_warning("Could not set WakeOnLan of %s to %s: %s",
425                             name, wol_to_string(config->wol), strerror(-r));
426
427         ifindex = udev_device_get_ifindex(device);
428         if (ifindex <= 0) {
429                 log_warning("Could not find ifindex");
430                 return -ENODEV;
431         }
432
433         if (config->name_policy && enable_name_policy()) {
434                 NamePolicy *policy;
435
436                 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
437                         switch (*policy) {
438                                 case NAMEPOLICY_ONBOARD:
439                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
440                                         break;
441                                 case NAMEPOLICY_SLOT:
442                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
443                                         break;
444                                 case NAMEPOLICY_PATH:
445                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
446                                         break;
447                                 case NAMEPOLICY_MAC:
448                                         new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
449                                         break;
450                                 default:
451                                         break;
452                         }
453                 }
454         }
455
456         if (!new_name && config->name) {
457                 new_name = config->name;
458         }
459
460         switch (config->mac_policy) {
461                 case MACPOLICY_PERSISTENT:
462                         if (!mac_is_permanent(device)) {
463                                 r = get_mac(device, false, &generated_mac);
464                                 if (r < 0)
465                                         return r;
466                                 mac = &generated_mac;
467                         }
468                         break;
469                 case MACPOLICY_RANDOM:
470                         if (!mac_is_random(device)) {
471                                 r = get_mac(device, true, &generated_mac);
472                                 if (r < 0)
473                                         return r;
474                                 mac = &generated_mac;
475                         }
476                         break;
477                 default:
478                         mac = config->mac;
479         }
480
481         r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu);
482         if (r < 0) {
483                 log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r));
484                 return r;
485         }
486
487         return 0;
488 }