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