]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
network: enable ipv6 when the network has static ipv6 configurations
[thirdparty/systemd.git] / src / network / networkd-network.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f579559b 2
9aa5d8ba
YW
3#include <net/if.h>
4#include <netinet/in.h>
01234e1f 5#include <linux/netdevice.h>
69a93e7d 6
b5efdb8a 7#include "alloc-util.h"
f579559b
TG
8#include "conf-files.h"
9#include "conf-parser.h"
37de2509 10#include "dns-domain.h"
3ffd4af2 11#include "fd-util.h"
07630cea 12#include "hostname-util.h"
88295a05 13#include "in-addr-util.h"
fc2f9534 14#include "network-internal.h"
23f53b99 15#include "networkd-manager.h"
3ffd4af2 16#include "networkd-network.h"
6bedfcbb 17#include "parse-util.h"
8a516214 18#include "set.h"
cebe1257 19#include "socket-util.h"
8fcde012 20#include "stat-util.h"
8b43440b 21#include "string-table.h"
07630cea 22#include "string-util.h"
700f1186 23#include "strv.h"
07630cea 24#include "util.h"
f579559b 25
c448459d
ZJS
26/* Let's assume that anything above this number is a user misconfiguration. */
27#define MAX_NTP_SERVERS 128
28
add8d07d 29/* Set defaults following RFC7844 */
30void network_apply_anonymize_if_set(Network *network) {
31 if (!network->dhcp_anonymize)
32 return;
33 /* RFC7844 3.7
34 SHOULD NOT send the Host Name option */
35 network->dhcp_send_hostname = false;
36 /* RFC7844 section 3.:
37 MAY contain the Client Identifier option
38 Section 3.5:
39 clients MUST use client identifiers based solely
40 on the link-layer address */
41 /* NOTE: Using MAC, as it does not reveal extra information,
42 * and some servers might not answer if this option is not sent */
43 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
44 /* RFC 7844 3.10:
45 SHOULD NOT use the Vendor Class Identifier option */
727ba17f 46 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
add8d07d 47 /* RFC7844 section 3.6.:
48 The client intending to protect its privacy SHOULD only request a
49 minimal number of options in the PRL and SHOULD also randomly shuffle
50 the ordering of option codes in the PRL. If this random ordering
51 cannot be implemented, the client MAY order the option codes in the
52 PRL by option code number (lowest to highest).
53 */
54 /* NOTE: dhcp_use_mtu is false by default,
55 * though it was not initiallized to any value in network_load_one.
56 * Maybe there should be another var called *send*?
57 * (to use the MTU sent by the server but to do not send
58 * the option in the PRL). */
59 network->dhcp_use_mtu = false;
28522b0d 60 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
61 * but this is needed to use them. */
62 network->dhcp_use_routes = true;
add8d07d 63 /* RFC7844 section 3.6.
64 * same comments as previous option */
65 network->dhcp_use_timezone = false;
66}
67
cebe1257
YW
68static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
69 const char *kind_string;
70 NetDev *netdev;
71 int r;
72
96db6412
YW
73 /* For test-networkd-conf, the check must be earlier than the assertions. */
74 if (!name)
75 return 0;
76
cebe1257
YW
77 assert(network);
78 assert(network->manager);
79 assert(network->filename);
80 assert(ret_netdev);
81
cebe1257
YW
82 if (kind == _NETDEV_KIND_TUNNEL)
83 kind_string = "tunnel";
84 else {
85 kind_string = netdev_kind_to_string(kind);
86 if (!kind_string)
87 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
88 "%s: Invalid NetDev kind of %s, ignoring assignment.",
89 network->filename, name);
90 }
91
92 r = netdev_get(network->manager, name, &netdev);
93 if (r < 0)
94 return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
95 network->filename, name);
96
97 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
98 IN_SET(netdev->kind,
99 NETDEV_KIND_IPIP,
100 NETDEV_KIND_SIT,
101 NETDEV_KIND_GRE,
102 NETDEV_KIND_GRETAP,
103 NETDEV_KIND_IP6GRE,
104 NETDEV_KIND_IP6GRETAP,
105 NETDEV_KIND_VTI,
106 NETDEV_KIND_VTI6,
9282f75b
YW
107 NETDEV_KIND_IP6TNL,
108 NETDEV_KIND_ERSPAN)))
cebe1257
YW
109 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
110 "%s: NetDev %s is not a %s, ignoring assignment",
111 network->filename, name, kind_string);
112
113 *ret_netdev = netdev_ref(netdev);
114 return 1;
115}
116
117static int network_resolve_stacked_netdevs(Network *network) {
118 void *name, *kind;
119 Iterator i;
120 int r;
121
122 assert(network);
123
124 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names, i) {
125 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
126
127 r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev);
128 if (r <= 0)
129 continue;
130
131 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
132 if (r < 0)
133 return log_oom();
134
135 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
136 if (r < 0)
137 return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m",
138 network->filename, (const char *) name);
139
140 netdev = NULL;
141 }
142
143 return 0;
144}
145
96db6412 146int network_verify(Network *network) {
4bec2f23
YW
147 Address *address, *address_next;
148 Route *route, *route_next;
fcbf4cb7
YW
149 FdbEntry *fdb, *fdb_next;
150 Neighbor *neighbor, *neighbor_next;
151 AddressLabel *label, *label_next;
152 Prefix *prefix, *prefix_next;
153 RoutingPolicyRule *rule, *rule_next;
0321cea7
YW
154
155 assert(network);
156 assert(network->filename);
157
84ea567e
YW
158 if (set_isempty(network->match_mac) && strv_isempty(network->match_path) &&
159 strv_isempty(network->match_driver) && strv_isempty(network->match_type) &&
44005bfb
YW
160 strv_isempty(network->match_name) && strv_isempty(network->match_property) &&
161 !network->conditions)
84ea567e
YW
162 log_warning("%s: No valid settings found in the [Match] section. "
163 "The file will match all interfaces. "
164 "If that is intended, please add Name=* in the [Match] section.",
165 network->filename);
166
cebe1257 167 /* skip out early if configuration does not match the environment */
c4f58dea 168 if (!condition_test_list(network->conditions, NULL, NULL, NULL))
cebe1257 169 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
3772cfde
ZJS
170 "%s: Conditions in the file do not match the system environment, skipping.",
171 network->filename);
cebe1257
YW
172
173 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
174 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
175 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
176 (void) network_resolve_stacked_netdevs(network);
177
178 /* Free unnecessary entries. */
179 network->bond_name = mfree(network->bond_name);
180 network->bridge_name = mfree(network->bridge_name);
181 network->vrf_name = mfree(network->vrf_name);
182 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
183
0321cea7
YW
184 if (network->bond) {
185 /* Bonding slave does not support addressing. */
186 if (network->ipv6_accept_ra > 0) {
ab24039f
ZJS
187 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
188 network->filename);
0321cea7
YW
189 network->ipv6_accept_ra = 0;
190 }
191 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
ab24039f
ZJS
192 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
193 network->filename);
0321cea7
YW
194 network->link_local = ADDRESS_FAMILY_NO;
195 }
196 if (network->dhcp != ADDRESS_FAMILY_NO) {
ab24039f
ZJS
197 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
198 network->filename);
0321cea7
YW
199 network->dhcp = ADDRESS_FAMILY_NO;
200 }
201 if (network->dhcp_server) {
ab24039f
ZJS
202 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
203 network->filename);
0321cea7
YW
204 network->dhcp_server = false;
205 }
206 if (network->n_static_addresses > 0) {
ab24039f
ZJS
207 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
208 network->filename);
0321cea7
YW
209 while ((address = network->static_addresses))
210 address_free(address);
211 }
212 if (network->n_static_routes > 0) {
ab24039f
ZJS
213 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
214 network->filename);
0321cea7
YW
215 while ((route = network->static_routes))
216 route_free(route);
217 }
218 }
219
220 if (network->link_local < 0)
8f191801
YW
221 network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
222
29e81083
YW
223 if (FLAGS_SET(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4) &&
224 !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) {
225 log_warning("%s: fallback assignment of IPv4 link local address is enabled but DHCPv4 is disabled. "
226 "Disabling the fallback assignment.", network->filename);
227 SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false);
228 }
229
8f191801
YW
230 if (network->ipv6_accept_ra < 0 && network->bridge)
231 network->ipv6_accept_ra = false;
0321cea7
YW
232
233 /* IPMasquerade=yes implies IPForward=yes */
234 if (network->ip_masquerade)
235 network->ip_forward |= ADDRESS_FAMILY_IPV4;
236
933c70a0 237 if (network->mtu > 0 && network->dhcp_use_mtu) {
0321cea7
YW
238 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
239 "Disabling UseMTU=.", network->filename);
240 network->dhcp_use_mtu = false;
241 }
242
7da377ef
SS
243 if (network->dhcp_critical >= 0) {
244 if (network->keep_configuration >= 0)
245 log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
246 "Ignoring CriticalConnection=.", network->filename);
247 else if (network->dhcp_critical)
248 /* CriticalConnection=yes also preserve foreign static configurations. */
249 network->keep_configuration = KEEP_CONFIGURATION_YES;
250 else
95355a28
YW
251 /* For backward compatibility, we do not release DHCP addresses on manager stop. */
252 network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
7da377ef
SS
253 }
254
255 if (network->keep_configuration < 0)
95355a28
YW
256 /* For backward compatibility, we do not release DHCP addresses on manager stop. */
257 network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
7da377ef 258
4bec2f23 259 LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
fcbf4cb7 260 if (address_section_verify(address) < 0)
4bec2f23 261 address_free(address);
4bec2f23 262
fcbf4cb7
YW
263 LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes)
264 if (route_section_verify(route, network) < 0)
4bec2f23 265 route_free(route);
0321cea7 266
fcbf4cb7
YW
267 LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
268 if (section_is_invalid(fdb->section))
269 fdb_entry_free(fdb);
270
271 LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors)
044d4b40 272 if (neighbor_section_verify(neighbor) < 0)
fcbf4cb7
YW
273 neighbor_free(neighbor);
274
275 LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels)
276 if (section_is_invalid(label->section))
277 address_label_free(label);
278
279 LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes)
280 if (section_is_invalid(prefix->section))
281 prefix_free(prefix);
282
283 LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules)
0aabccc8 284 if (routing_policy_rule_section_verify(rule) < 0)
fcbf4cb7 285 routing_policy_rule_free(rule);
4912ab77 286
0321cea7
YW
287 return 0;
288}
289
212bd73c 290int network_load_one(Manager *manager, const char *filename) {
838b2f7a 291 _cleanup_free_ char *fname = NULL, *name = NULL;
35ac3b76 292 _cleanup_(network_unrefp) Network *network = NULL;
f579559b 293 _cleanup_fclose_ FILE *file = NULL;
047a0dac 294 const char *dropin_dirname;
838b2f7a 295 char *d;
f579559b
TG
296 int r;
297
bf1bc670
TA
298 assert(manager);
299 assert(filename);
300
f579559b
TG
301 file = fopen(filename, "re");
302 if (!file) {
303 if (errno == ENOENT)
304 return 0;
1e7a0e21
LP
305
306 return -errno;
f579559b
TG
307 }
308
ed88bcfb
ZJS
309 if (null_or_empty_fd(fileno(file))) {
310 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
311 return 0;
312 }
313
838b2f7a
YW
314 fname = strdup(filename);
315 if (!fname)
316 return log_oom();
317
318 name = strdup(basename(filename));
319 if (!name)
320 return log_oom();
321
322 d = strrchr(name, '.');
323 if (!d)
324 return -EINVAL;
325
326 *d = '\0';
327
328 dropin_dirname = strjoina(name, ".network.d");
329
17f9c355 330 network = new(Network, 1);
f579559b
TG
331 if (!network)
332 return log_oom();
333
17f9c355 334 *network = (Network) {
838b2f7a
YW
335 .filename = TAKE_PTR(fname),
336 .name = TAKE_PTR(name),
17f9c355 337
715d398e 338 .manager = manager,
35ac3b76
YW
339 .n_ref = 1,
340
17f9c355 341 .required_for_online = true,
4ac77d63 342 .required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
17f9c355 343 .dhcp = ADDRESS_FAMILY_NO,
7da377ef 344 .dhcp_critical = -1,
17f9c355
YW
345 .dhcp_use_ntp = true,
346 .dhcp_use_dns = true,
347 .dhcp_use_hostname = true,
348 .dhcp_use_routes = true,
5238e957 349 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
17f9c355
YW
350 .dhcp_send_hostname = true,
351 /* To enable/disable RFC7844 Anonymity Profiles */
352 .dhcp_anonymize = false,
353 .dhcp_route_metric = DHCP_ROUTE_METRIC,
c9c908a6 354 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
17f9c355
YW
355 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
356 .dhcp_route_table = RT_TABLE_MAIN,
357 .dhcp_route_table_set = false,
358 /* NOTE: from man: UseMTU=... Defaults to false*/
359 .dhcp_use_mtu = false,
360 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
361 .dhcp_use_timezone = false,
362 .rapid_commit = true,
363
caa8ca42
SS
364 .dhcp6_use_ntp = true,
365 .dhcp6_use_dns = true,
366
17f9c355
YW
367 .dhcp_server_emit_dns = true,
368 .dhcp_server_emit_ntp = true,
369 .dhcp_server_emit_router = true,
370 .dhcp_server_emit_timezone = true,
371
372 .router_emit_dns = true,
373 .router_emit_domains = true,
374
375 .use_bpdu = -1,
376 .hairpin = -1,
377 .fast_leave = -1,
378 .allow_port_to_be_root = -1,
379 .unicast_flood = -1,
7f15b714 380 .multicast_flood = -1,
d3aa8b49 381 .multicast_to_unicast = -1,
7f15b714
TJ
382 .neighbor_suppression = -1,
383 .learning = -1,
1087623b
SS
384 .bridge_proxy_arp = -1,
385 .bridge_proxy_arp_wifi = -1,
17f9c355 386 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
0fadb2a4 387 .multicast_router = _MULTICAST_ROUTER_INVALID,
17f9c355
YW
388
389 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
390
7ece6f58 391 .dns_default_route = -1,
17f9c355
YW
392 .llmnr = RESOLVE_SUPPORT_YES,
393 .mdns = RESOLVE_SUPPORT_NO,
394 .dnssec_mode = _DNSSEC_MODE_INVALID,
395 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
396
0321cea7 397 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
2d792895 398 .link_local = _ADDRESS_FAMILY_INVALID,
17f9c355
YW
399
400 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
401 .ipv6_accept_ra = -1,
402 .ipv6_dad_transmits = -1,
403 .ipv6_hop_limit = -1,
404 .ipv6_proxy_ndp = -1,
405 .duid.type = _DUID_TYPE_INVALID,
406 .proxy_arp = -1,
407 .arp = -1,
408 .multicast = -1,
409 .allmulticast = -1,
410 .ipv6_accept_ra_use_dns = true,
062c2eea
SS
411 .ipv6_accept_ra_use_autonomous_prefix = true,
412 .ipv6_accept_ra_use_onlink_prefix = true,
17f9c355 413 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
d5fa3339 414 .ipv6_accept_ra_route_table_set = false,
c423be28 415
7da377ef
SS
416 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
417
c423be28 418 .can_triple_sampling = -1,
17f9c355 419 };
f579559b 420
dc0d4078 421 r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
23bb31aa
ZJS
422 "Match\0"
423 "Link\0"
424 "Network\0"
425 "Address\0"
e4a71bf3 426 "Neighbor\0"
95b74ef6 427 "IPv6AddressLabel\0"
bce67bbe 428 "RoutingPolicyRule\0"
23bb31aa
ZJS
429 "Route\0"
430 "DHCP\0"
431 "DHCPv4\0" /* compat */
caa8ca42 432 "DHCPv6\0"
23bb31aa
ZJS
433 "DHCPServer\0"
434 "IPv6AcceptRA\0"
a0e5c15d 435 "IPv6NDPProxyAddress\0"
23bb31aa
ZJS
436 "Bridge\0"
437 "BridgeFDB\0"
9d5d0090 438 "BridgeVLAN\0"
7d5cac19 439 "IPv6PrefixDelegation\0"
06828bb6
HP
440 "IPv6Prefix\0"
441 "CAN\0",
23bb31aa 442 config_item_perf_lookup, network_network_gperf_lookup,
bcde742e 443 CONFIG_PARSE_WARN, network);
102bc043 444 if (r < 0)
f579559b 445 return r;
f579559b 446
add8d07d 447 network_apply_anonymize_if_set(network);
448
fa7cd711
YW
449 r = network_add_ipv4ll_route(network);
450 if (r < 0)
451 log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
452
5d5003ab
YW
453 r = network_add_default_route_on_device(network);
454 if (r < 0)
455 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
456 network->filename);
457
715d398e 458 r = ordered_hashmap_ensure_allocated(&manager->networks, &string_hash_ops);
dbffab87
TG
459 if (r < 0)
460 return r;
461
715d398e 462 r = ordered_hashmap_put(manager->networks, network->name, network);
dbffab87
TG
463 if (r < 0)
464 return r;
465
0321cea7
YW
466 if (network_verify(network) < 0)
467 return 0;
b3070dc0 468
f579559b 469 network = NULL;
f579559b
TG
470 return 0;
471}
472
473int network_load(Manager *manager) {
477e73b5
ZJS
474 _cleanup_strv_free_ char **files = NULL;
475 char **f;
f579559b
TG
476 int r;
477
478 assert(manager);
479
715d398e 480 ordered_hashmap_clear_with_destructor(manager->networks, network_unref);
f579559b 481
dc0d4078 482 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
f647962d
MS
483 if (r < 0)
484 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b 485
715d398e 486 STRV_FOREACH(f, files) {
f579559b
TG
487 r = network_load_one(manager, *f);
488 if (r < 0)
489 return r;
490 }
491
f579559b
TG
492 return 0;
493}
494
35ac3b76 495static Network *network_free(Network *network) {
a0e5c15d 496 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
bce67bbe
SS
497 RoutingPolicyRule *rule;
498 FdbEntry *fdb_entry;
e4a71bf3 499 Neighbor *neighbor;
95b74ef6 500 AddressLabel *label;
057abfd8 501 Prefix *prefix;
bce67bbe 502 Address *address;
bce67bbe 503 Route *route;
f579559b
TG
504
505 if (!network)
35ac3b76 506 return NULL;
f579559b
TG
507
508 free(network->filename);
509
e90d0374 510 set_free_free(network->match_mac);
5256e00e
TG
511 strv_free(network->match_path);
512 strv_free(network->match_driver);
513 strv_free(network->match_type);
514 strv_free(network->match_name);
44005bfb 515 strv_free(network->match_property);
c4f58dea 516 condition_free_list(network->conditions);
f579559b
TG
517
518 free(network->description);
edb85f0d 519 free(network->dhcp_vendor_class_identifier);
af1c0de0 520 strv_free(network->dhcp_user_class);
27cb34f5 521 free(network->dhcp_hostname);
727b5734 522 set_free(network->dhcp_black_listed_ip);
c106cc36
TG
523 free(network->mac);
524
b0e39c82 525 strv_free(network->ntp);
5512a963 526 free(network->dns);
5e2a51d5
ZJS
527 ordered_set_free_free(network->search_domains);
528 ordered_set_free_free(network->route_domains);
0d4ad91d 529 strv_free(network->bind_carrier);
cdd7812b 530
5e2a51d5 531 ordered_set_free_free(network->router_search_domains);
cdd7812b 532 free(network->router_dns);
e520ce64 533 set_free_free(network->ndisc_black_listed_prefix);
3bef724f 534
cebe1257
YW
535 free(network->bridge_name);
536 free(network->bond_name);
537 free(network->vrf_name);
538 hashmap_free_free_key(network->stacked_netdev_names);
47e2dc31 539 netdev_unref(network->bridge);
47e2dc31 540 netdev_unref(network->bond);
6cb955c6 541 netdev_unref(network->vrf);
fa6f1e54 542 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
326cb406 543
f048a16b 544 while ((route = network->static_routes))
f579559b
TG
545 route_free(route);
546
f048a16b 547 while ((address = network->static_addresses))
f579559b
TG
548 address_free(address);
549
b98b483b
AR
550 while ((fdb_entry = network->static_fdb_entries))
551 fdb_entry_free(fdb_entry);
552
a0e5c15d
FK
553 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
554 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
555
e4a71bf3
WKI
556 while ((neighbor = network->neighbors))
557 neighbor_free(neighbor);
558
95b74ef6
SS
559 while ((label = network->address_labels))
560 address_label_free(label);
561
057abfd8
PF
562 while ((prefix = network->static_prefixes))
563 prefix_free(prefix);
564
bce67bbe
SS
565 while ((rule = network->rules))
566 routing_policy_rule_free(rule);
567
6ae115c1
TG
568 hashmap_free(network->addresses_by_section);
569 hashmap_free(network->routes_by_section);
b98b483b 570 hashmap_free(network->fdb_entries_by_section);
e4a71bf3 571 hashmap_free(network->neighbors_by_section);
95b74ef6 572 hashmap_free(network->address_labels_by_section);
057abfd8 573 hashmap_free(network->prefixes_by_section);
bce67bbe 574 hashmap_free(network->rules_by_section);
6ae115c1 575
dbffab87 576 if (network->manager) {
715d398e
YW
577 if (network->manager->networks && network->name)
578 ordered_hashmap_remove(network->manager->networks, network->name);
27dfc982
YW
579
580 if (network->manager->duids_requesting_uuid)
581 set_remove(network->manager->duids_requesting_uuid, &network->duid);
dbffab87
TG
582 }
583
584 free(network->name);
f579559b 585
8eb9058d 586 free(network->dhcp_server_timezone);
1a04db0f
LP
587 free(network->dhcp_server_dns);
588 free(network->dhcp_server_ntp);
8eb9058d 589
8a516214
LP
590 set_free_free(network->dnssec_negative_trust_anchors);
591
35ac3b76 592 return mfree(network);
f579559b
TG
593}
594
35ac3b76
YW
595DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
596
dbffab87
TG
597int network_get_by_name(Manager *manager, const char *name, Network **ret) {
598 Network *network;
599
600 assert(manager);
601 assert(name);
602 assert(ret);
603
715d398e 604 network = ordered_hashmap_get(manager->networks, name);
dbffab87
TG
605 if (!network)
606 return -ENOENT;
607
608 *ret = network;
609
610 return 0;
611}
612
51517f9e 613int network_get(Manager *manager, sd_device *device,
505f8da7
TG
614 const char *ifname, const struct ether_addr *address,
615 Network **ret) {
51517f9e 616 Network *network;
715d398e 617 Iterator i;
f579559b
TG
618
619 assert(manager);
f579559b 620 assert(ret);
af3aa302 621
715d398e 622 ORDERED_HASHMAP_FOREACH(network, manager->networks, i)
44005bfb
YW
623 if (net_match_config(network->match_mac, network->match_path, network->match_driver,
624 network->match_type, network->match_name, network->match_property,
b38de0e9 625 device, address, ifname)) {
24c083df 626 if (network->match_name && device) {
ca6038b8
TG
627 const char *attr;
628 uint8_t name_assign_type = NET_NAME_UNKNOWN;
629
51517f9e 630 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
dc751688 631 (void) safe_atou8(attr, &name_assign_type);
32bc8adc
TG
632
633 if (name_assign_type == NET_NAME_ENUM)
a2fae7bb
TG
634 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
635 ifname, network->filename);
32bc8adc 636 else
a2fae7bb 637 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 638 } else
a2fae7bb 639 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 640
f579559b
TG
641 *ret = network;
642 return 0;
643 }
f579559b
TG
644
645 *ret = NULL;
646
647 return -ENOENT;
648}
649
7d342c03 650int network_apply(Network *network, Link *link) {
c4a03a56
TG
651 assert(network);
652 assert(link);
653
c9c908a6 654 link->network = network_ref(network);
f579559b 655
5512a963 656 if (network->n_dns > 0 ||
3df9bec5 657 !strv_isempty(network->ntp) ||
5e2a51d5
ZJS
658 !ordered_set_isempty(network->search_domains) ||
659 !ordered_set_isempty(network->route_domains))
84de38c5 660 link_dirty(link);
3bef724f 661
f579559b
TG
662 return 0;
663}
02b59d57 664
adfeee49 665bool network_has_static_ipv6_configurations(Network *network) {
439689c6 666 Address *address;
adfeee49
YW
667 Route *route;
668 FdbEntry *fdb;
669 Neighbor *neighbor;
439689c6
SS
670
671 assert(network);
672
adfeee49 673 LIST_FOREACH(addresses, address, network->static_addresses)
439689c6
SS
674 if (address->family == AF_INET6)
675 return true;
adfeee49
YW
676
677 LIST_FOREACH(routes, route, network->static_routes)
678 if (route->family == AF_INET6)
679 return true;
680
681 LIST_FOREACH(static_fdb_entries, fdb, network->static_fdb_entries)
682 if (fdb->family == AF_INET6)
683 return true;
684
685 LIST_FOREACH(neighbors, neighbor, network->neighbors)
686 if (neighbor->family == AF_INET6)
687 return true;
688
689 if (!LIST_IS_EMPTY(network->address_labels))
690 return true;
691
692 if (!LIST_IS_EMPTY(network->static_prefixes))
693 return true;
439689c6
SS
694
695 return false;
696}
697
cebe1257 698int config_parse_stacked_netdev(const char *unit,
02b59d57
TG
699 const char *filename,
700 unsigned line,
701 const char *section,
702 unsigned section_line,
703 const char *lvalue,
704 int ltype,
705 const char *rvalue,
706 void *data,
707 void *userdata) {
95dba435
YW
708 _cleanup_free_ char *name = NULL;
709 NetDevKind kind = ltype;
cebe1257 710 Hashmap **h = data;
02b59d57
TG
711 int r;
712
713 assert(filename);
714 assert(lvalue);
715 assert(rvalue);
716 assert(data);
95dba435
YW
717 assert(IN_SET(kind,
718 NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
69c317a0 719 NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
98d20a17 720 NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
721 NETDEV_KIND_XFRM));
54abf461 722
cebe1257 723 if (!ifname_valid(rvalue)) {
3772cfde
ZJS
724 log_syntax(unit, LOG_ERR, filename, line, 0,
725 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
726 return 0;
727 }
728
cebe1257
YW
729 name = strdup(rvalue);
730 if (!name)
731 return log_oom();
3e570042 732
cebe1257
YW
733 r = hashmap_ensure_allocated(h, &string_hash_ops);
734 if (r < 0)
735 return log_oom();
326cb406 736
cebe1257 737 r = hashmap_put(*h, name, INT_TO_PTR(kind));
83ec4592 738 if (r < 0)
3772cfde 739 log_syntax(unit, LOG_ERR, filename, line, r,
83ec4592
ZJS
740 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
741 else if (r == 0)
742 log_syntax(unit, LOG_DEBUG, filename, line, r,
743 "NetDev '%s' specified twice, ignoring.", name);
744 else
745 name = NULL;
47e2dc31 746
fe6b2d55
TG
747 return 0;
748}
7951dea2 749
3df9bec5
LP
750int config_parse_domains(
751 const char *unit,
752 const char *filename,
753 unsigned line,
754 const char *section,
755 unsigned section_line,
756 const char *lvalue,
757 int ltype,
758 const char *rvalue,
759 void *data,
760 void *userdata) {
761
762 const char *p;
763 Network *n = data;
6192b846
TG
764 int r;
765
3df9bec5
LP
766 assert(n);
767 assert(lvalue);
768 assert(rvalue);
6192b846 769
3df9bec5 770 if (isempty(rvalue)) {
5e2a51d5
ZJS
771 n->search_domains = ordered_set_free_free(n->search_domains);
772 n->route_domains = ordered_set_free_free(n->route_domains);
3df9bec5
LP
773 return 0;
774 }
67272d15 775
3df9bec5
LP
776 p = rvalue;
777 for (;;) {
778 _cleanup_free_ char *w = NULL, *normalized = NULL;
779 const char *domain;
780 bool is_route;
781
782 r = extract_first_word(&p, &w, NULL, 0);
783 if (r < 0) {
ab24039f
ZJS
784 log_syntax(unit, LOG_ERR, filename, line, r,
785 "Failed to extract search or route domain, ignoring: %s", rvalue);
3df9bec5
LP
786 break;
787 }
788 if (r == 0)
789 break;
790
791 is_route = w[0] == '~';
792 domain = is_route ? w + 1 : w;
793
794 if (dns_name_is_root(domain) || streq(domain, "*")) {
ab24039f
ZJS
795 /* If the root domain appears as is, or the special token "*" is found, we'll
796 * consider this as routing domain, unconditionally. */
3df9bec5 797 is_route = true;
ab24039f
ZJS
798 domain = "."; /* make sure we don't allow empty strings, thus write the root
799 * domain as "." */
3df9bec5 800 } else {
7470cc4c 801 r = dns_name_normalize(domain, 0, &normalized);
3df9bec5 802 if (r < 0) {
ab24039f
ZJS
803 log_syntax(unit, LOG_ERR, filename, line, r,
804 "'%s' is not a valid domain name, ignoring.", domain);
3df9bec5
LP
805 continue;
806 }
807
808 domain = normalized;
809
810 if (is_localhost(domain)) {
ab24039f
ZJS
811 log_syntax(unit, LOG_ERR, filename, line, 0,
812 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
813 domain);
37de2509 814 continue;
3df9bec5 815 }
37de2509 816 }
40274ed6 817
5e2a51d5
ZJS
818 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
819 r = ordered_set_ensure_allocated(set, &string_hash_ops);
820 if (r < 0)
821 return r;
822
823 r = ordered_set_put_strdup(*set, domain);
09451975
LP
824 if (r < 0)
825 return log_oom();
40274ed6 826 }
f15b6e5a 827
6192b846
TG
828 return 0;
829}
830
60c35566 831int config_parse_ipv6token(
7f77697a
TG
832 const char* unit,
833 const char *filename,
834 unsigned line,
835 const char *section,
836 unsigned section_line,
837 const char *lvalue,
838 int ltype,
839 const char *rvalue,
840 void *data,
841 void *userdata) {
842
843 union in_addr_union buffer;
844 struct in6_addr *token = data;
845 int r;
846
847 assert(filename);
848 assert(lvalue);
849 assert(rvalue);
850 assert(token);
851
852 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
853 if (r < 0) {
ab24039f
ZJS
854 log_syntax(unit, LOG_ERR, filename, line, r,
855 "Failed to parse IPv6 token, ignoring: %s", rvalue);
7f77697a
TG
856 return 0;
857 }
858
c606db69 859 if (in_addr_is_null(AF_INET6, &buffer)) {
ab24039f
ZJS
860 log_syntax(unit, LOG_ERR, filename, line, 0,
861 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
7f77697a
TG
862 return 0;
863 }
864
865 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
ab24039f
ZJS
866 log_syntax(unit, LOG_ERR, filename, line, 0,
867 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
7f77697a
TG
868 return 0;
869 }
870
871 *token = buffer.in6;
872
873 return 0;
874}
8add5f79 875
49092e22 876static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1f0d9695
LP
877 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
878 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
879 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
49092e22
SS
880};
881
882DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
883
884int config_parse_ipv6_privacy_extensions(
885 const char* unit,
886 const char *filename,
887 unsigned line,
888 const char *section,
889 unsigned section_line,
890 const char *lvalue,
891 int ltype,
892 const char *rvalue,
893 void *data,
894 void *userdata) {
895
896 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
897 int k;
898
899 assert(filename);
900 assert(lvalue);
901 assert(rvalue);
902 assert(ipv6_privacy_extensions);
903
904 /* Our enum shall be a superset of booleans, hence first try
905 * to parse as boolean, and then as enum */
906
907 k = parse_boolean(rvalue);
908 if (k > 0)
1f0d9695 909 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
49092e22 910 else if (k == 0)
1f0d9695 911 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
49092e22 912 else {
1f0d9695 913 IPv6PrivacyExtensions s;
49092e22
SS
914
915 s = ipv6_privacy_extensions_from_string(rvalue);
1f0d9695
LP
916 if (s < 0) {
917
918 if (streq(rvalue, "kernel"))
919 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
920 else {
ab24039f
ZJS
921 log_syntax(unit, LOG_ERR, filename, line, 0,
922 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1f0d9695
LP
923 return 0;
924 }
49092e22
SS
925 }
926
927 *ipv6_privacy_extensions = s;
928 }
929
930 return 0;
931}
a7d0ef44 932
1ac608c9
LP
933int config_parse_hostname(
934 const char *unit,
935 const char *filename,
936 unsigned line,
937 const char *section,
938 unsigned section_line,
939 const char *lvalue,
940 int ltype,
941 const char *rvalue,
942 void *data,
943 void *userdata) {
944
6528693a
YW
945 _cleanup_free_ char *hn = NULL;
946 char **hostname = data;
a7d0ef44
SS
947 int r;
948
949 assert(filename);
950 assert(lvalue);
951 assert(rvalue);
952
1ac608c9 953 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
a7d0ef44
SS
954 if (r < 0)
955 return r;
956
1ac608c9 957 if (!hostname_is_valid(hn, false)) {
ab24039f
ZJS
958 log_syntax(unit, LOG_ERR, filename, line, 0,
959 "Hostname is not valid, ignoring assignment: %s", rvalue);
a7d0ef44
SS
960 return 0;
961 }
962
6528693a
YW
963 r = dns_name_is_valid(hn);
964 if (r < 0) {
ab24039f
ZJS
965 log_syntax(unit, LOG_ERR, filename, line, r,
966 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
6528693a
YW
967 return 0;
968 }
969 if (r == 0) {
ab24039f
ZJS
970 log_syntax(unit, LOG_ERR, filename, line, 0,
971 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
6528693a
YW
972 return 0;
973 }
974
975 return free_and_replace(*hostname, hn);
a7d0ef44 976}
8eb9058d
LP
977
978int config_parse_timezone(
979 const char *unit,
980 const char *filename,
981 unsigned line,
982 const char *section,
983 unsigned section_line,
984 const char *lvalue,
985 int ltype,
986 const char *rvalue,
987 void *data,
988 void *userdata) {
989
19f9e4e2
YW
990 _cleanup_free_ char *tz = NULL;
991 char **datap = data;
8eb9058d
LP
992 int r;
993
994 assert(filename);
995 assert(lvalue);
996 assert(rvalue);
997
998 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
999 if (r < 0)
1000 return r;
1001
089fb865 1002 if (!timezone_is_valid(tz, LOG_ERR)) {
ab24039f
ZJS
1003 log_syntax(unit, LOG_ERR, filename, line, 0,
1004 "Timezone is not valid, ignoring assignment: %s", rvalue);
8eb9058d
LP
1005 return 0;
1006 }
1007
19f9e4e2 1008 return free_and_replace(*datap, tz);
8eb9058d 1009}
1a04db0f 1010
53253824
SS
1011int config_parse_dns(
1012 const char *unit,
1013 const char *filename,
1014 unsigned line,
1015 const char *section,
1016 unsigned section_line,
1017 const char *lvalue,
1018 int ltype,
1019 const char *rvalue,
1020 void *data,
1021 void *userdata) {
1022
1023 Network *n = userdata;
1024 int r;
1025
1026 assert(filename);
1027 assert(lvalue);
1028 assert(rvalue);
1029
1030 for (;;) {
1031 _cleanup_free_ char *w = NULL;
1032 union in_addr_union a;
5512a963 1033 struct in_addr_data *m;
53253824
SS
1034 int family;
1035
5512a963 1036 r = extract_first_word(&rvalue, &w, NULL, 0);
53253824
SS
1037 if (r == -ENOMEM)
1038 return log_oom();
1039 if (r < 0) {
ab24039f
ZJS
1040 log_syntax(unit, LOG_ERR, filename, line, r,
1041 "Invalid syntax, ignoring: %s", rvalue);
53253824
SS
1042 break;
1043 }
5512a963
LP
1044 if (r == 0)
1045 break;
53253824
SS
1046
1047 r = in_addr_from_string_auto(w, &family, &a);
1048 if (r < 0) {
ab24039f
ZJS
1049 log_syntax(unit, LOG_ERR, filename, line, r,
1050 "Failed to parse dns server address, ignoring: %s", w);
53253824
SS
1051 continue;
1052 }
1053
62d74c78 1054 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
5512a963 1055 if (!m)
53253824
SS
1056 return log_oom();
1057
5512a963
LP
1058 m[n->n_dns++] = (struct in_addr_data) {
1059 .family = family,
1060 .address = a,
1061 };
1062
1063 n->dns = m;
53253824
SS
1064 }
1065
1066 return 0;
1067}
1068
8a516214
LP
1069int config_parse_dnssec_negative_trust_anchors(
1070 const char *unit,
1071 const char *filename,
1072 unsigned line,
1073 const char *section,
1074 unsigned section_line,
1075 const char *lvalue,
1076 int ltype,
1077 const char *rvalue,
1078 void *data,
1079 void *userdata) {
1080
1081 const char *p = rvalue;
1082 Network *n = data;
1083 int r;
1084
3df9bec5 1085 assert(n);
8a516214
LP
1086 assert(lvalue);
1087 assert(rvalue);
1088
1089 if (isempty(rvalue)) {
1090 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1091 return 0;
1092 }
1093
1094 for (;;) {
1095 _cleanup_free_ char *w = NULL;
1096
1097 r = extract_first_word(&p, &w, NULL, 0);
1098 if (r < 0) {
ab24039f
ZJS
1099 log_syntax(unit, LOG_ERR, filename, line, r,
1100 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
8a516214
LP
1101 break;
1102 }
1103 if (r == 0)
1104 break;
1105
1106 r = dns_name_is_valid(w);
1107 if (r <= 0) {
ab24039f
ZJS
1108 log_syntax(unit, LOG_ERR, filename, line, r,
1109 "%s is not a valid domain name, ignoring.", w);
8a516214
LP
1110 continue;
1111 }
1112
cbbf38ae
LP
1113 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1114 if (r < 0)
1115 return log_oom();
1116
8a516214
LP
1117 r = set_put(n->dnssec_negative_trust_anchors, w);
1118 if (r < 0)
1119 return log_oom();
1120 if (r > 0)
1121 w = NULL;
1122 }
1123
1124 return 0;
1125}
b2a81c0b 1126
26575990
LP
1127int config_parse_ntp(
1128 const char *unit,
1129 const char *filename,
1130 unsigned line,
1131 const char *section,
1132 unsigned section_line,
1133 const char *lvalue,
1134 int ltype,
1135 const char *rvalue,
1136 void *data,
1137 void *userdata) {
1138
1139 char ***l = data;
1140 int r;
1141
1142 assert(l);
1143 assert(lvalue);
1144 assert(rvalue);
1145
1146 if (isempty(rvalue)) {
1147 *l = strv_free(*l);
1148 return 0;
1149 }
1150
1151 for (;;) {
1152 _cleanup_free_ char *w = NULL;
1153
1154 r = extract_first_word(&rvalue, &w, NULL, 0);
1155 if (r == -ENOMEM)
1156 return log_oom();
1157 if (r < 0) {
ab24039f
ZJS
1158 log_syntax(unit, LOG_ERR, filename, line, r,
1159 "Failed to extract NTP server name, ignoring: %s", rvalue);
26575990
LP
1160 break;
1161 }
1162 if (r == 0)
1163 break;
1164
1165 r = dns_name_is_valid_or_address(w);
1166 if (r <= 0) {
ab24039f
ZJS
1167 log_syntax(unit, LOG_ERR, filename, line, r,
1168 "%s is not a valid domain name or IP address, ignoring.", w);
26575990 1169 continue;
af1c0de0
SS
1170 }
1171
c448459d
ZJS
1172 if (strv_length(*l) > MAX_NTP_SERVERS) {
1173 log_syntax(unit, LOG_WARNING, filename, line, 0,
1174 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1175 MAX_NTP_SERVERS, w);
1176 break;
1177 }
1178
1179 r = strv_consume(l, TAKE_PTR(w));
af1c0de0
SS
1180 if (r < 0)
1181 return log_oom();
af1c0de0
SS
1182 }
1183
1184 return 0;
1185}
1186
4ac77d63
YW
1187int config_parse_required_for_online(
1188 const char *unit,
1189 const char *filename,
1190 unsigned line,
1191 const char *section,
1192 unsigned section_line,
1193 const char *lvalue,
1194 int ltype,
1195 const char *rvalue,
1196 void *data,
1197 void *userdata) {
1198
1199 Network *network = data;
1200 LinkOperationalState s;
1201 bool required = true;
1202 int r;
1203
1204 if (isempty(rvalue)) {
1205 network->required_for_online = true;
1206 network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED;
1207 return 0;
1208 }
1209
1210 s = link_operstate_from_string(rvalue);
1211 if (s < 0) {
1212 r = parse_boolean(rvalue);
1213 if (r < 0) {
1214 log_syntax(unit, LOG_ERR, filename, line, r,
1215 "Failed to parse %s= setting, ignoring assignment: %s",
1216 lvalue, rvalue);
1217 return 0;
1218 }
1219
1220 required = r;
1221 s = LINK_OPERSTATE_DEGRADED;
1222 }
1223
1224 network->required_for_online = required;
1225 network->required_operstate_for_online = s;
1226
1227 return 0;
1228}
7da377ef
SS
1229
1230DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1231 "Failed to parse KeepConfiguration= setting");
1232
1233static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
95355a28
YW
1234 [KEEP_CONFIGURATION_NO] = "no",
1235 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1236 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1237 [KEEP_CONFIGURATION_STATIC] = "static",
1238 [KEEP_CONFIGURATION_YES] = "yes",
7da377ef
SS
1239};
1240
1241DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);