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