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