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