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