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