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