]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #16030 from yuwata/network-read-mtime-of-dropin-configs-15521
[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_use_ntp = true,
414 .dhcp6_use_dns = true,
415
416 .dhcp6_pd_assign_prefix = true,
417
418 .dhcp_server_emit_dns = true,
419 .dhcp_server_emit_ntp = true,
420 .dhcp_server_emit_sip = true,
421 .dhcp_server_emit_router = true,
422 .dhcp_server_emit_timezone = true,
423
424 .router_prefix_subnet_id = -1,
425 .router_emit_dns = true,
426 .router_emit_domains = true,
427
428 .use_bpdu = -1,
429 .hairpin = -1,
430 .fast_leave = -1,
431 .allow_port_to_be_root = -1,
432 .unicast_flood = -1,
433 .multicast_flood = -1,
434 .multicast_to_unicast = -1,
435 .neighbor_suppression = -1,
436 .learning = -1,
437 .bridge_proxy_arp = -1,
438 .bridge_proxy_arp_wifi = -1,
439 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
440 .multicast_router = _MULTICAST_ROUTER_INVALID,
441
442 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
443
444 .dns_default_route = -1,
445 .llmnr = RESOLVE_SUPPORT_YES,
446 .mdns = RESOLVE_SUPPORT_NO,
447 .dnssec_mode = _DNSSEC_MODE_INVALID,
448 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
449
450 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
451 .link_local = _ADDRESS_FAMILY_INVALID,
452
453 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
454 .ipv6_accept_ra = -1,
455 .ipv6_dad_transmits = -1,
456 .ipv6_hop_limit = -1,
457 .ipv6_proxy_ndp = -1,
458 .duid.type = _DUID_TYPE_INVALID,
459 .proxy_arp = -1,
460 .arp = -1,
461 .multicast = -1,
462 .allmulticast = -1,
463 .ipv6_accept_ra_use_dns = true,
464 .ipv6_accept_ra_use_autonomous_prefix = true,
465 .ipv6_accept_ra_use_onlink_prefix = true,
466 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
467 .ipv6_accept_ra_route_table_set = false,
468 .ipv6_accept_ra_start_dhcp6_client = true,
469
470 .configure_without_carrier = false,
471 .ignore_carrier_loss = -1,
472 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
473 .ipv6_address_gen_mode = _LINK_IPV6_ADDRESS_GEN_MODE_INVALID,
474 .can_triple_sampling = -1,
475 .can_termination = -1,
476 .ip_service_type = -1,
477 };
478
479 r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
480 "Match\0"
481 "Link\0"
482 "Network\0"
483 "Address\0"
484 "Neighbor\0"
485 "IPv6AddressLabel\0"
486 "RoutingPolicyRule\0"
487 "Route\0"
488 "NextHop\0"
489 "DHCP\0" /* compat */
490 "DHCPv4\0"
491 "DHCPv6\0"
492 "DHCPServer\0"
493 "IPv6AcceptRA\0"
494 "IPv6NDPProxyAddress\0"
495 "Bridge\0"
496 "BridgeFDB\0"
497 "BridgeVLAN\0"
498 "IPv6PrefixDelegation\0"
499 "IPv6Prefix\0"
500 "IPv6RoutePrefix\0"
501 "LLDP\0"
502 "TrafficControlQueueingDiscipline\0"
503 "CAN\0"
504 "QDisc\0"
505 "BFIFO\0"
506 "CAKE\0"
507 "ControlledDelay\0"
508 "DeficitRoundRobinScheduler\0"
509 "DeficitRoundRobinSchedulerClass\0"
510 "PFIFO\0"
511 "PFIFOFast\0"
512 "PFIFOHeadDrop\0"
513 "FairQueueing\0"
514 "FairQueueingControlledDelay\0"
515 "GenericRandomEarlyDetection\0"
516 "HeavyHitterFilter\0"
517 "HierarchyTokenBucket\0"
518 "HierarchyTokenBucketClass\0"
519 "NetworkEmulator\0"
520 "PIE\0"
521 "StochasticFairBlue\0"
522 "StochasticFairnessQueueing\0"
523 "TokenBucketFilter\0"
524 "TrivialLinkEqualizer\0",
525 config_item_perf_lookup, network_network_gperf_lookup,
526 CONFIG_PARSE_WARN, network, &dropins);
527 if (r < 0)
528 return r;
529
530 network_apply_anonymize_if_set(network);
531
532 r = network_add_ipv4ll_route(network);
533 if (r < 0)
534 log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
535
536 r = network_add_default_route_on_device(network);
537 if (r < 0)
538 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
539 network->filename);
540
541 struct stat stats;
542 if (stat(filename, &stats) >= 0)
543 network->timestamp = timespec_load(&stats.st_mtim);
544
545 char **f;
546 STRV_FOREACH(f, dropins) {
547 usec_t t;
548
549 if (stat(*f, &stats) < 0) {
550 network->timestamp = 0;
551 break;
552 }
553
554 t = timespec_load(&stats.st_mtim);
555 if (t > network->timestamp)
556 network->timestamp = t;
557 }
558
559 if (network_verify(network) < 0)
560 /* Ignore .network files that do not match the conditions. */
561 return 0;
562
563 r = ordered_hashmap_ensure_allocated(networks, &string_hash_ops);
564 if (r < 0)
565 return r;
566
567 r = ordered_hashmap_put(*networks, network->name, network);
568 if (r < 0)
569 return r;
570
571 network = NULL;
572 return 0;
573 }
574
575 int network_load(Manager *manager, OrderedHashmap **networks) {
576 _cleanup_strv_free_ char **files = NULL;
577 char **f;
578 int r;
579
580 assert(manager);
581
582 ordered_hashmap_clear_with_destructor(*networks, network_unref);
583
584 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
585 if (r < 0)
586 return log_error_errno(r, "Failed to enumerate network files: %m");
587
588 STRV_FOREACH(f, files) {
589 r = network_load_one(manager, networks, *f);
590 if (r < 0)
591 log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
592 }
593
594 return 0;
595 }
596
597 int network_reload(Manager *manager) {
598 OrderedHashmap *new_networks = NULL;
599 Network *n, *old;
600 Iterator i;
601 int r;
602
603 assert(manager);
604
605 r = network_load(manager, &new_networks);
606 if (r < 0)
607 goto failure;
608
609 ORDERED_HASHMAP_FOREACH(n, new_networks, i) {
610 r = network_get_by_name(manager, n->name, &old);
611 if (r < 0)
612 continue; /* The .network file is new. */
613
614 if (n->timestamp != old->timestamp)
615 continue; /* The .network file is modified. */
616
617 if (!streq(n->filename, old->filename))
618 continue;
619
620 r = ordered_hashmap_replace(new_networks, old->name, old);
621 if (r < 0)
622 goto failure;
623
624 network_ref(old);
625 network_unref(n);
626 }
627
628 ordered_hashmap_free_with_destructor(manager->networks, network_unref);
629 manager->networks = new_networks;
630
631 return 0;
632
633 failure:
634 ordered_hashmap_free_with_destructor(new_networks, network_unref);
635
636 return r;
637 }
638
639 static Network *network_free(Network *network) {
640 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
641 RoutePrefix *route_prefix;
642 RoutingPolicyRule *rule;
643 AddressLabel *label;
644 FdbEntry *fdb_entry;
645 Neighbor *neighbor;
646 Address *address;
647 NextHop *nexthop;
648 Prefix *prefix;
649 Route *route;
650
651 if (!network)
652 return NULL;
653
654 free(network->filename);
655
656 set_free_free(network->match_mac);
657 set_free_free(network->match_permanent_mac);
658 strv_free(network->match_path);
659 strv_free(network->match_driver);
660 strv_free(network->match_type);
661 strv_free(network->match_name);
662 strv_free(network->match_property);
663 strv_free(network->match_wlan_iftype);
664 strv_free(network->match_ssid);
665 set_free_free(network->match_bssid);
666 condition_free_list(network->conditions);
667
668 free(network->description);
669 free(network->dhcp_vendor_class_identifier);
670 free(network->dhcp_mudurl);
671 strv_free(network->dhcp_user_class);
672 free(network->dhcp_hostname);
673 set_free(network->dhcp_black_listed_ip);
674 set_free(network->dhcp_request_options);
675 set_free(network->dhcp6_request_options);
676 free(network->mac);
677 free(network->dhcp6_mudurl);
678 strv_free(network->dhcp6_user_class);
679 strv_free(network->dhcp6_vendor_class);
680
681 if (network->dhcp_acd)
682 sd_ipv4acd_unref(network->dhcp_acd);
683
684 strv_free(network->ntp);
685 free(network->dns);
686 strv_free(network->sip);
687 strv_free(network->smtp);
688 ordered_set_free_free(network->search_domains);
689 ordered_set_free_free(network->route_domains);
690 strv_free(network->bind_carrier);
691
692 ordered_set_free_free(network->router_search_domains);
693 free(network->router_dns);
694 set_free_free(network->ndisc_black_listed_prefix);
695
696 free(network->bridge_name);
697 free(network->bond_name);
698 free(network->vrf_name);
699 hashmap_free_free_key(network->stacked_netdev_names);
700 netdev_unref(network->bridge);
701 netdev_unref(network->bond);
702 netdev_unref(network->vrf);
703 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
704
705 while ((route = network->static_routes))
706 route_free(route);
707
708 while ((nexthop = network->static_nexthops))
709 nexthop_free(nexthop);
710
711 while ((address = network->static_addresses))
712 address_free(address);
713
714 while ((fdb_entry = network->static_fdb_entries))
715 fdb_entry_free(fdb_entry);
716
717 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
718 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
719
720 while ((neighbor = network->neighbors))
721 neighbor_free(neighbor);
722
723 while ((label = network->address_labels))
724 address_label_free(label);
725
726 while ((prefix = network->static_prefixes))
727 prefix_free(prefix);
728
729 while ((route_prefix = network->static_route_prefixes))
730 route_prefix_free(route_prefix);
731
732 while ((rule = network->rules))
733 routing_policy_rule_free(rule);
734
735 hashmap_free(network->addresses_by_section);
736 hashmap_free(network->routes_by_section);
737 hashmap_free(network->nexthops_by_section);
738 hashmap_free(network->fdb_entries_by_section);
739 hashmap_free(network->neighbors_by_section);
740 hashmap_free(network->address_labels_by_section);
741 hashmap_free(network->prefixes_by_section);
742 hashmap_free(network->route_prefixes_by_section);
743 hashmap_free(network->rules_by_section);
744 ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
745
746 if (network->manager &&
747 network->manager->duids_requesting_uuid)
748 set_remove(network->manager->duids_requesting_uuid, &network->duid);
749
750 free(network->name);
751
752 free(network->dhcp_server_timezone);
753 free(network->dhcp_server_dns);
754 free(network->dhcp_server_ntp);
755 free(network->dhcp_server_sip);
756 free(network->dhcp_server_pop3);
757 free(network->dhcp_server_smtp);
758 free(network->dhcp_server_lpr);
759
760 set_free_free(network->dnssec_negative_trust_anchors);
761
762 free(network->lldp_mud);
763
764 ordered_hashmap_free(network->dhcp_client_send_options);
765 ordered_hashmap_free(network->dhcp_client_send_vendor_options);
766 ordered_hashmap_free(network->dhcp_server_send_options);
767 ordered_hashmap_free(network->dhcp_server_send_vendor_options);
768 ordered_hashmap_free(network->ipv6_tokens);
769 ordered_hashmap_free(network->dhcp6_client_send_options);
770 ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
771
772 return mfree(network);
773 }
774
775 DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
776
777 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
778 Network *network;
779
780 assert(manager);
781 assert(name);
782 assert(ret);
783
784 network = ordered_hashmap_get(manager->networks, name);
785 if (!network)
786 return -ENOENT;
787
788 *ret = network;
789
790 return 0;
791 }
792
793 int network_get(Manager *manager, unsigned short iftype, sd_device *device,
794 const char *ifname, char * const *alternative_names,
795 const struct ether_addr *address, const struct ether_addr *permanent_address,
796 enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid,
797 Network **ret) {
798 Network *network;
799 Iterator i;
800
801 assert(manager);
802 assert(ret);
803
804 ORDERED_HASHMAP_FOREACH(network, manager->networks, i)
805 if (net_match_config(network->match_mac, network->match_permanent_mac,
806 network->match_path, network->match_driver,
807 network->match_type, network->match_name, network->match_property,
808 network->match_wlan_iftype, network->match_ssid, network->match_bssid,
809 iftype, device, address, permanent_address,
810 ifname, alternative_names, wlan_iftype, ssid, bssid)) {
811 if (network->match_name && device) {
812 const char *attr;
813 uint8_t name_assign_type = NET_NAME_UNKNOWN;
814
815 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
816 (void) safe_atou8(attr, &name_assign_type);
817
818 if (name_assign_type == NET_NAME_ENUM)
819 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
820 ifname, network->filename);
821 else
822 log_debug("%s: found matching network '%s'", ifname, network->filename);
823 } else
824 log_debug("%s: found matching network '%s'", ifname, network->filename);
825
826 *ret = network;
827 return 0;
828 }
829
830 *ret = NULL;
831
832 return -ENOENT;
833 }
834
835 int network_apply(Network *network, Link *link) {
836 assert(network);
837 assert(link);
838
839 link->network = network_ref(network);
840
841 if (network->n_dns > 0 ||
842 !strv_isempty(network->ntp) ||
843 !ordered_set_isempty(network->search_domains) ||
844 !ordered_set_isempty(network->route_domains))
845 link_dirty(link);
846
847 return 0;
848 }
849
850 bool network_has_static_ipv6_configurations(Network *network) {
851 Address *address;
852 Route *route;
853 FdbEntry *fdb;
854 Neighbor *neighbor;
855
856 assert(network);
857
858 LIST_FOREACH(addresses, address, network->static_addresses)
859 if (address->family == AF_INET6)
860 return true;
861
862 LIST_FOREACH(routes, route, network->static_routes)
863 if (route->family == AF_INET6)
864 return true;
865
866 LIST_FOREACH(static_fdb_entries, fdb, network->static_fdb_entries)
867 if (fdb->family == AF_INET6)
868 return true;
869
870 LIST_FOREACH(neighbors, neighbor, network->neighbors)
871 if (neighbor->family == AF_INET6)
872 return true;
873
874 if (!LIST_IS_EMPTY(network->address_labels))
875 return true;
876
877 if (!LIST_IS_EMPTY(network->static_prefixes))
878 return true;
879
880 return false;
881 }
882
883 int config_parse_stacked_netdev(const char *unit,
884 const char *filename,
885 unsigned line,
886 const char *section,
887 unsigned section_line,
888 const char *lvalue,
889 int ltype,
890 const char *rvalue,
891 void *data,
892 void *userdata) {
893 _cleanup_free_ char *name = NULL;
894 NetDevKind kind = ltype;
895 Hashmap **h = data;
896 int r;
897
898 assert(filename);
899 assert(lvalue);
900 assert(rvalue);
901 assert(data);
902 assert(IN_SET(kind,
903 NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
904 NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
905 NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
906 NETDEV_KIND_XFRM));
907
908 if (!ifname_valid(rvalue)) {
909 log_syntax(unit, LOG_ERR, filename, line, 0,
910 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
911 return 0;
912 }
913
914 name = strdup(rvalue);
915 if (!name)
916 return log_oom();
917
918 r = hashmap_ensure_allocated(h, &string_hash_ops);
919 if (r < 0)
920 return log_oom();
921
922 r = hashmap_put(*h, name, INT_TO_PTR(kind));
923 if (r < 0)
924 log_syntax(unit, LOG_ERR, filename, line, r,
925 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
926 else if (r == 0)
927 log_syntax(unit, LOG_DEBUG, filename, line, r,
928 "NetDev '%s' specified twice, ignoring.", name);
929 else
930 name = NULL;
931
932 return 0;
933 }
934
935 int config_parse_domains(
936 const char *unit,
937 const char *filename,
938 unsigned line,
939 const char *section,
940 unsigned section_line,
941 const char *lvalue,
942 int ltype,
943 const char *rvalue,
944 void *data,
945 void *userdata) {
946
947 const char *p;
948 Network *n = data;
949 int r;
950
951 assert(n);
952 assert(lvalue);
953 assert(rvalue);
954
955 if (isempty(rvalue)) {
956 n->search_domains = ordered_set_free_free(n->search_domains);
957 n->route_domains = ordered_set_free_free(n->route_domains);
958 return 0;
959 }
960
961 p = rvalue;
962 for (;;) {
963 _cleanup_free_ char *w = NULL, *normalized = NULL;
964 const char *domain;
965 bool is_route;
966
967 r = extract_first_word(&p, &w, NULL, 0);
968 if (r < 0) {
969 log_syntax(unit, LOG_ERR, filename, line, r,
970 "Failed to extract search or route domain, ignoring: %s", rvalue);
971 break;
972 }
973 if (r == 0)
974 break;
975
976 is_route = w[0] == '~';
977 domain = is_route ? w + 1 : w;
978
979 if (dns_name_is_root(domain) || streq(domain, "*")) {
980 /* If the root domain appears as is, or the special token "*" is found, we'll
981 * consider this as routing domain, unconditionally. */
982 is_route = true;
983 domain = "."; /* make sure we don't allow empty strings, thus write the root
984 * domain as "." */
985 } else {
986 r = dns_name_normalize(domain, 0, &normalized);
987 if (r < 0) {
988 log_syntax(unit, LOG_ERR, filename, line, r,
989 "'%s' is not a valid domain name, ignoring.", domain);
990 continue;
991 }
992
993 domain = normalized;
994
995 if (is_localhost(domain)) {
996 log_syntax(unit, LOG_ERR, filename, line, 0,
997 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
998 domain);
999 continue;
1000 }
1001 }
1002
1003 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
1004 r = ordered_set_ensure_allocated(set, &string_hash_ops);
1005 if (r < 0)
1006 return r;
1007
1008 r = ordered_set_put_strdup(*set, domain);
1009 if (r < 0)
1010 return log_oom();
1011 }
1012
1013 return 0;
1014 }
1015
1016 int config_parse_ipv6token(
1017 const char* unit,
1018 const char *filename,
1019 unsigned line,
1020 const char *section,
1021 unsigned section_line,
1022 const char *lvalue,
1023 int ltype,
1024 const char *rvalue,
1025 void *data,
1026 void *userdata) {
1027
1028 union in_addr_union buffer;
1029 struct in6_addr *token = data;
1030 int r;
1031
1032 assert(filename);
1033 assert(lvalue);
1034 assert(rvalue);
1035 assert(token);
1036
1037 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
1038 if (r < 0) {
1039 log_syntax(unit, LOG_ERR, filename, line, r,
1040 "Failed to parse IPv6 token, ignoring: %s", rvalue);
1041 return 0;
1042 }
1043
1044 if (in_addr_is_null(AF_INET6, &buffer)) {
1045 log_syntax(unit, LOG_ERR, filename, line, 0,
1046 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
1047 return 0;
1048 }
1049
1050 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
1051 log_syntax(unit, LOG_ERR, filename, line, 0,
1052 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
1053 return 0;
1054 }
1055
1056 *token = buffer.in6;
1057
1058 return 0;
1059 }
1060
1061 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1062 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
1063 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
1064 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
1065 };
1066
1067 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
1068 IPV6_PRIVACY_EXTENSIONS_YES);
1069
1070 int config_parse_ipv6_privacy_extensions(
1071 const char* unit,
1072 const char *filename,
1073 unsigned line,
1074 const char *section,
1075 unsigned section_line,
1076 const char *lvalue,
1077 int ltype,
1078 const char *rvalue,
1079 void *data,
1080 void *userdata) {
1081
1082 IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
1083
1084 assert(filename);
1085 assert(lvalue);
1086 assert(rvalue);
1087 assert(ipv6_privacy_extensions);
1088
1089 s = ipv6_privacy_extensions_from_string(rvalue);
1090 if (s < 0) {
1091 if (streq(rvalue, "kernel"))
1092 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
1093 else {
1094 log_syntax(unit, LOG_ERR, filename, line, 0,
1095 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1096 return 0;
1097 }
1098 }
1099
1100 *ipv6_privacy_extensions = s;
1101
1102 return 0;
1103 }
1104
1105 int config_parse_hostname(
1106 const char *unit,
1107 const char *filename,
1108 unsigned line,
1109 const char *section,
1110 unsigned section_line,
1111 const char *lvalue,
1112 int ltype,
1113 const char *rvalue,
1114 void *data,
1115 void *userdata) {
1116
1117 _cleanup_free_ char *hn = NULL;
1118 char **hostname = data;
1119 int r;
1120
1121 assert(filename);
1122 assert(lvalue);
1123 assert(rvalue);
1124
1125 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
1126 if (r < 0)
1127 return r;
1128
1129 if (!hostname_is_valid(hn, false)) {
1130 log_syntax(unit, LOG_ERR, filename, line, 0,
1131 "Hostname is not valid, ignoring assignment: %s", rvalue);
1132 return 0;
1133 }
1134
1135 r = dns_name_is_valid(hn);
1136 if (r < 0) {
1137 log_syntax(unit, LOG_ERR, filename, line, r,
1138 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
1139 return 0;
1140 }
1141 if (r == 0) {
1142 log_syntax(unit, LOG_ERR, filename, line, 0,
1143 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
1144 return 0;
1145 }
1146
1147 return free_and_replace(*hostname, hn);
1148 }
1149
1150 int config_parse_timezone(
1151 const char *unit,
1152 const char *filename,
1153 unsigned line,
1154 const char *section,
1155 unsigned section_line,
1156 const char *lvalue,
1157 int ltype,
1158 const char *rvalue,
1159 void *data,
1160 void *userdata) {
1161
1162 _cleanup_free_ char *tz = NULL;
1163 char **datap = data;
1164 int r;
1165
1166 assert(filename);
1167 assert(lvalue);
1168 assert(rvalue);
1169
1170 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1171 if (r < 0)
1172 return r;
1173
1174 if (!timezone_is_valid(tz, LOG_ERR)) {
1175 log_syntax(unit, LOG_ERR, filename, line, 0,
1176 "Timezone is not valid, ignoring assignment: %s", rvalue);
1177 return 0;
1178 }
1179
1180 return free_and_replace(*datap, tz);
1181 }
1182
1183 int config_parse_dns(
1184 const char *unit,
1185 const char *filename,
1186 unsigned line,
1187 const char *section,
1188 unsigned section_line,
1189 const char *lvalue,
1190 int ltype,
1191 const char *rvalue,
1192 void *data,
1193 void *userdata) {
1194
1195 Network *n = userdata;
1196 int r;
1197
1198 assert(filename);
1199 assert(lvalue);
1200 assert(rvalue);
1201
1202 for (;;) {
1203 _cleanup_free_ char *w = NULL;
1204 union in_addr_union a;
1205 struct in_addr_data *m;
1206 int family;
1207
1208 r = extract_first_word(&rvalue, &w, NULL, 0);
1209 if (r == -ENOMEM)
1210 return log_oom();
1211 if (r < 0) {
1212 log_syntax(unit, LOG_ERR, filename, line, r,
1213 "Invalid syntax, ignoring: %s", rvalue);
1214 break;
1215 }
1216 if (r == 0)
1217 break;
1218
1219 r = in_addr_from_string_auto(w, &family, &a);
1220 if (r < 0) {
1221 log_syntax(unit, LOG_ERR, filename, line, r,
1222 "Failed to parse dns server address, ignoring: %s", w);
1223 continue;
1224 }
1225
1226 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
1227 if (!m)
1228 return log_oom();
1229
1230 m[n->n_dns++] = (struct in_addr_data) {
1231 .family = family,
1232 .address = a,
1233 };
1234
1235 n->dns = m;
1236 }
1237
1238 return 0;
1239 }
1240
1241 int config_parse_dnssec_negative_trust_anchors(
1242 const char *unit,
1243 const char *filename,
1244 unsigned line,
1245 const char *section,
1246 unsigned section_line,
1247 const char *lvalue,
1248 int ltype,
1249 const char *rvalue,
1250 void *data,
1251 void *userdata) {
1252
1253 const char *p = rvalue;
1254 Network *n = data;
1255 int r;
1256
1257 assert(n);
1258 assert(lvalue);
1259 assert(rvalue);
1260
1261 if (isempty(rvalue)) {
1262 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1263 return 0;
1264 }
1265
1266 for (;;) {
1267 _cleanup_free_ char *w = NULL;
1268
1269 r = extract_first_word(&p, &w, NULL, 0);
1270 if (r < 0) {
1271 log_syntax(unit, LOG_ERR, filename, line, r,
1272 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1273 break;
1274 }
1275 if (r == 0)
1276 break;
1277
1278 r = dns_name_is_valid(w);
1279 if (r <= 0) {
1280 log_syntax(unit, LOG_ERR, filename, line, r,
1281 "%s is not a valid domain name, ignoring.", w);
1282 continue;
1283 }
1284
1285 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1286 if (r < 0)
1287 return log_oom();
1288
1289 r = set_put(n->dnssec_negative_trust_anchors, w);
1290 if (r < 0)
1291 return log_oom();
1292 if (r > 0)
1293 w = NULL;
1294 }
1295
1296 return 0;
1297 }
1298
1299 int config_parse_ntp(
1300 const char *unit,
1301 const char *filename,
1302 unsigned line,
1303 const char *section,
1304 unsigned section_line,
1305 const char *lvalue,
1306 int ltype,
1307 const char *rvalue,
1308 void *data,
1309 void *userdata) {
1310
1311 char ***l = data;
1312 int r;
1313
1314 assert(l);
1315 assert(lvalue);
1316 assert(rvalue);
1317
1318 if (isempty(rvalue)) {
1319 *l = strv_free(*l);
1320 return 0;
1321 }
1322
1323 for (;;) {
1324 _cleanup_free_ char *w = NULL;
1325
1326 r = extract_first_word(&rvalue, &w, NULL, 0);
1327 if (r == -ENOMEM)
1328 return log_oom();
1329 if (r < 0) {
1330 log_syntax(unit, LOG_ERR, filename, line, r,
1331 "Failed to extract NTP server name, ignoring: %s", rvalue);
1332 break;
1333 }
1334 if (r == 0)
1335 break;
1336
1337 r = dns_name_is_valid_or_address(w);
1338 if (r <= 0) {
1339 log_syntax(unit, LOG_ERR, filename, line, r,
1340 "%s is not a valid domain name or IP address, ignoring.", w);
1341 continue;
1342 }
1343
1344 if (strv_length(*l) > MAX_NTP_SERVERS) {
1345 log_syntax(unit, LOG_WARNING, filename, line, 0,
1346 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1347 MAX_NTP_SERVERS, w);
1348 break;
1349 }
1350
1351 r = strv_consume(l, TAKE_PTR(w));
1352 if (r < 0)
1353 return log_oom();
1354 }
1355
1356 return 0;
1357 }
1358
1359 int config_parse_required_for_online(
1360 const char *unit,
1361 const char *filename,
1362 unsigned line,
1363 const char *section,
1364 unsigned section_line,
1365 const char *lvalue,
1366 int ltype,
1367 const char *rvalue,
1368 void *data,
1369 void *userdata) {
1370
1371 Network *network = data;
1372 LinkOperationalStateRange range;
1373 bool required = true;
1374 int r;
1375
1376 if (isempty(rvalue)) {
1377 network->required_for_online = true;
1378 network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
1379 return 0;
1380 }
1381
1382 r = parse_operational_state_range(rvalue, &range);
1383 if (r < 0) {
1384 r = parse_boolean(rvalue);
1385 if (r < 0) {
1386 log_syntax(unit, LOG_ERR, filename, line, r,
1387 "Failed to parse %s= setting, ignoring assignment: %s",
1388 lvalue, rvalue);
1389 return 0;
1390 }
1391
1392 required = r;
1393 range = LINK_OPERSTATE_RANGE_DEFAULT;
1394 }
1395
1396 network->required_for_online = required;
1397 network->required_operstate_for_online = range;
1398
1399 return 0;
1400 }
1401
1402 DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1403 "Failed to parse KeepConfiguration= setting");
1404
1405 static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
1406 [KEEP_CONFIGURATION_NO] = "no",
1407 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1408 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1409 [KEEP_CONFIGURATION_STATIC] = "static",
1410 [KEEP_CONFIGURATION_YES] = "yes",
1411 };
1412
1413 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);