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