]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #28228 from yuwata/repart-free-area
[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
46 /* Let's assume that anything above this number is a user misconfiguration. */
47 #define MAX_NTP_SERVERS 128U
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; /* network_drop_invalid_addresses() logs internally. */
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_qdisc(network);
326 network_drop_invalid_tclass(network);
327 r = sr_iov_drop_invalid_sections(UINT32_MAX, network->sr_iov_by_section);
328 if (r < 0)
329 return r; /* sr_iov_drop_invalid_sections() logs internally. */
330 network_drop_invalid_static_leases(network);
331
332 network_adjust_dhcp_server(network);
333
334 return 0;
335 }
336
337 int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
338 _cleanup_free_ char *fname = NULL, *name = NULL;
339 _cleanup_(network_unrefp) Network *network = NULL;
340 const char *dropin_dirname;
341 char *d;
342 int r;
343
344 assert(manager);
345 assert(filename);
346
347 r = null_or_empty_path(filename);
348 if (r < 0)
349 return log_warning_errno(r, "Failed to check if \"%s\" is empty: %m", filename);
350 if (r > 0) {
351 log_debug("Skipping empty file: %s", filename);
352 return 0;
353 }
354
355 fname = strdup(filename);
356 if (!fname)
357 return log_oom();
358
359 name = strdup(basename(filename));
360 if (!name)
361 return log_oom();
362
363 d = strrchr(name, '.');
364 if (!d)
365 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid file name: %s", filename);
366
367 *d = '\0';
368
369 dropin_dirname = strjoina(name, ".network.d");
370
371 network = new(Network, 1);
372 if (!network)
373 return log_oom();
374
375 *network = (Network) {
376 .filename = TAKE_PTR(fname),
377 .name = TAKE_PTR(name),
378
379 .manager = manager,
380 .n_ref = 1,
381
382 .required_for_online = -1,
383 .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
384 .activation_policy = _ACTIVATION_POLICY_INVALID,
385 .group = -1,
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_captive_portal = 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_captive_portal = true,
418 .dhcp6_use_rapid_commit = true,
419 .dhcp6_duid.type = _DUID_TYPE_INVALID,
420 .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID,
421 .dhcp6_send_release = true,
422
423 .dhcp_pd = -1,
424 .dhcp_pd_announce = true,
425 .dhcp_pd_assign = true,
426 .dhcp_pd_manage_temporary_address = true,
427 .dhcp_pd_subnet_id = -1,
428 .dhcp_pd_route_metric = DHCP6PD_ROUTE_METRIC,
429
430 .dhcp_server_bind_to_interface = true,
431 .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
432 .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
433 .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
434 .dhcp_server_emit_router = true,
435 .dhcp_server_emit_timezone = true,
436
437 .router_lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC,
438 .router_dns_lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
439 .router_emit_dns = true,
440 .router_emit_domains = true,
441
442 .use_bpdu = -1,
443 .hairpin = -1,
444 .isolated = -1,
445 .fast_leave = -1,
446 .allow_port_to_be_root = -1,
447 .unicast_flood = -1,
448 .multicast_flood = -1,
449 .multicast_to_unicast = -1,
450 .neighbor_suppression = -1,
451 .learning = -1,
452 .bridge_proxy_arp = -1,
453 .bridge_proxy_arp_wifi = -1,
454 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
455 .multicast_router = _MULTICAST_ROUTER_INVALID,
456
457 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
458 .lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID,
459
460 .dns_default_route = -1,
461 .llmnr = RESOLVE_SUPPORT_YES,
462 .mdns = RESOLVE_SUPPORT_NO,
463 .dnssec_mode = _DNSSEC_MODE_INVALID,
464 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
465
466 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
467 .link_local = _ADDRESS_FAMILY_INVALID,
468 .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
469
470 .ipv4_accept_local = -1,
471 .ipv4_route_localnet = -1,
472 .ipv6_privacy_extensions = _IPV6_PRIVACY_EXTENSIONS_INVALID,
473 .ipv6_dad_transmits = -1,
474 .ipv6_hop_limit = -1,
475 .ipv6_proxy_ndp = -1,
476 .proxy_arp = -1,
477
478 .ipv6_accept_ra = -1,
479 .ipv6_accept_ra_use_dns = true,
480 .ipv6_accept_ra_use_gateway = true,
481 .ipv6_accept_ra_use_captive_portal = true,
482 .ipv6_accept_ra_use_route_prefix = true,
483 .ipv6_accept_ra_use_autonomous_prefix = true,
484 .ipv6_accept_ra_use_onlink_prefix = true,
485 .ipv6_accept_ra_use_mtu = true,
486 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
487 .ipv6_accept_ra_route_metric_high = IPV6RA_ROUTE_METRIC_HIGH,
488 .ipv6_accept_ra_route_metric_medium = IPV6RA_ROUTE_METRIC_MEDIUM,
489 .ipv6_accept_ra_route_metric_low = IPV6RA_ROUTE_METRIC_LOW,
490 .ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
491
492 .can_termination = -1,
493
494 .ipoib_mode = _IP_OVER_INFINIBAND_MODE_INVALID,
495 .ipoib_umcast = -1,
496 };
497
498 r = config_parse_many(
499 STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root = */ NULL,
500 "Match\0"
501 "Link\0"
502 "SR-IOV\0"
503 "Network\0"
504 "Address\0"
505 "Neighbor\0"
506 "IPv6AddressLabel\0"
507 "RoutingPolicyRule\0"
508 "Route\0"
509 "NextHop\0"
510 "DHCP\0" /* compat */
511 "DHCPv4\0"
512 "DHCPv6\0"
513 "DHCPv6PrefixDelegation\0" /* compat */
514 "DHCPPrefixDelegation\0"
515 "DHCPServer\0"
516 "DHCPServerStaticLease\0"
517 "IPv6AcceptRA\0"
518 "IPv6NDPProxyAddress\0"
519 "Bridge\0"
520 "BridgeFDB\0"
521 "BridgeMDB\0"
522 "BridgeVLAN\0"
523 "IPv6SendRA\0"
524 "IPv6PrefixDelegation\0"
525 "IPv6Prefix\0"
526 "IPv6RoutePrefix\0"
527 "LLDP\0"
528 "TrafficControlQueueingDiscipline\0"
529 "CAN\0"
530 "QDisc\0"
531 "BFIFO\0"
532 "CAKE\0"
533 "ControlledDelay\0"
534 "DeficitRoundRobinScheduler\0"
535 "DeficitRoundRobinSchedulerClass\0"
536 "EnhancedTransmissionSelection\0"
537 "FairQueueing\0"
538 "FairQueueingControlledDelay\0"
539 "FlowQueuePIE\0"
540 "GenericRandomEarlyDetection\0"
541 "HeavyHitterFilter\0"
542 "HierarchyTokenBucket\0"
543 "HierarchyTokenBucketClass\0"
544 "NetworkEmulator\0"
545 "PFIFO\0"
546 "PFIFOFast\0"
547 "PFIFOHeadDrop\0"
548 "PIE\0"
549 "QuickFairQueueing\0"
550 "QuickFairQueueingClass\0"
551 "StochasticFairBlue\0"
552 "StochasticFairnessQueueing\0"
553 "TokenBucketFilter\0"
554 "TrivialLinkEqualizer\0",
555 config_item_perf_lookup, network_network_gperf_lookup,
556 CONFIG_PARSE_WARN,
557 network,
558 &network->stats_by_path,
559 &network->dropins);
560 if (r < 0)
561 return r; /* config_parse_many() logs internally. */
562
563 r = network_add_ipv4ll_route(network);
564 if (r < 0)
565 return log_warning_errno(r, "%s: Failed to add IPv4LL route: %m", network->filename);
566
567 r = network_add_default_route_on_device(network);
568 if (r < 0)
569 return log_warning_errno(r, "%s: Failed to add default route on device: %m",
570 network->filename);
571
572 r = network_verify(network);
573 if (r < 0)
574 return r; /* network_verify() logs internally. */
575
576 r = ordered_hashmap_ensure_put(networks, &string_hash_ops, network->name, network);
577 if (r < 0)
578 return log_warning_errno(r, "%s: Failed to store configuration into hashmap: %m", filename);
579
580 TAKE_PTR(network);
581 return 0;
582 }
583
584 int network_load(Manager *manager, OrderedHashmap **networks) {
585 _cleanup_strv_free_ char **files = NULL;
586 int r;
587
588 assert(manager);
589
590 ordered_hashmap_clear_with_destructor(*networks, network_unref);
591
592 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
593 if (r < 0)
594 return log_error_errno(r, "Failed to enumerate network files: %m");
595
596 STRV_FOREACH(f, files)
597 (void) network_load_one(manager, networks, *f);
598
599 return 0;
600 }
601
602 int network_reload(Manager *manager) {
603 OrderedHashmap *new_networks = NULL;
604 Network *n, *old;
605 int r;
606
607 assert(manager);
608
609 r = network_load(manager, &new_networks);
610 if (r < 0)
611 goto failure;
612
613 ORDERED_HASHMAP_FOREACH(n, new_networks) {
614 r = network_get_by_name(manager, n->name, &old);
615 if (r < 0) {
616 log_debug("Found new .network file: %s", n->filename);
617 continue;
618 }
619
620 if (!stats_by_path_equal(n->stats_by_path, old->stats_by_path)) {
621 log_debug("Found updated .network file: %s", n->filename);
622 continue;
623 }
624
625 r = ordered_hashmap_replace(new_networks, old->name, old);
626 if (r < 0)
627 goto failure;
628
629 network_ref(old);
630 network_unref(n);
631 }
632
633 ordered_hashmap_free_with_destructor(manager->networks, network_unref);
634 manager->networks = new_networks;
635
636 return manager_build_dhcp_pd_subnet_ids(manager);
637
638 failure:
639 ordered_hashmap_free_with_destructor(new_networks, network_unref);
640
641 return r;
642 }
643
644 int manager_build_dhcp_pd_subnet_ids(Manager *manager) {
645 Network *n;
646 int r;
647
648 assert(manager);
649
650 set_clear(manager->dhcp_pd_subnet_ids);
651
652 ORDERED_HASHMAP_FOREACH(n, manager->networks) {
653 if (n->unmanaged)
654 continue;
655
656 if (!n->dhcp_pd)
657 continue;
658
659 if (n->dhcp_pd_subnet_id < 0)
660 continue;
661
662 r = set_ensure_put(&manager->dhcp_pd_subnet_ids, &uint64_hash_ops, &n->dhcp_pd_subnet_id);
663 if (r < 0)
664 return r;
665 }
666
667 return 0;
668 }
669
670 static Network *network_free(Network *network) {
671 if (!network)
672 return NULL;
673
674 free(network->name);
675 free(network->filename);
676 free(network->description);
677 strv_free(network->dropins);
678 hashmap_free(network->stats_by_path);
679
680 /* conditions */
681 net_match_clear(&network->match);
682 condition_free_list(network->conditions);
683
684 /* link settings */
685 strv_free(network->bind_carrier);
686
687 /* NTP */
688 strv_free(network->ntp);
689
690 /* DNS */
691 for (unsigned i = 0; i < network->n_dns; i++)
692 in_addr_full_free(network->dns[i]);
693 free(network->dns);
694 ordered_set_free(network->search_domains);
695 ordered_set_free(network->route_domains);
696 set_free_free(network->dnssec_negative_trust_anchors);
697
698 /* DHCP server */
699 free(network->dhcp_server_relay_agent_circuit_id);
700 free(network->dhcp_server_relay_agent_remote_id);
701 free(network->dhcp_server_boot_server_name);
702 free(network->dhcp_server_boot_filename);
703 free(network->dhcp_server_timezone);
704 free(network->dhcp_server_uplink_name);
705 for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
706 free(network->dhcp_server_emit[t].addresses);
707 ordered_hashmap_free(network->dhcp_server_send_options);
708 ordered_hashmap_free(network->dhcp_server_send_vendor_options);
709
710 /* DHCP client */
711 free(network->dhcp_vendor_class_identifier);
712 free(network->dhcp_mudurl);
713 free(network->dhcp_hostname);
714 free(network->dhcp_label);
715 set_free(network->dhcp_deny_listed_ip);
716 set_free(network->dhcp_allow_listed_ip);
717 strv_free(network->dhcp_user_class);
718 set_free(network->dhcp_request_options);
719 ordered_hashmap_free(network->dhcp_client_send_options);
720 ordered_hashmap_free(network->dhcp_client_send_vendor_options);
721 free(network->dhcp_netlabel);
722
723 /* DHCPv6 client */
724 free(network->dhcp6_mudurl);
725 strv_free(network->dhcp6_user_class);
726 strv_free(network->dhcp6_vendor_class);
727 set_free(network->dhcp6_request_options);
728 ordered_hashmap_free(network->dhcp6_client_send_options);
729 ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
730 free(network->dhcp6_netlabel);
731
732 /* DHCP PD */
733 free(network->dhcp_pd_uplink_name);
734 set_free(network->dhcp_pd_tokens);
735 free(network->dhcp_pd_netlabel);
736
737 /* Router advertisement */
738 ordered_set_free(network->router_search_domains);
739 free(network->router_dns);
740 free(network->router_uplink_name);
741
742 /* NDisc */
743 set_free(network->ndisc_deny_listed_router);
744 set_free(network->ndisc_allow_listed_router);
745 set_free(network->ndisc_deny_listed_prefix);
746 set_free(network->ndisc_allow_listed_prefix);
747 set_free(network->ndisc_deny_listed_route_prefix);
748 set_free(network->ndisc_allow_listed_route_prefix);
749 set_free(network->ndisc_tokens);
750 free(network->ndisc_netlabel);
751
752 /* LLDP */
753 free(network->lldp_mudurl);
754
755 /* netdev */
756 free(network->batadv_name);
757 free(network->bridge_name);
758 free(network->bond_name);
759 free(network->vrf_name);
760 hashmap_free_free_key(network->stacked_netdev_names);
761 netdev_unref(network->bridge);
762 netdev_unref(network->bond);
763 netdev_unref(network->vrf);
764 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
765
766 /* static configs */
767 set_free_free(network->ipv6_proxy_ndp_addresses);
768 ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
769 hashmap_free_with_destructor(network->routes_by_section, route_free);
770 hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
771 hashmap_free_with_destructor(network->bridge_fdb_entries_by_section, bridge_fdb_free);
772 hashmap_free_with_destructor(network->bridge_mdb_entries_by_section, bridge_mdb_free);
773 hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free);
774 hashmap_free_with_destructor(network->address_labels_by_section, address_label_free);
775 hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
776 hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
777 hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
778 hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free);
779 ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
780 hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
781 hashmap_free_with_destructor(network->tclasses_by_section, tclass_free);
782
783 return mfree(network);
784 }
785
786 DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
787
788 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
789 Network *network;
790
791 assert(manager);
792 assert(name);
793 assert(ret);
794
795 network = ordered_hashmap_get(manager->networks, name);
796 if (!network)
797 return -ENOENT;
798
799 *ret = network;
800
801 return 0;
802 }
803
804 bool network_has_static_ipv6_configurations(Network *network) {
805 Address *address;
806 Route *route;
807 BridgeFDB *fdb;
808 BridgeMDB *mdb;
809 Neighbor *neighbor;
810
811 assert(network);
812
813 ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section)
814 if (address->family == AF_INET6)
815 return true;
816
817 HASHMAP_FOREACH(route, network->routes_by_section)
818 if (route->family == AF_INET6)
819 return true;
820
821 HASHMAP_FOREACH(fdb, network->bridge_fdb_entries_by_section)
822 if (fdb->family == AF_INET6)
823 return true;
824
825 HASHMAP_FOREACH(mdb, network->bridge_mdb_entries_by_section)
826 if (mdb->family == AF_INET6)
827 return true;
828
829 HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
830 if (neighbor->family == AF_INET6)
831 return true;
832
833 if (!hashmap_isempty(network->address_labels_by_section))
834 return true;
835
836 if (!hashmap_isempty(network->prefixes_by_section))
837 return true;
838
839 if (!hashmap_isempty(network->route_prefixes_by_section))
840 return true;
841
842 return false;
843 }
844
845 int config_parse_stacked_netdev(
846 const char *unit,
847 const char *filename,
848 unsigned line,
849 const char *section,
850 unsigned section_line,
851 const char *lvalue,
852 int ltype,
853 const char *rvalue,
854 void *data,
855 void *userdata) {
856
857 _cleanup_free_ char *name = NULL;
858 NetDevKind kind = ltype;
859 Hashmap **h = ASSERT_PTR(data);
860 int r;
861
862 assert(filename);
863 assert(lvalue);
864 assert(rvalue);
865 assert(IN_SET(kind,
866 NETDEV_KIND_IPOIB,
867 NETDEV_KIND_IPVLAN,
868 NETDEV_KIND_IPVTAP,
869 NETDEV_KIND_MACSEC,
870 NETDEV_KIND_MACVLAN,
871 NETDEV_KIND_MACVTAP,
872 NETDEV_KIND_VLAN,
873 NETDEV_KIND_VXLAN,
874 NETDEV_KIND_XFRM,
875 _NETDEV_KIND_TUNNEL));
876
877 if (!ifname_valid(rvalue)) {
878 log_syntax(unit, LOG_WARNING, filename, line, 0,
879 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
880 return 0;
881 }
882
883 name = strdup(rvalue);
884 if (!name)
885 return log_oom();
886
887 r = hashmap_ensure_put(h, &string_hash_ops, name, INT_TO_PTR(kind));
888 if (r == -ENOMEM)
889 return log_oom();
890 if (r < 0)
891 log_syntax(unit, LOG_WARNING, filename, line, r,
892 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
893 else if (r == 0)
894 log_syntax(unit, LOG_DEBUG, filename, line, r,
895 "NetDev '%s' specified twice, ignoring.", name);
896 else
897 TAKE_PTR(name);
898
899 return 0;
900 }
901
902 int config_parse_domains(
903 const char *unit,
904 const char *filename,
905 unsigned line,
906 const char *section,
907 unsigned section_line,
908 const char *lvalue,
909 int ltype,
910 const char *rvalue,
911 void *data,
912 void *userdata) {
913
914 Network *n = ASSERT_PTR(userdata);
915 int r;
916
917 assert(filename);
918 assert(lvalue);
919 assert(rvalue);
920
921 if (isempty(rvalue)) {
922 n->search_domains = ordered_set_free(n->search_domains);
923 n->route_domains = ordered_set_free(n->route_domains);
924 return 0;
925 }
926
927 for (const char *p = rvalue;;) {
928 _cleanup_free_ char *w = NULL, *normalized = NULL;
929 const char *domain;
930 bool is_route;
931
932 r = extract_first_word(&p, &w, NULL, 0);
933 if (r == -ENOMEM)
934 return log_oom();
935 if (r < 0) {
936 log_syntax(unit, LOG_WARNING, filename, line, r,
937 "Failed to extract search or route domain, ignoring: %s", rvalue);
938 return 0;
939 }
940 if (r == 0)
941 return 0;
942
943 is_route = w[0] == '~';
944 domain = is_route ? w + 1 : w;
945
946 if (dns_name_is_root(domain) || streq(domain, "*")) {
947 /* If the root domain appears as is, or the special token "*" is found, we'll
948 * consider this as routing domain, unconditionally. */
949 is_route = true;
950 domain = "."; /* make sure we don't allow empty strings, thus write the root
951 * domain as "." */
952 } else {
953 r = dns_name_normalize(domain, 0, &normalized);
954 if (r < 0) {
955 log_syntax(unit, LOG_WARNING, filename, line, r,
956 "'%s' is not a valid domain name, ignoring.", domain);
957 continue;
958 }
959
960 domain = normalized;
961
962 if (is_localhost(domain)) {
963 log_syntax(unit, LOG_WARNING, filename, line, 0,
964 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
965 domain);
966 continue;
967 }
968 }
969
970 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
971 r = ordered_set_put_strdup(set, domain);
972 if (r == -EEXIST)
973 continue;
974 if (r < 0)
975 return log_oom();
976 }
977 }
978
979 int config_parse_timezone(
980 const char *unit,
981 const char *filename,
982 unsigned line,
983 const char *section,
984 unsigned section_line,
985 const char *lvalue,
986 int ltype,
987 const char *rvalue,
988 void *data,
989 void *userdata) {
990
991 char **tz = ASSERT_PTR(data);
992 int r;
993
994 assert(filename);
995 assert(lvalue);
996 assert(rvalue);
997
998 if (isempty(rvalue)) {
999 *tz = mfree(*tz);
1000 return 0;
1001 }
1002
1003 r = verify_timezone(rvalue, LOG_WARNING);
1004 if (r < 0) {
1005 log_syntax(unit, LOG_WARNING, filename, line, r,
1006 "Timezone is not valid, ignoring assignment: %s", rvalue);
1007 return 0;
1008 }
1009
1010 return free_and_strdup_warn(tz, rvalue);
1011 }
1012
1013 int config_parse_dns(
1014 const char *unit,
1015 const char *filename,
1016 unsigned line,
1017 const char *section,
1018 unsigned section_line,
1019 const char *lvalue,
1020 int ltype,
1021 const char *rvalue,
1022 void *data,
1023 void *userdata) {
1024
1025 Network *n = ASSERT_PTR(userdata);
1026 int r;
1027
1028 assert(filename);
1029 assert(lvalue);
1030 assert(rvalue);
1031
1032 if (isempty(rvalue)) {
1033 for (unsigned i = 0; i < n->n_dns; i++)
1034 in_addr_full_free(n->dns[i]);
1035 n->dns = mfree(n->dns);
1036 n->n_dns = 0;
1037 return 0;
1038 }
1039
1040 for (const char *p = rvalue;;) {
1041 _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
1042 _cleanup_free_ char *w = NULL;
1043 struct in_addr_full **m;
1044
1045 r = extract_first_word(&p, &w, NULL, 0);
1046 if (r == -ENOMEM)
1047 return log_oom();
1048 if (r < 0) {
1049 log_syntax(unit, LOG_WARNING, filename, line, r,
1050 "Invalid syntax, ignoring: %s", rvalue);
1051 return 0;
1052 }
1053 if (r == 0)
1054 return 0;
1055
1056 r = in_addr_full_new_from_string(w, &dns);
1057 if (r < 0) {
1058 log_syntax(unit, LOG_WARNING, filename, line, r,
1059 "Failed to parse dns server address, ignoring: %s", w);
1060 continue;
1061 }
1062
1063 if (IN_SET(dns->port, 53, 853))
1064 dns->port = 0;
1065
1066 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
1067 if (!m)
1068 return log_oom();
1069
1070 m[n->n_dns++] = TAKE_PTR(dns);
1071 n->dns = m;
1072 }
1073 }
1074
1075 int config_parse_dnssec_negative_trust_anchors(
1076 const char *unit,
1077 const char *filename,
1078 unsigned line,
1079 const char *section,
1080 unsigned section_line,
1081 const char *lvalue,
1082 int ltype,
1083 const char *rvalue,
1084 void *data,
1085 void *userdata) {
1086
1087 Set **nta = ASSERT_PTR(data);
1088 int r;
1089
1090 assert(filename);
1091 assert(lvalue);
1092 assert(rvalue);
1093
1094 if (isempty(rvalue)) {
1095 *nta = set_free_free(*nta);
1096 return 0;
1097 }
1098
1099 for (const char *p = rvalue;;) {
1100 _cleanup_free_ char *w = NULL;
1101
1102 r = extract_first_word(&p, &w, NULL, 0);
1103 if (r == -ENOMEM)
1104 return log_oom();
1105 if (r < 0) {
1106 log_syntax(unit, LOG_WARNING, filename, line, r,
1107 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1108 return 0;
1109 }
1110 if (r == 0)
1111 return 0;
1112
1113 r = dns_name_is_valid(w);
1114 if (r <= 0) {
1115 log_syntax(unit, LOG_WARNING, filename, line, r,
1116 "%s is not a valid domain name, ignoring.", w);
1117 continue;
1118 }
1119
1120 r = set_ensure_consume(nta, &dns_name_hash_ops, TAKE_PTR(w));
1121 if (r < 0)
1122 return log_oom();
1123 }
1124 }
1125
1126 int config_parse_ntp(
1127 const char *unit,
1128 const char *filename,
1129 unsigned line,
1130 const char *section,
1131 unsigned section_line,
1132 const char *lvalue,
1133 int ltype,
1134 const char *rvalue,
1135 void *data,
1136 void *userdata) {
1137
1138 char ***l = ASSERT_PTR(data);
1139 int r;
1140
1141 assert(filename);
1142 assert(lvalue);
1143 assert(rvalue);
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 = ASSERT_PTR(userdata);
1197 LinkOperationalStateRange range;
1198 bool required = true;
1199 int r;
1200
1201 assert(filename);
1202 assert(lvalue);
1203 assert(rvalue);
1204
1205 if (isempty(rvalue)) {
1206 network->required_for_online = -1;
1207 network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
1208 return 0;
1209 }
1210
1211 r = parse_operational_state_range(rvalue, &range);
1212 if (r < 0) {
1213 r = parse_boolean(rvalue);
1214 if (r < 0) {
1215 log_syntax(unit, LOG_WARNING, filename, line, r,
1216 "Failed to parse %s= setting, ignoring assignment: %s",
1217 lvalue, rvalue);
1218 return 0;
1219 }
1220
1221 required = r;
1222 range = LINK_OPERSTATE_RANGE_DEFAULT;
1223 }
1224
1225 network->required_for_online = required;
1226 network->required_operstate_for_online = range;
1227
1228 return 0;
1229 }
1230
1231 int config_parse_link_group(
1232 const char *unit,
1233 const char *filename,
1234 unsigned line,
1235 const char *section,
1236 unsigned section_line,
1237 const char *lvalue,
1238 int ltype,
1239 const char *rvalue,
1240 void *data,
1241 void *userdata) {
1242
1243 Network *network = ASSERT_PTR(userdata);
1244 int r;
1245 int32_t group;
1246
1247 assert(filename);
1248 assert(lvalue);
1249 assert(rvalue);
1250
1251 if (isempty(rvalue)) {
1252 network->group = -1;
1253 return 0;
1254 }
1255
1256 r = safe_atoi32(rvalue, &group);
1257 if (r < 0) {
1258 log_syntax(unit, LOG_WARNING, filename, line, r,
1259 "Failed to parse Group=, ignoring assignment: %s", rvalue);
1260 return 0;
1261 }
1262
1263 if (group < 0) {
1264 log_syntax(unit, LOG_WARNING, filename, line, r,
1265 "Value of Group= must be in the range 0…2147483647, ignoring assignment: %s", rvalue);
1266 return 0;
1267 }
1268
1269 network->group = group;
1270 return 0;
1271 }
1272
1273 int config_parse_ignore_carrier_loss(
1274 const char *unit,
1275 const char *filename,
1276 unsigned line,
1277 const char *section,
1278 unsigned section_line,
1279 const char *lvalue,
1280 int ltype,
1281 const char *rvalue,
1282 void *data,
1283 void *userdata) {
1284
1285 Network *network = ASSERT_PTR(userdata);
1286 usec_t usec;
1287 int r;
1288
1289 assert(filename);
1290 assert(lvalue);
1291 assert(rvalue);
1292
1293 if (isempty(rvalue)) {
1294 network->ignore_carrier_loss_set = false;
1295 return 0;
1296 }
1297
1298 r = parse_boolean(rvalue);
1299 if (r >= 0) {
1300 network->ignore_carrier_loss_set = true;
1301 network->ignore_carrier_loss_usec = r > 0 ? USEC_INFINITY : 0;
1302 return 0;
1303 }
1304
1305 r = parse_sec(rvalue, &usec);
1306 if (r < 0) {
1307 log_syntax(unit, LOG_WARNING, filename, line, r,
1308 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1309 return 0;
1310 }
1311
1312 network->ignore_carrier_loss_set = true;
1313 network->ignore_carrier_loss_usec = usec;
1314 return 0;
1315 }
1316
1317 DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
1318 "Failed to parse RequiredFamilyForOnline= setting");
1319
1320 DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1321 "Failed to parse KeepConfiguration= setting");
1322
1323 static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
1324 [KEEP_CONFIGURATION_NO] = "no",
1325 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1326 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1327 [KEEP_CONFIGURATION_STATIC] = "static",
1328 [KEEP_CONFIGURATION_YES] = "yes",
1329 };
1330
1331 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
1332
1333 static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = {
1334 [ACTIVATION_POLICY_UP] = "up",
1335 [ACTIVATION_POLICY_ALWAYS_UP] = "always-up",
1336 [ACTIVATION_POLICY_MANUAL] = "manual",
1337 [ACTIVATION_POLICY_ALWAYS_DOWN] = "always-down",
1338 [ACTIVATION_POLICY_DOWN] = "down",
1339 [ACTIVATION_POLICY_BOUND] = "bound",
1340 };
1341
1342 DEFINE_STRING_TABLE_LOOKUP(activation_policy, ActivationPolicy);
1343 DEFINE_CONFIG_PARSE_ENUM(config_parse_activation_policy, activation_policy, ActivationPolicy, "Failed to parse activation policy");