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