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