]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/net/link-config.c
ether-addr-util: introduce parse_ether_addr()
[thirdparty/systemd.git] / src / udev / net / link-config.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
af6f0d42 2
01234e1f 3#include <linux/netdevice.h>
43b3a5ef 4#include <netinet/ether.h>
a0b191b7 5#include <unistd.h>
43b3a5ef 6
e5eadf53 7#include "sd-device.h"
07630cea 8#include "sd-netlink.h"
af6f0d42 9
b5efdb8a 10#include "alloc-util.h"
07630cea
LP
11#include "conf-files.h"
12#include "conf-parser.h"
d3867133 13#include "creds-util.h"
dc0d4078 14#include "def.h"
e0e789c1 15#include "device-private.h"
b220632c 16#include "device-util.h"
a5010333 17#include "ethtool-util.h"
3ffd4af2 18#include "fd-util.h"
d3867133 19#include "fileio.h"
07630cea 20#include "link-config.h"
af6f0d42 21#include "log.h"
0a970718 22#include "memory-util.h"
7e19cc54 23#include "net-condition.h"
b355d0c9 24#include "netif-naming-scheme.h"
b5cc5591 25#include "netif-util.h"
1c4baffc 26#include "netlink-util.h"
6bedfcbb 27#include "parse-util.h"
b0c82192 28#include "path-lookup.h"
07630cea 29#include "path-util.h"
4e731273 30#include "proc-cmdline.h"
3df3e884 31#include "random-util.h"
8fcde012 32#include "stat-util.h"
8b43440b 33#include "string-table.h"
07630cea
LP
34#include "string-util.h"
35#include "strv.h"
fc27088a 36#include "utf8.h"
af6f0d42 37
afca7ac1 38struct LinkConfigContext {
ce01c07f 39 LIST_HEAD(LinkConfig, links);
a5010333 40 int ethtool_fd;
f6194225 41 bool enable_name_policy;
dc0d4078 42 usec_t network_dirs_ts_usec;
af6f0d42
TG
43};
44
ce01c07f 45static LinkConfig* link_config_free(LinkConfig *link) {
9a4b012e 46 if (!link)
75db809a 47 return NULL;
5b9d4dc0 48
9a4b012e
TG
49 free(link->filename);
50
5722fb89 51 net_match_clear(&link->match);
c4f58dea 52 condition_free_list(link->conditions);
9a4b012e
TG
53
54 free(link->description);
55 free(link->mac);
56 free(link->name_policy);
57 free(link->name);
a5053a15 58 strv_free(link->alternative_names);
ef1d2c07 59 free(link->alternative_names_policy);
9a4b012e 60 free(link->alias);
d3867133
YW
61 free(link->wol_password_file);
62 erase_and_free(link->wol_password);
9a4b012e 63
75db809a 64 return mfree(link);
af6f0d42
TG
65}
66
ce01c07f 67DEFINE_TRIVIAL_CLEANUP_FUNC(LinkConfig*, link_config_free);
9a4b012e 68
afca7ac1 69static void link_configs_free(LinkConfigContext *ctx) {
ce01c07f 70 LinkConfig *link, *link_next;
af6f0d42
TG
71
72 if (!ctx)
73 return;
74
9a4b012e
TG
75 LIST_FOREACH_SAFE(links, link, link_next, ctx->links)
76 link_config_free(link);
af6f0d42
TG
77}
78
afca7ac1 79LinkConfigContext *link_config_ctx_free(LinkConfigContext *ctx) {
af6f0d42 80 if (!ctx)
75db809a 81 return NULL;
af6f0d42 82
03e334a1 83 safe_close(ctx->ethtool_fd);
af6f0d42 84 link_configs_free(ctx);
75db809a 85 return mfree(ctx);
af6f0d42
TG
86}
87
afca7ac1
YW
88int link_config_ctx_new(LinkConfigContext **ret) {
89 _cleanup_(link_config_ctx_freep) LinkConfigContext *ctx = NULL;
9a4b012e
TG
90
91 if (!ret)
92 return -EINVAL;
93
afca7ac1 94 ctx = new(LinkConfigContext, 1);
9a4b012e
TG
95 if (!ctx)
96 return -ENOMEM;
97
afca7ac1
YW
98 *ctx = (LinkConfigContext) {
99 .ethtool_fd = -1,
100 .enable_name_policy = true,
101 };
9a4b012e 102
1cc6c93a 103 *ret = TAKE_PTR(ctx);
9a4b012e
TG
104
105 return 0;
106}
107
d3867133
YW
108static int link_parse_wol_password(LinkConfig *link, const char *str) {
109 _cleanup_(erase_and_freep) uint8_t *p = NULL;
110 int r;
111
112 assert(link);
113 assert(str);
114
115 assert_cc(sizeof(struct ether_addr) == SOPASS_MAX);
116
117 p = new(uint8_t, SOPASS_MAX);
118 if (!p)
119 return -ENOMEM;
120
121 /* Reuse ether_addr_from_string(), as their formats are equivalent. */
122 r = ether_addr_from_string(str, (struct ether_addr*) p);
123 if (r < 0)
124 return r;
125
126 erase_and_free(link->wol_password);
127 link->wol_password = TAKE_PTR(p);
128 return 0;
129}
130
131static int link_read_wol_password_from_file(LinkConfig *link) {
132 _cleanup_(erase_and_freep) char *password = NULL;
133 int r;
134
135 assert(link);
136
137 if (!link->wol_password_file)
138 return 0;
139
140 r = read_full_file_full(
141 AT_FDCWD, link->wol_password_file, UINT64_MAX, SIZE_MAX,
142 READ_FULL_FILE_SECURE | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
143 NULL, &password, NULL);
144 if (r < 0)
145 return r;
146
147 return link_parse_wol_password(link, password);
148}
149
150static int link_read_wol_password_from_cred(LinkConfig *link) {
151 _cleanup_free_ char *base = NULL, *cred_name = NULL;
152 _cleanup_(erase_and_freep) char *password = NULL;
153 int r;
154
155 assert(link);
156 assert(link->filename);
157
158 if (link->wol == UINT32_MAX)
159 return 0; /* WakeOnLan= is not specified. */
160 if (!FLAGS_SET(link->wol, WAKE_MAGICSECURE))
161 return 0; /* secureon is not specified in WakeOnLan=. */
162 if (link->wol_password)
163 return 0; /* WakeOnLanPassword= is specified. */
164 if (link->wol_password_file)
165 return 0; /* a file name is specified in WakeOnLanPassword=, but failed to read it. */
166
167 r = path_extract_filename(link->filename, &base);
168 if (r < 0)
169 return r;
170
171 cred_name = strjoin(base, ".wol.password");
172 if (!cred_name)
173 return -ENOMEM;
174
175 r = read_credential(cred_name, (void**) &password, NULL);
176 if (r == -ENOENT)
177 r = read_credential("wol.password", (void**) &password, NULL);
178 if (r < 0)
179 return r;
180
181 return link_parse_wol_password(link, password);
182}
183
184static int link_adjust_wol_options(LinkConfig *link) {
185 int r;
186
187 assert(link);
188
189 r = link_read_wol_password_from_file(link);
190 if (r == -ENOMEM)
191 return log_oom();
192 if (r < 0)
193 log_warning_errno(r, "Failed to read WakeOnLan password from %s, ignoring: %m", link->wol_password_file);
194
195 r = link_read_wol_password_from_cred(link);
196 if (r == -ENOMEM)
197 return log_oom();
198 if (r < 0)
199 log_warning_errno(r, "Failed to read WakeOnLan password from credential, ignoring: %m");
200
201 if (link->wol != UINT32_MAX && link->wol_password)
202 /* Enable WAKE_MAGICSECURE flag when WakeOnLanPassword=. Note that when
203 * WakeOnLanPassword= is set without WakeOnLan=, then ethtool_set_wol() enables
204 * WAKE_MAGICSECURE flag and other flags are not changed. */
205 link->wol |= WAKE_MAGICSECURE;
206
207 return 0;
208}
209
afca7ac1 210int link_load_one(LinkConfigContext *ctx, const char *filename) {
ce01c07f 211 _cleanup_(link_config_freep) LinkConfig *link = NULL;
6cdab9f1 212 _cleanup_free_ char *name = NULL;
e406e8a2 213 const char *dropin_dirname;
79a60834 214 size_t i;
af6f0d42
TG
215 int r;
216
187dc6e5
TA
217 assert(ctx);
218 assert(filename);
219
e8e2788d
YW
220 r = null_or_empty_path(filename);
221 if (r == -ENOENT)
222 return 0;
223 if (r < 0)
224 return r;
225 if (r > 0) {
ed88bcfb
ZJS
226 log_debug("Skipping empty file: %s", filename);
227 return 0;
228 }
229
6cdab9f1
YW
230 name = strdup(filename);
231 if (!name)
232 return -ENOMEM;
233
ce01c07f 234 link = new(LinkConfig, 1);
ecb08ec6 235 if (!link)
e8a42907 236 return -ENOMEM;
af6f0d42 237
ce01c07f 238 *link = (LinkConfig) {
6cdab9f1 239 .filename = TAKE_PTR(name),
54ed9f88 240 .mac_address_policy = _MAC_ADDRESS_POLICY_INVALID,
c50404ae 241 .wol = UINT32_MAX, /* UINT32_MAX means do not change WOL setting. */
6cdab9f1
YW
242 .duplex = _DUP_INVALID,
243 .port = _NET_DEV_PORT_INVALID,
244 .autonegotiation = -1,
a34811e4
YW
245 .rx_flow_control = -1,
246 .tx_flow_control = -1,
247 .autoneg_flow_control = -1,
ef4a91a7 248 .txqueuelen = UINT32_MAX,
ee751240
YW
249 .coalesce.use_adaptive_rx_coalesce = -1,
250 .coalesce.use_adaptive_tx_coalesce = -1,
6cdab9f1 251 };
5fde13d7 252
79a60834 253 for (i = 0; i < ELEMENTSOF(link->features); i++)
cc2ff878 254 link->features[i] = -1;
50725d10 255
e406e8a2
YW
256 dropin_dirname = strjoina(basename(filename), ".d");
257 r = config_parse_many(
258 STRV_MAKE_CONST(filename),
259 (const char* const*) CONF_PATHS_STRV("systemd/network"),
260 dropin_dirname,
261 "Match\0Link\0",
262 config_item_perf_lookup, link_config_gperf_lookup,
263 CONFIG_PARSE_WARN, link, NULL);
36f822c4 264 if (r < 0)
ecb08ec6 265 return r;
af6f0d42 266
5722fb89 267 if (net_match_is_empty(&link->match) && !link->conditions) {
dade7349
ZJS
268 log_warning("%s: No valid settings found in the [Match] section, ignoring file. "
269 "To match all interfaces, add OriginalName=* in the [Match] section.",
84ea567e 270 filename);
dade7349
ZJS
271 return 0;
272 }
84ea567e 273
a0b191b7 274 if (!condition_test_list(link->conditions, environ, NULL, NULL, NULL)) {
176d9c0e
YW
275 log_debug("%s: Conditions do not match the system environment, skipping.", filename);
276 return 0;
277 }
278
a7a12bf4
YW
279 if (IN_SET(link->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM) && link->mac) {
280 log_warning("%s: MACAddress= in [Link] section will be ignored when MACAddressPolicy= "
281 "is set to \"persistent\" or \"random\".",
282 filename);
283 link->mac = mfree(link->mac);
284 }
285
d3867133
YW
286 r = link_adjust_wol_options(link);
287 if (r < 0)
288 return r;
289
6cdab9f1 290 log_debug("Parsed configuration file %s", filename);
af6f0d42 291
4b4a6c9b 292 LIST_PREPEND(links, ctx->links, TAKE_PTR(link));
af6f0d42 293 return 0;
af6f0d42
TG
294}
295
f6194225 296static bool enable_name_policy(void) {
1d84ad94 297 bool b;
f6194225 298
1d84ad94 299 return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
f6194225
TG
300}
301
015b097c 302static int link_unsigned_attribute(sd_device *device, const char *attr, unsigned *type) {
0b189e8f
ZJS
303 const char *s;
304 int r;
305
015b097c 306 r = sd_device_get_sysattr_value(device, attr, &s);
0b189e8f 307 if (r < 0)
015b097c 308 return log_device_debug_errno(device, r, "Failed to query %s: %m", attr);
0b189e8f
ZJS
309
310 r = safe_atou(s, type);
311 if (r < 0)
015b097c 312 return log_device_warning_errno(device, r, "Failed to parse %s \"%s\": %m", attr, s);
0b189e8f 313
015b097c 314 log_device_debug(device, "Device has %s=%u", attr, *type);
0b189e8f
ZJS
315 return 0;
316}
317
afca7ac1 318int link_config_load(LinkConfigContext *ctx) {
b9c54c46 319 _cleanup_strv_free_ char **files = NULL;
edf029b7 320 char **f;
a39f92d3 321 int r;
af6f0d42
TG
322
323 link_configs_free(ctx);
324
f6194225
TG
325 if (!enable_name_policy()) {
326 ctx->enable_name_policy = false;
3f85ef0f 327 log_info("Network interface NamePolicy= disabled on kernel command line, ignoring.");
f6194225
TG
328 }
329
97f2d76d 330 /* update timestamp */
dc0d4078 331 paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, true);
af6f0d42 332
dc0d4078 333 r = conf_files_list_strv(&files, ".link", NULL, 0, NETWORK_DIRS);
f647962d
MS
334 if (r < 0)
335 return log_error_errno(r, "failed to enumerate link files: %m");
af6f0d42
TG
336
337 STRV_FOREACH_BACKWARDS(f, files) {
a378400b 338 r = link_load_one(ctx, *f);
af6f0d42 339 if (r < 0)
e8a42907 340 log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
af6f0d42
TG
341 }
342
343 return 0;
344}
345
afca7ac1 346bool link_config_should_reload(LinkConfigContext *ctx) {
dc0d4078 347 return paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, false);
af6f0d42
TG
348}
349
751dcd8d 350int link_config_get(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, LinkConfig **ret) {
f5767302 351 unsigned name_assign_type = NET_NAME_UNKNOWN;
4bb7cc82 352 struct ether_addr permanent_mac = {};
f25e642b 353 unsigned short iftype;
ce01c07f 354 LinkConfig *link;
4bb7cc82 355 const char *name;
70f32a26 356 unsigned flags;
ef62949a 357 int ifindex, r;
af6f0d42 358
3b64e4d4 359 assert(ctx);
751dcd8d 360 assert(rtnl);
3b64e4d4
TG
361 assert(device);
362 assert(ret);
363
4bb7cc82
YW
364 r = sd_device_get_sysname(device, &name);
365 if (r < 0)
366 return r;
367
ef62949a
YW
368 r = sd_device_get_ifindex(device, &ifindex);
369 if (r < 0)
370 return r;
371
751dcd8d 372 r = rtnl_get_link_info(rtnl, ifindex, &iftype, &flags);
ef62949a
YW
373 if (r < 0)
374 return r;
375
70f32a26
YW
376 /* Do not configure loopback interfaces by .link files. */
377 if (flags & IFF_LOOPBACK)
378 return -ENOENT;
379
4bb7cc82
YW
380 r = ethtool_get_permanent_macaddr(&ctx->ethtool_fd, name, &permanent_mac);
381 if (r < 0)
382 log_device_debug_errno(device, r, "Failed to get permanent MAC address, ignoring: %m");
383
f5767302
YW
384 (void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type);
385
af6f0d42 386 LIST_FOREACH(links, link, ctx->links) {
1a3caa49
YW
387 r = net_match_config(&link->match, device, NULL, &permanent_mac, NULL, iftype, NULL, NULL, 0, NULL, NULL);
388 if (r < 0)
389 return r;
390 if (r == 0)
391 continue;
392
393 if (link->match.ifname && !strv_contains(link->match.ifname, "*") && name_assign_type == NET_NAME_ENUM)
394 log_device_warning(device, "Config file %s is applied to device based on potentially unpredictable interface name.",
395 link->filename);
396 else
397 log_device_debug(device, "Config file %s is applied", link->filename);
398
399 *ret = link;
400 return 0;
af6f0d42
TG
401 }
402
403 return -ENOENT;
404}
405
ce01c07f 406static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig *config, sd_device *device) {
2e17fed5
YW
407 const char *name;
408 int r;
409
410 assert(ethtool_fd);
411 assert(config);
412 assert(device);
413
414 r = sd_device_get_sysname(device, &name);
415 if (r < 0)
416 return log_device_error_errno(device, r, "Failed to get sysname: %m");
417
418 r = ethtool_set_glinksettings(ethtool_fd, name,
419 config->autonegotiation, config->advertise,
420 config->speed, config->duplex, config->port);
421 if (r < 0) {
a7994dd3
YW
422 if (config->autonegotiation >= 0)
423 log_device_warning_errno(device, r, "Could not %s auto negotiation, ignoring: %m",
424 enable_disable(config->autonegotiation));
2e17fed5
YW
425
426 if (!eqzero(config->advertise))
a7994dd3
YW
427 log_device_warning_errno(device, r, "Could not set advertise mode, ignoring: %m");
428
429 if (config->speed > 0)
430 log_device_warning_errno(device, r, "Could not set speed to %"PRIu64"Mbps, ignoring: %m",
431 DIV_ROUND_UP(config->speed, 1000000));
432
433 if (config->duplex >= 0)
434 log_device_warning_errno(device, r, "Could not set duplex to %s, ignoring: %m",
435 duplex_to_string(config->duplex));
436
437 if (config->port >= 0)
438 log_device_warning_errno(device, r, "Could not set port to '%s', ignoring: %m",
439 port_to_string(config->port));
2e17fed5
YW
440 }
441
d3867133 442 r = ethtool_set_wol(ethtool_fd, name, config->wol, config->wol_password);
c50404ae
YW
443 if (r < 0) {
444 _cleanup_free_ char *str = NULL;
445
446 (void) wol_options_to_string_alloc(config->wol, &str);
b4b2a492
YW
447 log_device_warning_errno(device, r, "Could not set WakeOnLan%s%s, ignoring: %m",
448 isempty(str) ? "" : " to ", strempty(str));
c50404ae 449 }
2e17fed5
YW
450
451 r = ethtool_set_features(ethtool_fd, name, config->features);
452 if (r < 0)
453 log_device_warning_errno(device, r, "Could not set offload features, ignoring: %m");
454
80662eec
YW
455 r = ethtool_set_channels(ethtool_fd, name, &config->channels);
456 if (r < 0)
457 log_device_warning_errno(device, r, "Could not set channels, ignoring: %m");
2e17fed5 458
80662eec
YW
459 r = ethtool_set_nic_buffer_size(ethtool_fd, name, &config->ring);
460 if (r < 0)
461 log_device_warning_errno(device, r, "Could not set ring buffer, ignoring: %m");
2e17fed5 462
80662eec
YW
463 r = ethtool_set_flow_control(ethtool_fd, name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
464 if (r < 0)
465 log_device_warning_errno(device, r, "Could not set flow control, ignoring: %m");
2e17fed5 466
6c35ea5e
DDM
467 r = ethtool_set_nic_coalesce_settings(ethtool_fd, name, &config->coalesce);
468 if (r < 0)
469 log_device_warning_errno(device, r, "Could not set coalesce settings, ignoring: %m");
470
2e17fed5
YW
471 return 0;
472}
473
54ed9f88 474static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr *mac) {
015b097c 475 unsigned addr_type;
54ed9f88 476 bool want_random = policy == MAC_ADDRESS_POLICY_RANDOM;
f1ac7002 477 int r;
16b9b87a 478
54ed9f88 479 assert(IN_SET(policy, MAC_ADDRESS_POLICY_RANDOM, MAC_ADDRESS_POLICY_PERSISTENT));
3c9b8860 480
015b097c 481 r = link_unsigned_attribute(device, "addr_assign_type", &addr_type);
f1ac7002 482 if (r < 0)
015b097c
ZJS
483 return r;
484 switch (addr_type) {
485 case NET_ADDR_SET:
9d9fed9e
ZJS
486 log_device_debug(device, "MAC on the device already set by userspace");
487 return 0;
015b097c 488 case NET_ADDR_STOLEN:
9d9fed9e
ZJS
489 log_device_debug(device, "MAC on the device already set based on another device");
490 return 0;
015b097c
ZJS
491 case NET_ADDR_RANDOM:
492 case NET_ADDR_PERM:
493 break;
494 default:
09c69eca
YW
495 log_device_warning(device, "Unknown addr_assign_type %u, ignoring", addr_type);
496 return 0;
015b097c 497 }
04b67d49 498
9d9fed9e
ZJS
499 if (want_random == (addr_type == NET_ADDR_RANDOM)) {
500 log_device_debug(device, "MAC on the device already matches policy *%s*",
501 mac_address_policy_to_string(policy));
502 return 0;
503 }
16b9b87a 504
015b097c
ZJS
505 if (want_random) {
506 log_device_debug(device, "Using random bytes to generate MAC");
550c8784
LP
507
508 /* We require genuine randomness here, since we want to make sure we won't collide with other
509 * systems booting up at the very same time. We do allow RDRAND however, since this is not
510 * cryptographic key material. */
a745117d
LP
511 r = genuine_random_bytes(mac->ether_addr_octet, ETH_ALEN, RANDOM_ALLOW_RDRAND);
512 if (r < 0)
513 return log_device_error_errno(device, r, "Failed to acquire random data to generate MAC: %m");
015b097c 514 } else {
dbe81cbd 515 uint64_t result;
9bf3b535 516
96848152
ZJS
517 r = net_get_unique_predictable_data(device,
518 naming_scheme_has(NAMING_STABLE_VIRTUAL_MACS),
519 &result);
16b9b87a 520 if (r < 0)
015b097c 521 return log_device_warning_errno(device, r, "Could not generate persistent MAC: %m");
16b9b87a 522
96848152 523 log_device_debug(device, "Using generated persistent MAC address");
9bf3b535 524 assert_cc(ETH_ALEN <= sizeof(result));
dbe81cbd 525 memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
16b9b87a
TG
526 }
527
528 /* see eth_random_addr in the kernel */
3c9b8860
TG
529 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
530 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
015b097c 531 return 1;
16b9b87a
TG
532}
533
ce01c07f 534static int link_config_apply_rtnl_settings(sd_netlink **rtnl, const LinkConfig *config, sd_device *device) {
2e17fed5
YW
535 struct ether_addr generated_mac, *mac = NULL;
536 int ifindex, r;
af6f0d42 537
2e17fed5 538 assert(rtnl);
3e137a1b
TG
539 assert(config);
540 assert(device);
3e137a1b 541
2e17fed5 542 r = sd_device_get_ifindex(device, &ifindex);
e5eadf53 543 if (r < 0)
2e17fed5 544 return log_device_error_errno(device, r, "Could not find ifindex: %m");
af6f0d42 545
2e17fed5
YW
546 if (IN_SET(config->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM)) {
547 if (get_mac(device, config->mac_address_policy, &generated_mac) > 0)
548 mac = &generated_mac;
549 } else
550 mac = config->mac;
af6f0d42 551
face9fcc
YW
552 r = rtnl_set_link_properties(rtnl, ifindex, config->alias, mac,
553 config->txqueues, config->rxqueues, config->txqueuelen,
554 config->mtu, config->gso_max_size, config->gso_max_segments);
50725d10 555 if (r < 0)
face9fcc
YW
556 log_device_warning_errno(device, r,
557 "Could not set Alias=, MACAddress=, "
558 "TransmitQueues=, ReceiveQueues=, TransmitQueueLength=, MTU=, "
559 "GenericSegmentOffloadMaxBytes= or GenericSegmentOffloadMaxSegments=, "
560 "ignoring: %m");
50725d10 561
2e17fed5
YW
562 return 0;
563}
224ded67 564
ce01c07f 565static int link_config_generate_new_name(const LinkConfigContext *ctx, const LinkConfig *config, sd_device *device, const char **ret_name) {
2e17fed5 566 unsigned name_type = NET_NAME_UNKNOWN;
2e17fed5 567 int r;
a34811e4 568
2e17fed5
YW
569 assert(ctx);
570 assert(config);
571 assert(device);
572 assert(ret_name);
43b3a5ef 573
015b097c 574 (void) link_unsigned_attribute(device, "name_assign_type", &name_type);
0b189e8f 575
73d2bb08
ZJS
576 if (IN_SET(name_type, NET_NAME_USER, NET_NAME_RENAMED)
577 && !naming_scheme_has(NAMING_ALLOW_RERENAMES)) {
578 log_device_debug(device, "Device already has a name given by userspace, not renaming.");
579 goto no_rename;
580 }
581
0b189e8f 582 if (ctx->enable_name_policy && config->name_policy)
78f8849f 583 for (NamePolicy *p = config->name_policy; *p != _NAMEPOLICY_INVALID; p++) {
5cdb3f70
YW
584 const char *new_name = NULL;
585 NamePolicy policy = *p;
0b189e8f
ZJS
586
587 switch (policy) {
588 case NAMEPOLICY_KERNEL:
589 if (name_type != NET_NAME_PREDICTABLE)
590 continue;
591
592 /* The kernel claims to have given a predictable name, keep it. */
593 log_device_debug(device, "Policy *%s*: keeping predictable kernel name",
594 name_policy_to_string(policy));
595 goto no_rename;
3907446f
ZJS
596 case NAMEPOLICY_KEEP:
597 if (!IN_SET(name_type, NET_NAME_USER, NET_NAME_RENAMED))
598 continue;
599
600 log_device_debug(device, "Policy *%s*: keeping existing userspace name",
601 name_policy_to_string(policy));
602 goto no_rename;
0b189e8f
ZJS
603 case NAMEPOLICY_DATABASE:
604 (void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &new_name);
605 break;
606 case NAMEPOLICY_ONBOARD:
607 (void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &new_name);
608 break;
609 case NAMEPOLICY_SLOT:
610 (void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &new_name);
611 break;
612 case NAMEPOLICY_PATH:
613 (void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &new_name);
614 break;
615 case NAMEPOLICY_MAC:
616 (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &new_name);
617 break;
618 default:
04499a70 619 assert_not_reached();
5fde13d7 620 }
5cdb3f70
YW
621 if (ifname_valid(new_name)) {
622 log_device_debug(device, "Policy *%s* yields \"%s\".", name_policy_to_string(policy), new_name);
623 *ret_name = new_name;
624 return 0;
625 }
daeb71a3 626 }
daeb71a3 627
2e17fed5
YW
628 if (config->name) {
629 log_device_debug(device, "Policies didn't yield a name, using specified Name=%s.", config->name);
630 *ret_name = config->name;
631 return 0;
632 }
16b9b87a 633
2e17fed5
YW
634 log_device_debug(device, "Policies didn't yield a name and Name= is not given, not renaming.");
635no_rename:
636 r = sd_device_get_sysname(device, ret_name);
f647962d 637 if (r < 0)
2e17fed5
YW
638 return log_device_error_errno(device, r, "Failed to get sysname: %m");
639
640 return 0;
641}
642
ce01c07f 643static int link_config_apply_alternative_names(sd_netlink **rtnl, const LinkConfig *config, sd_device *device, const char *new_name) {
2e17fed5
YW
644 _cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL;
645 const char *current_name;
646 int ifindex, r;
647
648 assert(rtnl);
649 assert(config);
650 assert(device);
651
652 r = sd_device_get_sysname(device, &current_name);
653 if (r < 0)
654 return log_device_error_errno(device, r, "Failed to get sysname: %m");
655
656 r = sd_device_get_ifindex(device, &ifindex);
657 if (r < 0)
658 return log_device_error_errno(device, r, "Could not find ifindex: %m");
43b3a5ef 659
ef1d2c07
YW
660 if (config->alternative_names) {
661 altnames = strv_copy(config->alternative_names);
662 if (!altnames)
663 return log_oom();
664 }
665
666 if (config->alternative_names_policy)
667 for (NamePolicy *p = config->alternative_names_policy; *p != _NAMEPOLICY_INVALID; p++) {
61fd7d67 668 const char *n = NULL;
ef1d2c07
YW
669
670 switch (*p) {
671 case NAMEPOLICY_DATABASE:
672 (void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &n);
673 break;
674 case NAMEPOLICY_ONBOARD:
675 (void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &n);
676 break;
677 case NAMEPOLICY_SLOT:
678 (void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &n);
679 break;
680 case NAMEPOLICY_PATH:
681 (void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &n);
682 break;
683 case NAMEPOLICY_MAC:
684 (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &n);
685 break;
686 default:
04499a70 687 assert_not_reached();
ef1d2c07
YW
688 }
689 if (!isempty(n)) {
690 r = strv_extend(&altnames, n);
691 if (r < 0)
692 return log_oom();
693 }
694 }
695
696 if (new_name)
697 strv_remove(altnames, new_name);
2e17fed5 698 strv_remove(altnames, current_name);
97fdae33 699
2e17fed5 700 r = rtnl_get_link_alternative_names(rtnl, ifindex, &current_altnames);
97fdae33 701 if (r < 0)
2e17fed5 702 log_device_debug_errno(device, r, "Failed to get alternative names, ignoring: %m");
97fdae33
YW
703
704 char **p;
705 STRV_FOREACH(p, current_altnames)
706 strv_remove(altnames, *p);
707
ef1d2c07 708 strv_uniq(altnames);
4d016e96 709 strv_sort(altnames);
2e17fed5
YW
710 r = rtnl_set_link_alternative_names(rtnl, ifindex, altnames);
711 if (r < 0)
712 log_device_full_errno(device, r == -EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, r,
713 "Could not set AlternativeName= or apply AlternativeNamesPolicy=, ignoring: %m");
714
715 return 0;
716}
a5053a15 717
751dcd8d 718int link_config_apply(LinkConfigContext *ctx, const LinkConfig *config, sd_netlink **rtnl, sd_device *device, const char **ret_name) {
2e17fed5 719 const char *new_name;
a1130022 720 sd_device_action_t a;
2e17fed5
YW
721 int r;
722
723 assert(ctx);
724 assert(config);
751dcd8d 725 assert(rtnl);
2e17fed5
YW
726 assert(device);
727 assert(ret_name);
728
a1130022 729 r = sd_device_get_action(device, &a);
e0e789c1
YW
730 if (r < 0)
731 return log_device_error_errno(device, r, "Failed to get ACTION= property: %m");
732
a1130022 733 if (!IN_SET(a, SD_DEVICE_ADD, SD_DEVICE_BIND, SD_DEVICE_MOVE)) {
e0e789c1
YW
734 log_device_debug(device, "Skipping to apply .link settings on '%s' uevent.", device_action_to_string(a));
735
736 r = sd_device_get_sysname(device, ret_name);
737 if (r < 0)
738 return log_device_error_errno(device, r, "Failed to get sysname: %m");
739
740 return 0;
741 }
742
2e17fed5
YW
743 r = link_config_apply_ethtool_settings(&ctx->ethtool_fd, config, device);
744 if (r < 0)
745 return r;
746
751dcd8d 747 r = link_config_apply_rtnl_settings(rtnl, config, device);
2e17fed5
YW
748 if (r < 0)
749 return r;
750
a1130022 751 if (a == SD_DEVICE_MOVE) {
e0e789c1
YW
752 log_device_debug(device, "Skipping to apply Name= and NamePolicy= on '%s' uevent.", device_action_to_string(a));
753
754 r = sd_device_get_sysname(device, &new_name);
755 if (r < 0)
756 return log_device_error_errno(device, r, "Failed to get sysname: %m");
757 } else {
758 r = link_config_generate_new_name(ctx, config, device, &new_name);
759 if (r < 0)
760 return r;
761 }
2e17fed5 762
751dcd8d 763 r = link_config_apply_alternative_names(rtnl, config, device, new_name);
2e17fed5
YW
764 if (r < 0)
765 return r;
d95b83b8 766
2e17fed5 767 *ret_name = new_name;
af6f0d42
TG
768 return 0;
769}
be32eb9b 770
afca7ac1 771int link_get_driver(LinkConfigContext *ctx, sd_device *device, char **ret) {
847a8a5f 772 const char *name;
a7f7d1bd 773 char *driver = NULL;
847a8a5f
TG
774 int r;
775
e5eadf53
YW
776 r = sd_device_get_sysname(device, &name);
777 if (r < 0)
778 return r;
847a8a5f 779
aedca892 780 r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver);
847a8a5f
TG
781 if (r < 0)
782 return r;
783
784 *ret = driver;
785 return 0;
786}
787
fc27088a
YW
788int config_parse_ifalias(
789 const char *unit,
790 const char *filename,
791 unsigned line,
792 const char *section,
793 unsigned section_line,
794 const char *lvalue,
795 int ltype,
796 const char *rvalue,
797 void *data,
798 void *userdata) {
799
800 char **s = data;
801
802 assert(filename);
803 assert(lvalue);
804 assert(rvalue);
805 assert(data);
806
807 if (!isempty(rvalue)) {
808 *s = mfree(*s);
809 return 0;
810 }
811
812 if (!ascii_is_valid(rvalue)) {
813 log_syntax(unit, LOG_WARNING, filename, line, 0,
814 "Interface alias is not ASCII clean, ignoring assignment: %s", rvalue);
815 return 0;
816 }
817
818 if (strlen(rvalue) >= IFALIASZ) {
819 log_syntax(unit, LOG_WARNING, filename, line, 0,
820 "Interface alias is too long, ignoring assignment: %s", rvalue);
821 return 0;
822 }
823
b3f9c17a 824 return free_and_strdup_warn(s, rvalue);
fc27088a
YW
825}
826
face9fcc
YW
827int config_parse_rx_tx_queues(
828 const char *unit,
829 const char *filename,
830 unsigned line,
831 const char *section,
832 unsigned section_line,
833 const char *lvalue,
834 int ltype,
835 const char *rvalue,
836 void *data,
837 void *userdata) {
838
839 uint32_t k, *v = data;
840 int r;
841
842 if (isempty(rvalue)) {
843 *v = 0;
844 return 0;
845 }
846
847 r = safe_atou32(rvalue, &k);
848 if (r < 0) {
849 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
850 return 0;
851 }
852 if (k == 0 || k > 4096) {
853 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid %s=, ignoring assignment: %s.", lvalue, rvalue);
854 return 0;
855 }
856
857 *v = k;
858 return 0;
859}
860
ef4a91a7
861int config_parse_txqueuelen(
862 const char *unit,
863 const char *filename,
864 unsigned line,
865 const char *section,
866 unsigned section_line,
867 const char *lvalue,
868 int ltype,
869 const char *rvalue,
870 void *data,
871 void *userdata) {
872
873 uint32_t k, *v = data;
874 int r;
875
876 if (isempty(rvalue)) {
877 *v = UINT32_MAX;
878 return 0;
879 }
880
881 r = safe_atou32(rvalue, &k);
882 if (r < 0) {
883 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
884 return 0;
885 }
886 if (k == UINT32_MAX) {
887 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid %s=, ignoring assignment: %s.", lvalue, rvalue);
888 return 0;
889 }
890
891 *v = k;
892 return 0;
893}
894
d3867133
YW
895int config_parse_wol_password(
896 const char *unit,
897 const char *filename,
898 unsigned line,
899 const char *section,
900 unsigned section_line,
901 const char *lvalue,
902 int ltype,
903 const char *rvalue,
904 void *data,
905 void *userdata) {
906
907 LinkConfig *link = userdata;
908 int r;
909
910 assert(filename);
911 assert(lvalue);
912 assert(rvalue);
913 assert(userdata);
914
915 if (isempty(rvalue)) {
916 link->wol_password = erase_and_free(link->wol_password);
917 link->wol_password_file = mfree(link->wol_password_file);
918 return 0;
919 }
920
921 if (path_is_absolute(rvalue) && path_is_safe(rvalue)) {
922 link->wol_password = erase_and_free(link->wol_password);
923 return free_and_strdup_warn(&link->wol_password_file, rvalue);
924 }
925
926 warn_file_is_world_accessible(filename, NULL, unit, line);
927
928 r = link_parse_wol_password(link, rvalue);
929 if (r == -ENOMEM)
930 return log_oom();
931 if (r < 0) {
932 log_syntax(unit, LOG_WARNING, filename, line, r,
933 "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
934 return 0;
935 }
936
937 link->wol_password_file = mfree(link->wol_password_file);
938 return 0;
939}
940
54ed9f88
ZJS
941static const char* const mac_address_policy_table[_MAC_ADDRESS_POLICY_MAX] = {
942 [MAC_ADDRESS_POLICY_PERSISTENT] = "persistent",
943 [MAC_ADDRESS_POLICY_RANDOM] = "random",
944 [MAC_ADDRESS_POLICY_NONE] = "none",
be32eb9b
TG
945};
946
54ed9f88 947DEFINE_STRING_TABLE_LOOKUP(mac_address_policy, MACAddressPolicy);
d03cb6b8
YW
948DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(
949 config_parse_mac_address_policy,
950 mac_address_policy,
951 MACAddressPolicy,
952 MAC_ADDRESS_POLICY_NONE,
953 "Failed to parse MAC address policy");
be32eb9b 954
2c5859af 955static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
04b67d49 956 [NAMEPOLICY_KERNEL] = "kernel",
3907446f 957 [NAMEPOLICY_KEEP] = "keep",
e51660ae 958 [NAMEPOLICY_DATABASE] = "database",
be32eb9b
TG
959 [NAMEPOLICY_ONBOARD] = "onboard",
960 [NAMEPOLICY_SLOT] = "slot",
961 [NAMEPOLICY_PATH] = "path",
3907446f 962 [NAMEPOLICY_MAC] = "mac",
be32eb9b
TG
963};
964
965DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
464cf22f
TG
966DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
967 _NAMEPOLICY_INVALID,
968 "Failed to parse interface name policy");
ef1d2c07
YW
969
970static const char* const alternative_names_policy_table[_NAMEPOLICY_MAX] = {
971 [NAMEPOLICY_DATABASE] = "database",
972 [NAMEPOLICY_ONBOARD] = "onboard",
973 [NAMEPOLICY_SLOT] = "slot",
974 [NAMEPOLICY_PATH] = "path",
975 [NAMEPOLICY_MAC] = "mac",
976};
977
978DEFINE_STRING_TABLE_LOOKUP(alternative_names_policy, NamePolicy);
979DEFINE_CONFIG_PARSE_ENUMV(config_parse_alternative_names_policy, alternative_names_policy, NamePolicy,
980 _NAMEPOLICY_INVALID,
981 "Failed to parse alternative names policy");