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