]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
network: header cleanup
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <net/if.h>
4 #include <netinet/in.h>
5 #include <linux/netdevice.h>
6 #include <unistd.h>
7
8 #include "alloc-util.h"
9 #include "conf-files.h"
10 #include "conf-parser.h"
11 #include "dns-domain.h"
12 #include "fd-util.h"
13 #include "hostname-util.h"
14 #include "in-addr-util.h"
15 #include "networkd-dhcp-server.h"
16 #include "network-internal.h"
17 #include "networkd-address-label.h"
18 #include "networkd-address.h"
19 #include "networkd-fdb.h"
20 #include "networkd-manager.h"
21 #include "networkd-mdb.h"
22 #include "networkd-neighbor.h"
23 #include "networkd-network.h"
24 #include "networkd-nexthop.h"
25 #include "networkd-radv.h"
26 #include "networkd-routing-policy-rule.h"
27 #include "networkd-sriov.h"
28 #include "parse-util.h"
29 #include "path-lookup.h"
30 #include "set.h"
31 #include "socket-util.h"
32 #include "stat-util.h"
33 #include "string-table.h"
34 #include "string-util.h"
35 #include "strv.h"
36 #include "tc.h"
37 #include "util.h"
38
39 /* Let's assume that anything above this number is a user misconfiguration. */
40 #define MAX_NTP_SERVERS 128
41
42 /* Set defaults following RFC7844 */
43 void network_apply_anonymize_if_set(Network *network) {
44 if (!network->dhcp_anonymize)
45 return;
46 /* RFC7844 3.7
47 SHOULD NOT send the Host Name option */
48 network->dhcp_send_hostname = false;
49 /* RFC7844 section 3.:
50 MAY contain the Client Identifier option
51 Section 3.5:
52 clients MUST use client identifiers based solely
53 on the link-layer address */
54 /* NOTE: Using MAC, as it does not reveal extra information,
55 * and some servers might not answer if this option is not sent */
56 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
57 /* RFC 7844 3.10:
58 SHOULD NOT use the Vendor Class Identifier option */
59 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
60 /* RFC7844 section 3.6.:
61 The client intending to protect its privacy SHOULD only request a
62 minimal number of options in the PRL and SHOULD also randomly shuffle
63 the ordering of option codes in the PRL. If this random ordering
64 cannot be implemented, the client MAY order the option codes in the
65 PRL by option code number (lowest to highest).
66 */
67 /* NOTE: dhcp_use_mtu is false by default,
68 * though it was not initiallized to any value in network_load_one.
69 * Maybe there should be another var called *send*?
70 * (to use the MTU sent by the server but to do not send
71 * the option in the PRL). */
72 network->dhcp_use_mtu = false;
73 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
74 * but this is needed to use them. */
75 network->dhcp_use_routes = true;
76 /* RFC7844 section 3.6.
77 * same comments as previous option */
78 network->dhcp_use_timezone = false;
79 }
80
81 static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
82 const char *kind_string;
83 NetDev *netdev;
84 int r;
85
86 /* For test-networkd-conf, the check must be earlier than the assertions. */
87 if (!name)
88 return 0;
89
90 assert(network);
91 assert(network->manager);
92 assert(network->filename);
93 assert(ret_netdev);
94
95 if (kind == _NETDEV_KIND_TUNNEL)
96 kind_string = "tunnel";
97 else {
98 kind_string = netdev_kind_to_string(kind);
99 if (!kind_string)
100 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
101 "%s: Invalid NetDev kind of %s, ignoring assignment.",
102 network->filename, name);
103 }
104
105 r = netdev_get(network->manager, name, &netdev);
106 if (r < 0)
107 return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
108 network->filename, name);
109
110 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
111 IN_SET(netdev->kind,
112 NETDEV_KIND_IPIP,
113 NETDEV_KIND_SIT,
114 NETDEV_KIND_GRE,
115 NETDEV_KIND_GRETAP,
116 NETDEV_KIND_IP6GRE,
117 NETDEV_KIND_IP6GRETAP,
118 NETDEV_KIND_VTI,
119 NETDEV_KIND_VTI6,
120 NETDEV_KIND_IP6TNL,
121 NETDEV_KIND_ERSPAN)))
122 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
123 "%s: NetDev %s is not a %s, ignoring assignment",
124 network->filename, name, kind_string);
125
126 *ret_netdev = netdev_ref(netdev);
127 return 1;
128 }
129
130 static int network_resolve_stacked_netdevs(Network *network) {
131 void *name, *kind;
132 int r;
133
134 assert(network);
135
136 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names) {
137 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
138
139 r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev);
140 if (r <= 0)
141 continue;
142
143 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
144 if (r < 0)
145 return log_oom();
146
147 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
148 if (r < 0)
149 return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m",
150 network->filename, (const char *) name);
151
152 netdev = NULL;
153 }
154
155 return 0;
156 }
157
158 int network_verify(Network *network) {
159 TrafficControl *tc;
160 SRIOV *sr_iov;
161
162 assert(network);
163 assert(network->filename);
164
165 if (set_isempty(network->match_mac) && set_isempty(network->match_permanent_mac) &&
166 strv_isempty(network->match_path) && strv_isempty(network->match_driver) &&
167 strv_isempty(network->match_type) && strv_isempty(network->match_name) &&
168 strv_isempty(network->match_property) && strv_isempty(network->match_wlan_iftype) &&
169 strv_isempty(network->match_ssid) && !network->conditions)
170 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
171 "%s: No valid settings found in the [Match] section, ignoring file. "
172 "To match all interfaces, add Name=* in the [Match] section.",
173 network->filename);
174
175 /* skip out early if configuration does not match the environment */
176 if (!condition_test_list(network->conditions, environ, NULL, NULL, NULL))
177 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
178 "%s: Conditions in the file do not match the system environment, skipping.",
179 network->filename);
180
181 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
182 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
183 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
184 (void) network_resolve_stacked_netdevs(network);
185
186 /* Free unnecessary entries. */
187 network->bond_name = mfree(network->bond_name);
188 network->bridge_name = mfree(network->bridge_name);
189 network->vrf_name = mfree(network->vrf_name);
190 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
191
192 if (network->bond) {
193 /* Bonding slave does not support addressing. */
194 if (network->ipv6_accept_ra > 0) {
195 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
196 network->filename);
197 network->ipv6_accept_ra = 0;
198 }
199 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
200 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
201 network->filename);
202 network->link_local = ADDRESS_FAMILY_NO;
203 }
204 if (network->dhcp != ADDRESS_FAMILY_NO) {
205 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
206 network->filename);
207 network->dhcp = ADDRESS_FAMILY_NO;
208 }
209 if (network->dhcp_server) {
210 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
211 network->filename);
212 network->dhcp_server = false;
213 }
214 if (!ordered_hashmap_isempty(network->addresses_by_section))
215 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
216 network->filename);
217 if (!hashmap_isempty(network->routes_by_section))
218 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
219 network->filename);
220
221 network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
222 network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free);
223 }
224
225 if (network->link_local < 0)
226 network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
227
228 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
229 if (network->ipv6_accept_ra > 0) {
230 log_warning("%s: IPv6AcceptRA= is enabled by the .network file but IPv6 link local addressing is disabled. "
231 "Disabling IPv6AcceptRA=.", network->filename);
232 network->ipv6_accept_ra = false;
233 }
234
235 if (FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
236 log_warning("%s: DHCPv6 client is enabled by the .network file but IPv6 link local addressing is disabled. "
237 "Disabling DHCPv6 client.", network->filename);
238 SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
239 }
240
241 if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE) {
242 log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
243 "Disabling IPv6PrefixDelegation=.", network->filename);
244 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
245 }
246 }
247
248 if (FLAGS_SET(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4) &&
249 !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) {
250 log_warning("%s: fallback assignment of IPv4 link local address is enabled but DHCPv4 is disabled. "
251 "Disabling the fallback assignment.", network->filename);
252 SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false);
253 }
254
255 if (network->ipv6_accept_ra < 0 && network->bridge)
256 network->ipv6_accept_ra = false;
257
258 /* IPMasquerade=yes implies IPForward=yes */
259 if (network->ip_masquerade)
260 network->ip_forward |= ADDRESS_FAMILY_IPV4;
261
262 if (network->mtu > 0 && network->dhcp_use_mtu) {
263 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
264 "Disabling UseMTU=.", network->filename);
265 network->dhcp_use_mtu = false;
266 }
267
268 if (network->dhcp_use_gateway < 0)
269 network->dhcp_use_gateway = network->dhcp_use_routes;
270
271 if (network->ignore_carrier_loss < 0)
272 network->ignore_carrier_loss = network->configure_without_carrier;
273
274 if (network->dhcp_critical >= 0) {
275 if (network->keep_configuration >= 0)
276 log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
277 "Ignoring CriticalConnection=.", network->filename);
278 else if (network->dhcp_critical)
279 /* CriticalConnection=yes also preserve foreign static configurations. */
280 network->keep_configuration = KEEP_CONFIGURATION_YES;
281 else
282 network->keep_configuration = KEEP_CONFIGURATION_NO;
283 }
284
285 if (network->keep_configuration < 0)
286 network->keep_configuration = KEEP_CONFIGURATION_NO;
287
288 if (network->ipv6_proxy_ndp == 0 && !set_isempty(network->ipv6_proxy_ndp_addresses)) {
289 log_warning("%s: IPv6ProxyNDP= is disabled. Ignoring IPv6ProxyNDPAddress=.", network->filename);
290 network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses);
291 }
292
293 network_verify_addresses(network);
294 network_verify_routes(network);
295 network_verify_nexthops(network);
296 network_verify_fdb_entries(network);
297 network_verify_mdb_entries(network);
298 network_verify_neighbors(network);
299 network_verify_address_labels(network);
300 network_verify_prefixes(network);
301 network_verify_route_prefixes(network);
302 network_verify_routing_policy_rules(network);
303
304 bool has_root = false, has_clsact = false;
305 ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section)
306 if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
307 traffic_control_free(tc);
308
309 ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section)
310 if (sr_iov_section_verify(sr_iov) < 0)
311 sr_iov_free(sr_iov);
312
313 return 0;
314 }
315
316 int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
317 _cleanup_free_ char *fname = NULL, *name = NULL;
318 _cleanup_(network_unrefp) Network *network = NULL;
319 _cleanup_fclose_ FILE *file = NULL;
320 const char *dropin_dirname;
321 char *d;
322 int r;
323
324 assert(manager);
325 assert(filename);
326
327 file = fopen(filename, "re");
328 if (!file) {
329 if (errno == ENOENT)
330 return 0;
331
332 return -errno;
333 }
334
335 if (null_or_empty_fd(fileno(file))) {
336 log_debug("Skipping empty file: %s", filename);
337 return 0;
338 }
339
340 fname = strdup(filename);
341 if (!fname)
342 return log_oom();
343
344 name = strdup(basename(filename));
345 if (!name)
346 return log_oom();
347
348 d = strrchr(name, '.');
349 if (!d)
350 return -EINVAL;
351
352 *d = '\0';
353
354 dropin_dirname = strjoina(name, ".network.d");
355
356 network = new(Network, 1);
357 if (!network)
358 return log_oom();
359
360 *network = (Network) {
361 .filename = TAKE_PTR(fname),
362 .name = TAKE_PTR(name),
363
364 .manager = manager,
365 .n_ref = 1,
366
367 .required_for_online = true,
368 .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
369 .dhcp = ADDRESS_FAMILY_NO,
370 .dhcp_critical = -1,
371 .dhcp_use_ntp = true,
372 .dhcp_use_sip = true,
373 .dhcp_use_dns = true,
374 .dhcp_use_hostname = true,
375 .dhcp_use_routes = true,
376 .dhcp_use_gateway = -1,
377 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
378 .dhcp_send_hostname = true,
379 .dhcp_send_release = true,
380 /* To enable/disable RFC7844 Anonymity Profiles */
381 .dhcp_anonymize = false,
382 .dhcp_route_metric = DHCP_ROUTE_METRIC,
383 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
384 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
385 .dhcp_route_table = RT_TABLE_MAIN,
386 .dhcp_route_table_set = false,
387 /* NOTE: from man: UseMTU=... Defaults to false*/
388 .dhcp_use_mtu = false,
389 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
390 .dhcp_use_timezone = false,
391 .rapid_commit = true,
392
393 .dhcp6_route_metric = DHCP_ROUTE_METRIC,
394 .dhcp6_use_ntp = true,
395 .dhcp6_use_dns = true,
396
397 .dhcp6_pd_subnet_id = -1,
398 .dhcp6_pd_assign = true,
399
400 .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
401 .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
402 .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
403
404 .dhcp_server_emit_router = true,
405 .dhcp_server_emit_timezone = true,
406
407 .router_emit_dns = true,
408 .router_emit_domains = true,
409
410 .use_bpdu = -1,
411 .hairpin = -1,
412 .fast_leave = -1,
413 .allow_port_to_be_root = -1,
414 .unicast_flood = -1,
415 .multicast_flood = -1,
416 .multicast_to_unicast = -1,
417 .neighbor_suppression = -1,
418 .learning = -1,
419 .bridge_proxy_arp = -1,
420 .bridge_proxy_arp_wifi = -1,
421 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
422 .multicast_router = _MULTICAST_ROUTER_INVALID,
423
424 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
425
426 .dns_default_route = -1,
427 .llmnr = RESOLVE_SUPPORT_YES,
428 .mdns = RESOLVE_SUPPORT_NO,
429 .dnssec_mode = _DNSSEC_MODE_INVALID,
430 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
431
432 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
433 .link_local = _ADDRESS_FAMILY_INVALID,
434 .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
435
436 .ipv4_accept_local = -1,
437
438 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
439 .ipv6_accept_ra = -1,
440 .ipv6_dad_transmits = -1,
441 .ipv6_hop_limit = -1,
442 .ipv6_proxy_ndp = -1,
443 .duid.type = _DUID_TYPE_INVALID,
444 .proxy_arp = -1,
445 .arp = -1,
446 .multicast = -1,
447 .allmulticast = -1,
448 .ipv6_accept_ra_use_dns = true,
449 .ipv6_accept_ra_use_autonomous_prefix = true,
450 .ipv6_accept_ra_use_onlink_prefix = true,
451 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
452 .ipv6_accept_ra_route_table_set = false,
453 .ipv6_accept_ra_start_dhcp6_client = true,
454
455 .configure_without_carrier = false,
456 .ignore_carrier_loss = -1,
457 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
458 .can_triple_sampling = -1,
459 .can_termination = -1,
460 .can_listen_only = -1,
461 .can_fd_mode = -1,
462 .can_non_iso = -1,
463 .ip_service_type = -1,
464 };
465
466 r = config_parse_many(
467 filename, NETWORK_DIRS, dropin_dirname,
468 "Match\0"
469 "Link\0"
470 "SR-IOV\0"
471 "Network\0"
472 "Address\0"
473 "Neighbor\0"
474 "IPv6AddressLabel\0"
475 "RoutingPolicyRule\0"
476 "Route\0"
477 "NextHop\0"
478 "DHCP\0" /* compat */
479 "DHCPv4\0"
480 "DHCPv6\0"
481 "DHCPv6PrefixDelegation\0"
482 "DHCPServer\0"
483 "IPv6AcceptRA\0"
484 "IPv6NDPProxyAddress\0"
485 "Bridge\0"
486 "BridgeFDB\0"
487 "BridgeMDB\0"
488 "BridgeVLAN\0"
489 "IPv6PrefixDelegation\0"
490 "IPv6Prefix\0"
491 "IPv6RoutePrefix\0"
492 "LLDP\0"
493 "TrafficControlQueueingDiscipline\0"
494 "CAN\0"
495 "QDisc\0"
496 "BFIFO\0"
497 "CAKE\0"
498 "ControlledDelay\0"
499 "DeficitRoundRobinScheduler\0"
500 "DeficitRoundRobinSchedulerClass\0"
501 "EnhancedTransmissionSelection\0"
502 "FairQueueing\0"
503 "FairQueueingControlledDelay\0"
504 "FlowQueuePIE\0"
505 "GenericRandomEarlyDetection\0"
506 "HeavyHitterFilter\0"
507 "HierarchyTokenBucket\0"
508 "HierarchyTokenBucketClass\0"
509 "NetworkEmulator\0"
510 "PFIFO\0"
511 "PFIFOFast\0"
512 "PFIFOHeadDrop\0"
513 "PIE\0"
514 "QuickFairQueueing\0"
515 "QuickFairQueueingClass\0"
516 "StochasticFairBlue\0"
517 "StochasticFairnessQueueing\0"
518 "TokenBucketFilter\0"
519 "TrivialLinkEqualizer\0",
520 config_item_perf_lookup, network_network_gperf_lookup,
521 CONFIG_PARSE_WARN,
522 network,
523 &network->timestamp);
524 if (r < 0)
525 return r;
526
527 network_apply_anonymize_if_set(network);
528
529 r = network_add_ipv4ll_route(network);
530 if (r < 0)
531 log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
532
533 r = network_add_default_route_on_device(network);
534 if (r < 0)
535 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
536 network->filename);
537
538 if (network_verify(network) < 0)
539 /* Ignore .network files that do not match the conditions. */
540 return 0;
541
542 r = ordered_hashmap_ensure_allocated(networks, &string_hash_ops);
543 if (r < 0)
544 return r;
545
546 r = ordered_hashmap_put(*networks, network->name, network);
547 if (r < 0)
548 return r;
549
550 network = NULL;
551 return 0;
552 }
553
554 int network_load(Manager *manager, OrderedHashmap **networks) {
555 _cleanup_strv_free_ char **files = NULL;
556 char **f;
557 int r;
558
559 assert(manager);
560
561 ordered_hashmap_clear_with_destructor(*networks, network_unref);
562
563 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
564 if (r < 0)
565 return log_error_errno(r, "Failed to enumerate network files: %m");
566
567 STRV_FOREACH(f, files) {
568 r = network_load_one(manager, networks, *f);
569 if (r < 0)
570 log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
571 }
572
573 return 0;
574 }
575
576 int network_reload(Manager *manager) {
577 OrderedHashmap *new_networks = NULL;
578 Network *n, *old;
579 int r;
580
581 assert(manager);
582
583 r = network_load(manager, &new_networks);
584 if (r < 0)
585 goto failure;
586
587 ORDERED_HASHMAP_FOREACH(n, new_networks) {
588 r = network_get_by_name(manager, n->name, &old);
589 if (r < 0)
590 continue; /* The .network file is new. */
591
592 if (n->timestamp != old->timestamp)
593 continue; /* The .network file is modified. */
594
595 if (!streq(n->filename, old->filename))
596 continue;
597
598 r = ordered_hashmap_replace(new_networks, old->name, old);
599 if (r < 0)
600 goto failure;
601
602 network_ref(old);
603 network_unref(n);
604 }
605
606 ordered_hashmap_free_with_destructor(manager->networks, network_unref);
607 manager->networks = new_networks;
608
609 return 0;
610
611 failure:
612 ordered_hashmap_free_with_destructor(new_networks, network_unref);
613
614 return r;
615 }
616
617 static Network *network_free(Network *network) {
618 if (!network)
619 return NULL;
620
621 free(network->filename);
622
623 set_free_free(network->match_mac);
624 set_free_free(network->match_permanent_mac);
625 strv_free(network->match_path);
626 strv_free(network->match_driver);
627 strv_free(network->match_type);
628 strv_free(network->match_name);
629 strv_free(network->match_property);
630 strv_free(network->match_wlan_iftype);
631 strv_free(network->match_ssid);
632 set_free_free(network->match_bssid);
633 condition_free_list(network->conditions);
634
635 free(network->description);
636 free(network->dhcp_vendor_class_identifier);
637 free(network->dhcp_mudurl);
638 strv_free(network->dhcp_user_class);
639 free(network->dhcp_hostname);
640 set_free(network->dhcp_deny_listed_ip);
641 set_free(network->dhcp_allow_listed_ip);
642 set_free(network->dhcp_request_options);
643 set_free(network->dhcp6_request_options);
644 free(network->mac);
645 free(network->dhcp6_mudurl);
646 strv_free(network->dhcp6_user_class);
647 strv_free(network->dhcp6_vendor_class);
648
649 if (network->dhcp_acd)
650 sd_ipv4acd_unref(network->dhcp_acd);
651
652 strv_free(network->ntp);
653 for (unsigned i = 0; i < network->n_dns; i++)
654 in_addr_full_free(network->dns[i]);
655 free(network->dns);
656 ordered_set_free_free(network->search_domains);
657 ordered_set_free_free(network->route_domains);
658 strv_free(network->bind_carrier);
659
660 ordered_set_free_free(network->router_search_domains);
661 free(network->router_dns);
662 set_free_free(network->ndisc_deny_listed_prefix);
663
664 free(network->bridge_name);
665 free(network->bond_name);
666 free(network->vrf_name);
667 hashmap_free_free_key(network->stacked_netdev_names);
668 netdev_unref(network->bridge);
669 netdev_unref(network->bond);
670 netdev_unref(network->vrf);
671 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
672
673 set_free_free(network->ipv6_proxy_ndp_addresses);
674 ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
675 hashmap_free_with_destructor(network->routes_by_section, route_free);
676 hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
677 hashmap_free_with_destructor(network->fdb_entries_by_section, fdb_entry_free);
678 hashmap_free_with_destructor(network->mdb_entries_by_section, mdb_entry_free);
679 hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free);
680 hashmap_free_with_destructor(network->address_labels_by_section, address_label_free);
681 hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
682 hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
683 hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
684 ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
685 ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
686
687 if (network->manager &&
688 network->manager->duids_requesting_uuid)
689 set_remove(network->manager->duids_requesting_uuid, &network->duid);
690
691 free(network->name);
692
693 free(network->dhcp_server_timezone);
694
695 for (sd_dhcp_lease_server_type t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
696 free(network->dhcp_server_emit[t].addresses);
697
698 set_free_free(network->dnssec_negative_trust_anchors);
699
700 free(network->lldp_mud);
701
702 ordered_hashmap_free(network->dhcp_client_send_options);
703 ordered_hashmap_free(network->dhcp_client_send_vendor_options);
704 ordered_hashmap_free(network->dhcp_server_send_options);
705 ordered_hashmap_free(network->dhcp_server_send_vendor_options);
706 ordered_set_free(network->ipv6_tokens);
707 ordered_hashmap_free(network->dhcp6_client_send_options);
708 ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
709
710 return mfree(network);
711 }
712
713 DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
714
715 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
716 Network *network;
717
718 assert(manager);
719 assert(name);
720 assert(ret);
721
722 network = ordered_hashmap_get(manager->networks, name);
723 if (!network)
724 return -ENOENT;
725
726 *ret = network;
727
728 return 0;
729 }
730
731 int network_get(Manager *manager, unsigned short iftype, sd_device *device,
732 const char *ifname, char * const *alternative_names, const char *driver,
733 const struct ether_addr *mac, const struct ether_addr *permanent_mac,
734 enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid,
735 Network **ret) {
736 Network *network;
737
738 assert(manager);
739 assert(ret);
740
741 ORDERED_HASHMAP_FOREACH(network, manager->networks)
742 if (net_match_config(network->match_mac, network->match_permanent_mac,
743 network->match_path, network->match_driver,
744 network->match_type, network->match_name, network->match_property,
745 network->match_wlan_iftype, network->match_ssid, network->match_bssid,
746 device, mac, permanent_mac, driver, iftype,
747 ifname, alternative_names, wlan_iftype, ssid, bssid)) {
748 if (network->match_name && device) {
749 const char *attr;
750 uint8_t name_assign_type = NET_NAME_UNKNOWN;
751
752 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
753 (void) safe_atou8(attr, &name_assign_type);
754
755 if (name_assign_type == NET_NAME_ENUM)
756 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
757 ifname, network->filename);
758 else
759 log_debug("%s: found matching network '%s'", ifname, network->filename);
760 } else
761 log_debug("%s: found matching network '%s'", ifname, network->filename);
762
763 *ret = network;
764 return 0;
765 }
766
767 *ret = NULL;
768
769 return -ENOENT;
770 }
771
772 int network_apply(Network *network, Link *link) {
773 assert(network);
774 assert(link);
775
776 link->network = network_ref(network);
777
778 if (network->n_dns > 0 ||
779 !strv_isempty(network->ntp) ||
780 !ordered_set_isempty(network->search_domains) ||
781 !ordered_set_isempty(network->route_domains))
782 link_dirty(link);
783
784 return 0;
785 }
786
787 bool network_has_static_ipv6_configurations(Network *network) {
788 Address *address;
789 Route *route;
790 FdbEntry *fdb;
791 MdbEntry *mdb;
792 Neighbor *neighbor;
793
794 assert(network);
795
796 ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section)
797 if (address->family == AF_INET6)
798 return true;
799
800 HASHMAP_FOREACH(route, network->routes_by_section)
801 if (route->family == AF_INET6)
802 return true;
803
804 HASHMAP_FOREACH(fdb, network->fdb_entries_by_section)
805 if (fdb->family == AF_INET6)
806 return true;
807
808 HASHMAP_FOREACH(mdb, network->mdb_entries_by_section)
809 if (mdb->family == AF_INET6)
810 return true;
811
812 HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
813 if (neighbor->family == AF_INET6)
814 return true;
815
816 if (!hashmap_isempty(network->address_labels_by_section))
817 return true;
818
819 if (!hashmap_isempty(network->prefixes_by_section))
820 return true;
821
822 if (!hashmap_isempty(network->route_prefixes_by_section))
823 return true;
824
825 return false;
826 }
827
828 int config_parse_stacked_netdev(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 _cleanup_free_ char *name = NULL;
839 NetDevKind kind = ltype;
840 Hashmap **h = data;
841 int r;
842
843 assert(filename);
844 assert(lvalue);
845 assert(rvalue);
846 assert(data);
847 assert(IN_SET(kind,
848 NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
849 NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
850 NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
851 NETDEV_KIND_XFRM));
852
853 if (!ifname_valid(rvalue)) {
854 log_syntax(unit, LOG_WARNING, filename, line, 0,
855 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
856 return 0;
857 }
858
859 name = strdup(rvalue);
860 if (!name)
861 return log_oom();
862
863 r = hashmap_ensure_allocated(h, &string_hash_ops);
864 if (r < 0)
865 return log_oom();
866
867 r = hashmap_put(*h, name, INT_TO_PTR(kind));
868 if (r < 0)
869 log_syntax(unit, LOG_WARNING, filename, line, r,
870 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
871 else if (r == 0)
872 log_syntax(unit, LOG_DEBUG, filename, line, r,
873 "NetDev '%s' specified twice, ignoring.", name);
874 else
875 name = NULL;
876
877 return 0;
878 }
879
880 int config_parse_domains(
881 const char *unit,
882 const char *filename,
883 unsigned line,
884 const char *section,
885 unsigned section_line,
886 const char *lvalue,
887 int ltype,
888 const char *rvalue,
889 void *data,
890 void *userdata) {
891
892 Network *n = data;
893 int r;
894
895 assert(n);
896 assert(lvalue);
897 assert(rvalue);
898
899 if (isempty(rvalue)) {
900 n->search_domains = ordered_set_free_free(n->search_domains);
901 n->route_domains = ordered_set_free_free(n->route_domains);
902 return 0;
903 }
904
905 for (const char *p = rvalue;;) {
906 _cleanup_free_ char *w = NULL, *normalized = NULL;
907 const char *domain;
908 bool is_route;
909
910 r = extract_first_word(&p, &w, NULL, 0);
911 if (r == -ENOMEM)
912 return log_oom();
913 if (r < 0) {
914 log_syntax(unit, LOG_WARNING, filename, line, r,
915 "Failed to extract search or route domain, ignoring: %s", rvalue);
916 return 0;
917 }
918 if (r == 0)
919 return 0;
920
921 is_route = w[0] == '~';
922 domain = is_route ? w + 1 : w;
923
924 if (dns_name_is_root(domain) || streq(domain, "*")) {
925 /* If the root domain appears as is, or the special token "*" is found, we'll
926 * consider this as routing domain, unconditionally. */
927 is_route = true;
928 domain = "."; /* make sure we don't allow empty strings, thus write the root
929 * domain as "." */
930 } else {
931 r = dns_name_normalize(domain, 0, &normalized);
932 if (r < 0) {
933 log_syntax(unit, LOG_WARNING, filename, line, r,
934 "'%s' is not a valid domain name, ignoring.", domain);
935 continue;
936 }
937
938 domain = normalized;
939
940 if (is_localhost(domain)) {
941 log_syntax(unit, LOG_WARNING, filename, line, 0,
942 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
943 domain);
944 continue;
945 }
946 }
947
948 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
949 r = ordered_set_ensure_allocated(set, &string_hash_ops);
950 if (r < 0)
951 return log_oom();
952
953 r = ordered_set_put_strdup(*set, domain);
954 if (r < 0)
955 return log_oom();
956 }
957 }
958
959 int config_parse_ipv6token(
960 const char* unit,
961 const char *filename,
962 unsigned line,
963 const char *section,
964 unsigned section_line,
965 const char *lvalue,
966 int ltype,
967 const char *rvalue,
968 void *data,
969 void *userdata) {
970
971 union in_addr_union buffer;
972 struct in6_addr *token = data;
973 int r;
974
975 assert(filename);
976 assert(lvalue);
977 assert(rvalue);
978 assert(token);
979
980 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
981 if (r < 0) {
982 log_syntax(unit, LOG_WARNING, filename, line, r,
983 "Failed to parse IPv6 token, ignoring: %s", rvalue);
984 return 0;
985 }
986
987 if (in_addr_is_null(AF_INET6, &buffer)) {
988 log_syntax(unit, LOG_WARNING, filename, line, 0,
989 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
990 return 0;
991 }
992
993 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
994 log_syntax(unit, LOG_WARNING, filename, line, 0,
995 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
996 return 0;
997 }
998
999 *token = buffer.in6;
1000
1001 return 0;
1002 }
1003
1004 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1005 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
1006 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
1007 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
1008 };
1009
1010 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
1011 IPV6_PRIVACY_EXTENSIONS_YES);
1012
1013 int config_parse_ipv6_privacy_extensions(
1014 const char* unit,
1015 const char *filename,
1016 unsigned line,
1017 const char *section,
1018 unsigned section_line,
1019 const char *lvalue,
1020 int ltype,
1021 const char *rvalue,
1022 void *data,
1023 void *userdata) {
1024
1025 IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
1026
1027 assert(filename);
1028 assert(lvalue);
1029 assert(rvalue);
1030 assert(ipv6_privacy_extensions);
1031
1032 s = ipv6_privacy_extensions_from_string(rvalue);
1033 if (s < 0) {
1034 if (streq(rvalue, "kernel"))
1035 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
1036 else {
1037 log_syntax(unit, LOG_WARNING, filename, line, 0,
1038 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1039 return 0;
1040 }
1041 }
1042
1043 *ipv6_privacy_extensions = s;
1044
1045 return 0;
1046 }
1047
1048 int config_parse_hostname(
1049 const char *unit,
1050 const char *filename,
1051 unsigned line,
1052 const char *section,
1053 unsigned section_line,
1054 const char *lvalue,
1055 int ltype,
1056 const char *rvalue,
1057 void *data,
1058 void *userdata) {
1059
1060 _cleanup_free_ char *hn = NULL;
1061 char **hostname = data;
1062 int r;
1063
1064 assert(filename);
1065 assert(lvalue);
1066 assert(rvalue);
1067
1068 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
1069 if (r < 0)
1070 return r;
1071
1072 if (!hostname_is_valid(hn, false)) {
1073 log_syntax(unit, LOG_WARNING, filename, line, 0,
1074 "Hostname is not valid, ignoring assignment: %s", rvalue);
1075 return 0;
1076 }
1077
1078 r = dns_name_is_valid(hn);
1079 if (r < 0) {
1080 log_syntax(unit, LOG_WARNING, filename, line, r,
1081 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
1082 return 0;
1083 }
1084 if (r == 0) {
1085 log_syntax(unit, LOG_WARNING, filename, line, 0,
1086 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
1087 return 0;
1088 }
1089
1090 return free_and_replace(*hostname, hn);
1091 }
1092
1093 int config_parse_timezone(
1094 const char *unit,
1095 const char *filename,
1096 unsigned line,
1097 const char *section,
1098 unsigned section_line,
1099 const char *lvalue,
1100 int ltype,
1101 const char *rvalue,
1102 void *data,
1103 void *userdata) {
1104
1105 _cleanup_free_ char *tz = NULL;
1106 char **datap = data;
1107 int r;
1108
1109 assert(filename);
1110 assert(lvalue);
1111 assert(rvalue);
1112
1113 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1114 if (r < 0)
1115 return r;
1116
1117 if (!timezone_is_valid(tz, LOG_WARNING)) {
1118 log_syntax(unit, LOG_WARNING, filename, line, 0,
1119 "Timezone is not valid, ignoring assignment: %s", rvalue);
1120 return 0;
1121 }
1122
1123 return free_and_replace(*datap, tz);
1124 }
1125
1126 int config_parse_dns(
1127 const char *unit,
1128 const char *filename,
1129 unsigned line,
1130 const char *section,
1131 unsigned section_line,
1132 const char *lvalue,
1133 int ltype,
1134 const char *rvalue,
1135 void *data,
1136 void *userdata) {
1137
1138 Network *n = userdata;
1139 int r;
1140
1141 assert(filename);
1142 assert(lvalue);
1143 assert(rvalue);
1144
1145 if (isempty(rvalue)) {
1146 for (unsigned i = 0; i < n->n_dns; i++)
1147 in_addr_full_free(n->dns[i]);
1148 n->dns = mfree(n->dns);
1149 n->n_dns = 0;
1150 return 0;
1151 }
1152
1153 for (const char *p = rvalue;;) {
1154 _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
1155 _cleanup_free_ char *w = NULL;
1156 struct in_addr_full **m;
1157
1158 r = extract_first_word(&p, &w, NULL, 0);
1159 if (r == -ENOMEM)
1160 return log_oom();
1161 if (r < 0) {
1162 log_syntax(unit, LOG_WARNING, filename, line, r,
1163 "Invalid syntax, ignoring: %s", rvalue);
1164 return 0;
1165 }
1166 if (r == 0)
1167 return 0;
1168
1169 r = in_addr_full_new_from_string(w, &dns);
1170 if (r < 0) {
1171 log_syntax(unit, LOG_WARNING, filename, line, r,
1172 "Failed to parse dns server address, ignoring: %s", w);
1173 continue;
1174 }
1175
1176 if (IN_SET(dns->port, 53, 853))
1177 dns->port = 0;
1178
1179 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
1180 if (!m)
1181 return log_oom();
1182
1183 m[n->n_dns++] = TAKE_PTR(dns);
1184 n->dns = m;
1185 }
1186 }
1187
1188 int config_parse_dnssec_negative_trust_anchors(
1189 const char *unit,
1190 const char *filename,
1191 unsigned line,
1192 const char *section,
1193 unsigned section_line,
1194 const char *lvalue,
1195 int ltype,
1196 const char *rvalue,
1197 void *data,
1198 void *userdata) {
1199
1200 Network *n = data;
1201 int r;
1202
1203 assert(n);
1204 assert(lvalue);
1205 assert(rvalue);
1206
1207 if (isempty(rvalue)) {
1208 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1209 return 0;
1210 }
1211
1212 for (const char *p = rvalue;;) {
1213 _cleanup_free_ char *w = NULL;
1214
1215 r = extract_first_word(&p, &w, NULL, 0);
1216 if (r == -ENOMEM)
1217 return log_oom();
1218 if (r < 0) {
1219 log_syntax(unit, LOG_WARNING, filename, line, r,
1220 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1221 return 0;
1222 }
1223 if (r == 0)
1224 return 0;
1225
1226 r = dns_name_is_valid(w);
1227 if (r <= 0) {
1228 log_syntax(unit, LOG_WARNING, filename, line, r,
1229 "%s is not a valid domain name, ignoring.", w);
1230 continue;
1231 }
1232
1233 r = set_ensure_consume(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops, TAKE_PTR(w));
1234 if (r < 0)
1235 return log_oom();
1236 }
1237 }
1238
1239 int config_parse_ntp(
1240 const char *unit,
1241 const char *filename,
1242 unsigned line,
1243 const char *section,
1244 unsigned section_line,
1245 const char *lvalue,
1246 int ltype,
1247 const char *rvalue,
1248 void *data,
1249 void *userdata) {
1250
1251 char ***l = data;
1252 int r;
1253
1254 assert(l);
1255 assert(lvalue);
1256 assert(rvalue);
1257
1258 if (isempty(rvalue)) {
1259 *l = strv_free(*l);
1260 return 0;
1261 }
1262
1263 for (const char *p = rvalue;;) {
1264 _cleanup_free_ char *w = NULL;
1265
1266 r = extract_first_word(&p, &w, NULL, 0);
1267 if (r == -ENOMEM)
1268 return log_oom();
1269 if (r < 0) {
1270 log_syntax(unit, LOG_WARNING, filename, line, r,
1271 "Failed to extract NTP server name, ignoring: %s", rvalue);
1272 return 0;
1273 }
1274 if (r == 0)
1275 return 0;
1276
1277 r = dns_name_is_valid_or_address(w);
1278 if (r <= 0) {
1279 log_syntax(unit, LOG_WARNING, filename, line, r,
1280 "%s is not a valid domain name or IP address, ignoring.", w);
1281 continue;
1282 }
1283
1284 if (strv_length(*l) > MAX_NTP_SERVERS) {
1285 log_syntax(unit, LOG_WARNING, filename, line, 0,
1286 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1287 MAX_NTP_SERVERS, w);
1288 return 0;
1289 }
1290
1291 r = strv_consume(l, TAKE_PTR(w));
1292 if (r < 0)
1293 return log_oom();
1294 }
1295 }
1296
1297 int config_parse_required_for_online(
1298 const char *unit,
1299 const char *filename,
1300 unsigned line,
1301 const char *section,
1302 unsigned section_line,
1303 const char *lvalue,
1304 int ltype,
1305 const char *rvalue,
1306 void *data,
1307 void *userdata) {
1308
1309 Network *network = data;
1310 LinkOperationalStateRange range;
1311 bool required = true;
1312 int r;
1313
1314 if (isempty(rvalue)) {
1315 network->required_for_online = true;
1316 network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
1317 return 0;
1318 }
1319
1320 r = parse_operational_state_range(rvalue, &range);
1321 if (r < 0) {
1322 r = parse_boolean(rvalue);
1323 if (r < 0) {
1324 log_syntax(unit, LOG_WARNING, filename, line, r,
1325 "Failed to parse %s= setting, ignoring assignment: %s",
1326 lvalue, rvalue);
1327 return 0;
1328 }
1329
1330 required = r;
1331 range = LINK_OPERSTATE_RANGE_DEFAULT;
1332 }
1333
1334 network->required_for_online = required;
1335 network->required_operstate_for_online = range;
1336
1337 return 0;
1338 }
1339
1340 DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1341 "Failed to parse KeepConfiguration= setting");
1342
1343 static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
1344 [KEEP_CONFIGURATION_NO] = "no",
1345 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1346 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1347 [KEEP_CONFIGURATION_STATIC] = "static",
1348 [KEEP_CONFIGURATION_YES] = "yes",
1349 };
1350
1351 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
1352
1353 static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
1354 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64",
1355 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none",
1356 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
1357 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random",
1358 };
1359
1360 DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
1361 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");