]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/net/link-config.c
udev: event - keep one rtnl per worker, rather than per event
[thirdparty/systemd.git] / src / udev / net / link-config.c
CommitLineData
af6f0d42
TG
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
43b3a5ef 22#include <netinet/ether.h>
04b67d49 23#include <linux/netdevice.h>
43b3a5ef 24
16b9b87a 25#include "sd-id128.h"
af6f0d42 26
04b67d49 27#include "missing.h"
16b9b87a 28#include "link-config.h"
a5010333
TG
29#include "ethtool-util.h"
30
43b3a5ef
TG
31#include "libudev-private.h"
32#include "sd-rtnl.h"
af6f0d42
TG
33#include "util.h"
34#include "log.h"
35#include "strv.h"
36#include "path-util.h"
37#include "conf-parser.h"
38#include "conf-files.h"
daeb71a3 39#include "fileio.h"
16b9b87a 40#include "hashmap.h"
3aeb37bc 41#include "rtnl-util.h"
c6f7c917 42#include "network-internal.h"
9bf3b535 43#include "siphash24.h"
af6f0d42
TG
44
45struct link_config_ctx {
46 LIST_HEAD(link_config, links);
47
a5010333
TG
48 int ethtool_fd;
49
f6194225
TG
50 bool enable_name_policy;
51
43b3a5ef
TG
52 sd_rtnl *rtnl;
53
97f2d76d 54 usec_t link_dirs_ts_usec;
af6f0d42
TG
55};
56
2ad8416d
ZJS
57static const char* const link_dirs[] = {
58 "/etc/systemd/network",
59 "/run/systemd/network",
60 "/usr/lib/systemd/network",
61#ifdef HAVE_SPLIT_USR
62 "/lib/systemd/network",
63#endif
64 NULL};
65
5b9d4dc0
TG
66DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
67#define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
68
af6f0d42 69int link_config_ctx_new(link_config_ctx **ret) {
5b9d4dc0 70 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
af6f0d42
TG
71
72 if (!ret)
73 return -EINVAL;
74
75 ctx = new0(link_config_ctx, 1);
76 if (!ctx)
77 return -ENOMEM;
78
79 LIST_HEAD_INIT(ctx->links);
80
97708579
TG
81 ctx->ethtool_fd = -1;
82
f6194225
TG
83 ctx->enable_name_policy = true;
84
af6f0d42 85 *ret = ctx;
5b9d4dc0
TG
86 ctx = NULL;
87
af6f0d42
TG
88 return 0;
89}
90
97708579
TG
91static int link_config_ctx_connect(link_config_ctx *ctx) {
92 int r;
93
847a8a5f
TG
94 if (ctx->ethtool_fd == -1) {
95 r = ethtool_connect(&ctx->ethtool_fd);
866ee368
TG
96 if (r < 0) {
97 log_warning("link_config: could not connect to ethtool: %s",
98 strerror(-r));
847a8a5f 99 return r;
866ee368 100 }
847a8a5f 101 }
97708579 102
847a8a5f
TG
103 if (!ctx->rtnl) {
104 r = sd_rtnl_open(&ctx->rtnl, 0);
866ee368
TG
105 if (r < 0) {
106 log_warning("link_config: could not connect to rtnl: %s",
107 strerror(-r));
847a8a5f 108 return r;
866ee368 109 }
847a8a5f 110 }
97708579
TG
111
112 return 0;
113}
114
af6f0d42
TG
115static void link_configs_free(link_config_ctx *ctx) {
116 link_config *link, *link_next;
117
118 if (!ctx)
119 return;
120
121 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
122 free(link->filename);
123 free(link->match_path);
124 free(link->match_driver);
125 free(link->match_type);
126 free(link->description);
d2df0d0e 127 free(link->alias);
edf029b7 128 free(link->name_policy);
af6f0d42
TG
129
130 free(link);
131 }
132}
133
134void link_config_ctx_free(link_config_ctx *ctx) {
135 if (!ctx)
136 return;
137
03e334a1 138 safe_close(ctx->ethtool_fd);
43b3a5ef
TG
139
140 sd_rtnl_unref(ctx->rtnl);
141
af6f0d42
TG
142 link_configs_free(ctx);
143
144 free(ctx);
145
146 return;
147}
148
149static int load_link(link_config_ctx *ctx, const char *filename) {
ecb08ec6 150 _cleanup_free_ link_config *link = NULL;
6e37cd2f 151 _cleanup_fclose_ FILE *file = NULL;
af6f0d42
TG
152 int r;
153
187dc6e5
TA
154 assert(ctx);
155 assert(filename);
156
af6f0d42
TG
157 file = fopen(filename, "re");
158 if (!file) {
159 if (errno == ENOENT)
160 return 0;
161 else
ecb08ec6 162 return -errno;
af6f0d42
TG
163 }
164
ed88bcfb
ZJS
165 if (null_or_empty_fd(fileno(file))) {
166 log_debug("Skipping empty file: %s", filename);
167 return 0;
168 }
169
af6f0d42 170 link = new0(link_config, 1);
ecb08ec6
ZJS
171 if (!link)
172 return log_oom();
af6f0d42 173
5fde13d7
TG
174 link->mac_policy = _MACPOLICY_INVALID;
175 link->wol = _WOL_INVALID;
176 link->duplex = _DUP_INVALID;
177
e9f3d2d5
ZJS
178 r = config_parse(NULL, filename, file,
179 "Match\0Link\0Ethernet\0",
180 config_item_perf_lookup, link_config_gperf_lookup,
36f822c4
ZJS
181 false, false, true, link);
182 if (r < 0)
ecb08ec6 183 return r;
36f822c4 184 else
98a375f6 185 log_debug("Parsed configuration file %s", filename);
af6f0d42
TG
186
187 link->filename = strdup(filename);
188
189 LIST_PREPEND(links, ctx->links, link);
ecb08ec6 190 link = NULL;
af6f0d42
TG
191
192 return 0;
af6f0d42
TG
193}
194
f6194225 195static bool enable_name_policy(void) {
f8a0bb52 196 _cleanup_free_ char *line = NULL;
a2a5291b 197 const char *word, *state;
f6194225
TG
198 int r;
199 size_t l;
200
74df0fca
LP
201 r = proc_cmdline(&line);
202 if (r < 0)
464cf22f
TG
203 log_warning("Failed to read /proc/cmdline, ignoring: %s",
204 strerror(-r));
74df0fca
LP
205 if (r <= 0)
206 return true;
f6194225 207
a2a5291b
ZJS
208 FOREACH_WORD_QUOTED(word, l, line, state)
209 if (strneq(word, "net.ifnames=0", l))
f6194225
TG
210 return false;
211
212 return true;
213}
214
af6f0d42
TG
215int link_config_load(link_config_ctx *ctx) {
216 int r;
edf029b7
TG
217 _cleanup_strv_free_ char **files;
218 char **f;
af6f0d42
TG
219
220 link_configs_free(ctx);
221
f6194225
TG
222 if (!enable_name_policy()) {
223 ctx->enable_name_policy = false;
224 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
225 }
226
97f2d76d 227 /* update timestamp */
2ad8416d 228 paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true);
af6f0d42 229
2ad8416d 230 r = conf_files_list_strv(&files, ".link", NULL, link_dirs);
af6f0d42
TG
231 if (r < 0) {
232 log_error("failed to enumerate link files: %s", strerror(-r));
233 return r;
234 }
235
236 STRV_FOREACH_BACKWARDS(f, files) {
237 r = load_link(ctx, *f);
238 if (r < 0)
239 return r;
240 }
241
242 return 0;
243}
244
245bool link_config_should_reload(link_config_ctx *ctx) {
2ad8416d 246 return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
af6f0d42
TG
247}
248
464cf22f
TG
249int link_config_get(link_config_ctx *ctx, struct udev_device *device,
250 link_config **ret) {
af6f0d42
TG
251 link_config *link;
252
253 LIST_FOREACH(links, link, ctx->links) {
eb7040ec 254 const char* attr_value = udev_device_get_sysattr_value(device, "address");
b3e01314 255
edbb03e9
TG
256 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
257 link->match_type, NULL, link->match_host,
258 link->match_virt, link->match_kernel, link->match_arch,
eb7040ec 259 attr_value ? ether_aton(attr_value) : NULL,
b3e01314 260 udev_device_get_property_value(device, "ID_PATH"),
9b1c2626 261 udev_device_get_driver(udev_device_get_parent(device)),
bf175aaf 262 udev_device_get_property_value(device, "ID_NET_DRIVER"),
b3e01314
TG
263 udev_device_get_devtype(device),
264 NULL)) {
be32eb9b
TG
265 log_debug("Config file %s applies to device %s",
266 link->filename,
267 udev_device_get_sysname(device));
af6f0d42
TG
268 *ret = link;
269 return 0;
270 }
271 }
272
be32eb9b
TG
273 *ret = NULL;
274
af6f0d42
TG
275 return -ENOENT;
276}
277
16b9b87a
TG
278static bool mac_is_random(struct udev_device *device) {
279 const char *s;
f1ac7002
TG
280 unsigned type;
281 int r;
16b9b87a 282
3c9b8860 283 /* if we can't get the assign type, assume it is not random */
16b9b87a
TG
284 s = udev_device_get_sysattr_value(device, "addr_assign_type");
285 if (!s)
3c9b8860
TG
286 return false;
287
f1ac7002
TG
288 r = safe_atou(s, &type);
289 if (r < 0)
290 return false;
16b9b87a 291
04b67d49
TG
292 return type == NET_ADDR_RANDOM;
293}
294
295static bool should_rename(struct udev_device *device, bool respect_predictable) {
296 const char *s;
297 unsigned type;
298 int r;
299
3c9b8860 300 /* if we can't get the assgin type, assume we should rename */
04b67d49
TG
301 s = udev_device_get_sysattr_value(device, "name_assign_type");
302 if (!s)
3c9b8860
TG
303 return true;
304
04b67d49
TG
305 r = safe_atou(s, &type);
306 if (r < 0)
307 return true;
308
309 switch (type) {
310 case NET_NAME_USER:
311 case NET_NAME_RENAMED:
3c9b8860
TG
312 /* these were already named by userspace, do not touch again */
313 return false;
04b67d49 314 case NET_NAME_PREDICTABLE:
3c9b8860 315 /* the kernel claims to have given a predictable name */
04b67d49 316 if (respect_predictable)
3c9b8860 317 return false;
04b67d49
TG
318 /* fall through */
319 case NET_NAME_ENUM:
320 default:
3c9b8860
TG
321 /* the name is known to be bad, or of an unknown type */
322 return true;
04b67d49 323 }
16b9b87a
TG
324}
325
464cf22f
TG
326static int get_mac(struct udev_device *device, bool want_random,
327 struct ether_addr *mac) {
9bf3b535 328 int r;
16b9b87a 329
16b9b87a 330 if (want_random)
9bf3b535 331 random_bytes(mac->ether_addr_octet, ETH_ALEN);
16b9b87a 332 else {
9bf3b535 333 uint8_t result[8];
9bf3b535 334
b5db00e5 335 r = net_get_unique_predictable_data(device, result);
16b9b87a 336 if (r < 0)
55428d84 337 return r;
16b9b87a 338
9bf3b535
LP
339 assert_cc(ETH_ALEN <= sizeof(result));
340 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
16b9b87a
TG
341 }
342
343 /* see eth_random_addr in the kernel */
3c9b8860
TG
344 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
345 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
16b9b87a 346
16b9b87a
TG
347 return 0;
348}
349
464cf22f
TG
350int link_config_apply(link_config_ctx *ctx, link_config *config,
351 struct udev_device *device, const char **name) {
3e137a1b 352 const char *old_name;
5fde13d7
TG
353 const char *new_name = NULL;
354 struct ether_addr generated_mac;
16b9b87a 355 struct ether_addr *mac = NULL;
04b67d49 356 bool respect_predictable = false;
43b3a5ef 357 int r, ifindex;
af6f0d42 358
3e137a1b
TG
359 assert(ctx);
360 assert(config);
361 assert(device);
362 assert(name);
363
97708579
TG
364 r = link_config_ctx_connect(ctx);
365 if (r < 0)
366 return r;
367
3e137a1b
TG
368 old_name = udev_device_get_sysname(device);
369 if (!old_name)
af6f0d42
TG
370 return -EINVAL;
371
464cf22f
TG
372 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed / 1024,
373 config->duplex);
5fde13d7 374 if (r < 0)
733f7a2c 375 log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s",
464cf22f
TG
376 old_name, config->speed / 1024,
377 duplex_to_string(config->duplex), strerror(-r));
a5010333 378
3e137a1b 379 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
5fde13d7
TG
380 if (r < 0)
381 log_warning("Could not set WakeOnLan of %s to %s: %s",
3e137a1b 382 old_name, wol_to_string(config->wol), strerror(-r));
af6f0d42 383
43b3a5ef
TG
384 ifindex = udev_device_get_ifindex(device);
385 if (ifindex <= 0) {
386 log_warning("Could not find ifindex");
387 return -ENODEV;
388 }
389
f6194225 390 if (ctx->enable_name_policy && config->name_policy) {
5fde13d7 391 NamePolicy *policy;
daeb71a3 392
68ba3877
TG
393 for (policy = config->name_policy;
394 !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
5fde13d7 395 switch (*policy) {
04b67d49
TG
396 case NAMEPOLICY_KERNEL:
397 respect_predictable = true;
398 break;
e51660ae
TG
399 case NAMEPOLICY_DATABASE:
400 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
401 break;
5fde13d7
TG
402 case NAMEPOLICY_ONBOARD:
403 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
daeb71a3 404 break;
5fde13d7
TG
405 case NAMEPOLICY_SLOT:
406 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
daeb71a3 407 break;
5fde13d7
TG
408 case NAMEPOLICY_PATH:
409 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
daeb71a3 410 break;
5fde13d7
TG
411 case NAMEPOLICY_MAC:
412 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
daeb71a3 413 break;
5fde13d7
TG
414 default:
415 break;
416 }
daeb71a3
TG
417 }
418 }
419
04b67d49 420 if (should_rename(device, respect_predictable)) {
3c9b8860 421 /* if not set by policy, fall back manually set name */
04b67d49 422 if (!new_name)
04b67d49
TG
423 new_name = config->name;
424 } else
425 new_name = NULL;
426
5fde13d7
TG
427 switch (config->mac_policy) {
428 case MACPOLICY_PERSISTENT:
92d927f8 429 if (mac_is_random(device)) {
5fde13d7 430 r = get_mac(device, false, &generated_mac);
a669ea98
TG
431 if (r == -ENOENT)
432 break;
433 else if (r < 0)
16b9b87a 434 return r;
5fde13d7 435 mac = &generated_mac;
16b9b87a 436 }
5fde13d7
TG
437 break;
438 case MACPOLICY_RANDOM:
16b9b87a 439 if (!mac_is_random(device)) {
5fde13d7 440 r = get_mac(device, true, &generated_mac);
a669ea98
TG
441 if (r == -ENOENT)
442 break;
443 else if (r < 0)
16b9b87a 444 return r;
5fde13d7 445 mac = &generated_mac;
16b9b87a 446 }
5fde13d7
TG
447 break;
448 default:
449 mac = config->mac;
16b9b87a
TG
450 }
451
464cf22f
TG
452 r = rtnl_set_link_properties(ctx->rtnl, ifindex, config->alias, mac,
453 config->mtu);
43b3a5ef 454 if (r < 0) {
464cf22f
TG
455 log_warning("Could not set Alias, MACAddress or MTU on %s: %s",
456 old_name, strerror(-r));
5fde13d7 457 return r;
43b3a5ef
TG
458 }
459
d95b83b8
TG
460 *name = new_name;
461
af6f0d42
TG
462 return 0;
463}
be32eb9b 464
847a8a5f
TG
465int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
466 const char *name;
467 char *driver;
468 int r;
469
470 r = link_config_ctx_connect(ctx);
471 if (r < 0)
472 return r;
473
474 name = udev_device_get_sysname(device);
475 if (!name)
476 return -EINVAL;
477
478 r = ethtool_get_driver(ctx->ethtool_fd, name, &driver);
479 if (r < 0)
480 return r;
481
482 *ret = driver;
483 return 0;
484}
485
2c5859af 486static const char* const mac_policy_table[_MACPOLICY_MAX] = {
be32eb9b
TG
487 [MACPOLICY_PERSISTENT] = "persistent",
488 [MACPOLICY_RANDOM] = "random"
489};
490
491DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
464cf22f
TG
492DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy,
493 "Failed to parse MAC address policy");
be32eb9b 494
2c5859af 495static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
04b67d49 496 [NAMEPOLICY_KERNEL] = "kernel",
e51660ae 497 [NAMEPOLICY_DATABASE] = "database",
be32eb9b
TG
498 [NAMEPOLICY_ONBOARD] = "onboard",
499 [NAMEPOLICY_SLOT] = "slot",
500 [NAMEPOLICY_PATH] = "path",
501 [NAMEPOLICY_MAC] = "mac"
502};
503
504DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
464cf22f
TG
505DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
506 _NAMEPOLICY_INVALID,
507 "Failed to parse interface name policy");