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