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