]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/net/link-config.c
udev: net - allow MTU and Speed to be specified with units
[thirdparty/systemd.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 #include "net-util.h"
42 #include "siphash24.h"
43
44 struct link_config_ctx {
45 LIST_HEAD(link_config, links);
46
47 int ethtool_fd;
48
49 bool enable_name_policy;
50
51 sd_rtnl *rtnl;
52
53 usec_t link_dirs_ts_usec;
54 };
55
56 static const char* const link_dirs[] = {
57 "/etc/systemd/network",
58 "/run/systemd/network",
59 "/usr/lib/systemd/network",
60 #ifdef HAVE_SPLIT_USR
61 "/lib/systemd/network",
62 #endif
63 NULL};
64
65 DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
66 #define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
67
68 int link_config_ctx_new(link_config_ctx **ret) {
69 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
70
71 if (!ret)
72 return -EINVAL;
73
74 ctx = new0(link_config_ctx, 1);
75 if (!ctx)
76 return -ENOMEM;
77
78 LIST_HEAD_INIT(ctx->links);
79
80 ctx->ethtool_fd = -1;
81
82 ctx->enable_name_policy = true;
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 == -1) {
94 r = ethtool_connect(&ctx->ethtool_fd);
95 if (r < 0)
96 return r;
97 }
98
99 if (!ctx->rtnl) {
100 r = sd_rtnl_open(&ctx->rtnl, 0);
101 if (r < 0)
102 return r;
103 }
104
105 return 0;
106 }
107
108 static void link_configs_free(link_config_ctx *ctx) {
109 link_config *link, *link_next;
110
111 if (!ctx)
112 return;
113
114 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
115 free(link->filename);
116 free(link->match_path);
117 free(link->match_driver);
118 free(link->match_type);
119 free(link->description);
120 free(link->alias);
121
122 free(link);
123 }
124 }
125
126 void link_config_ctx_free(link_config_ctx *ctx) {
127 if (!ctx)
128 return;
129
130 if (ctx->ethtool_fd >= 0)
131 close_nointr_nofail(ctx->ethtool_fd);
132
133 sd_rtnl_unref(ctx->rtnl);
134
135 link_configs_free(ctx);
136
137 free(ctx);
138
139 return;
140 }
141
142 static int load_link(link_config_ctx *ctx, const char *filename) {
143 link_config *link;
144 _cleanup_fclose_ FILE *file;
145 int r;
146
147 assert(ctx);
148 assert(filename);
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 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
169 (void*) link_config_gperf_lookup, false, false, link);
170 if (r < 0) {
171 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
172 goto failure;
173 } else
174 log_debug("Parsed configuration file %s", filename);
175
176 link->filename = strdup(filename);
177
178 LIST_PREPEND(links, ctx->links, link);
179
180 return 0;
181
182 failure:
183 free(link);
184 return r;
185 }
186
187 static bool enable_name_policy(void) {
188 _cleanup_free_ char *line;
189 char *w, *state;
190 int r;
191 size_t l;
192
193 r = proc_cmdline(&line);
194 if (r < 0)
195 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
196 if (r <= 0)
197 return true;
198
199 FOREACH_WORD_QUOTED(w, l, line, state)
200 if (strneq(w, "net.ifnames=0", l))
201 return false;
202
203 return true;
204 }
205
206 int link_config_load(link_config_ctx *ctx) {
207 int r;
208 char **files, **f;
209
210 link_configs_free(ctx);
211
212 if (!enable_name_policy()) {
213 ctx->enable_name_policy = false;
214 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
215 }
216
217 /* update timestamp */
218 paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true);
219
220 r = conf_files_list_strv(&files, ".link", NULL, link_dirs);
221 if (r < 0) {
222 log_error("failed to enumerate link files: %s", strerror(-r));
223 return r;
224 }
225
226 STRV_FOREACH_BACKWARDS(f, files) {
227 r = load_link(ctx, *f);
228 if (r < 0)
229 return r;
230 }
231
232 return 0;
233 }
234
235 bool link_config_should_reload(link_config_ctx *ctx) {
236 return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
237 }
238
239 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
240 link_config *link;
241
242 LIST_FOREACH(links, link, ctx->links) {
243
244 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
245 link->match_type, NULL, link->match_host,
246 link->match_virt, link->match_kernel, link->match_arch,
247 udev_device_get_sysattr_value(device, "address"),
248 udev_device_get_property_value(device, "ID_PATH"),
249 udev_device_get_driver(udev_device_get_parent(device)),
250 udev_device_get_property_value(device, "ID_NET_DRIVER"),
251 udev_device_get_devtype(device),
252 NULL)) {
253 log_debug("Config file %s applies to device %s",
254 link->filename,
255 udev_device_get_sysname(device));
256 *ret = link;
257 return 0;
258 }
259 }
260
261 *ret = NULL;
262
263 return -ENOENT;
264 }
265
266 static bool mac_is_random(struct udev_device *device) {
267 const char *s;
268 unsigned type;
269 int r;
270
271 s = udev_device_get_sysattr_value(device, "addr_assign_type");
272 if (!s)
273 return false; /* if we don't know, assume it is not random */
274 r = safe_atou(s, &type);
275 if (r < 0)
276 return false;
277
278 /* check for NET_ADDR_RANDOM */
279 return type == 1;
280 }
281
282 static bool mac_is_permanent(struct udev_device *device) {
283 const char *s;
284 unsigned type;
285 int r;
286
287 s = udev_device_get_sysattr_value(device, "addr_assign_type");
288 if (!s)
289 return true; /* if we don't know, assume it is permanent */
290 r = safe_atou(s, &type);
291 if (r < 0)
292 return true;
293
294 /* check for NET_ADDR_PERM */
295 return type == 0;
296 }
297
298 #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
299
300 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
301 int r;
302
303 if (want_random)
304 random_bytes(mac->ether_addr_octet, ETH_ALEN);
305 else {
306 const char *name;
307 uint8_t result[8];
308 size_t l, sz;
309 uint8_t *v;
310
311 /* fetch some persistent data unique (on this machine) to this device */
312 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
313 if (!name) {
314 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
315 if (!name) {
316 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
317 if (!name)
318 return -ENOENT;
319 }
320 }
321
322 l = strlen(name);
323 sz = sizeof(sd_id128_t) + l;
324 v = alloca(sz);
325
326 /* fetch some persistent data unique to this machine */
327 r = sd_id128_get_machine((sd_id128_t*) v);
328 if (r < 0)
329 return r;
330 memcpy(v + sizeof(sd_id128_t), name, l);
331
332 /* Let's hash the machine ID plus the device name. We
333 * use a fixed, but originally randomly created hash
334 * key here. */
335 siphash24(result, v, sz, HASH_KEY.bytes);
336
337 assert_cc(ETH_ALEN <= sizeof(result));
338 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
339 }
340
341 /* see eth_random_addr in the kernel */
342 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
343 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
344
345 return 0;
346 }
347
348 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
349 const char *old_name;
350 const char *new_name = NULL;
351 struct ether_addr generated_mac;
352 struct ether_addr *mac = NULL;
353 int r, ifindex;
354
355 assert(ctx);
356 assert(config);
357 assert(device);
358 assert(name);
359
360 r = link_config_ctx_connect(ctx);
361 if (r < 0)
362 return r;
363
364 old_name = udev_device_get_sysname(device);
365 if (!old_name)
366 return -EINVAL;
367
368 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
369 if (r < 0)
370 log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s",
371 old_name, config->speed / 1024, duplex_to_string(config->duplex),
372 strerror(-r));
373
374 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
375 if (r < 0)
376 log_warning("Could not set WakeOnLan of %s to %s: %s",
377 old_name, wol_to_string(config->wol), strerror(-r));
378
379 ifindex = udev_device_get_ifindex(device);
380 if (ifindex <= 0) {
381 log_warning("Could not find ifindex");
382 return -ENODEV;
383 }
384
385 if (ctx->enable_name_policy && config->name_policy) {
386 NamePolicy *policy;
387
388 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
389 switch (*policy) {
390 case NAMEPOLICY_DATABASE:
391 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
392 break;
393 case NAMEPOLICY_ONBOARD:
394 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
395 break;
396 case NAMEPOLICY_SLOT:
397 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
398 break;
399 case NAMEPOLICY_PATH:
400 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
401 break;
402 case NAMEPOLICY_MAC:
403 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
404 break;
405 default:
406 break;
407 }
408 }
409 }
410
411 if (new_name)
412 *name = new_name; /* a name was set by a policy */
413 else if (config->name)
414 *name = config->name; /* a name was set manually in the config */
415 else
416 *name = NULL;
417
418 switch (config->mac_policy) {
419 case MACPOLICY_PERSISTENT:
420 if (!mac_is_permanent(device)) {
421 r = get_mac(device, false, &generated_mac);
422 if (r < 0)
423 return r;
424 mac = &generated_mac;
425 }
426 break;
427 case MACPOLICY_RANDOM:
428 if (!mac_is_random(device)) {
429 r = get_mac(device, true, &generated_mac);
430 if (r < 0)
431 return r;
432 mac = &generated_mac;
433 }
434 break;
435 default:
436 mac = config->mac;
437 }
438
439 r = rtnl_set_link_properties(ctx->rtnl, ifindex, config->alias, mac, config->mtu);
440 if (r < 0) {
441 log_warning("Could not set Alias, MACAddress or MTU on %s: %s", old_name, strerror(-r));
442 return r;
443 }
444
445 return 0;
446 }
447
448 int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
449 const char *name;
450 char *driver;
451 int r;
452
453 r = link_config_ctx_connect(ctx);
454 if (r < 0)
455 return r;
456
457 name = udev_device_get_sysname(device);
458 if (!name)
459 return -EINVAL;
460
461 r = ethtool_get_driver(ctx->ethtool_fd, name, &driver);
462 if (r < 0)
463 return r;
464
465 *ret = driver;
466 return 0;
467 }
468
469 static const char* const mac_policy_table[] = {
470 [MACPOLICY_PERSISTENT] = "persistent",
471 [MACPOLICY_RANDOM] = "random"
472 };
473
474 DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
475 DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, "Failed to parse MAC address policy");
476
477 static const char* const name_policy_table[] = {
478 [NAMEPOLICY_DATABASE] = "database",
479 [NAMEPOLICY_ONBOARD] = "onboard",
480 [NAMEPOLICY_SLOT] = "slot",
481 [NAMEPOLICY_PATH] = "path",
482 [NAMEPOLICY_MAC] = "mac"
483 };
484
485 DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
486 DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, _NAMEPOLICY_INVALID, "Failed to parse interface name policy");