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