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