]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #16491 from keszybz/udev-logging
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 "networkd-dhcp-server.h"
16 #include "network-internal.h"
17 #include "networkd-manager.h"
18 #include "networkd-network.h"
19 #include "networkd-sriov.h"
20 #include "parse-util.h"
21 #include "path-lookup.h"
22 #include "set.h"
23 #include "socket-util.h"
24 #include "stat-util.h"
25 #include "string-table.h"
26 #include "string-util.h"
27 #include "strv.h"
28 #include "tc.h"
29 #include "util.h"
30
31 /* Let's assume that anything above this number is a user misconfiguration. */
32 #define MAX_NTP_SERVERS 128
33
34 /* Set defaults following RFC7844 */
35 void network_apply_anonymize_if_set(Network *network) {
36 if (!network->dhcp_anonymize)
37 return;
38 /* RFC7844 3.7
39 SHOULD NOT send the Host Name option */
40 network->dhcp_send_hostname = false;
41 /* RFC7844 section 3.:
42 MAY contain the Client Identifier option
43 Section 3.5:
44 clients MUST use client identifiers based solely
45 on the link-layer address */
46 /* NOTE: Using MAC, as it does not reveal extra information,
47 * and some servers might not answer if this option is not sent */
48 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
49 /* RFC 7844 3.10:
50 SHOULD NOT use the Vendor Class Identifier option */
51 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
52 /* RFC7844 section 3.6.:
53 The client intending to protect its privacy SHOULD only request a
54 minimal number of options in the PRL and SHOULD also randomly shuffle
55 the ordering of option codes in the PRL. If this random ordering
56 cannot be implemented, the client MAY order the option codes in the
57 PRL by option code number (lowest to highest).
58 */
59 /* NOTE: dhcp_use_mtu is false by default,
60 * though it was not initiallized to any value in network_load_one.
61 * Maybe there should be another var called *send*?
62 * (to use the MTU sent by the server but to do not send
63 * the option in the PRL). */
64 network->dhcp_use_mtu = false;
65 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
66 * but this is needed to use them. */
67 network->dhcp_use_routes = true;
68 /* RFC7844 section 3.6.
69 * same comments as previous option */
70 network->dhcp_use_timezone = false;
71 }
72
73 static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
74 const char *kind_string;
75 NetDev *netdev;
76 int r;
77
78 /* For test-networkd-conf, the check must be earlier than the assertions. */
79 if (!name)
80 return 0;
81
82 assert(network);
83 assert(network->manager);
84 assert(network->filename);
85 assert(ret_netdev);
86
87 if (kind == _NETDEV_KIND_TUNNEL)
88 kind_string = "tunnel";
89 else {
90 kind_string = netdev_kind_to_string(kind);
91 if (!kind_string)
92 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
93 "%s: Invalid NetDev kind of %s, ignoring assignment.",
94 network->filename, name);
95 }
96
97 r = netdev_get(network->manager, name, &netdev);
98 if (r < 0)
99 return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
100 network->filename, name);
101
102 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
103 IN_SET(netdev->kind,
104 NETDEV_KIND_IPIP,
105 NETDEV_KIND_SIT,
106 NETDEV_KIND_GRE,
107 NETDEV_KIND_GRETAP,
108 NETDEV_KIND_IP6GRE,
109 NETDEV_KIND_IP6GRETAP,
110 NETDEV_KIND_VTI,
111 NETDEV_KIND_VTI6,
112 NETDEV_KIND_IP6TNL,
113 NETDEV_KIND_ERSPAN)))
114 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
115 "%s: NetDev %s is not a %s, ignoring assignment",
116 network->filename, name, kind_string);
117
118 *ret_netdev = netdev_ref(netdev);
119 return 1;
120 }
121
122 static int network_resolve_stacked_netdevs(Network *network) {
123 void *name, *kind;
124 Iterator i;
125 int r;
126
127 assert(network);
128
129 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names, i) {
130 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
131
132 r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev);
133 if (r <= 0)
134 continue;
135
136 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
137 if (r < 0)
138 return log_oom();
139
140 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
141 if (r < 0)
142 return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m",
143 network->filename, (const char *) name);
144
145 netdev = NULL;
146 }
147
148 return 0;
149 }
150
151 int network_verify(Network *network) {
152 RoutePrefix *route_prefix, *route_prefix_next;
153 RoutingPolicyRule *rule, *rule_next;
154 Neighbor *neighbor, *neighbor_next;
155 AddressLabel *label, *label_next;
156 NextHop *nexthop, *nextnop_next;
157 Address *address, *address_next;
158 Prefix *prefix, *prefix_next;
159 Route *route, *route_next;
160 FdbEntry *fdb, *fdb_next;
161 TrafficControl *tc;
162 SRIOV *sr_iov;
163 Iterator i;
164
165 assert(network);
166 assert(network->filename);
167
168 if (set_isempty(network->match_mac) && set_isempty(network->match_permanent_mac) &&
169 strv_isempty(network->match_path) && strv_isempty(network->match_driver) &&
170 strv_isempty(network->match_type) && strv_isempty(network->match_name) &&
171 strv_isempty(network->match_property) && strv_isempty(network->match_wlan_iftype) &&
172 strv_isempty(network->match_ssid) && !network->conditions)
173 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
174 "%s: No valid settings found in the [Match] section, ignoring file. "
175 "To match all interfaces, add Name=* in the [Match] section.",
176 network->filename);
177
178 /* skip out early if configuration does not match the environment */
179 if (!condition_test_list(network->conditions, environ, NULL, NULL, NULL))
180 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
181 "%s: Conditions in the file do not match the system environment, skipping.",
182 network->filename);
183
184 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
185 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
186 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
187 (void) network_resolve_stacked_netdevs(network);
188
189 /* Free unnecessary entries. */
190 network->bond_name = mfree(network->bond_name);
191 network->bridge_name = mfree(network->bridge_name);
192 network->vrf_name = mfree(network->vrf_name);
193 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
194
195 if (network->bond) {
196 /* Bonding slave does not support addressing. */
197 if (network->ipv6_accept_ra > 0) {
198 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
199 network->filename);
200 network->ipv6_accept_ra = 0;
201 }
202 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
203 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
204 network->filename);
205 network->link_local = ADDRESS_FAMILY_NO;
206 }
207 if (network->dhcp != ADDRESS_FAMILY_NO) {
208 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
209 network->filename);
210 network->dhcp = ADDRESS_FAMILY_NO;
211 }
212 if (network->dhcp_server) {
213 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
214 network->filename);
215 network->dhcp_server = false;
216 }
217 if (network->n_static_addresses > 0) {
218 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
219 network->filename);
220 while ((address = network->static_addresses))
221 address_free(address);
222 }
223 if (network->n_static_routes > 0) {
224 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
225 network->filename);
226 while ((route = network->static_routes))
227 route_free(route);
228 }
229 }
230
231 if (network->link_local < 0)
232 network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
233
234 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
235 if (network->ipv6_accept_ra > 0) {
236 log_warning("%s: IPv6AcceptRA= is enabled by the .network file but IPv6 link local addressing is disabled. "
237 "Disabling IPv6AcceptRA=.", network->filename);
238 network->ipv6_accept_ra = false;
239 }
240
241 if (FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
242 log_warning("%s: DHCPv6 client is enabled by the .network file but IPv6 link local addressing is disabled. "
243 "Disabling DHCPv6 client.", network->filename);
244 SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
245 }
246
247 if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE) {
248 log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
249 "Disabling IPv6PrefixDelegation=.", network->filename);
250 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
251 }
252 }
253
254 if (FLAGS_SET(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4) &&
255 !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) {
256 log_warning("%s: fallback assignment of IPv4 link local address is enabled but DHCPv4 is disabled. "
257 "Disabling the fallback assignment.", network->filename);
258 SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false);
259 }
260
261 if (network->ipv6_accept_ra < 0 && network->bridge)
262 network->ipv6_accept_ra = false;
263
264 /* IPMasquerade=yes implies IPForward=yes */
265 if (network->ip_masquerade)
266 network->ip_forward |= ADDRESS_FAMILY_IPV4;
267
268 if (network->mtu > 0 && network->dhcp_use_mtu) {
269 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
270 "Disabling UseMTU=.", network->filename);
271 network->dhcp_use_mtu = false;
272 }
273
274 if (network->dhcp_use_gateway < 0)
275 network->dhcp_use_gateway = network->dhcp_use_routes;
276
277 if (network->ignore_carrier_loss < 0)
278 network->ignore_carrier_loss = network->configure_without_carrier;
279
280 if (network->dhcp_critical >= 0) {
281 if (network->keep_configuration >= 0)
282 log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
283 "Ignoring CriticalConnection=.", network->filename);
284 else if (network->dhcp_critical)
285 /* CriticalConnection=yes also preserve foreign static configurations. */
286 network->keep_configuration = KEEP_CONFIGURATION_YES;
287 else
288 network->keep_configuration = KEEP_CONFIGURATION_NO;
289 }
290
291 if (network->keep_configuration < 0)
292 network->keep_configuration = KEEP_CONFIGURATION_NO;
293
294 LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
295 if (address_section_verify(address) < 0)
296 address_free(address);
297
298 LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes)
299 if (route_section_verify(route, network) < 0)
300 route_free(route);
301
302 LIST_FOREACH_SAFE(nexthops, nexthop, nextnop_next, network->static_nexthops)
303 if (nexthop_section_verify(nexthop) < 0)
304 nexthop_free(nexthop);
305
306 LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
307 if (section_is_invalid(fdb->section))
308 fdb_entry_free(fdb);
309
310 LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors)
311 if (neighbor_section_verify(neighbor) < 0)
312 neighbor_free(neighbor);
313
314 LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels)
315 if (section_is_invalid(label->section))
316 address_label_free(label);
317
318 LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes)
319 if (section_is_invalid(prefix->section))
320 prefix_free(prefix);
321
322 LIST_FOREACH_SAFE(route_prefixes, route_prefix, route_prefix_next, network->static_route_prefixes)
323 if (section_is_invalid(route_prefix->section))
324 route_prefix_free(route_prefix);
325
326 LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules)
327 if (routing_policy_rule_section_verify(rule) < 0)
328 routing_policy_rule_free(rule);
329
330 bool has_root = false, has_clsact = false;
331 ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section, i)
332 if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
333 traffic_control_free(tc);
334
335 ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i)
336 if (sr_iov_section_verify(sr_iov) < 0)
337 sr_iov_free(sr_iov);
338
339 return 0;
340 }
341
342 int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
343 _cleanup_free_ char *fname = NULL, *name = NULL;
344 _cleanup_(network_unrefp) Network *network = NULL;
345 _cleanup_fclose_ FILE *file = NULL;
346 const char *dropin_dirname;
347 char *d;
348 int r;
349
350 assert(manager);
351 assert(filename);
352
353 file = fopen(filename, "re");
354 if (!file) {
355 if (errno == ENOENT)
356 return 0;
357
358 return -errno;
359 }
360
361 if (null_or_empty_fd(fileno(file))) {
362 log_debug("Skipping empty file: %s", filename);
363 return 0;
364 }
365
366 fname = strdup(filename);
367 if (!fname)
368 return log_oom();
369
370 name = strdup(basename(filename));
371 if (!name)
372 return log_oom();
373
374 d = strrchr(name, '.');
375 if (!d)
376 return -EINVAL;
377
378 *d = '\0';
379
380 dropin_dirname = strjoina(name, ".network.d");
381
382 network = new(Network, 1);
383 if (!network)
384 return log_oom();
385
386 *network = (Network) {
387 .filename = TAKE_PTR(fname),
388 .name = TAKE_PTR(name),
389
390 .manager = manager,
391 .n_ref = 1,
392
393 .required_for_online = true,
394 .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
395 .dhcp = ADDRESS_FAMILY_NO,
396 .dhcp_critical = -1,
397 .dhcp_use_ntp = true,
398 .dhcp_use_sip = true,
399 .dhcp_use_dns = true,
400 .dhcp_use_hostname = true,
401 .dhcp_use_routes = true,
402 .dhcp_use_gateway = -1,
403 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
404 .dhcp_send_hostname = true,
405 .dhcp_send_release = true,
406 /* To enable/disable RFC7844 Anonymity Profiles */
407 .dhcp_anonymize = false,
408 .dhcp_route_metric = DHCP_ROUTE_METRIC,
409 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
410 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
411 .dhcp_route_table = RT_TABLE_MAIN,
412 .dhcp_route_table_set = false,
413 /* NOTE: from man: UseMTU=... Defaults to false*/
414 .dhcp_use_mtu = false,
415 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
416 .dhcp_use_timezone = false,
417 .rapid_commit = true,
418
419 .dhcp6_route_metric = DHCP_ROUTE_METRIC,
420 .dhcp6_use_ntp = true,
421 .dhcp6_use_dns = true,
422
423 .dhcp6_pd_assign_prefix = true,
424
425 .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
426 .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
427 .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
428
429 .dhcp_server_emit_router = true,
430 .dhcp_server_emit_timezone = true,
431
432 .router_prefix_subnet_id = -1,
433 .router_emit_dns = true,
434 .router_emit_domains = true,
435
436 .use_bpdu = -1,
437 .hairpin = -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
452 .dns_default_route = -1,
453 .llmnr = RESOLVE_SUPPORT_YES,
454 .mdns = RESOLVE_SUPPORT_NO,
455 .dnssec_mode = _DNSSEC_MODE_INVALID,
456 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
457
458 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
459 .link_local = _ADDRESS_FAMILY_INVALID,
460 .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
461
462 .ipv4_accept_local = -1,
463
464 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
465 .ipv6_accept_ra = -1,
466 .ipv6_dad_transmits = -1,
467 .ipv6_hop_limit = -1,
468 .ipv6_proxy_ndp = -1,
469 .duid.type = _DUID_TYPE_INVALID,
470 .proxy_arp = -1,
471 .arp = -1,
472 .multicast = -1,
473 .allmulticast = -1,
474 .ipv6_accept_ra_use_dns = true,
475 .ipv6_accept_ra_use_autonomous_prefix = true,
476 .ipv6_accept_ra_use_onlink_prefix = true,
477 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
478 .ipv6_accept_ra_route_table_set = false,
479 .ipv6_accept_ra_start_dhcp6_client = true,
480
481 .configure_without_carrier = false,
482 .ignore_carrier_loss = -1,
483 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
484 .can_triple_sampling = -1,
485 .can_termination = -1,
486 .ip_service_type = -1,
487 };
488
489 r = config_parse_many(
490 filename, NETWORK_DIRS, dropin_dirname,
491 "Match\0"
492 "Link\0"
493 "SR-IOV\0"
494 "Network\0"
495 "Address\0"
496 "Neighbor\0"
497 "IPv6AddressLabel\0"
498 "RoutingPolicyRule\0"
499 "Route\0"
500 "NextHop\0"
501 "DHCP\0" /* compat */
502 "DHCPv4\0"
503 "DHCPv6\0"
504 "DHCPServer\0"
505 "IPv6AcceptRA\0"
506 "IPv6NDPProxyAddress\0"
507 "Bridge\0"
508 "BridgeFDB\0"
509 "BridgeVLAN\0"
510 "IPv6PrefixDelegation\0"
511 "IPv6Prefix\0"
512 "IPv6RoutePrefix\0"
513 "LLDP\0"
514 "TrafficControlQueueingDiscipline\0"
515 "CAN\0"
516 "QDisc\0"
517 "BFIFO\0"
518 "CAKE\0"
519 "ControlledDelay\0"
520 "DeficitRoundRobinScheduler\0"
521 "DeficitRoundRobinSchedulerClass\0"
522 "EnhancedTransmissionSelection\0"
523 "FairQueueing\0"
524 "FairQueueingControlledDelay\0"
525 "GenericRandomEarlyDetection\0"
526 "HeavyHitterFilter\0"
527 "HierarchyTokenBucket\0"
528 "HierarchyTokenBucketClass\0"
529 "NetworkEmulator\0"
530 "PFIFO\0"
531 "PFIFOFast\0"
532 "PFIFOHeadDrop\0"
533 "PIE\0"
534 "QuickFairQueueing\0"
535 "QuickFairQueueingClass\0"
536 "StochasticFairBlue\0"
537 "StochasticFairnessQueueing\0"
538 "TokenBucketFilter\0"
539 "TrivialLinkEqualizer\0",
540 config_item_perf_lookup, network_network_gperf_lookup,
541 CONFIG_PARSE_WARN,
542 network,
543 &network->timestamp);
544 if (r < 0)
545 return r;
546
547 network_apply_anonymize_if_set(network);
548
549 r = network_add_ipv4ll_route(network);
550 if (r < 0)
551 log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
552
553 r = network_add_default_route_on_device(network);
554 if (r < 0)
555 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
556 network->filename);
557
558 if (network_verify(network) < 0)
559 /* Ignore .network files that do not match the conditions. */
560 return 0;
561
562 r = ordered_hashmap_ensure_allocated(networks, &string_hash_ops);
563 if (r < 0)
564 return r;
565
566 r = ordered_hashmap_put(*networks, network->name, network);
567 if (r < 0)
568 return r;
569
570 network = NULL;
571 return 0;
572 }
573
574 int network_load(Manager *manager, OrderedHashmap **networks) {
575 _cleanup_strv_free_ char **files = NULL;
576 char **f;
577 int r;
578
579 assert(manager);
580
581 ordered_hashmap_clear_with_destructor(*networks, network_unref);
582
583 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
584 if (r < 0)
585 return log_error_errno(r, "Failed to enumerate network files: %m");
586
587 STRV_FOREACH(f, files) {
588 r = network_load_one(manager, networks, *f);
589 if (r < 0)
590 log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
591 }
592
593 return 0;
594 }
595
596 int network_reload(Manager *manager) {
597 OrderedHashmap *new_networks = NULL;
598 Network *n, *old;
599 Iterator i;
600 int r;
601
602 assert(manager);
603
604 r = network_load(manager, &new_networks);
605 if (r < 0)
606 goto failure;
607
608 ORDERED_HASHMAP_FOREACH(n, new_networks, i) {
609 r = network_get_by_name(manager, n->name, &old);
610 if (r < 0)
611 continue; /* The .network file is new. */
612
613 if (n->timestamp != old->timestamp)
614 continue; /* The .network file is modified. */
615
616 if (!streq(n->filename, old->filename))
617 continue;
618
619 r = ordered_hashmap_replace(new_networks, old->name, old);
620 if (r < 0)
621 goto failure;
622
623 network_ref(old);
624 network_unref(n);
625 }
626
627 ordered_hashmap_free_with_destructor(manager->networks, network_unref);
628 manager->networks = new_networks;
629
630 return 0;
631
632 failure:
633 ordered_hashmap_free_with_destructor(new_networks, network_unref);
634
635 return r;
636 }
637
638 static Network *network_free(Network *network) {
639 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
640 RoutePrefix *route_prefix;
641 RoutingPolicyRule *rule;
642 AddressLabel *label;
643 FdbEntry *fdb_entry;
644 Neighbor *neighbor;
645 Address *address;
646 NextHop *nexthop;
647 Prefix *prefix;
648 Route *route;
649
650 if (!network)
651 return NULL;
652
653 free(network->filename);
654
655 set_free_free(network->match_mac);
656 set_free_free(network->match_permanent_mac);
657 strv_free(network->match_path);
658 strv_free(network->match_driver);
659 strv_free(network->match_type);
660 strv_free(network->match_name);
661 strv_free(network->match_property);
662 strv_free(network->match_wlan_iftype);
663 strv_free(network->match_ssid);
664 set_free_free(network->match_bssid);
665 condition_free_list(network->conditions);
666
667 free(network->description);
668 free(network->dhcp_vendor_class_identifier);
669 free(network->dhcp_mudurl);
670 strv_free(network->dhcp_user_class);
671 free(network->dhcp_hostname);
672 set_free(network->dhcp_deny_listed_ip);
673 set_free(network->dhcp_allow_listed_ip);
674 set_free(network->dhcp_request_options);
675 set_free(network->dhcp6_request_options);
676 free(network->mac);
677 free(network->dhcp6_mudurl);
678 strv_free(network->dhcp6_user_class);
679 strv_free(network->dhcp6_vendor_class);
680
681 if (network->dhcp_acd)
682 sd_ipv4acd_unref(network->dhcp_acd);
683
684 strv_free(network->ntp);
685 free(network->dns);
686 ordered_set_free_free(network->search_domains);
687 ordered_set_free_free(network->route_domains);
688 strv_free(network->bind_carrier);
689
690 ordered_set_free_free(network->router_search_domains);
691 free(network->router_dns);
692 set_free_free(network->ndisc_deny_listed_prefix);
693
694 free(network->bridge_name);
695 free(network->bond_name);
696 free(network->vrf_name);
697 hashmap_free_free_key(network->stacked_netdev_names);
698 netdev_unref(network->bridge);
699 netdev_unref(network->bond);
700 netdev_unref(network->vrf);
701 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
702
703 while ((route = network->static_routes))
704 route_free(route);
705
706 while ((nexthop = network->static_nexthops))
707 nexthop_free(nexthop);
708
709 while ((address = network->static_addresses))
710 address_free(address);
711
712 while ((fdb_entry = network->static_fdb_entries))
713 fdb_entry_free(fdb_entry);
714
715 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
716 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
717
718 while ((neighbor = network->neighbors))
719 neighbor_free(neighbor);
720
721 while ((label = network->address_labels))
722 address_label_free(label);
723
724 while ((prefix = network->static_prefixes))
725 prefix_free(prefix);
726
727 while ((route_prefix = network->static_route_prefixes))
728 route_prefix_free(route_prefix);
729
730 while ((rule = network->rules))
731 routing_policy_rule_free(rule);
732
733 hashmap_free(network->addresses_by_section);
734 hashmap_free(network->routes_by_section);
735 hashmap_free(network->nexthops_by_section);
736 hashmap_free(network->fdb_entries_by_section);
737 hashmap_free(network->neighbors_by_section);
738 hashmap_free(network->address_labels_by_section);
739 hashmap_free(network->prefixes_by_section);
740 hashmap_free(network->route_prefixes_by_section);
741 hashmap_free(network->rules_by_section);
742 ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
743 ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
744
745 if (network->manager &&
746 network->manager->duids_requesting_uuid)
747 set_remove(network->manager->duids_requesting_uuid, &network->duid);
748
749 free(network->name);
750
751 free(network->dhcp_server_timezone);
752
753 for (sd_dhcp_lease_server_type t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
754 free(network->dhcp_server_emit[t].addresses);
755
756 set_free_free(network->dnssec_negative_trust_anchors);
757
758 free(network->lldp_mud);
759
760 ordered_hashmap_free(network->dhcp_client_send_options);
761 ordered_hashmap_free(network->dhcp_client_send_vendor_options);
762 ordered_hashmap_free(network->dhcp_server_send_options);
763 ordered_hashmap_free(network->dhcp_server_send_vendor_options);
764 ordered_hashmap_free(network->ipv6_tokens);
765 ordered_hashmap_free(network->dhcp6_client_send_options);
766 ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
767
768 return mfree(network);
769 }
770
771 DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
772
773 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
774 Network *network;
775
776 assert(manager);
777 assert(name);
778 assert(ret);
779
780 network = ordered_hashmap_get(manager->networks, name);
781 if (!network)
782 return -ENOENT;
783
784 *ret = network;
785
786 return 0;
787 }
788
789 int network_get(Manager *manager, unsigned short iftype, sd_device *device,
790 const char *ifname, char * const *alternative_names, const char *driver,
791 const struct ether_addr *mac, const struct ether_addr *permanent_mac,
792 enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid,
793 Network **ret) {
794 Network *network;
795 Iterator i;
796
797 assert(manager);
798 assert(ret);
799
800 ORDERED_HASHMAP_FOREACH(network, manager->networks, i)
801 if (net_match_config(network->match_mac, network->match_permanent_mac,
802 network->match_path, network->match_driver,
803 network->match_type, network->match_name, network->match_property,
804 network->match_wlan_iftype, network->match_ssid, network->match_bssid,
805 device, mac, permanent_mac, driver, iftype,
806 ifname, alternative_names, wlan_iftype, ssid, bssid)) {
807 if (network->match_name && device) {
808 const char *attr;
809 uint8_t name_assign_type = NET_NAME_UNKNOWN;
810
811 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
812 (void) safe_atou8(attr, &name_assign_type);
813
814 if (name_assign_type == NET_NAME_ENUM)
815 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
816 ifname, network->filename);
817 else
818 log_debug("%s: found matching network '%s'", ifname, network->filename);
819 } else
820 log_debug("%s: found matching network '%s'", ifname, network->filename);
821
822 *ret = network;
823 return 0;
824 }
825
826 *ret = NULL;
827
828 return -ENOENT;
829 }
830
831 int network_apply(Network *network, Link *link) {
832 assert(network);
833 assert(link);
834
835 link->network = network_ref(network);
836
837 if (network->n_dns > 0 ||
838 !strv_isempty(network->ntp) ||
839 !ordered_set_isempty(network->search_domains) ||
840 !ordered_set_isempty(network->route_domains))
841 link_dirty(link);
842
843 return 0;
844 }
845
846 bool network_has_static_ipv6_configurations(Network *network) {
847 Address *address;
848 Route *route;
849 FdbEntry *fdb;
850 Neighbor *neighbor;
851
852 assert(network);
853
854 LIST_FOREACH(addresses, address, network->static_addresses)
855 if (address->family == AF_INET6)
856 return true;
857
858 LIST_FOREACH(routes, route, network->static_routes)
859 if (route->family == AF_INET6)
860 return true;
861
862 LIST_FOREACH(static_fdb_entries, fdb, network->static_fdb_entries)
863 if (fdb->family == AF_INET6)
864 return true;
865
866 LIST_FOREACH(neighbors, neighbor, network->neighbors)
867 if (neighbor->family == AF_INET6)
868 return true;
869
870 if (!LIST_IS_EMPTY(network->address_labels))
871 return true;
872
873 if (!LIST_IS_EMPTY(network->static_prefixes))
874 return true;
875
876 return false;
877 }
878
879 int config_parse_stacked_netdev(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 _cleanup_free_ char *name = NULL;
890 NetDevKind kind = ltype;
891 Hashmap **h = data;
892 int r;
893
894 assert(filename);
895 assert(lvalue);
896 assert(rvalue);
897 assert(data);
898 assert(IN_SET(kind,
899 NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
900 NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
901 NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
902 NETDEV_KIND_XFRM));
903
904 if (!ifname_valid(rvalue)) {
905 log_syntax(unit, LOG_WARNING, filename, line, 0,
906 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
907 return 0;
908 }
909
910 name = strdup(rvalue);
911 if (!name)
912 return log_oom();
913
914 r = hashmap_ensure_allocated(h, &string_hash_ops);
915 if (r < 0)
916 return log_oom();
917
918 r = hashmap_put(*h, name, INT_TO_PTR(kind));
919 if (r < 0)
920 log_syntax(unit, LOG_WARNING, filename, line, r,
921 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
922 else if (r == 0)
923 log_syntax(unit, LOG_DEBUG, filename, line, r,
924 "NetDev '%s' specified twice, ignoring.", name);
925 else
926 name = NULL;
927
928 return 0;
929 }
930
931 int config_parse_domains(
932 const char *unit,
933 const char *filename,
934 unsigned line,
935 const char *section,
936 unsigned section_line,
937 const char *lvalue,
938 int ltype,
939 const char *rvalue,
940 void *data,
941 void *userdata) {
942
943 Network *n = data;
944 int r;
945
946 assert(n);
947 assert(lvalue);
948 assert(rvalue);
949
950 if (isempty(rvalue)) {
951 n->search_domains = ordered_set_free_free(n->search_domains);
952 n->route_domains = ordered_set_free_free(n->route_domains);
953 return 0;
954 }
955
956 for (const char *p = rvalue;;) {
957 _cleanup_free_ char *w = NULL, *normalized = NULL;
958 const char *domain;
959 bool is_route;
960
961 r = extract_first_word(&p, &w, NULL, 0);
962 if (r == -ENOMEM)
963 return log_oom();
964 if (r < 0) {
965 log_syntax(unit, LOG_WARNING, filename, line, r,
966 "Failed to extract search or route domain, ignoring: %s", rvalue);
967 return 0;
968 }
969 if (r == 0)
970 return 0;
971
972 is_route = w[0] == '~';
973 domain = is_route ? w + 1 : w;
974
975 if (dns_name_is_root(domain) || streq(domain, "*")) {
976 /* If the root domain appears as is, or the special token "*" is found, we'll
977 * consider this as routing domain, unconditionally. */
978 is_route = true;
979 domain = "."; /* make sure we don't allow empty strings, thus write the root
980 * domain as "." */
981 } else {
982 r = dns_name_normalize(domain, 0, &normalized);
983 if (r < 0) {
984 log_syntax(unit, LOG_WARNING, filename, line, r,
985 "'%s' is not a valid domain name, ignoring.", domain);
986 continue;
987 }
988
989 domain = normalized;
990
991 if (is_localhost(domain)) {
992 log_syntax(unit, LOG_WARNING, filename, line, 0,
993 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
994 domain);
995 continue;
996 }
997 }
998
999 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
1000 r = ordered_set_ensure_allocated(set, &string_hash_ops);
1001 if (r < 0)
1002 return log_oom();
1003
1004 r = ordered_set_put_strdup(*set, domain);
1005 if (r < 0)
1006 return log_oom();
1007 }
1008 }
1009
1010 int config_parse_ipv6token(
1011 const char* unit,
1012 const char *filename,
1013 unsigned line,
1014 const char *section,
1015 unsigned section_line,
1016 const char *lvalue,
1017 int ltype,
1018 const char *rvalue,
1019 void *data,
1020 void *userdata) {
1021
1022 union in_addr_union buffer;
1023 struct in6_addr *token = data;
1024 int r;
1025
1026 assert(filename);
1027 assert(lvalue);
1028 assert(rvalue);
1029 assert(token);
1030
1031 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
1032 if (r < 0) {
1033 log_syntax(unit, LOG_WARNING, filename, line, r,
1034 "Failed to parse IPv6 token, ignoring: %s", rvalue);
1035 return 0;
1036 }
1037
1038 if (in_addr_is_null(AF_INET6, &buffer)) {
1039 log_syntax(unit, LOG_WARNING, filename, line, 0,
1040 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
1041 return 0;
1042 }
1043
1044 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
1045 log_syntax(unit, LOG_WARNING, filename, line, 0,
1046 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
1047 return 0;
1048 }
1049
1050 *token = buffer.in6;
1051
1052 return 0;
1053 }
1054
1055 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1056 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
1057 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
1058 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
1059 };
1060
1061 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
1062 IPV6_PRIVACY_EXTENSIONS_YES);
1063
1064 int config_parse_ipv6_privacy_extensions(
1065 const char* unit,
1066 const char *filename,
1067 unsigned line,
1068 const char *section,
1069 unsigned section_line,
1070 const char *lvalue,
1071 int ltype,
1072 const char *rvalue,
1073 void *data,
1074 void *userdata) {
1075
1076 IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
1077
1078 assert(filename);
1079 assert(lvalue);
1080 assert(rvalue);
1081 assert(ipv6_privacy_extensions);
1082
1083 s = ipv6_privacy_extensions_from_string(rvalue);
1084 if (s < 0) {
1085 if (streq(rvalue, "kernel"))
1086 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
1087 else {
1088 log_syntax(unit, LOG_WARNING, filename, line, 0,
1089 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1090 return 0;
1091 }
1092 }
1093
1094 *ipv6_privacy_extensions = s;
1095
1096 return 0;
1097 }
1098
1099 int config_parse_hostname(
1100 const char *unit,
1101 const char *filename,
1102 unsigned line,
1103 const char *section,
1104 unsigned section_line,
1105 const char *lvalue,
1106 int ltype,
1107 const char *rvalue,
1108 void *data,
1109 void *userdata) {
1110
1111 _cleanup_free_ char *hn = NULL;
1112 char **hostname = data;
1113 int r;
1114
1115 assert(filename);
1116 assert(lvalue);
1117 assert(rvalue);
1118
1119 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
1120 if (r < 0)
1121 return r;
1122
1123 if (!hostname_is_valid(hn, false)) {
1124 log_syntax(unit, LOG_WARNING, filename, line, 0,
1125 "Hostname is not valid, ignoring assignment: %s", rvalue);
1126 return 0;
1127 }
1128
1129 r = dns_name_is_valid(hn);
1130 if (r < 0) {
1131 log_syntax(unit, LOG_WARNING, filename, line, r,
1132 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
1133 return 0;
1134 }
1135 if (r == 0) {
1136 log_syntax(unit, LOG_WARNING, filename, line, 0,
1137 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
1138 return 0;
1139 }
1140
1141 return free_and_replace(*hostname, hn);
1142 }
1143
1144 int config_parse_timezone(
1145 const char *unit,
1146 const char *filename,
1147 unsigned line,
1148 const char *section,
1149 unsigned section_line,
1150 const char *lvalue,
1151 int ltype,
1152 const char *rvalue,
1153 void *data,
1154 void *userdata) {
1155
1156 _cleanup_free_ char *tz = NULL;
1157 char **datap = data;
1158 int r;
1159
1160 assert(filename);
1161 assert(lvalue);
1162 assert(rvalue);
1163
1164 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1165 if (r < 0)
1166 return r;
1167
1168 if (!timezone_is_valid(tz, LOG_WARNING)) {
1169 log_syntax(unit, LOG_WARNING, filename, line, 0,
1170 "Timezone is not valid, ignoring assignment: %s", rvalue);
1171 return 0;
1172 }
1173
1174 return free_and_replace(*datap, tz);
1175 }
1176
1177 int config_parse_dns(
1178 const char *unit,
1179 const char *filename,
1180 unsigned line,
1181 const char *section,
1182 unsigned section_line,
1183 const char *lvalue,
1184 int ltype,
1185 const char *rvalue,
1186 void *data,
1187 void *userdata) {
1188
1189 Network *n = userdata;
1190 int r;
1191
1192 assert(filename);
1193 assert(lvalue);
1194 assert(rvalue);
1195
1196 if (isempty(rvalue)) {
1197 n->dns = mfree(n->dns);
1198 n->n_dns = 0;
1199 return 0;
1200 }
1201
1202 for (const char *p = rvalue;;) {
1203 _cleanup_free_ char *w = NULL;
1204 union in_addr_union a;
1205 struct in_addr_data *m;
1206 int family;
1207
1208 r = extract_first_word(&p, &w, NULL, 0);
1209 if (r == -ENOMEM)
1210 return log_oom();
1211 if (r < 0) {
1212 log_syntax(unit, LOG_WARNING, filename, line, r,
1213 "Invalid syntax, ignoring: %s", rvalue);
1214 return 0;
1215 }
1216 if (r == 0)
1217 return 0;
1218
1219 r = in_addr_from_string_auto(w, &family, &a);
1220 if (r < 0) {
1221 log_syntax(unit, LOG_WARNING, filename, line, r,
1222 "Failed to parse dns server address, ignoring: %s", w);
1223 continue;
1224 }
1225
1226 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
1227 if (!m)
1228 return log_oom();
1229
1230 m[n->n_dns++] = (struct in_addr_data) {
1231 .family = family,
1232 .address = a,
1233 };
1234
1235 n->dns = m;
1236 }
1237 }
1238
1239 int config_parse_dnssec_negative_trust_anchors(
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 *n = data;
1252 int r;
1253
1254 assert(n);
1255 assert(lvalue);
1256 assert(rvalue);
1257
1258 if (isempty(rvalue)) {
1259 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1260 return 0;
1261 }
1262
1263 for (const char *p = rvalue;;) {
1264 _cleanup_free_ char *w = NULL;
1265
1266 r = extract_first_word(&p, &w, NULL, 0);
1267 if (r == -ENOMEM)
1268 return log_oom();
1269 if (r < 0) {
1270 log_syntax(unit, LOG_WARNING, filename, line, r,
1271 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1272 return 0;
1273 }
1274 if (r == 0)
1275 return 0;
1276
1277 r = dns_name_is_valid(w);
1278 if (r <= 0) {
1279 log_syntax(unit, LOG_WARNING, filename, line, r,
1280 "%s is not a valid domain name, ignoring.", w);
1281 continue;
1282 }
1283
1284 r = set_ensure_consume(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops, TAKE_PTR(w));
1285 if (r < 0)
1286 return log_oom();
1287 }
1288 }
1289
1290 int config_parse_ntp(
1291 const char *unit,
1292 const char *filename,
1293 unsigned line,
1294 const char *section,
1295 unsigned section_line,
1296 const char *lvalue,
1297 int ltype,
1298 const char *rvalue,
1299 void *data,
1300 void *userdata) {
1301
1302 char ***l = data;
1303 int r;
1304
1305 assert(l);
1306 assert(lvalue);
1307 assert(rvalue);
1308
1309 if (isempty(rvalue)) {
1310 *l = strv_free(*l);
1311 return 0;
1312 }
1313
1314 for (const char *p = rvalue;;) {
1315 _cleanup_free_ char *w = NULL;
1316
1317 r = extract_first_word(&p, &w, NULL, 0);
1318 if (r == -ENOMEM)
1319 return log_oom();
1320 if (r < 0) {
1321 log_syntax(unit, LOG_WARNING, filename, line, r,
1322 "Failed to extract NTP server name, ignoring: %s", rvalue);
1323 return 0;
1324 }
1325 if (r == 0)
1326 return 0;
1327
1328 r = dns_name_is_valid_or_address(w);
1329 if (r <= 0) {
1330 log_syntax(unit, LOG_WARNING, filename, line, r,
1331 "%s is not a valid domain name or IP address, ignoring.", w);
1332 continue;
1333 }
1334
1335 if (strv_length(*l) > MAX_NTP_SERVERS) {
1336 log_syntax(unit, LOG_WARNING, filename, line, 0,
1337 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1338 MAX_NTP_SERVERS, w);
1339 return 0;
1340 }
1341
1342 r = strv_consume(l, TAKE_PTR(w));
1343 if (r < 0)
1344 return log_oom();
1345 }
1346 }
1347
1348 int config_parse_required_for_online(
1349 const char *unit,
1350 const char *filename,
1351 unsigned line,
1352 const char *section,
1353 unsigned section_line,
1354 const char *lvalue,
1355 int ltype,
1356 const char *rvalue,
1357 void *data,
1358 void *userdata) {
1359
1360 Network *network = data;
1361 LinkOperationalStateRange range;
1362 bool required = true;
1363 int r;
1364
1365 if (isempty(rvalue)) {
1366 network->required_for_online = true;
1367 network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
1368 return 0;
1369 }
1370
1371 r = parse_operational_state_range(rvalue, &range);
1372 if (r < 0) {
1373 r = parse_boolean(rvalue);
1374 if (r < 0) {
1375 log_syntax(unit, LOG_WARNING, filename, line, r,
1376 "Failed to parse %s= setting, ignoring assignment: %s",
1377 lvalue, rvalue);
1378 return 0;
1379 }
1380
1381 required = r;
1382 range = LINK_OPERSTATE_RANGE_DEFAULT;
1383 }
1384
1385 network->required_for_online = required;
1386 network->required_operstate_for_online = range;
1387
1388 return 0;
1389 }
1390
1391 DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1392 "Failed to parse KeepConfiguration= setting");
1393
1394 static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
1395 [KEEP_CONFIGURATION_NO] = "no",
1396 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1397 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1398 [KEEP_CONFIGURATION_STATIC] = "static",
1399 [KEEP_CONFIGURATION_YES] = "yes",
1400 };
1401
1402 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
1403
1404 static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
1405 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64",
1406 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none",
1407 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
1408 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random",
1409 };
1410
1411 DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
1412 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");