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