]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/udev/net/link-config.c
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / udev / net / link-config.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <linux/netdevice.h>
4#include <net/if_arp.h>
5#include <unistd.h>
6
7#include "sd-device.h"
8#include "sd-netlink.h"
9
10#include "alloc-util.h"
11#include "arphrd-util.h"
12#include "condition.h"
13#include "conf-files.h"
14#include "conf-parser.h"
15#include "creds-util.h"
16#include "device-private.h"
17#include "device-util.h"
18#include "escape.h"
19#include "ether-addr-util.h"
20#include "ethtool-util.h"
21#include "extract-word.h"
22#include "fd-util.h"
23#include "fileio.h"
24#include "hashmap.h"
25#include "link-config.h"
26#include "log-link.h"
27#include "memory-util.h"
28#include "net-condition.h"
29#include "netif-naming-scheme.h"
30#include "netif-sriov.h"
31#include "netif-util.h"
32#include "netlink-util.h"
33#include "network-util.h"
34#include "parse-util.h"
35#include "path-util.h"
36#include "proc-cmdline.h"
37#include "random-util.h"
38#include "socket-util.h"
39#include "specifier.h"
40#include "stat-util.h"
41#include "string-table.h"
42#include "string-util.h"
43#include "strv.h"
44#include "udev-builtin.h"
45#include "utf8.h"
46
47static const Specifier link_specifier_table[] = {
48 COMMON_SYSTEM_SPECIFIERS,
49 COMMON_TMP_SPECIFIERS,
50 {}
51};
52
53struct LinkConfigContext {
54 LIST_HEAD(LinkConfig, configs);
55 int ethtool_fd;
56 Hashmap *stats_by_path;
57};
58
59static LinkConfig* link_config_free(LinkConfig *config) {
60 if (!config)
61 return NULL;
62
63 free(config->filename);
64 strv_free(config->dropins);
65
66 net_match_clear(&config->match);
67 condition_free_list(config->conditions);
68
69 free(config->description);
70 strv_free(config->properties);
71 strv_free(config->import_properties);
72 strv_free(config->unset_properties);
73 free(config->name_policy);
74 free(config->name);
75 strv_free(config->alternative_names);
76 free(config->alternative_names_policy);
77 free(config->alias);
78 free(config->wol_password_file);
79 erase_and_free(config->wol_password);
80 cpu_set_done(&config->rps_cpu_mask);
81
82 ordered_hashmap_free(config->sr_iov_by_section);
83
84 return mfree(config);
85}
86
87DEFINE_TRIVIAL_CLEANUP_FUNC(LinkConfig*, link_config_free);
88
89static void link_configs_free(LinkConfigContext *ctx) {
90 if (!ctx)
91 return;
92
93 ctx->stats_by_path = hashmap_free(ctx->stats_by_path);
94
95 LIST_FOREACH(configs, config, ctx->configs)
96 link_config_free(config);
97}
98
99LinkConfigContext *link_config_ctx_free(LinkConfigContext *ctx) {
100 if (!ctx)
101 return NULL;
102
103 safe_close(ctx->ethtool_fd);
104 link_configs_free(ctx);
105 return mfree(ctx);
106}
107
108int link_config_ctx_new(LinkConfigContext **ret) {
109 _cleanup_(link_config_ctx_freep) LinkConfigContext *ctx = NULL;
110
111 if (!ret)
112 return -EINVAL;
113
114 ctx = new(LinkConfigContext, 1);
115 if (!ctx)
116 return -ENOMEM;
117
118 *ctx = (LinkConfigContext) {
119 .ethtool_fd = -EBADF,
120 };
121
122 *ret = TAKE_PTR(ctx);
123
124 return 0;
125}
126
127static int link_parse_wol_password(LinkConfig *config, const char *str) {
128 _cleanup_(erase_and_freep) uint8_t *p = NULL;
129 int r;
130
131 assert(config);
132 assert(str);
133
134 assert_cc(sizeof(struct ether_addr) == SOPASS_MAX);
135
136 p = new(uint8_t, SOPASS_MAX);
137 if (!p)
138 return -ENOMEM;
139
140 /* Reuse parse_ether_addr(), as their formats are equivalent. */
141 r = parse_ether_addr(str, (struct ether_addr*) p);
142 if (r < 0)
143 return r;
144
145 erase_and_free(config->wol_password);
146 config->wol_password = TAKE_PTR(p);
147 return 0;
148}
149
150static int link_read_wol_password_from_file(LinkConfig *config) {
151 _cleanup_(erase_and_freep) char *password = NULL;
152 int r;
153
154 assert(config);
155
156 if (!config->wol_password_file)
157 return 0;
158
159 r = read_full_file_full(
160 AT_FDCWD, config->wol_password_file, UINT64_MAX, SIZE_MAX,
161 READ_FULL_FILE_SECURE | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
162 NULL, &password, NULL);
163 if (r < 0)
164 return r;
165
166 return link_parse_wol_password(config, password);
167}
168
169static int link_read_wol_password_from_cred(LinkConfig *config) {
170 _cleanup_free_ char *base = NULL, *cred_name = NULL;
171 _cleanup_(erase_and_freep) char *password = NULL;
172 int r;
173
174 assert(config);
175 assert(config->filename);
176
177 if (config->wol == UINT32_MAX)
178 return 0; /* WakeOnLan= is not specified. */
179 if (!FLAGS_SET(config->wol, WAKE_MAGICSECURE))
180 return 0; /* secureon is not specified in WakeOnLan=. */
181 if (config->wol_password)
182 return 0; /* WakeOnLanPassword= is specified. */
183 if (config->wol_password_file)
184 return 0; /* a file name is specified in WakeOnLanPassword=, but failed to read it. */
185
186 r = path_extract_filename(config->filename, &base);
187 if (r < 0)
188 return r;
189
190 cred_name = strjoin(base, ".wol.password");
191 if (!cred_name)
192 return -ENOMEM;
193
194 r = read_credential(cred_name, (void**) &password, NULL);
195 if (r == -ENOENT)
196 r = read_credential("wol.password", (void**) &password, NULL);
197 if (r < 0)
198 return r;
199
200 return link_parse_wol_password(config, password);
201}
202
203static int link_adjust_wol_options(LinkConfig *config) {
204 int r;
205
206 assert(config);
207
208 r = link_read_wol_password_from_file(config);
209 if (r == -ENOMEM)
210 return log_oom();
211 if (r < 0)
212 log_warning_errno(r, "Failed to read WakeOnLan password from %s, ignoring: %m", config->wol_password_file);
213
214 r = link_read_wol_password_from_cred(config);
215 if (r == -ENOMEM)
216 return log_oom();
217 if (r < 0)
218 log_warning_errno(r, "Failed to read WakeOnLan password from credential, ignoring: %m");
219
220 if (config->wol != UINT32_MAX && config->wol_password)
221 /* Enable WAKE_MAGICSECURE flag when WakeOnLanPassword=. Note that when
222 * WakeOnLanPassword= is set without WakeOnLan=, then ethtool_set_wol() enables
223 * WAKE_MAGICSECURE flag and other flags are not changed. */
224 config->wol |= WAKE_MAGICSECURE;
225
226 return 0;
227}
228
229int link_load_one(LinkConfigContext *ctx, const char *filename) {
230 _cleanup_(link_config_freep) LinkConfig *config = NULL;
231 _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
232 _cleanup_free_ char *name = NULL, *file_basename = NULL;
233 const char *dropin_dirname;
234 int r;
235
236 assert(ctx);
237 assert(filename);
238
239 r = null_or_empty_path(filename);
240 if (r < 0)
241 return log_warning_errno(r, "Failed to check if \"%s\" is empty: %m", filename);
242 if (r > 0) {
243 log_debug("Skipping empty file: %s", filename);
244 return 0;
245 }
246
247 name = strdup(filename);
248 if (!name)
249 return log_oom();
250
251 config = new(LinkConfig, 1);
252 if (!config)
253 return log_oom();
254
255 *config = (LinkConfig) {
256 .filename = TAKE_PTR(name),
257 .mac_address_policy = MAC_ADDRESS_POLICY_NONE,
258 .wol = UINT32_MAX, /* UINT32_MAX means do not change WOL setting. */
259 .duplex = _DUP_INVALID,
260 .port = _NET_DEV_PORT_INVALID,
261 .autonegotiation = -1,
262 .rx_flow_control = -1,
263 .tx_flow_control = -1,
264 .autoneg_flow_control = -1,
265 .txqueuelen = UINT32_MAX,
266 .coalesce.use_adaptive_rx_coalesce = -1,
267 .coalesce.use_adaptive_tx_coalesce = -1,
268 .mdi = ETH_TP_MDI_INVALID,
269 .sr_iov_num_vfs = UINT32_MAX,
270 .eee_enabled = -1,
271 .eee_tx_lpi_enabled = -1,
272 .eee_tx_lpi_timer_usec = USEC_INFINITY,
273 };
274
275 FOREACH_ELEMENT(feature, config->features)
276 *feature = -1;
277
278 r = path_extract_filename(filename, &file_basename);
279 if (r < 0)
280 return log_error_errno(r, "Failed to extract file name of '%s': %m", filename);
281
282 dropin_dirname = strjoina(file_basename, ".d");
283 r = config_parse_many(
284 STRV_MAKE_CONST(filename),
285 NETWORK_DIRS,
286 dropin_dirname,
287 /* root = */ NULL,
288 "Match\0"
289 "Link\0"
290 "SR-IOV\0"
291 "EnergyEfficientEthernet\0",
292 config_item_perf_lookup, link_config_gperf_lookup,
293 CONFIG_PARSE_WARN, config, &stats_by_path,
294 &config->dropins);
295 if (r < 0)
296 return r; /* config_parse_many() logs internally. */
297
298 if (ctx->stats_by_path) {
299 r = hashmap_move(ctx->stats_by_path, stats_by_path);
300 if (r < 0)
301 log_warning_errno(r, "Failed to save stats of '%s' and its drop-in configs, ignoring: %m", filename);
302 } else
303 ctx->stats_by_path = TAKE_PTR(stats_by_path);
304
305 if (net_match_is_empty(&config->match) && !config->conditions) {
306 log_warning("%s: No valid settings found in the [Match] section, ignoring file. "
307 "To match all interfaces, add OriginalName=* in the [Match] section.",
308 filename);
309 return 0;
310 }
311
312 if (!condition_test_list(config->conditions, environ, NULL, NULL, NULL)) {
313 log_debug("%s: Conditions do not match the system environment, skipping.", filename);
314 return 0;
315 }
316
317 if (IN_SET(config->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM) &&
318 config->hw_addr.length > 0)
319 log_warning("%s: MACAddress= in [Link] section will be ignored when MACAddressPolicy= "
320 "is set to \"persistent\" or \"random\".",
321 filename);
322
323 r = link_adjust_wol_options(config);
324 if (r < 0)
325 return r; /* link_adjust_wol_options() logs internally. */
326
327 r = sr_iov_drop_invalid_sections(config->sr_iov_num_vfs, config->sr_iov_by_section);
328 if (r < 0)
329 return r; /* sr_iov_drop_invalid_sections() logs internally. */
330
331 log_debug("Parsed configuration file \"%s\"", filename);
332
333 LIST_PREPEND(configs, ctx->configs, TAKE_PTR(config));
334 return 0;
335}
336
337int link_config_load(LinkConfigContext *ctx) {
338 _cleanup_strv_free_ char **files = NULL;
339 int r;
340
341 assert(ctx);
342
343 link_configs_free(ctx);
344
345 r = conf_files_list_strv(&files, ".link", NULL, 0, NETWORK_DIRS);
346 if (r < 0)
347 return log_error_errno(r, "failed to enumerate link files: %m");
348
349 STRV_FOREACH_BACKWARDS(f, files)
350 (void) link_load_one(ctx, *f);
351
352 return 0;
353}
354
355bool link_config_should_reload(LinkConfigContext *ctx) {
356 _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
357 int r;
358
359 assert(ctx);
360
361 r = config_get_stats_by_path(".link", NULL, 0, NETWORK_DIRS, /* check_dropins = */ true, &stats_by_path);
362 if (r < 0) {
363 log_warning_errno(r, "Failed to get stats of .link files, ignoring: %m");
364 return true;
365 }
366
367 return !stats_by_path_equal(ctx->stats_by_path, stats_by_path);
368}
369
370Link* link_free(Link *link) {
371 if (!link)
372 return NULL;
373
374 udev_event_unref(link->event);
375 free(link->kind);
376 return mfree(link);
377}
378
379int link_new(LinkConfigContext *ctx, UdevEvent *event, Link **ret) {
380 sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
381 _cleanup_(link_freep) Link *link = NULL;
382 int r;
383
384 assert(ctx);
385 assert(event);
386 assert(ret);
387
388 link = new(Link, 1);
389 if (!link)
390 return -ENOMEM;
391
392 *link = (Link) {
393 .event = udev_event_ref(event),
394 };
395
396 r = device_get_ifname(dev, &link->ifname);
397 if (r < 0)
398 return r;
399
400 r = sd_device_get_ifindex(dev, &link->ifindex);
401 if (r < 0)
402 return r;
403
404 r = sd_device_get_action(dev, &link->action);
405 if (r < 0)
406 return r;
407
408 r = device_get_sysattr_unsigned(dev, "name_assign_type", &link->name_assign_type);
409 if (r < 0)
410 log_link_debug_errno(link, r, "Failed to get \"name_assign_type\" attribute, ignoring: %m");
411 else
412 log_link_debug(link, "Device has name_assign_type attribute: %u", link->name_assign_type);
413
414 r = device_get_sysattr_unsigned(dev, "addr_assign_type", &link->addr_assign_type);
415 if (r < 0)
416 log_link_debug_errno(link, r, "Failed to get \"addr_assign_type\" attribute, ignoring: %m");
417 else
418 log_link_debug(link, "Device has addr_assign_type attribute: %u", link->addr_assign_type);
419
420 r = rtnl_get_link_info(&event->rtnl, link->ifindex, &link->iftype, &link->flags,
421 &link->kind, &link->hw_addr, &link->permanent_hw_addr);
422 if (r < 0)
423 return r;
424
425 if (link->hw_addr.length > 0 && link->permanent_hw_addr.length == 0) {
426 r = ethtool_get_permanent_hw_addr(&ctx->ethtool_fd, link->ifname, &link->permanent_hw_addr);
427 if (r < 0)
428 log_link_debug_errno(link, r, "Failed to get permanent hardware address, ignoring: %m");
429 }
430
431 r = sd_device_get_property_value(dev, "ID_NET_DRIVER", &link->driver);
432 if (r < 0 && r != -ENOENT)
433 log_link_debug_errno(link, r, "Failed to get driver, ignoring: %m");
434
435 *ret = TAKE_PTR(link);
436 return 0;
437}
438
439int link_get_config(LinkConfigContext *ctx, Link *link) {
440 int r;
441
442 assert(ctx);
443 assert(link);
444
445 /* Do not configure loopback interfaces by .link files. */
446 if (link->flags & IFF_LOOPBACK)
447 return -ENOENT;
448
449 LIST_FOREACH(configs, config, ctx->configs) {
450 r = net_match_config(
451 &config->match,
452 link->event->dev,
453 &link->hw_addr,
454 &link->permanent_hw_addr,
455 link->driver,
456 link->iftype,
457 link->kind,
458 link->ifname,
459 /* alternative_names = */ NULL,
460 /* wlan_iftype = */ 0,
461 /* ssid = */ NULL,
462 /* bssid = */ NULL);
463 if (r < 0)
464 return r;
465 if (r == 0)
466 continue;
467
468 if (config->match.ifname && !strv_contains(config->match.ifname, "*") && link->name_assign_type == NET_NAME_ENUM)
469 log_link_warning(link, "Config file %s is applied to device based on potentially unpredictable interface name.",
470 config->filename);
471 else
472 log_link_debug(link, "Config file %s is applied", config->filename);
473
474 link->config = config;
475 return 0;
476 }
477
478 return -ENOENT;
479}
480
481static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
482 LinkConfig *config = ASSERT_PTR(ASSERT_PTR(link)->config);
483 const char *name = ASSERT_PTR(link->ifname);
484 int r;
485
486 assert(link->event);
487 assert(ethtool_fd);
488
489 if (link->event->event_mode != EVENT_UDEV_WORKER) {
490 log_link_debug(link, "Running in test mode, skipping application of ethtool settings.");
491 return 0;
492 }
493
494 r = ethtool_set_glinksettings(ethtool_fd, name,
495 config->autonegotiation, config->advertise,
496 config->speed, config->duplex, config->port, config->mdi);
497 if (r < 0) {
498 if (config->autonegotiation >= 0)
499 log_link_warning_errno(link, r, "Could not %s auto negotiation, ignoring: %m",
500 enable_disable(config->autonegotiation));
501
502 if (!eqzero(config->advertise))
503 log_link_warning_errno(link, r, "Could not set advertise mode, ignoring: %m");
504
505 if (config->speed > 0)
506 log_link_warning_errno(link, r, "Could not set speed to %"PRIu64"Mbps, ignoring: %m",
507 DIV_ROUND_UP(config->speed, 1000000));
508
509 if (config->duplex >= 0)
510 log_link_warning_errno(link, r, "Could not set duplex to %s, ignoring: %m",
511 duplex_to_string(config->duplex));
512
513 if (config->port >= 0)
514 log_link_warning_errno(link, r, "Could not set port to '%s', ignoring: %m",
515 port_to_string(config->port));
516
517 if (config->mdi != ETH_TP_MDI_INVALID)
518 log_link_warning_errno(link, r, "Could not set MDI-X to '%s', ignoring: %m",
519 mdi_to_string(config->mdi));
520 }
521
522 r = ethtool_set_wol(ethtool_fd, name, config->wol, config->wol_password);
523 if (r < 0) {
524 _cleanup_free_ char *str = NULL;
525
526 (void) wol_options_to_string_alloc(config->wol, &str);
527 log_link_warning_errno(link, r, "Could not set WakeOnLan%s%s, ignoring: %m",
528 isempty(str) ? "" : " to ", strempty(str));
529 }
530
531 r = ethtool_set_features(ethtool_fd, name, config->features);
532 if (r < 0)
533 log_link_warning_errno(link, r, "Could not set offload features, ignoring: %m");
534
535 r = ethtool_set_channels(ethtool_fd, name, &config->channels);
536 if (r < 0)
537 log_link_warning_errno(link, r, "Could not set channels, ignoring: %m");
538
539 r = ethtool_set_nic_buffer_size(ethtool_fd, name, &config->ring);
540 if (r < 0)
541 log_link_warning_errno(link, r, "Could not set ring buffer, ignoring: %m");
542
543 r = ethtool_set_flow_control(ethtool_fd, name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
544 if (r < 0)
545 log_link_warning_errno(link, r, "Could not set flow control, ignoring: %m");
546
547 r = ethtool_set_nic_coalesce_settings(ethtool_fd, name, &config->coalesce);
548 if (r < 0)
549 log_link_warning_errno(link, r, "Could not set coalesce settings, ignoring: %m");
550
551 r = ethtool_set_eee_settings(ethtool_fd, name, config->eee_enabled, config->eee_tx_lpi_enabled, config->eee_tx_lpi_timer_usec, config->eee_advertise[0]);
552 if (r < 0)
553 log_link_warning_errno(link, r, "Could not set energy efficient ethernet settings, ignoring: %m");
554
555 return 0;
556}
557
558static bool hw_addr_is_valid(Link *link, const struct hw_addr_data *hw_addr) {
559 assert(link);
560 assert(hw_addr);
561
562 switch (link->iftype) {
563 case ARPHRD_ETHER:
564 /* Refuse all zero and all 0xFF. */
565 assert(hw_addr->length == ETH_ALEN);
566 return !ether_addr_is_null(&hw_addr->ether) && !ether_addr_is_broadcast(&hw_addr->ether);
567
568 case ARPHRD_INFINIBAND:
569 /* The last 8 bytes cannot be zero. */
570 assert(hw_addr->length == INFINIBAND_ALEN);
571 return !memeqzero(hw_addr->bytes + INFINIBAND_ALEN - 8, 8);
572
573 default:
574 assert_not_reached();
575 }
576}
577
578static int link_generate_new_hw_addr(Link *link, struct hw_addr_data *ret) {
579 struct hw_addr_data hw_addr = HW_ADDR_NULL;
580 bool is_static = false;
581 uint8_t *p;
582 size_t len;
583 int r;
584
585 assert(link);
586 assert(link->config);
587 assert(link->event);
588 assert(link->event->dev);
589 assert(ret);
590
591 if (link->hw_addr.length == 0)
592 goto finalize;
593
594 if (link->config->mac_address_policy == MAC_ADDRESS_POLICY_NONE) {
595 log_link_debug(link, "Using static MAC address.");
596 hw_addr = link->config->hw_addr;
597 is_static = true;
598 goto finalize;
599 }
600
601 if (!IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND))
602 goto finalize;
603
604 switch (link->addr_assign_type) {
605 case NET_ADDR_SET:
606 log_link_debug(link, "MAC address on the device already set by userspace.");
607 goto finalize;
608 case NET_ADDR_STOLEN:
609 log_link_debug(link, "MAC address on the device already set based on another device.");
610 goto finalize;
611 case NET_ADDR_RANDOM:
612 case NET_ADDR_PERM:
613 break;
614 default:
615 log_link_warning(link, "Unknown addr_assign_type %u, ignoring", link->addr_assign_type);
616 goto finalize;
617 }
618
619 if ((link->config->mac_address_policy == MAC_ADDRESS_POLICY_RANDOM) == (link->addr_assign_type == NET_ADDR_RANDOM)) {
620 log_link_debug(link, "MAC address on the device already matches policy \"%s\".",
621 mac_address_policy_to_string(link->config->mac_address_policy));
622 goto finalize;
623 }
624
625 hw_addr = (struct hw_addr_data) {
626 .length = arphrd_to_hw_addr_len(link->iftype),
627 };
628
629 switch (link->iftype) {
630 case ARPHRD_ETHER:
631 p = hw_addr.bytes;
632 len = hw_addr.length;
633 break;
634 case ARPHRD_INFINIBAND:
635 p = hw_addr.bytes + INFINIBAND_ALEN - 8;
636 len = 8;
637 break;
638 default:
639 assert_not_reached();
640 }
641
642 if (link->config->mac_address_policy == MAC_ADDRESS_POLICY_RANDOM)
643 /* We require genuine randomness here, since we want to make sure we won't collide with other
644 * systems booting up at the very same time. */
645 for (;;) {
646 random_bytes(p, len);
647 if (hw_addr_is_valid(link, &hw_addr))
648 break;
649 }
650
651 else {
652 uint64_t result;
653
654 r = net_get_unique_predictable_data(link->event->dev,
655 naming_scheme_has(NAMING_STABLE_VIRTUAL_MACS),
656 &result);
657 if (r < 0)
658 return log_link_warning_errno(link, r, "Could not generate persistent MAC address: %m");
659
660 assert(len <= sizeof(result));
661 memcpy(p, &result, len);
662 if (!hw_addr_is_valid(link, &hw_addr))
663 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
664 "Could not generate valid persistent MAC address: %m");
665 }
666
667finalize:
668
669 r = net_verify_hardware_address(link->ifname, is_static, link->iftype, &link->hw_addr, &hw_addr);
670 if (r < 0)
671 return r;
672
673 if (hw_addr_equal(&link->hw_addr, &hw_addr)) {
674 *ret = HW_ADDR_NULL;
675 return 0;
676 }
677
678 if (hw_addr.length > 0)
679 log_link_debug(link, "Applying %s MAC address: %s",
680 link->config->mac_address_policy == MAC_ADDRESS_POLICY_NONE ? "static" :
681 mac_address_policy_to_string(link->config->mac_address_policy),
682 HW_ADDR_TO_STR(&hw_addr));
683
684 *ret = hw_addr;
685 return 0;
686}
687
688static int link_apply_rtnl_settings(Link *link) {
689 struct hw_addr_data hw_addr = {};
690 LinkConfig *config = ASSERT_PTR(ASSERT_PTR(link)->config);
691 int r;
692
693 assert(link->event);
694
695 if (link->event->event_mode != EVENT_UDEV_WORKER) {
696 log_link_debug(link, "Running in test mode, skipping application of rtnl settings.");
697 return 0;
698 }
699
700 (void) link_generate_new_hw_addr(link, &hw_addr);
701
702 r = rtnl_set_link_properties(&link->event->rtnl, link->ifindex, config->alias, &hw_addr,
703 config->txqueues, config->rxqueues, config->txqueuelen,
704 config->mtu, config->gso_max_size, config->gso_max_segments);
705 if (r < 0)
706 log_link_warning_errno(link, r,
707 "Could not set Alias=, MACAddress=/MACAddressPolicy=, "
708 "TransmitQueues=, ReceiveQueues=, TransmitQueueLength=, MTUBytes=, "
709 "GenericSegmentOffloadMaxBytes= or GenericSegmentOffloadMaxSegments=, "
710 "ignoring: %m");
711
712 return 0;
713}
714
715static bool enable_name_policy(void) {
716 static int cached = -1;
717 bool b;
718 int r;
719
720 if (cached >= 0)
721 return cached;
722
723 r = proc_cmdline_get_bool("net.ifnames", /* flags = */ 0, &b);
724 if (r < 0)
725 log_warning_errno(r, "Failed to parse net.ifnames= kernel command line option, ignoring: %m");
726 if (r <= 0)
727 return (cached = true);
728
729 if (!b)
730 log_info("Network interface NamePolicy= disabled on kernel command line.");
731
732 return (cached = b);
733}
734
735static int link_generate_new_name(Link *link) {
736 LinkConfig *config = ASSERT_PTR(ASSERT_PTR(link)->config);;
737 sd_device *device = ASSERT_PTR(ASSERT_PTR(link->event)->dev);
738
739 if (link->action != SD_DEVICE_ADD) {
740 log_link_debug(link, "Not applying Name= and NamePolicy= on '%s' uevent.",
741 device_action_to_string(link->action));
742 goto no_rename;
743 }
744
745 if (IN_SET(link->name_assign_type, NET_NAME_USER, NET_NAME_RENAMED) &&
746 !naming_scheme_has(NAMING_ALLOW_RERENAMES)) {
747 log_link_debug(link, "Device already has a name given by userspace, not renaming.");
748 goto no_rename;
749 }
750
751 if (enable_name_policy() && config->name_policy)
752 for (NamePolicy *policy = config->name_policy; *policy != _NAMEPOLICY_INVALID; policy++) {
753 const char *new_name = NULL;
754
755 switch (*policy) {
756 case NAMEPOLICY_KERNEL:
757 if (link->name_assign_type != NET_NAME_PREDICTABLE)
758 continue;
759
760 /* The kernel claims to have given a predictable name, keep it. */
761 log_link_debug(link, "Policy *%s*: keeping predictable kernel name",
762 name_policy_to_string(*policy));
763 goto no_rename;
764 case NAMEPOLICY_KEEP:
765 if (!IN_SET(link->name_assign_type, NET_NAME_USER, NET_NAME_RENAMED))
766 continue;
767
768 log_link_debug(link, "Policy *%s*: keeping existing userspace name",
769 name_policy_to_string(*policy));
770 goto no_rename;
771 case NAMEPOLICY_DATABASE:
772 (void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &new_name);
773 break;
774 case NAMEPOLICY_ONBOARD:
775 (void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &new_name);
776 break;
777 case NAMEPOLICY_SLOT:
778 (void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &new_name);
779 break;
780 case NAMEPOLICY_PATH:
781 (void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &new_name);
782 break;
783 case NAMEPOLICY_MAC:
784 (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &new_name);
785 break;
786 default:
787 assert_not_reached();
788 }
789 if (ifname_valid(new_name)) {
790 log_link_debug(link, "Policy *%s* yields \"%s\".", name_policy_to_string(*policy), new_name);
791 link->new_name = new_name;
792 return 0;
793 }
794 }
795
796 if (link->config->name) {
797 log_link_debug(link, "Policies didn't yield a name, using specified Name=%s.", link->config->name);
798 link->new_name = link->config->name;
799 return 0;
800 }
801
802 log_link_debug(link, "Policies didn't yield a name and Name= is not given, not renaming.");
803no_rename:
804 if (!naming_scheme_has(NAMING_USE_INTERFACE_PROPERTY))
805 return sd_device_get_sysname(device, &link->new_name);
806
807 link->new_name = link->ifname;
808 return 0;
809}
810
811static int link_generate_alternative_names(Link *link) {
812 _cleanup_strv_free_ char **altnames = NULL;
813 LinkConfig *config = ASSERT_PTR(ASSERT_PTR(link)->config);
814 sd_device *device = ASSERT_PTR(ASSERT_PTR(link->event)->dev);
815 int r;
816
817 assert(!ASSERT_PTR(link->event)->altnames);
818
819 if (link->action != SD_DEVICE_ADD) {
820 log_link_debug(link, "Not applying AlternativeNames= and AlternativeNamesPolicy= on '%s' uevent.",
821 device_action_to_string(link->action));
822 return 0;
823 }
824
825 if (config->alternative_names) {
826 altnames = strv_copy(config->alternative_names);
827 if (!altnames)
828 return log_oom();
829 }
830
831 if (config->alternative_names_policy)
832 for (NamePolicy *p = config->alternative_names_policy; *p != _NAMEPOLICY_INVALID; p++) {
833 const char *n = NULL;
834
835 switch (*p) {
836 case NAMEPOLICY_DATABASE:
837 (void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &n);
838 break;
839 case NAMEPOLICY_ONBOARD:
840 (void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &n);
841 break;
842 case NAMEPOLICY_SLOT:
843 (void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &n);
844 break;
845 case NAMEPOLICY_PATH:
846 (void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &n);
847 break;
848 case NAMEPOLICY_MAC:
849 (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &n);
850 break;
851 default:
852 assert_not_reached();
853 }
854 if (ifname_valid_full(n, IFNAME_VALID_ALTERNATIVE)) {
855 r = strv_extend(&altnames, n);
856 if (r < 0)
857 return log_oom();
858 }
859 }
860
861 link->event->altnames = TAKE_PTR(altnames);
862 return 0;
863}
864
865static int sr_iov_configure(Link *link, sd_netlink **rtnl, SRIOV *sr_iov, SRIOVAttribute attr) {
866 int r;
867
868 assert(link);
869 assert(rtnl);
870 assert(link->ifindex > 0);
871
872 if (!sr_iov_has_config(sr_iov, attr))
873 return 0;
874
875 if (!*rtnl) {
876 r = sd_netlink_open(rtnl);
877 if (r < 0)
878 return r;
879 }
880
881 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
882 r = sd_rtnl_message_new_link(*rtnl, &req, RTM_SETLINK, link->ifindex);
883 if (r < 0)
884 return r;
885
886 r = sr_iov_set_netlink_message(sr_iov, attr, req);
887 if (r < 0)
888 return r;
889
890 return sd_netlink_call(*rtnl, req, 0, NULL);
891}
892
893static int link_apply_sr_iov_config(Link *link) {
894 SRIOV *sr_iov;
895 uint32_t n;
896 int r;
897
898 assert(link);
899 assert(link->config);
900 assert(ASSERT_PTR(link->event)->dev);
901
902 if (link->event->event_mode != EVENT_UDEV_WORKER) {
903 log_link_debug(link, "Running in test mode, skipping application of SR-IOV settings.");
904 return 0;
905 }
906
907 r = sr_iov_set_num_vfs(link->event->dev, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section);
908 if (r < 0)
909 log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m");
910
911 if (ordered_hashmap_isempty(link->config->sr_iov_by_section))
912 return 0;
913
914 r = sr_iov_get_num_vfs(link->event->dev, &n);
915 if (r < 0) {
916 log_link_warning_errno(link, r, "Failed to get the number of SR-IOV virtual functions, ignoring all [SR-IOV] sections: %m");
917 return 0;
918 }
919 if (n == 0) {
920 log_link_warning(link, "No SR-IOV virtual function exists, ignoring all [SR-IOV] sections: %m");
921 return 0;
922 }
923
924 ORDERED_HASHMAP_FOREACH(sr_iov, link->config->sr_iov_by_section) {
925 if (sr_iov->vf >= n) {
926 log_link_warning(link, "SR-IOV virtual function %"PRIu32" does not exist, ignoring [SR-IOV] section for the virtual function.", sr_iov->vf);
927 continue;
928 }
929
930 for (SRIOVAttribute attr = 0; attr < _SR_IOV_ATTRIBUTE_MAX; attr++) {
931 r = sr_iov_configure(link, &link->event->rtnl, sr_iov, attr);
932 if (r < 0)
933 log_link_warning_errno(link, r,
934 "Failed to set up %s for SR-IOV virtual function %"PRIu32", ignoring: %m",
935 sr_iov_attribute_to_string(attr), sr_iov->vf);
936 }
937 }
938
939 return 0;
940}
941
942static int link_apply_rps_cpu_mask(Link *link) {
943 _cleanup_free_ char *mask_str = NULL;
944 LinkConfig *config;
945 int r;
946
947 config = ASSERT_PTR(ASSERT_PTR(link)->config);
948 assert(ASSERT_PTR(link->event)->dev);
949
950 if (link->event->event_mode != EVENT_UDEV_WORKER) {
951 log_link_debug(link, "Running in test mode, skipping application of RPS setting.");
952 return 0;
953 }
954
955 /* Skip if the config is not specified. */
956 if (!config->rps_cpu_mask.set)
957 return 0;
958
959 mask_str = cpu_set_to_mask_string(&config->rps_cpu_mask);
960 if (!mask_str)
961 return log_oom();
962
963 log_link_debug(link, "Applying RPS CPU mask: %s", mask_str);
964
965 /* Currently, this will set CPU mask to all rx queue of matched device. */
966 FOREACH_DEVICE_SYSATTR(link->event->dev, attr) {
967 const char *c;
968
969 c = path_startswith(attr, "queues/");
970 if (!c)
971 continue;
972
973 c = startswith(c, "rx-");
974 if (!c)
975 continue;
976
977 c += strcspn(c, "/");
978
979 if (!path_equal(c, "/rps_cpus"))
980 continue;
981
982 r = sd_device_set_sysattr_value(link->event->dev, attr, mask_str);
983 if (r < 0)
984 log_link_warning_errno(link, r, "Failed to write %s sysfs attribute, ignoring: %m", attr);
985 }
986
987 return 0;
988}
989
990static int link_apply_udev_properties(Link *link) {
991 LinkConfig *config = ASSERT_PTR(ASSERT_PTR(link)->config);
992 UdevEvent *event = ASSERT_PTR(link->event);
993
994 /* 1. apply ImportProperty=. */
995 STRV_FOREACH(p, config->import_properties)
996 (void) udev_builtin_import_property(event, *p);
997
998 /* 2. apply Property=. */
999 STRV_FOREACH(p, config->properties) {
1000 _cleanup_free_ char *key = NULL;
1001 const char *eq;
1002
1003 eq = strchr(*p, '=');
1004 if (!eq)
1005 continue;
1006
1007 key = strndup(*p, eq - *p);
1008 if (!key)
1009 return log_oom();
1010
1011 (void) udev_builtin_add_property(event, key, eq + 1);
1012 }
1013
1014 /* 3. apply UnsetProperty=. */
1015 STRV_FOREACH(p, config->unset_properties)
1016 (void) udev_builtin_add_property(event, *p, NULL);
1017
1018 /* 4. set the default properties. */
1019 (void) udev_builtin_add_property(event, "ID_NET_LINK_FILE", config->filename);
1020
1021 _cleanup_free_ char *joined = NULL;
1022 STRV_FOREACH(d, config->dropins) {
1023 _cleanup_free_ char *escaped = NULL;
1024
1025 escaped = xescape(*d, ":");
1026 if (!escaped)
1027 return log_oom();
1028
1029 if (!strextend_with_separator(&joined, ":", escaped))
1030 return log_oom();
1031 }
1032
1033 (void) udev_builtin_add_property(event, "ID_NET_LINK_FILE_DROPINS", joined);
1034
1035 if (link->new_name)
1036 (void) udev_builtin_add_property(event, "ID_NET_NAME", link->new_name);
1037
1038 return 0;
1039}
1040
1041int link_apply_config(LinkConfigContext *ctx, Link *link) {
1042 int r;
1043
1044 assert(ctx);
1045 assert(link);
1046
1047 r = link_apply_ethtool_settings(link, &ctx->ethtool_fd);
1048 if (r < 0)
1049 return r;
1050
1051 r = link_apply_rtnl_settings(link);
1052 if (r < 0)
1053 return r;
1054
1055 r = link_generate_new_name(link);
1056 if (r < 0)
1057 return r;
1058
1059 r = link_generate_alternative_names(link);
1060 if (r < 0)
1061 return r;
1062
1063 r = link_apply_sr_iov_config(link);
1064 if (r < 0)
1065 return r;
1066
1067 r = link_apply_rps_cpu_mask(link);
1068 if (r < 0)
1069 return r;
1070
1071 return link_apply_udev_properties(link);
1072}
1073
1074int config_parse_udev_property(
1075 const char *unit,
1076 const char *filename,
1077 unsigned line,
1078 const char *section,
1079 unsigned section_line,
1080 const char *lvalue,
1081 int ltype,
1082 const char *rvalue,
1083 void *data,
1084 void *userdata) {
1085
1086 char ***properties = ASSERT_PTR(data);
1087 int r;
1088
1089 assert(filename);
1090 assert(lvalue);
1091 assert(rvalue);
1092
1093 if (isempty(rvalue)) {
1094 /* Empty assignment resets the list */
1095 *properties = strv_free(*properties);
1096 return 0;
1097 }
1098
1099 for (const char *p = rvalue;; ) {
1100 _cleanup_free_ char *word = NULL, *resolved = NULL, *key = NULL;
1101 const char *eq;
1102
1103 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
1104 if (r == -ENOMEM)
1105 return log_oom();
1106 if (r < 0) {
1107 log_syntax(unit, LOG_WARNING, filename, line, r,
1108 "Invalid syntax, ignoring assignment: %s", rvalue);
1109 return 0;
1110 }
1111 if (r == 0)
1112 return 0;
1113
1114 r = specifier_printf(word, SIZE_MAX, link_specifier_table, NULL, NULL, &resolved);
1115 if (r < 0) {
1116 log_syntax(unit, LOG_WARNING, filename, line, r,
1117 "Failed to resolve specifiers in %s, ignoring assignment: %m", word);
1118 continue;
1119 }
1120
1121 if (!udev_property_assignment_is_valid(resolved)) {
1122 log_syntax(unit, LOG_WARNING, filename, line, 0,
1123 "Invalid udev property, ignoring assignment: %s", word);
1124 continue;
1125 }
1126
1127 assert_se(eq = strchr(resolved, '='));
1128 key = strndup(resolved, eq - resolved);
1129 if (!key)
1130 return log_oom();
1131
1132 if (!device_property_can_set(key)) {
1133 log_syntax(unit, LOG_WARNING, filename, line, 0,
1134 "Invalid udev property name '%s', ignoring assignment: %s", key, resolved);
1135 continue;
1136 }
1137
1138 r = strv_env_replace_consume(properties, TAKE_PTR(resolved));
1139 if (r < 0)
1140 return log_error_errno(r, "Failed to update properties: %m");
1141 }
1142}
1143
1144int config_parse_udev_property_name(
1145 const char *unit,
1146 const char *filename,
1147 unsigned line,
1148 const char *section,
1149 unsigned section_line,
1150 const char *lvalue,
1151 int ltype,
1152 const char *rvalue,
1153 void *data,
1154 void *userdata) {
1155
1156 char ***properties = ASSERT_PTR(data);
1157 int r;
1158
1159 assert(filename);
1160 assert(lvalue);
1161 assert(rvalue);
1162
1163 if (isempty(rvalue)) {
1164 /* Empty assignment resets the list */
1165 *properties = strv_free(*properties);
1166 return 0;
1167 }
1168
1169 for (const char *p = rvalue;; ) {
1170 _cleanup_free_ char *word = NULL, *resolved = NULL;
1171
1172 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
1173 if (r == -ENOMEM)
1174 return log_oom();
1175 if (r < 0) {
1176 log_syntax(unit, LOG_WARNING, filename, line, r,
1177 "Invalid syntax, ignoring assignment: %s", rvalue);
1178 return 0;
1179 }
1180 if (r == 0)
1181 return 0;
1182
1183 r = specifier_printf(word, SIZE_MAX, link_specifier_table, NULL, NULL, &resolved);
1184 if (r < 0) {
1185 log_syntax(unit, LOG_WARNING, filename, line, r,
1186 "Failed to resolve specifiers in %s, ignoring assignment: %m", word);
1187 continue;
1188 }
1189
1190 if (!udev_property_name_is_valid(resolved)) {
1191 log_syntax(unit, LOG_WARNING, filename, line, 0,
1192 "Invalid udev property name, ignoring assignment: %s", resolved);
1193 continue;
1194 }
1195
1196 if (!device_property_can_set(resolved)) {
1197 log_syntax(unit, LOG_WARNING, filename, line, 0,
1198 "Invalid udev property name, ignoring assignment: %s", resolved);
1199 continue;
1200 }
1201
1202 r = strv_consume(properties, TAKE_PTR(resolved));
1203 if (r < 0)
1204 return log_error_errno(r, "Failed to update properties: %m");
1205 }
1206}
1207
1208int config_parse_ifalias(
1209 const char *unit,
1210 const char *filename,
1211 unsigned line,
1212 const char *section,
1213 unsigned section_line,
1214 const char *lvalue,
1215 int ltype,
1216 const char *rvalue,
1217 void *data,
1218 void *userdata) {
1219
1220 char **s = ASSERT_PTR(data);
1221
1222 assert(filename);
1223 assert(lvalue);
1224 assert(rvalue);
1225
1226 if (isempty(rvalue)) {
1227 *s = mfree(*s);
1228 return 0;
1229 }
1230
1231 if (!ascii_is_valid(rvalue)) {
1232 log_syntax(unit, LOG_WARNING, filename, line, 0,
1233 "Interface alias is not ASCII clean, ignoring assignment: %s", rvalue);
1234 return 0;
1235 }
1236
1237 if (strlen(rvalue) >= IFALIASZ) {
1238 log_syntax(unit, LOG_WARNING, filename, line, 0,
1239 "Interface alias is too long, ignoring assignment: %s", rvalue);
1240 return 0;
1241 }
1242
1243 return free_and_strdup_warn(s, rvalue);
1244}
1245
1246int config_parse_rx_tx_queues(
1247 const char *unit,
1248 const char *filename,
1249 unsigned line,
1250 const char *section,
1251 unsigned section_line,
1252 const char *lvalue,
1253 int ltype,
1254 const char *rvalue,
1255 void *data,
1256 void *userdata) {
1257
1258 uint32_t k, *v = data;
1259 int r;
1260
1261 if (isempty(rvalue)) {
1262 *v = 0;
1263 return 0;
1264 }
1265
1266 r = safe_atou32(rvalue, &k);
1267 if (r < 0) {
1268 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
1269 return 0;
1270 }
1271 if (k == 0 || k > 4096) {
1272 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid %s=, ignoring assignment: %s.", lvalue, rvalue);
1273 return 0;
1274 }
1275
1276 *v = k;
1277 return 0;
1278}
1279
1280int config_parse_txqueuelen(
1281 const char *unit,
1282 const char *filename,
1283 unsigned line,
1284 const char *section,
1285 unsigned section_line,
1286 const char *lvalue,
1287 int ltype,
1288 const char *rvalue,
1289 void *data,
1290 void *userdata) {
1291
1292 uint32_t k, *v = data;
1293 int r;
1294
1295 if (isempty(rvalue)) {
1296 *v = UINT32_MAX;
1297 return 0;
1298 }
1299
1300 r = safe_atou32(rvalue, &k);
1301 if (r < 0) {
1302 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
1303 return 0;
1304 }
1305 if (k == UINT32_MAX) {
1306 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid %s=, ignoring assignment: %s.", lvalue, rvalue);
1307 return 0;
1308 }
1309
1310 *v = k;
1311 return 0;
1312}
1313
1314int config_parse_wol_password(
1315 const char *unit,
1316 const char *filename,
1317 unsigned line,
1318 const char *section,
1319 unsigned section_line,
1320 const char *lvalue,
1321 int ltype,
1322 const char *rvalue,
1323 void *data,
1324 void *userdata) {
1325
1326 LinkConfig *config = ASSERT_PTR(userdata);
1327 int r;
1328
1329 assert(filename);
1330 assert(lvalue);
1331 assert(rvalue);
1332
1333 if (isempty(rvalue)) {
1334 config->wol_password = erase_and_free(config->wol_password);
1335 config->wol_password_file = mfree(config->wol_password_file);
1336 return 0;
1337 }
1338
1339 if (path_is_absolute(rvalue) && path_is_safe(rvalue)) {
1340 config->wol_password = erase_and_free(config->wol_password);
1341 return free_and_strdup_warn(&config->wol_password_file, rvalue);
1342 }
1343
1344 warn_file_is_world_accessible(filename, NULL, unit, line);
1345
1346 r = link_parse_wol_password(config, rvalue);
1347 if (r == -ENOMEM)
1348 return log_oom();
1349 if (r < 0) {
1350 log_syntax(unit, LOG_WARNING, filename, line, r,
1351 "Failed to parse %s=, ignoring assignment: %s.", lvalue, rvalue);
1352 return 0;
1353 }
1354
1355 config->wol_password_file = mfree(config->wol_password_file);
1356 return 0;
1357}
1358
1359int config_parse_rps_cpu_mask(
1360 const char *unit,
1361 const char *filename,
1362 unsigned line,
1363 const char *section,
1364 unsigned section_line,
1365 const char *lvalue,
1366 int ltype,
1367 const char *rvalue,
1368 void *data,
1369 void *userdata) {
1370
1371 CPUSet *mask = ASSERT_PTR(data);
1372 int r;
1373
1374 assert(rvalue);
1375
1376 if (streq(rvalue, "disable")) {
1377 _cleanup_(cpu_set_done) CPUSet c = {};
1378
1379 r = cpu_set_realloc(&c, 1);
1380 if (r < 0)
1381 return log_oom();
1382
1383 return cpu_set_done_and_replace(*mask, c);
1384 }
1385
1386 if (streq(rvalue, "all")) {
1387 _cleanup_(cpu_set_done) CPUSet c = {};
1388
1389 r = cpu_set_add_all(&c);
1390 if (r < 0) {
1391 log_syntax(unit, LOG_WARNING, filename, line, r,
1392 "Failed to create CPU affinity mask representing \"all\" cpus, ignoring: %m");
1393 return 0;
1394 }
1395
1396 return cpu_set_done_and_replace(*mask, c);
1397 }
1398
1399 return config_parse_cpu_set(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
1400}
1401
1402static const char* const mac_address_policy_table[_MAC_ADDRESS_POLICY_MAX] = {
1403 [MAC_ADDRESS_POLICY_PERSISTENT] = "persistent",
1404 [MAC_ADDRESS_POLICY_RANDOM] = "random",
1405 [MAC_ADDRESS_POLICY_NONE] = "none",
1406};
1407
1408DEFINE_STRING_TABLE_LOOKUP(mac_address_policy, MACAddressPolicy);
1409DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(
1410 config_parse_mac_address_policy,
1411 mac_address_policy,
1412 MACAddressPolicy,
1413 MAC_ADDRESS_POLICY_NONE);
1414
1415DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
1416 _NAMEPOLICY_INVALID);
1417
1418DEFINE_CONFIG_PARSE_ENUMV(config_parse_alternative_names_policy, alternative_names_policy, NamePolicy,
1419 _NAMEPOLICY_INVALID);