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