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