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