]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
network: add NextServer= and Filename= setting to [DHCPServer] section
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 "net-condition.h"
16 #include "netdev/macvlan.h"
17 #include "networkd-address-label.h"
18 #include "networkd-address.h"
19 #include "networkd-bridge-fdb.h"
20 #include "networkd-bridge-mdb.h"
21 #include "networkd-dhcp-common.h"
22 #include "networkd-dhcp-server-static-lease.h"
23 #include "networkd-dhcp-server.h"
24 #include "networkd-ipv6-proxy-ndp.h"
25 #include "networkd-manager.h"
26 #include "networkd-ndisc.h"
27 #include "networkd-neighbor.h"
28 #include "networkd-network.h"
29 #include "networkd-nexthop.h"
30 #include "networkd-radv.h"
31 #include "networkd-route.h"
32 #include "networkd-routing-policy-rule.h"
33 #include "networkd-sriov.h"
34 #include "parse-util.h"
35 #include "path-lookup.h"
36 #include "radv-internal.h"
37 #include "set.h"
38 #include "socket-util.h"
39 #include "stat-util.h"
40 #include "string-table.h"
41 #include "string-util.h"
42 #include "strv.h"
43 #include "tc.h"
44 #include "util.h"
45
46 /* Let's assume that anything above this number is a user misconfiguration. */
47 #define MAX_NTP_SERVERS 128
48
49 static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret) {
50 const char *kind_string;
51 NetDev *netdev;
52 int r;
53
54 /* For test-networkd-conf, the check must be earlier than the assertions. */
55 if (!name)
56 return 0;
57
58 assert(network);
59 assert(network->manager);
60 assert(network->filename);
61 assert(ret);
62
63 if (kind == _NETDEV_KIND_TUNNEL)
64 kind_string = "tunnel";
65 else {
66 kind_string = netdev_kind_to_string(kind);
67 if (!kind_string)
68 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
69 "%s: Invalid NetDev kind of %s, ignoring assignment.",
70 network->filename, name);
71 }
72
73 r = netdev_get(network->manager, name, &netdev);
74 if (r < 0)
75 return log_warning_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
76 network->filename, name);
77
78 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
79 IN_SET(netdev->kind,
80 NETDEV_KIND_ERSPAN,
81 NETDEV_KIND_GRE,
82 NETDEV_KIND_GRETAP,
83 NETDEV_KIND_IP6GRE,
84 NETDEV_KIND_IP6GRETAP,
85 NETDEV_KIND_IP6TNL,
86 NETDEV_KIND_IPIP,
87 NETDEV_KIND_SIT,
88 NETDEV_KIND_VTI,
89 NETDEV_KIND_VTI6)))
90 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
91 "%s: NetDev %s is not a %s, ignoring assignment",
92 network->filename, name, kind_string);
93
94 *ret = netdev_ref(netdev);
95 return 1;
96 }
97
98 static int network_resolve_stacked_netdevs(Network *network) {
99 void *name, *kind;
100 int r;
101
102 assert(network);
103
104 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names) {
105 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
106
107 if (network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev) <= 0)
108 continue;
109
110 r = hashmap_ensure_put(&network->stacked_netdevs, &string_hash_ops, netdev->ifname, netdev);
111 if (r == -ENOMEM)
112 return log_oom();
113 if (r < 0)
114 log_warning_errno(r, "%s: Failed to add NetDev '%s' to network, ignoring: %m",
115 network->filename, (const char *) name);
116
117 netdev = NULL;
118 }
119
120 return 0;
121 }
122
123 int network_verify(Network *network) {
124 int r;
125
126 assert(network);
127 assert(network->manager);
128 assert(network->filename);
129
130 if (net_match_is_empty(&network->match) && !network->conditions)
131 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
132 "%s: No valid settings found in the [Match] section, ignoring file. "
133 "To match all interfaces, add Name=* in the [Match] section.",
134 network->filename);
135
136 /* skip out early if configuration does not match the environment */
137 if (!condition_test_list(network->conditions, environ, NULL, NULL, NULL))
138 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
139 "%s: Conditions in the file do not match the system environment, skipping.",
140 network->filename);
141
142 if (network->keep_master) {
143 if (network->batadv_name)
144 log_warning("%s: BatmanAdvanced= set with KeepMaster= enabled, ignoring BatmanAdvanced=.",
145 network->filename);
146 if (network->bond_name)
147 log_warning("%s: Bond= set with KeepMaster= enabled, ignoring Bond=.",
148 network->filename);
149 if (network->bridge_name)
150 log_warning("%s: Bridge= set with KeepMaster= enabled, ignoring Bridge=.",
151 network->filename);
152 if (network->vrf_name)
153 log_warning("%s: VRF= set with KeepMaster= enabled, ignoring VRF=.",
154 network->filename);
155
156 network->batadv_name = mfree(network->batadv_name);
157 network->bond_name = mfree(network->bond_name);
158 network->bridge_name = mfree(network->bridge_name);
159 network->vrf_name = mfree(network->vrf_name);
160 }
161
162 (void) network_resolve_netdev_one(network, network->batadv_name, NETDEV_KIND_BATADV, &network->batadv);
163 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
164 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
165 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
166 r = network_resolve_stacked_netdevs(network);
167 if (r < 0)
168 return r;
169
170 /* Free unnecessary entries. */
171 network->batadv_name = mfree(network->batadv_name);
172 network->bond_name = mfree(network->bond_name);
173 network->bridge_name = mfree(network->bridge_name);
174 network->vrf_name = mfree(network->vrf_name);
175 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
176
177 if (network->bond) {
178 /* Bonding slave does not support addressing. */
179 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
180 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
181 network->filename);
182 network->link_local = ADDRESS_FAMILY_NO;
183 }
184 if (network->dhcp_server) {
185 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
186 network->filename);
187 network->dhcp_server = false;
188 }
189 if (!ordered_hashmap_isempty(network->addresses_by_section))
190 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
191 network->filename);
192 if (!hashmap_isempty(network->routes_by_section))
193 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
194 network->filename);
195
196 network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
197 network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free);
198 }
199
200 if (network->link_local < 0) {
201 network->link_local = ADDRESS_FAMILY_IPV6;
202
203 if (network->keep_master || network->bridge)
204 network->link_local = ADDRESS_FAMILY_NO;
205 else {
206 NetDev *netdev;
207
208 HASHMAP_FOREACH(netdev, network->stacked_netdevs) {
209 MacVlan *m;
210
211 if (netdev->kind == NETDEV_KIND_MACVLAN)
212 m = MACVLAN(netdev);
213 else if (netdev->kind == NETDEV_KIND_MACVTAP)
214 m = MACVTAP(netdev);
215 else
216 continue;
217
218 assert(m);
219
220 if (m->mode == NETDEV_MACVLAN_MODE_PASSTHRU)
221 network->link_local = ADDRESS_FAMILY_NO;
222
223 /* There won't be a passthru MACVLAN/MACVTAP if there's already one in another mode */
224 break;
225 }
226 }
227 }
228
229 if (network->ipv6ll_address_gen_mode == IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE)
230 SET_FLAG(network->link_local, ADDRESS_FAMILY_IPV6, false);
231
232 if (in6_addr_is_set(&network->ipv6ll_stable_secret) &&
233 network->ipv6ll_address_gen_mode < 0)
234 network->ipv6ll_address_gen_mode = IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY;
235
236 /* IPMasquerade implies IPForward */
237 network->ip_forward |= network->ip_masquerade;
238
239 network_adjust_ipv6_proxy_ndp(network);
240 network_adjust_ipv6_accept_ra(network);
241 network_adjust_dhcp(network);
242 network_adjust_radv(network);
243 network_adjust_bridge_vlan(network);
244
245 if (network->mtu > 0 && network->dhcp_use_mtu) {
246 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
247 "Disabling UseMTU=.", network->filename);
248 network->dhcp_use_mtu = false;
249 }
250
251 if (network->dhcp_critical >= 0) {
252 if (network->keep_configuration >= 0) {
253 if (network->manager->keep_configuration < 0)
254 log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
255 "Ignoring CriticalConnection=.", network->filename);
256 } else if (network->dhcp_critical)
257 /* CriticalConnection=yes also preserve foreign static configurations. */
258 network->keep_configuration = KEEP_CONFIGURATION_YES;
259 else
260 network->keep_configuration = KEEP_CONFIGURATION_NO;
261 }
262
263 if (!strv_isempty(network->bind_carrier)) {
264 if (!IN_SET(network->activation_policy, _ACTIVATION_POLICY_INVALID, ACTIVATION_POLICY_BOUND))
265 log_warning("%s: ActivationPolicy=bound is required with BindCarrier=. "
266 "Setting ActivationPolicy=bound.", network->filename);
267 network->activation_policy = ACTIVATION_POLICY_BOUND;
268 } else if (network->activation_policy == ACTIVATION_POLICY_BOUND) {
269 log_warning("%s: ActivationPolicy=bound requires BindCarrier=. "
270 "Ignoring ActivationPolicy=bound.", network->filename);
271 network->activation_policy = ACTIVATION_POLICY_UP;
272 }
273
274 if (network->activation_policy == _ACTIVATION_POLICY_INVALID)
275 network->activation_policy = ACTIVATION_POLICY_UP;
276
277 if (network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
278 if (network->ignore_carrier_loss_set && network->ignore_carrier_loss_usec < USEC_INFINITY)
279 log_warning("%s: IgnoreCarrierLoss=no or finite timespan conflicts with ActivationPolicy=always-up. "
280 "Setting IgnoreCarrierLoss=yes.", network->filename);
281 network->ignore_carrier_loss_set = true;
282 network->ignore_carrier_loss_usec = USEC_INFINITY;
283 }
284
285 if (!network->ignore_carrier_loss_set) {
286 network->ignore_carrier_loss_set = true;
287 network->ignore_carrier_loss_usec = network->configure_without_carrier ? USEC_INFINITY : 0;
288 }
289
290 if (IN_SET(network->activation_policy, ACTIVATION_POLICY_DOWN, ACTIVATION_POLICY_ALWAYS_DOWN, ACTIVATION_POLICY_MANUAL)) {
291 if (network->required_for_online < 0 ||
292 (network->required_for_online == true && network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN)) {
293 log_debug("%s: Setting RequiredForOnline=no because ActivationPolicy=%s.", network->filename,
294 activation_policy_to_string(network->activation_policy));
295 network->required_for_online = false;
296 } else if (network->required_for_online == true)
297 log_warning("%s: RequiredForOnline=yes and ActivationPolicy=%s, "
298 "this may cause a delay at boot.", network->filename,
299 activation_policy_to_string(network->activation_policy));
300 }
301
302 if (network->required_for_online < 0)
303 network->required_for_online = true;
304
305 if (network->keep_configuration < 0)
306 network->keep_configuration = KEEP_CONFIGURATION_NO;
307
308 if (network->ipv6_proxy_ndp == 0 && !set_isempty(network->ipv6_proxy_ndp_addresses)) {
309 log_warning("%s: IPv6ProxyNDP= is disabled. Ignoring IPv6ProxyNDPAddress=.", network->filename);
310 network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses);
311 }
312
313 r = network_drop_invalid_addresses(network);
314 if (r < 0)
315 return r;
316 network_drop_invalid_routes(network);
317 network_drop_invalid_nexthops(network);
318 network_drop_invalid_bridge_fdb_entries(network);
319 network_drop_invalid_bridge_mdb_entries(network);
320 network_drop_invalid_neighbors(network);
321 network_drop_invalid_address_labels(network);
322 network_drop_invalid_prefixes(network);
323 network_drop_invalid_route_prefixes(network);
324 network_drop_invalid_routing_policy_rules(network);
325 network_drop_invalid_traffic_control(network);
326 r = sr_iov_drop_invalid_sections(UINT32_MAX, network->sr_iov_by_section);
327 if (r < 0)
328 return r;
329 network_drop_invalid_static_leases(network);
330
331 network_adjust_dhcp_server(network);
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 const char *dropin_dirname;
340 char *d;
341 int r;
342
343 assert(manager);
344 assert(filename);
345
346 r = null_or_empty_path(filename);
347 if (r == -ENOENT)
348 return 0;
349 if (r < 0)
350 return r;
351 if (r > 0) {
352 log_debug("Skipping empty file: %s", filename);
353 return 0;
354 }
355
356 fname = strdup(filename);
357 if (!fname)
358 return log_oom();
359
360 name = strdup(basename(filename));
361 if (!name)
362 return log_oom();
363
364 d = strrchr(name, '.');
365 if (!d)
366 return -EINVAL;
367
368 *d = '\0';
369
370 dropin_dirname = strjoina(name, ".network.d");
371
372 network = new(Network, 1);
373 if (!network)
374 return log_oom();
375
376 *network = (Network) {
377 .filename = TAKE_PTR(fname),
378 .name = TAKE_PTR(name),
379
380 .manager = manager,
381 .n_ref = 1,
382
383 .required_for_online = -1,
384 .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
385 .activation_policy = _ACTIVATION_POLICY_INVALID,
386 .arp = -1,
387 .multicast = -1,
388 .allmulticast = -1,
389 .promiscuous = -1,
390
391 .keep_configuration = manager->keep_configuration,
392
393 .dhcp_duid.type = _DUID_TYPE_INVALID,
394 .dhcp_critical = -1,
395 .dhcp_use_ntp = true,
396 .dhcp_routes_to_ntp = true,
397 .dhcp_use_sip = true,
398 .dhcp_use_dns = true,
399 .dhcp_routes_to_dns = true,
400 .dhcp_use_hostname = true,
401 .dhcp_use_routes = true,
402 .dhcp_use_gateway = -1,
403 .dhcp_send_hostname = true,
404 .dhcp_send_release = true,
405 .dhcp_route_metric = DHCP_ROUTE_METRIC,
406 .dhcp_client_identifier = _DHCP_CLIENT_ID_INVALID,
407 .dhcp_route_table = RT_TABLE_MAIN,
408 .dhcp_ip_service_type = -1,
409 .dhcp_broadcast = -1,
410
411 .dhcp6_use_address = true,
412 .dhcp6_use_pd_prefix = true,
413 .dhcp6_use_dns = true,
414 .dhcp6_use_hostname = true,
415 .dhcp6_use_ntp = true,
416 .dhcp6_duid.type = _DUID_TYPE_INVALID,
417 .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID,
418
419 .dhcp_pd = -1,
420 .dhcp_pd_announce = true,
421 .dhcp_pd_assign = true,
422 .dhcp_pd_manage_temporary_address = true,
423 .dhcp_pd_subnet_id = -1,
424 .dhcp_pd_route_metric = DHCP6PD_ROUTE_METRIC,
425
426 .dhcp_server_bind_to_interface = true,
427 .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
428 .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
429 .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
430 .dhcp_server_emit_router = true,
431 .dhcp_server_emit_timezone = true,
432
433 .router_lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC,
434 .router_dns_lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
435 .router_emit_dns = true,
436 .router_emit_domains = true,
437
438 .use_bpdu = -1,
439 .hairpin = -1,
440 .isolated = -1,
441 .fast_leave = -1,
442 .allow_port_to_be_root = -1,
443 .unicast_flood = -1,
444 .multicast_flood = -1,
445 .multicast_to_unicast = -1,
446 .neighbor_suppression = -1,
447 .learning = -1,
448 .bridge_proxy_arp = -1,
449 .bridge_proxy_arp_wifi = -1,
450 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
451 .multicast_router = _MULTICAST_ROUTER_INVALID,
452
453 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
454 .lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID,
455
456 .dns_default_route = -1,
457 .llmnr = RESOLVE_SUPPORT_YES,
458 .mdns = RESOLVE_SUPPORT_NO,
459 .dnssec_mode = _DNSSEC_MODE_INVALID,
460 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
461
462 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
463 .link_local = _ADDRESS_FAMILY_INVALID,
464 .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
465
466 .ipv4_accept_local = -1,
467 .ipv4_route_localnet = -1,
468 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
469 .ipv6_dad_transmits = -1,
470 .ipv6_hop_limit = -1,
471 .ipv6_proxy_ndp = -1,
472 .proxy_arp = -1,
473
474 .ipv6_accept_ra = -1,
475 .ipv6_accept_ra_use_dns = true,
476 .ipv6_accept_ra_use_gateway = true,
477 .ipv6_accept_ra_use_route_prefix = true,
478 .ipv6_accept_ra_use_autonomous_prefix = true,
479 .ipv6_accept_ra_use_onlink_prefix = true,
480 .ipv6_accept_ra_use_mtu = true,
481 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
482 .ipv6_accept_ra_route_metric = DHCP_ROUTE_METRIC,
483 .ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
484
485 .can_termination = -1,
486
487 .ipoib_mode = _IP_OVER_INFINIBAND_MODE_INVALID,
488 .ipoib_umcast = -1,
489 };
490
491 r = config_parse_many(
492 STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname,
493 "Match\0"
494 "Link\0"
495 "SR-IOV\0"
496 "Network\0"
497 "Address\0"
498 "Neighbor\0"
499 "IPv6AddressLabel\0"
500 "RoutingPolicyRule\0"
501 "Route\0"
502 "NextHop\0"
503 "DHCP\0" /* compat */
504 "DHCPv4\0"
505 "DHCPv6\0"
506 "DHCPv6PrefixDelegation\0" /* compat */
507 "DHCPPrefixDelegation\0"
508 "DHCPServer\0"
509 "DHCPServerStaticLease\0"
510 "IPv6AcceptRA\0"
511 "IPv6NDPProxyAddress\0"
512 "Bridge\0"
513 "BridgeFDB\0"
514 "BridgeMDB\0"
515 "BridgeVLAN\0"
516 "IPv6SendRA\0"
517 "IPv6PrefixDelegation\0"
518 "IPv6Prefix\0"
519 "IPv6RoutePrefix\0"
520 "LLDP\0"
521 "TrafficControlQueueingDiscipline\0"
522 "CAN\0"
523 "QDisc\0"
524 "BFIFO\0"
525 "CAKE\0"
526 "ControlledDelay\0"
527 "DeficitRoundRobinScheduler\0"
528 "DeficitRoundRobinSchedulerClass\0"
529 "EnhancedTransmissionSelection\0"
530 "FairQueueing\0"
531 "FairQueueingControlledDelay\0"
532 "FlowQueuePIE\0"
533 "GenericRandomEarlyDetection\0"
534 "HeavyHitterFilter\0"
535 "HierarchyTokenBucket\0"
536 "HierarchyTokenBucketClass\0"
537 "NetworkEmulator\0"
538 "PFIFO\0"
539 "PFIFOFast\0"
540 "PFIFOHeadDrop\0"
541 "PIE\0"
542 "QuickFairQueueing\0"
543 "QuickFairQueueingClass\0"
544 "StochasticFairBlue\0"
545 "StochasticFairnessQueueing\0"
546 "TokenBucketFilter\0"
547 "TrivialLinkEqualizer\0",
548 config_item_perf_lookup, network_network_gperf_lookup,
549 CONFIG_PARSE_WARN,
550 network,
551 &network->stats_by_path);
552 if (r < 0)
553 return r;
554
555 r = network_add_ipv4ll_route(network);
556 if (r < 0)
557 return log_warning_errno(r, "%s: Failed to add IPv4LL route: %m", network->filename);
558
559 r = network_add_default_route_on_device(network);
560 if (r < 0)
561 return log_warning_errno(r, "%s: Failed to add default route on device: %m",
562 network->filename);
563
564 r = network_verify(network);
565 if (r == -ENOMEM)
566 return r;
567 if (r < 0)
568 /* Ignore .network files that do not match the conditions. */
569 return 0;
570
571 r = ordered_hashmap_ensure_put(networks, &string_hash_ops, network->name, network);
572 if (r < 0)
573 return r;
574
575 TAKE_PTR(network);
576 return 0;
577 }
578
579 int network_load(Manager *manager, OrderedHashmap **networks) {
580 _cleanup_strv_free_ char **files = NULL;
581 char **f;
582 int r;
583
584 assert(manager);
585
586 ordered_hashmap_clear_with_destructor(*networks, network_unref);
587
588 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
589 if (r < 0)
590 return log_error_errno(r, "Failed to enumerate network files: %m");
591
592 STRV_FOREACH(f, files) {
593 r = network_load_one(manager, networks, *f);
594 if (r < 0)
595 return log_error_errno(r, "Failed to load %s: %m", *f);
596 }
597
598 return 0;
599 }
600
601 static bool stats_by_path_equal(Hashmap *a, Hashmap *b) {
602 struct stat *st_a, *st_b;
603 const char *path;
604
605 assert(a);
606 assert(b);
607
608 if (hashmap_size(a) != hashmap_size(b))
609 return false;
610
611 HASHMAP_FOREACH_KEY(st_a, path, a) {
612 st_b = hashmap_get(b, path);
613 if (!st_b)
614 return false;
615
616 if (!stat_inode_unmodified(st_a, st_b))
617 return false;
618 }
619
620 return true;
621 }
622
623 int network_reload(Manager *manager) {
624 OrderedHashmap *new_networks = NULL;
625 Network *n, *old;
626 int r;
627
628 assert(manager);
629
630 r = network_load(manager, &new_networks);
631 if (r < 0)
632 goto failure;
633
634 ORDERED_HASHMAP_FOREACH(n, new_networks) {
635 r = network_get_by_name(manager, n->name, &old);
636 if (r < 0) {
637 log_debug("Found new .network file: %s", n->filename);
638 continue;
639 }
640
641 if (!stats_by_path_equal(n->stats_by_path, old->stats_by_path)) {
642 log_debug("Found updated .network file: %s", n->filename);
643 continue;
644 }
645
646 r = ordered_hashmap_replace(new_networks, old->name, old);
647 if (r < 0)
648 goto failure;
649
650 network_ref(old);
651 network_unref(n);
652 }
653
654 ordered_hashmap_free_with_destructor(manager->networks, network_unref);
655 manager->networks = new_networks;
656
657 return manager_build_dhcp_pd_subnet_ids(manager);
658
659 failure:
660 ordered_hashmap_free_with_destructor(new_networks, network_unref);
661
662 return r;
663 }
664
665 int manager_build_dhcp_pd_subnet_ids(Manager *manager) {
666 Network *n;
667 int r;
668
669 assert(manager);
670
671 set_clear(manager->dhcp_pd_subnet_ids);
672
673 ORDERED_HASHMAP_FOREACH(n, manager->networks) {
674 if (n->unmanaged)
675 continue;
676
677 if (!n->dhcp_pd)
678 continue;
679
680 if (n->dhcp_pd_subnet_id < 0)
681 continue;
682
683 r = set_ensure_put(&manager->dhcp_pd_subnet_ids, &uint64_hash_ops, &n->dhcp_pd_subnet_id);
684 if (r < 0)
685 return r;
686 }
687
688 return 0;
689 }
690
691 static Network *network_free(Network *network) {
692 if (!network)
693 return NULL;
694
695 free(network->filename);
696 hashmap_free(network->stats_by_path);
697
698 net_match_clear(&network->match);
699 condition_free_list(network->conditions);
700
701 free(network->dhcp_server_relay_agent_circuit_id);
702 free(network->dhcp_server_relay_agent_remote_id);
703 free(network->dhcp_server_filename);
704
705 free(network->description);
706 free(network->dhcp_vendor_class_identifier);
707 free(network->dhcp_mudurl);
708 strv_free(network->dhcp_user_class);
709 free(network->dhcp_hostname);
710 free(network->dhcp_label);
711 set_free(network->dhcp_deny_listed_ip);
712 set_free(network->dhcp_allow_listed_ip);
713 set_free(network->dhcp_request_options);
714 set_free(network->dhcp6_request_options);
715 free(network->dhcp6_mudurl);
716 strv_free(network->dhcp6_user_class);
717 strv_free(network->dhcp6_vendor_class);
718
719 strv_free(network->ntp);
720 for (unsigned i = 0; i < network->n_dns; i++)
721 in_addr_full_free(network->dns[i]);
722 free(network->dns);
723 ordered_set_free(network->search_domains);
724 ordered_set_free(network->route_domains);
725 strv_free(network->bind_carrier);
726
727 ordered_set_free(network->router_search_domains);
728 free(network->router_dns);
729 set_free(network->ndisc_deny_listed_router);
730 set_free(network->ndisc_allow_listed_router);
731 set_free(network->ndisc_deny_listed_prefix);
732 set_free(network->ndisc_allow_listed_prefix);
733 set_free(network->ndisc_deny_listed_route_prefix);
734 set_free(network->ndisc_allow_listed_route_prefix);
735
736 free(network->batadv_name);
737 free(network->bridge_name);
738 free(network->bond_name);
739 free(network->vrf_name);
740 hashmap_free_free_key(network->stacked_netdev_names);
741 netdev_unref(network->bridge);
742 netdev_unref(network->bond);
743 netdev_unref(network->vrf);
744 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
745
746 set_free_free(network->ipv6_proxy_ndp_addresses);
747 ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
748 hashmap_free_with_destructor(network->routes_by_section, route_free);
749 hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
750 hashmap_free_with_destructor(network->bridge_fdb_entries_by_section, bridge_fdb_free);
751 hashmap_free_with_destructor(network->bridge_mdb_entries_by_section, bridge_mdb_free);
752 hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free);
753 hashmap_free_with_destructor(network->address_labels_by_section, address_label_free);
754 hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
755 hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
756 hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
757 hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free);
758 ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
759 hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
760
761 free(network->name);
762
763 free(network->dhcp_server_timezone);
764 free(network->dhcp_server_uplink_name);
765 free(network->router_uplink_name);
766 free(network->dhcp_pd_uplink_name);
767
768 for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
769 free(network->dhcp_server_emit[t].addresses);
770
771 set_free_free(network->dnssec_negative_trust_anchors);
772
773 free(network->lldp_mudurl);
774
775 ordered_hashmap_free(network->dhcp_client_send_options);
776 ordered_hashmap_free(network->dhcp_client_send_vendor_options);
777 ordered_hashmap_free(network->dhcp_server_send_options);
778 ordered_hashmap_free(network->dhcp_server_send_vendor_options);
779 ordered_hashmap_free(network->dhcp6_client_send_options);
780 ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
781 set_free(network->dhcp_pd_tokens);
782 set_free(network->ndisc_tokens);
783
784 return mfree(network);
785 }
786
787 DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
788
789 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
790 Network *network;
791
792 assert(manager);
793 assert(name);
794 assert(ret);
795
796 network = ordered_hashmap_get(manager->networks, name);
797 if (!network)
798 return -ENOENT;
799
800 *ret = network;
801
802 return 0;
803 }
804
805 bool network_has_static_ipv6_configurations(Network *network) {
806 Address *address;
807 Route *route;
808 BridgeFDB *fdb;
809 BridgeMDB *mdb;
810 Neighbor *neighbor;
811
812 assert(network);
813
814 ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section)
815 if (address->family == AF_INET6)
816 return true;
817
818 HASHMAP_FOREACH(route, network->routes_by_section)
819 if (route->family == AF_INET6)
820 return true;
821
822 HASHMAP_FOREACH(fdb, network->bridge_fdb_entries_by_section)
823 if (fdb->family == AF_INET6)
824 return true;
825
826 HASHMAP_FOREACH(mdb, network->bridge_mdb_entries_by_section)
827 if (mdb->family == AF_INET6)
828 return true;
829
830 HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
831 if (neighbor->family == AF_INET6)
832 return true;
833
834 if (!hashmap_isempty(network->address_labels_by_section))
835 return true;
836
837 if (!hashmap_isempty(network->prefixes_by_section))
838 return true;
839
840 if (!hashmap_isempty(network->route_prefixes_by_section))
841 return true;
842
843 return false;
844 }
845
846 int config_parse_stacked_netdev(
847 const char *unit,
848 const char *filename,
849 unsigned line,
850 const char *section,
851 unsigned section_line,
852 const char *lvalue,
853 int ltype,
854 const char *rvalue,
855 void *data,
856 void *userdata) {
857
858 _cleanup_free_ char *name = NULL;
859 NetDevKind kind = ltype;
860 Hashmap **h = data;
861 int r;
862
863 assert(filename);
864 assert(lvalue);
865 assert(rvalue);
866 assert(data);
867 assert(IN_SET(kind,
868 NETDEV_KIND_IPOIB,
869 NETDEV_KIND_IPVLAN,
870 NETDEV_KIND_IPVTAP,
871 NETDEV_KIND_L2TP,
872 NETDEV_KIND_MACSEC,
873 NETDEV_KIND_MACVLAN,
874 NETDEV_KIND_MACVTAP,
875 NETDEV_KIND_VLAN,
876 NETDEV_KIND_VXLAN,
877 NETDEV_KIND_XFRM,
878 _NETDEV_KIND_TUNNEL));
879
880 if (!ifname_valid(rvalue)) {
881 log_syntax(unit, LOG_WARNING, filename, line, 0,
882 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
883 return 0;
884 }
885
886 name = strdup(rvalue);
887 if (!name)
888 return log_oom();
889
890 r = hashmap_ensure_put(h, &string_hash_ops, name, INT_TO_PTR(kind));
891 if (r == -ENOMEM)
892 return log_oom();
893 if (r < 0)
894 log_syntax(unit, LOG_WARNING, filename, line, r,
895 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
896 else if (r == 0)
897 log_syntax(unit, LOG_DEBUG, filename, line, r,
898 "NetDev '%s' specified twice, ignoring.", name);
899 else
900 TAKE_PTR(name);
901
902 return 0;
903 }
904
905 int config_parse_domains(
906 const char *unit,
907 const char *filename,
908 unsigned line,
909 const char *section,
910 unsigned section_line,
911 const char *lvalue,
912 int ltype,
913 const char *rvalue,
914 void *data,
915 void *userdata) {
916
917 Network *n = userdata;
918 int r;
919
920 assert(filename);
921 assert(lvalue);
922 assert(rvalue);
923 assert(n);
924
925 if (isempty(rvalue)) {
926 n->search_domains = ordered_set_free(n->search_domains);
927 n->route_domains = ordered_set_free(n->route_domains);
928 return 0;
929 }
930
931 for (const char *p = rvalue;;) {
932 _cleanup_free_ char *w = NULL, *normalized = NULL;
933 const char *domain;
934 bool is_route;
935
936 r = extract_first_word(&p, &w, NULL, 0);
937 if (r == -ENOMEM)
938 return log_oom();
939 if (r < 0) {
940 log_syntax(unit, LOG_WARNING, filename, line, r,
941 "Failed to extract search or route domain, ignoring: %s", rvalue);
942 return 0;
943 }
944 if (r == 0)
945 return 0;
946
947 is_route = w[0] == '~';
948 domain = is_route ? w + 1 : w;
949
950 if (dns_name_is_root(domain) || streq(domain, "*")) {
951 /* If the root domain appears as is, or the special token "*" is found, we'll
952 * consider this as routing domain, unconditionally. */
953 is_route = true;
954 domain = "."; /* make sure we don't allow empty strings, thus write the root
955 * domain as "." */
956 } else {
957 r = dns_name_normalize(domain, 0, &normalized);
958 if (r < 0) {
959 log_syntax(unit, LOG_WARNING, filename, line, r,
960 "'%s' is not a valid domain name, ignoring.", domain);
961 continue;
962 }
963
964 domain = normalized;
965
966 if (is_localhost(domain)) {
967 log_syntax(unit, LOG_WARNING, filename, line, 0,
968 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
969 domain);
970 continue;
971 }
972 }
973
974 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
975 r = ordered_set_put_strdup(set, domain);
976 if (r == -EEXIST)
977 continue;
978 if (r < 0)
979 return log_oom();
980 }
981 }
982
983 int config_parse_hostname(
984 const char *unit,
985 const char *filename,
986 unsigned line,
987 const char *section,
988 unsigned section_line,
989 const char *lvalue,
990 int ltype,
991 const char *rvalue,
992 void *data,
993 void *userdata) {
994
995 char **hostname = data;
996 int r;
997
998 assert(filename);
999 assert(lvalue);
1000 assert(rvalue);
1001 assert(data);
1002
1003 if (isempty(rvalue)) {
1004 *hostname = mfree(*hostname);
1005 return 0;
1006 }
1007
1008 if (!hostname_is_valid(rvalue, 0)) {
1009 log_syntax(unit, LOG_WARNING, filename, line, 0,
1010 "Hostname is not valid, ignoring assignment: %s", rvalue);
1011 return 0;
1012 }
1013
1014 r = dns_name_is_valid(rvalue);
1015 if (r < 0) {
1016 log_syntax(unit, LOG_WARNING, filename, line, r,
1017 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
1018 return 0;
1019 }
1020 if (r == 0) {
1021 log_syntax(unit, LOG_WARNING, filename, line, 0,
1022 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
1023 return 0;
1024 }
1025
1026 return free_and_strdup_warn(hostname, rvalue);
1027 }
1028
1029 int config_parse_timezone(
1030 const char *unit,
1031 const char *filename,
1032 unsigned line,
1033 const char *section,
1034 unsigned section_line,
1035 const char *lvalue,
1036 int ltype,
1037 const char *rvalue,
1038 void *data,
1039 void *userdata) {
1040
1041 char **tz = data;
1042 int r;
1043
1044 assert(filename);
1045 assert(lvalue);
1046 assert(rvalue);
1047 assert(data);
1048
1049 if (isempty(rvalue)) {
1050 *tz = mfree(*tz);
1051 return 0;
1052 }
1053
1054 r = verify_timezone(rvalue, LOG_WARNING);
1055 if (r < 0) {
1056 log_syntax(unit, LOG_WARNING, filename, line, r,
1057 "Timezone is not valid, ignoring assignment: %s", rvalue);
1058 return 0;
1059 }
1060
1061 return free_and_strdup_warn(tz, rvalue);
1062 }
1063
1064 int config_parse_dns(
1065 const char *unit,
1066 const char *filename,
1067 unsigned line,
1068 const char *section,
1069 unsigned section_line,
1070 const char *lvalue,
1071 int ltype,
1072 const char *rvalue,
1073 void *data,
1074 void *userdata) {
1075
1076 Network *n = userdata;
1077 int r;
1078
1079 assert(filename);
1080 assert(lvalue);
1081 assert(rvalue);
1082 assert(n);
1083
1084 if (isempty(rvalue)) {
1085 for (unsigned i = 0; i < n->n_dns; i++)
1086 in_addr_full_free(n->dns[i]);
1087 n->dns = mfree(n->dns);
1088 n->n_dns = 0;
1089 return 0;
1090 }
1091
1092 for (const char *p = rvalue;;) {
1093 _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
1094 _cleanup_free_ char *w = NULL;
1095 struct in_addr_full **m;
1096
1097 r = extract_first_word(&p, &w, NULL, 0);
1098 if (r == -ENOMEM)
1099 return log_oom();
1100 if (r < 0) {
1101 log_syntax(unit, LOG_WARNING, filename, line, r,
1102 "Invalid syntax, ignoring: %s", rvalue);
1103 return 0;
1104 }
1105 if (r == 0)
1106 return 0;
1107
1108 r = in_addr_full_new_from_string(w, &dns);
1109 if (r < 0) {
1110 log_syntax(unit, LOG_WARNING, filename, line, r,
1111 "Failed to parse dns server address, ignoring: %s", w);
1112 continue;
1113 }
1114
1115 if (IN_SET(dns->port, 53, 853))
1116 dns->port = 0;
1117
1118 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
1119 if (!m)
1120 return log_oom();
1121
1122 m[n->n_dns++] = TAKE_PTR(dns);
1123 n->dns = m;
1124 }
1125 }
1126
1127 int config_parse_dnssec_negative_trust_anchors(
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 Set **nta = data;
1140 int r;
1141
1142 assert(filename);
1143 assert(lvalue);
1144 assert(rvalue);
1145 assert(nta);
1146
1147 if (isempty(rvalue)) {
1148 *nta = set_free_free(*nta);
1149 return 0;
1150 }
1151
1152 for (const char *p = rvalue;;) {
1153 _cleanup_free_ char *w = NULL;
1154
1155 r = extract_first_word(&p, &w, NULL, 0);
1156 if (r == -ENOMEM)
1157 return log_oom();
1158 if (r < 0) {
1159 log_syntax(unit, LOG_WARNING, filename, line, r,
1160 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1161 return 0;
1162 }
1163 if (r == 0)
1164 return 0;
1165
1166 r = dns_name_is_valid(w);
1167 if (r <= 0) {
1168 log_syntax(unit, LOG_WARNING, filename, line, r,
1169 "%s is not a valid domain name, ignoring.", w);
1170 continue;
1171 }
1172
1173 r = set_ensure_consume(nta, &dns_name_hash_ops, TAKE_PTR(w));
1174 if (r < 0)
1175 return log_oom();
1176 }
1177 }
1178
1179 int config_parse_ntp(
1180 const char *unit,
1181 const char *filename,
1182 unsigned line,
1183 const char *section,
1184 unsigned section_line,
1185 const char *lvalue,
1186 int ltype,
1187 const char *rvalue,
1188 void *data,
1189 void *userdata) {
1190
1191 char ***l = data;
1192 int r;
1193
1194 assert(filename);
1195 assert(lvalue);
1196 assert(rvalue);
1197 assert(l);
1198
1199 if (isempty(rvalue)) {
1200 *l = strv_free(*l);
1201 return 0;
1202 }
1203
1204 for (const char *p = rvalue;;) {
1205 _cleanup_free_ char *w = NULL;
1206
1207 r = extract_first_word(&p, &w, NULL, 0);
1208 if (r == -ENOMEM)
1209 return log_oom();
1210 if (r < 0) {
1211 log_syntax(unit, LOG_WARNING, filename, line, r,
1212 "Failed to extract NTP server name, ignoring: %s", rvalue);
1213 return 0;
1214 }
1215 if (r == 0)
1216 return 0;
1217
1218 r = dns_name_is_valid_or_address(w);
1219 if (r <= 0) {
1220 log_syntax(unit, LOG_WARNING, filename, line, r,
1221 "%s is not a valid domain name or IP address, ignoring.", w);
1222 continue;
1223 }
1224
1225 if (strv_length(*l) > MAX_NTP_SERVERS) {
1226 log_syntax(unit, LOG_WARNING, filename, line, 0,
1227 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1228 MAX_NTP_SERVERS, w);
1229 return 0;
1230 }
1231
1232 r = strv_consume(l, TAKE_PTR(w));
1233 if (r < 0)
1234 return log_oom();
1235 }
1236 }
1237
1238 int config_parse_required_for_online(
1239 const char *unit,
1240 const char *filename,
1241 unsigned line,
1242 const char *section,
1243 unsigned section_line,
1244 const char *lvalue,
1245 int ltype,
1246 const char *rvalue,
1247 void *data,
1248 void *userdata) {
1249
1250 Network *network = userdata;
1251 LinkOperationalStateRange range;
1252 bool required = true;
1253 int r;
1254
1255 assert(filename);
1256 assert(lvalue);
1257 assert(rvalue);
1258 assert(network);
1259
1260 if (isempty(rvalue)) {
1261 network->required_for_online = -1;
1262 network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
1263 return 0;
1264 }
1265
1266 r = parse_operational_state_range(rvalue, &range);
1267 if (r < 0) {
1268 r = parse_boolean(rvalue);
1269 if (r < 0) {
1270 log_syntax(unit, LOG_WARNING, filename, line, r,
1271 "Failed to parse %s= setting, ignoring assignment: %s",
1272 lvalue, rvalue);
1273 return 0;
1274 }
1275
1276 required = r;
1277 range = LINK_OPERSTATE_RANGE_DEFAULT;
1278 }
1279
1280 network->required_for_online = required;
1281 network->required_operstate_for_online = range;
1282
1283 return 0;
1284 }
1285
1286 int config_parse_link_group(
1287 const char *unit,
1288 const char *filename,
1289 unsigned line,
1290 const char *section,
1291 unsigned section_line,
1292 const char *lvalue,
1293 int ltype,
1294 const char *rvalue,
1295 void *data,
1296 void *userdata) {
1297
1298 Network *network = userdata;
1299 int r;
1300 int32_t group;
1301
1302 assert(filename);
1303 assert(lvalue);
1304 assert(rvalue);
1305 assert(network);
1306
1307 if (isempty(rvalue)) {
1308 network->group = -1;
1309 return 0;
1310 }
1311
1312 r = safe_atoi32(rvalue, &group);
1313 if (r < 0) {
1314 log_syntax(unit, LOG_WARNING, filename, line, r,
1315 "Failed to parse Group=, ignoring assignment: %s", rvalue);
1316 return 0;
1317 }
1318
1319 if (group < 0) {
1320 log_syntax(unit, LOG_WARNING, filename, line, r,
1321 "Value of Group= must be in the range 0…2147483647, ignoring assignment: %s", rvalue);
1322 return 0;
1323 }
1324
1325 network->group = group;
1326 return 0;
1327 }
1328
1329 int config_parse_ignore_carrier_loss(
1330 const char *unit,
1331 const char *filename,
1332 unsigned line,
1333 const char *section,
1334 unsigned section_line,
1335 const char *lvalue,
1336 int ltype,
1337 const char *rvalue,
1338 void *data,
1339 void *userdata) {
1340
1341 Network *network = userdata;
1342 usec_t usec;
1343 int r;
1344
1345 assert(filename);
1346 assert(lvalue);
1347 assert(rvalue);
1348 assert(network);
1349
1350 if (isempty(rvalue)) {
1351 network->ignore_carrier_loss_set = false;
1352 return 0;
1353 }
1354
1355 r = parse_boolean(rvalue);
1356 if (r >= 0) {
1357 network->ignore_carrier_loss_set = true;
1358 network->ignore_carrier_loss_usec = r > 0 ? USEC_INFINITY : 0;
1359 return 0;
1360 }
1361
1362 r = parse_sec(rvalue, &usec);
1363 if (r < 0) {
1364 log_syntax(unit, LOG_WARNING, filename, line, r,
1365 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1366 return 0;
1367 }
1368
1369 network->ignore_carrier_loss_set = true;
1370 network->ignore_carrier_loss_usec = usec;
1371 return 0;
1372 }
1373
1374 DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
1375 "Failed to parse RequiredFamilyForOnline= setting");
1376
1377 DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1378 "Failed to parse KeepConfiguration= setting");
1379
1380 static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
1381 [KEEP_CONFIGURATION_NO] = "no",
1382 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1383 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1384 [KEEP_CONFIGURATION_STATIC] = "static",
1385 [KEEP_CONFIGURATION_YES] = "yes",
1386 };
1387
1388 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
1389
1390 static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = {
1391 [ACTIVATION_POLICY_UP] = "up",
1392 [ACTIVATION_POLICY_ALWAYS_UP] = "always-up",
1393 [ACTIVATION_POLICY_MANUAL] = "manual",
1394 [ACTIVATION_POLICY_ALWAYS_DOWN] = "always-down",
1395 [ACTIVATION_POLICY_DOWN] = "down",
1396 [ACTIVATION_POLICY_BOUND] = "bound",
1397 };
1398
1399 DEFINE_STRING_TABLE_LOOKUP(activation_policy, ActivationPolicy);
1400 DEFINE_CONFIG_PARSE_ENUM(config_parse_activation_policy, activation_policy, ActivationPolicy, "Failed to parse activation policy");