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