]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #11795 from yuwata/fix-network-routing-policy-11280
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <net/if.h>
5
6 #include "alloc-util.h"
7 #include "conf-files.h"
8 #include "conf-parser.h"
9 #include "dns-domain.h"
10 #include "fd-util.h"
11 #include "hostname-util.h"
12 #include "in-addr-util.h"
13 #include "missing_network.h"
14 #include "network-internal.h"
15 #include "networkd-manager.h"
16 #include "networkd-network.h"
17 #include "parse-util.h"
18 #include "set.h"
19 #include "socket-util.h"
20 #include "stat-util.h"
21 #include "string-table.h"
22 #include "string-util.h"
23 #include "strv.h"
24 #include "util.h"
25
26 /* Let's assume that anything above this number is a user misconfiguration. */
27 #define MAX_NTP_SERVERS 128
28
29 static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
30 siphash24_compress(c->filename, strlen(c->filename), state);
31 siphash24_compress(&c->line, sizeof(c->line), state);
32 }
33
34 static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
35 int r;
36
37 r = strcmp(x->filename, y->filename);
38 if (r != 0)
39 return r;
40
41 return CMP(x->line, y->line);
42 }
43
44 DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
45
46 int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
47 NetworkConfigSection *cs;
48
49 cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
50 if (!cs)
51 return -ENOMEM;
52
53 strcpy(cs->filename, filename);
54 cs->line = line;
55
56 *s = TAKE_PTR(cs);
57
58 return 0;
59 }
60
61 void network_config_section_free(NetworkConfigSection *cs) {
62 free(cs);
63 }
64
65 /* Set defaults following RFC7844 */
66 void network_apply_anonymize_if_set(Network *network) {
67 if (!network->dhcp_anonymize)
68 return;
69 /* RFC7844 3.7
70 SHOULD NOT send the Host Name option */
71 network->dhcp_send_hostname = false;
72 /* RFC7844 section 3.:
73 MAY contain the Client Identifier option
74 Section 3.5:
75 clients MUST use client identifiers based solely
76 on the link-layer address */
77 /* NOTE: Using MAC, as it does not reveal extra information,
78 * and some servers might not answer if this option is not sent */
79 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
80 /* RFC 7844 3.10:
81 SHOULD NOT use the Vendor Class Identifier option */
82 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
83 /* RFC7844 section 3.6.:
84 The client intending to protect its privacy SHOULD only request a
85 minimal number of options in the PRL and SHOULD also randomly shuffle
86 the ordering of option codes in the PRL. If this random ordering
87 cannot be implemented, the client MAY order the option codes in the
88 PRL by option code number (lowest to highest).
89 */
90 /* NOTE: dhcp_use_mtu is false by default,
91 * though it was not initiallized to any value in network_load_one.
92 * Maybe there should be another var called *send*?
93 * (to use the MTU sent by the server but to do not send
94 * the option in the PRL). */
95 network->dhcp_use_mtu = false;
96 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
97 * but this is needed to use them. */
98 network->dhcp_use_routes = true;
99 /* RFC7844 section 3.6.
100 * same comments as previous option */
101 network->dhcp_use_timezone = false;
102 }
103
104 static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
105 const char *kind_string;
106 NetDev *netdev;
107 int r;
108
109 assert(network);
110 assert(network->manager);
111 assert(network->filename);
112 assert(ret_netdev);
113
114 if (!name)
115 return 0;
116
117 if (kind == _NETDEV_KIND_TUNNEL)
118 kind_string = "tunnel";
119 else {
120 kind_string = netdev_kind_to_string(kind);
121 if (!kind_string)
122 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
123 "%s: Invalid NetDev kind of %s, ignoring assignment.",
124 network->filename, name);
125 }
126
127 r = netdev_get(network->manager, name, &netdev);
128 if (r < 0)
129 return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
130 network->filename, name);
131
132 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
133 IN_SET(netdev->kind,
134 NETDEV_KIND_IPIP,
135 NETDEV_KIND_SIT,
136 NETDEV_KIND_GRE,
137 NETDEV_KIND_GRETAP,
138 NETDEV_KIND_IP6GRE,
139 NETDEV_KIND_IP6GRETAP,
140 NETDEV_KIND_VTI,
141 NETDEV_KIND_VTI6,
142 NETDEV_KIND_IP6TNL)))
143 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
144 "%s: NetDev %s is not a %s, ignoring assignment",
145 network->filename, name, kind_string);
146
147 *ret_netdev = netdev_ref(netdev);
148 return 1;
149 }
150
151 static int network_resolve_stacked_netdevs(Network *network) {
152 void *name, *kind;
153 Iterator i;
154 int r;
155
156 assert(network);
157
158 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names, i) {
159 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
160
161 r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev);
162 if (r <= 0)
163 continue;
164
165 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
166 if (r < 0)
167 return log_oom();
168
169 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
170 if (r < 0)
171 return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m",
172 network->filename, (const char *) name);
173
174 netdev = NULL;
175 }
176
177 return 0;
178 }
179
180 static uint32_t network_get_stacked_netdevs_mtu(Network *network) {
181 uint32_t mtu = 0;
182 NetDev *dev;
183 Iterator i;
184
185 HASHMAP_FOREACH(dev, network->stacked_netdevs, i)
186 if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0)
187 /* See vlan_dev_change_mtu() in kernel.
188 * Note that the additional 4bytes may not be necessary for all devices. */
189 mtu = MAX(mtu, dev->mtu + 4);
190
191 else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu)
192 /* See macvlan_change_mtu() in kernel. */
193 mtu = dev->mtu;
194
195 return mtu;
196 }
197
198 static int network_verify(Network *network) {
199 Address *address;
200 Route *route;
201 uint32_t mtu;
202
203 assert(network);
204 assert(network->filename);
205
206 /* skip out early if configuration does not match the environment */
207 if (!net_match_config(NULL, NULL, NULL, NULL, NULL,
208 network->match_host, network->match_virt, network->match_kernel_cmdline,
209 network->match_kernel_version, network->match_arch,
210 NULL, NULL, NULL, NULL, NULL))
211 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
212 "%s: Conditions in the file do not match the system environment, skipping.",
213 network->filename);
214
215 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
216 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
217 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
218 (void) network_resolve_stacked_netdevs(network);
219
220 /* Free unnecessary entries. */
221 network->bond_name = mfree(network->bond_name);
222 network->bridge_name = mfree(network->bridge_name);
223 network->vrf_name = mfree(network->vrf_name);
224 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
225
226 if (network->bond) {
227 /* Bonding slave does not support addressing. */
228 if (network->ipv6_accept_ra > 0) {
229 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
230 network->filename);
231 network->ipv6_accept_ra = 0;
232 }
233 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
234 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
235 network->filename);
236 network->link_local = ADDRESS_FAMILY_NO;
237 }
238 if (network->dhcp != ADDRESS_FAMILY_NO) {
239 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
240 network->filename);
241 network->dhcp = ADDRESS_FAMILY_NO;
242 }
243 if (network->dhcp_server) {
244 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
245 network->filename);
246 network->dhcp_server = false;
247 }
248 if (network->n_static_addresses > 0) {
249 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
250 network->filename);
251 while ((address = network->static_addresses))
252 address_free(address);
253 }
254 if (network->n_static_routes > 0) {
255 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
256 network->filename);
257 while ((route = network->static_routes))
258 route_free(route);
259 }
260 }
261
262 if (network->link_local < 0)
263 network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
264
265 if (network->ipv6_accept_ra < 0 && network->bridge)
266 network->ipv6_accept_ra = false;
267
268 /* IPMasquerade=yes implies IPForward=yes */
269 if (network->ip_masquerade)
270 network->ip_forward |= ADDRESS_FAMILY_IPV4;
271
272 network->mtu_is_set = network->mtu > 0;
273 mtu = network_get_stacked_netdevs_mtu(network);
274 if (network->mtu < mtu) {
275 if (network->mtu_is_set)
276 log_notice("%s: Bumping MTUBytes= from %"PRIu32" to %"PRIu32" because of stacked device",
277 network->filename, network->mtu, mtu);
278 network->mtu = mtu;
279 }
280
281 if (network->mtu_is_set && network->dhcp_use_mtu) {
282 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
283 "Disabling UseMTU=.", network->filename);
284 network->dhcp_use_mtu = false;
285 }
286
287 LIST_FOREACH(routes, route, network->static_routes)
288 if (!route->family)
289 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
290 "%s: Route section without Gateway field configured. "
291 "Ignoring %s.",
292 network->filename, network->filename);
293
294 LIST_FOREACH(addresses, address, network->static_addresses)
295 if (!address->family)
296 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
297 "%s: Address section without Address field configured. "
298 "Ignoring %s.",
299 network->filename, network->filename);
300
301 return 0;
302 }
303
304 int network_load_one(Manager *manager, const char *filename) {
305 _cleanup_free_ char *fname = NULL, *name = NULL;
306 _cleanup_(network_freep) Network *network = NULL;
307 _cleanup_fclose_ FILE *file = NULL;
308 const char *dropin_dirname;
309 char *d;
310 int r;
311
312 assert(manager);
313 assert(filename);
314
315 file = fopen(filename, "re");
316 if (!file) {
317 if (errno == ENOENT)
318 return 0;
319
320 return -errno;
321 }
322
323 if (null_or_empty_fd(fileno(file))) {
324 log_debug("Skipping empty file: %s", filename);
325 return 0;
326 }
327
328 fname = strdup(filename);
329 if (!fname)
330 return log_oom();
331
332 name = strdup(basename(filename));
333 if (!name)
334 return log_oom();
335
336 d = strrchr(name, '.');
337 if (!d)
338 return -EINVAL;
339
340 *d = '\0';
341
342 dropin_dirname = strjoina(name, ".network.d");
343
344 network = new(Network, 1);
345 if (!network)
346 return log_oom();
347
348 *network = (Network) {
349 .filename = TAKE_PTR(fname),
350 .name = TAKE_PTR(name),
351
352 .required_for_online = true,
353 .dhcp = ADDRESS_FAMILY_NO,
354 .dhcp_use_ntp = true,
355 .dhcp_use_dns = true,
356 .dhcp_use_hostname = true,
357 .dhcp_use_routes = true,
358 /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
359 .dhcp_send_hostname = true,
360 /* To enable/disable RFC7844 Anonymity Profiles */
361 .dhcp_anonymize = false,
362 .dhcp_route_metric = DHCP_ROUTE_METRIC,
363 /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
364 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
365 .dhcp_route_table = RT_TABLE_MAIN,
366 .dhcp_route_table_set = false,
367 /* NOTE: from man: UseMTU=... Defaults to false*/
368 .dhcp_use_mtu = false,
369 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
370 .dhcp_use_timezone = false,
371 .rapid_commit = true,
372
373 .dhcp_server_emit_dns = true,
374 .dhcp_server_emit_ntp = true,
375 .dhcp_server_emit_router = true,
376 .dhcp_server_emit_timezone = true,
377
378 .router_emit_dns = true,
379 .router_emit_domains = true,
380
381 .use_bpdu = -1,
382 .hairpin = -1,
383 .fast_leave = -1,
384 .allow_port_to_be_root = -1,
385 .unicast_flood = -1,
386 .multicast_to_unicast = -1,
387 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
388
389 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
390
391 .dns_default_route = -1,
392 .llmnr = RESOLVE_SUPPORT_YES,
393 .mdns = RESOLVE_SUPPORT_NO,
394 .dnssec_mode = _DNSSEC_MODE_INVALID,
395 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
396
397 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
398 .link_local = _ADDRESS_FAMILY_BOOLEAN_INVALID,
399
400 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
401 .ipv6_accept_ra = -1,
402 .ipv6_dad_transmits = -1,
403 .ipv6_hop_limit = -1,
404 .ipv6_proxy_ndp = -1,
405 .duid.type = _DUID_TYPE_INVALID,
406 .proxy_arp = -1,
407 .arp = -1,
408 .multicast = -1,
409 .allmulticast = -1,
410 .ipv6_accept_ra_use_dns = true,
411 .ipv6_accept_ra_use_autonomous_prefix = true,
412 .ipv6_accept_ra_use_onlink_prefix = true,
413 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
414 .ipv6_accept_ra_route_table_set = false,
415 };
416
417 r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
418 "Match\0"
419 "Link\0"
420 "Network\0"
421 "Address\0"
422 "Neighbor\0"
423 "IPv6AddressLabel\0"
424 "RoutingPolicyRule\0"
425 "Route\0"
426 "DHCP\0"
427 "DHCPv4\0" /* compat */
428 "DHCPServer\0"
429 "IPv6AcceptRA\0"
430 "IPv6NDPProxyAddress\0"
431 "Bridge\0"
432 "BridgeFDB\0"
433 "BridgeVLAN\0"
434 "IPv6PrefixDelegation\0"
435 "IPv6Prefix\0"
436 "CAN\0",
437 config_item_perf_lookup, network_network_gperf_lookup,
438 CONFIG_PARSE_WARN, network);
439 if (r < 0)
440 return r;
441
442 network_apply_anonymize_if_set(network);
443
444 LIST_PREPEND(networks, manager->networks, network);
445 network->manager = manager;
446
447 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
448 if (r < 0)
449 return r;
450
451 r = hashmap_put(manager->networks_by_name, network->name, network);
452 if (r < 0)
453 return r;
454
455 if (network_verify(network) < 0)
456 return 0;
457
458 network = NULL;
459 return 0;
460 }
461
462 int network_load(Manager *manager) {
463 Network *network;
464 _cleanup_strv_free_ char **files = NULL;
465 char **f;
466 int r;
467
468 assert(manager);
469
470 while ((network = manager->networks))
471 network_free(network);
472
473 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
474 if (r < 0)
475 return log_error_errno(r, "Failed to enumerate network files: %m");
476
477 STRV_FOREACH_BACKWARDS(f, files) {
478 r = network_load_one(manager, *f);
479 if (r < 0)
480 return r;
481 }
482
483 return 0;
484 }
485
486 void network_free(Network *network) {
487 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
488 RoutingPolicyRule *rule;
489 FdbEntry *fdb_entry;
490 Neighbor *neighbor;
491 AddressLabel *label;
492 Prefix *prefix;
493 Address *address;
494 Route *route;
495
496 if (!network)
497 return;
498
499 free(network->filename);
500
501 set_free_free(network->match_mac);
502 strv_free(network->match_path);
503 strv_free(network->match_driver);
504 strv_free(network->match_type);
505 strv_free(network->match_name);
506
507 free(network->description);
508 free(network->dhcp_vendor_class_identifier);
509 strv_free(network->dhcp_user_class);
510 free(network->dhcp_hostname);
511
512 free(network->mac);
513
514 strv_free(network->ntp);
515 free(network->dns);
516 ordered_set_free_free(network->search_domains);
517 ordered_set_free_free(network->route_domains);
518 strv_free(network->bind_carrier);
519
520 ordered_set_free_free(network->router_search_domains);
521 free(network->router_dns);
522
523 free(network->bridge_name);
524 free(network->bond_name);
525 free(network->vrf_name);
526 hashmap_free_free_key(network->stacked_netdev_names);
527 netdev_unref(network->bridge);
528 netdev_unref(network->bond);
529 netdev_unref(network->vrf);
530 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
531
532 while ((route = network->static_routes))
533 route_free(route);
534
535 while ((address = network->static_addresses))
536 address_free(address);
537
538 while ((fdb_entry = network->static_fdb_entries))
539 fdb_entry_free(fdb_entry);
540
541 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
542 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
543
544 while ((neighbor = network->neighbors))
545 neighbor_free(neighbor);
546
547 while ((label = network->address_labels))
548 address_label_free(label);
549
550 while ((prefix = network->static_prefixes))
551 prefix_free(prefix);
552
553 while ((rule = network->rules))
554 routing_policy_rule_free(rule);
555
556 hashmap_free(network->addresses_by_section);
557 hashmap_free(network->routes_by_section);
558 hashmap_free(network->fdb_entries_by_section);
559 hashmap_free(network->neighbors_by_section);
560 hashmap_free(network->address_labels_by_section);
561 hashmap_free(network->prefixes_by_section);
562 hashmap_free(network->rules_by_section);
563
564 if (network->manager) {
565 if (network->manager->networks)
566 LIST_REMOVE(networks, network->manager->networks, network);
567
568 if (network->manager->networks_by_name && network->name)
569 hashmap_remove(network->manager->networks_by_name, network->name);
570
571 if (network->manager->duids_requesting_uuid)
572 set_remove(network->manager->duids_requesting_uuid, &network->duid);
573 }
574
575 free(network->name);
576
577 condition_free_list(network->match_host);
578 condition_free_list(network->match_virt);
579 condition_free_list(network->match_kernel_cmdline);
580 condition_free_list(network->match_kernel_version);
581 condition_free_list(network->match_arch);
582
583 free(network->dhcp_server_timezone);
584 free(network->dhcp_server_dns);
585 free(network->dhcp_server_ntp);
586
587 set_free_free(network->dnssec_negative_trust_anchors);
588
589 free(network);
590 }
591
592 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
593 Network *network;
594
595 assert(manager);
596 assert(name);
597 assert(ret);
598
599 network = hashmap_get(manager->networks_by_name, name);
600 if (!network)
601 return -ENOENT;
602
603 *ret = network;
604
605 return 0;
606 }
607
608 int network_get(Manager *manager, sd_device *device,
609 const char *ifname, const struct ether_addr *address,
610 Network **ret) {
611 const char *path = NULL, *driver = NULL, *devtype = NULL;
612 Network *network;
613
614 assert(manager);
615 assert(ret);
616
617 if (device) {
618 (void) sd_device_get_property_value(device, "ID_PATH", &path);
619
620 (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver);
621
622 (void) sd_device_get_devtype(device, &devtype);
623 }
624
625 LIST_FOREACH(networks, network, manager->networks) {
626 if (net_match_config(network->match_mac, network->match_path,
627 network->match_driver, network->match_type,
628 network->match_name, network->match_host,
629 network->match_virt, network->match_kernel_cmdline,
630 network->match_kernel_version, network->match_arch,
631 address, path, driver, devtype, ifname)) {
632 if (network->match_name && device) {
633 const char *attr;
634 uint8_t name_assign_type = NET_NAME_UNKNOWN;
635
636 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
637 (void) safe_atou8(attr, &name_assign_type);
638
639 if (name_assign_type == NET_NAME_ENUM)
640 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
641 ifname, network->filename);
642 else
643 log_debug("%s: found matching network '%s'", ifname, network->filename);
644 } else
645 log_debug("%s: found matching network '%s'", ifname, network->filename);
646
647 *ret = network;
648 return 0;
649 }
650 }
651
652 *ret = NULL;
653
654 return -ENOENT;
655 }
656
657 int network_apply(Network *network, Link *link) {
658 int r;
659
660 assert(network);
661 assert(link);
662
663 link->network = network;
664
665 if (network->ipv4ll_route) {
666 Route *route;
667
668 r = route_new_static(network, NULL, 0, &route);
669 if (r < 0)
670 return r;
671
672 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
673 if (r == 0)
674 return -EINVAL;
675 if (r < 0)
676 return -errno;
677
678 route->family = AF_INET;
679 route->dst_prefixlen = 16;
680 route->scope = RT_SCOPE_LINK;
681 route->priority = IPV4LL_ROUTE_METRIC;
682 route->protocol = RTPROT_STATIC;
683 }
684
685 if (network->n_dns > 0 ||
686 !strv_isempty(network->ntp) ||
687 !ordered_set_isempty(network->search_domains) ||
688 !ordered_set_isempty(network->route_domains))
689 link_dirty(link);
690
691 return 0;
692 }
693
694 bool network_has_static_ipv6_addresses(Network *network) {
695 Address *address;
696
697 assert(network);
698
699 LIST_FOREACH(addresses, address, network->static_addresses) {
700 if (address->family == AF_INET6)
701 return true;
702 }
703
704 return false;
705 }
706
707 int config_parse_stacked_netdev(const char *unit,
708 const char *filename,
709 unsigned line,
710 const char *section,
711 unsigned section_line,
712 const char *lvalue,
713 int ltype,
714 const char *rvalue,
715 void *data,
716 void *userdata) {
717 _cleanup_free_ char *kind_string = NULL, *name = NULL;
718 Hashmap **h = data;
719 NetDevKind kind;
720 char *p;
721 int r;
722
723 assert(filename);
724 assert(lvalue);
725 assert(rvalue);
726 assert(data);
727
728 if (ltype == _NETDEV_KIND_TUNNEL)
729 kind = _NETDEV_KIND_TUNNEL;
730 else {
731 kind_string = strdup(lvalue);
732 if (!kind_string)
733 return log_oom();
734
735 /* the keys are CamelCase versions of the kind */
736 for (p = kind_string; *p; p++)
737 *p = tolower(*p);
738
739 kind = netdev_kind_from_string(kind_string);
740 if (kind < 0 || IN_SET(kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VRF)) {
741 log_syntax(unit, LOG_ERR, filename, line, 0,
742 "Invalid NetDev kind: %s", lvalue);
743 return 0;
744 }
745 }
746
747 if (!ifname_valid(rvalue)) {
748 log_syntax(unit, LOG_ERR, filename, line, 0,
749 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
750 return 0;
751 }
752
753 name = strdup(rvalue);
754 if (!name)
755 return log_oom();
756
757 r = hashmap_ensure_allocated(h, &string_hash_ops);
758 if (r < 0)
759 return log_oom();
760
761 r = hashmap_put(*h, name, INT_TO_PTR(kind));
762 if (r < 0)
763 log_syntax(unit, LOG_ERR, filename, line, r,
764 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
765 else if (r == 0)
766 log_syntax(unit, LOG_DEBUG, filename, line, r,
767 "NetDev '%s' specified twice, ignoring.", name);
768 else
769 name = NULL;
770
771 return 0;
772 }
773
774 int config_parse_domains(
775 const char *unit,
776 const char *filename,
777 unsigned line,
778 const char *section,
779 unsigned section_line,
780 const char *lvalue,
781 int ltype,
782 const char *rvalue,
783 void *data,
784 void *userdata) {
785
786 const char *p;
787 Network *n = data;
788 int r;
789
790 assert(n);
791 assert(lvalue);
792 assert(rvalue);
793
794 if (isempty(rvalue)) {
795 n->search_domains = ordered_set_free_free(n->search_domains);
796 n->route_domains = ordered_set_free_free(n->route_domains);
797 return 0;
798 }
799
800 p = rvalue;
801 for (;;) {
802 _cleanup_free_ char *w = NULL, *normalized = NULL;
803 const char *domain;
804 bool is_route;
805
806 r = extract_first_word(&p, &w, NULL, 0);
807 if (r < 0) {
808 log_syntax(unit, LOG_ERR, filename, line, r,
809 "Failed to extract search or route domain, ignoring: %s", rvalue);
810 break;
811 }
812 if (r == 0)
813 break;
814
815 is_route = w[0] == '~';
816 domain = is_route ? w + 1 : w;
817
818 if (dns_name_is_root(domain) || streq(domain, "*")) {
819 /* If the root domain appears as is, or the special token "*" is found, we'll
820 * consider this as routing domain, unconditionally. */
821 is_route = true;
822 domain = "."; /* make sure we don't allow empty strings, thus write the root
823 * domain as "." */
824 } else {
825 r = dns_name_normalize(domain, 0, &normalized);
826 if (r < 0) {
827 log_syntax(unit, LOG_ERR, filename, line, r,
828 "'%s' is not a valid domain name, ignoring.", domain);
829 continue;
830 }
831
832 domain = normalized;
833
834 if (is_localhost(domain)) {
835 log_syntax(unit, LOG_ERR, filename, line, 0,
836 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
837 domain);
838 continue;
839 }
840 }
841
842 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
843 r = ordered_set_ensure_allocated(set, &string_hash_ops);
844 if (r < 0)
845 return r;
846
847 r = ordered_set_put_strdup(*set, domain);
848 if (r < 0)
849 return log_oom();
850 }
851
852 return 0;
853 }
854
855 int config_parse_ipv4ll(
856 const char* unit,
857 const char *filename,
858 unsigned line,
859 const char *section,
860 unsigned section_line,
861 const char *lvalue,
862 int ltype,
863 const char *rvalue,
864 void *data,
865 void *userdata) {
866
867 AddressFamilyBoolean *link_local = data;
868
869 assert(filename);
870 assert(lvalue);
871 assert(rvalue);
872 assert(data);
873
874 /* Note that this is mostly like
875 * config_parse_address_family_boolean(), except that it
876 * applies only to IPv4 */
877
878 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
879
880 return 0;
881 }
882
883 int config_parse_dhcp(
884 const char* unit,
885 const char *filename,
886 unsigned line,
887 const char *section,
888 unsigned section_line,
889 const char *lvalue,
890 int ltype,
891 const char *rvalue,
892 void *data,
893 void *userdata) {
894
895 AddressFamilyBoolean *dhcp = data, s;
896
897 assert(filename);
898 assert(lvalue);
899 assert(rvalue);
900 assert(data);
901
902 /* Note that this is mostly like
903 * config_parse_address_family_boolean(), except that it
904 * understands some old names for the enum values */
905
906 s = address_family_boolean_from_string(rvalue);
907 if (s < 0) {
908
909 /* Previously, we had a slightly different enum here,
910 * support its values for compatbility. */
911
912 if (streq(rvalue, "none"))
913 s = ADDRESS_FAMILY_NO;
914 else if (streq(rvalue, "v4"))
915 s = ADDRESS_FAMILY_IPV4;
916 else if (streq(rvalue, "v6"))
917 s = ADDRESS_FAMILY_IPV6;
918 else if (streq(rvalue, "both"))
919 s = ADDRESS_FAMILY_YES;
920 else {
921 log_syntax(unit, LOG_ERR, filename, line, 0,
922 "Failed to parse DHCP option, ignoring: %s", rvalue);
923 return 0;
924 }
925
926 log_syntax(unit, LOG_WARNING, filename, line, 0,
927 "DHCP=%s is deprecated, please use DHCP=%s instead.",
928 rvalue, address_family_boolean_to_string(s));
929 }
930
931 *dhcp = s;
932 return 0;
933 }
934
935 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
936 [DHCP_CLIENT_ID_MAC] = "mac",
937 [DHCP_CLIENT_ID_DUID] = "duid",
938 [DHCP_CLIENT_ID_DUID_ONLY] = "duid-only",
939 };
940
941 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier);
942 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier,
943 "Failed to parse client identifier type");
944
945 int config_parse_ipv6token(
946 const char* unit,
947 const char *filename,
948 unsigned line,
949 const char *section,
950 unsigned section_line,
951 const char *lvalue,
952 int ltype,
953 const char *rvalue,
954 void *data,
955 void *userdata) {
956
957 union in_addr_union buffer;
958 struct in6_addr *token = data;
959 int r;
960
961 assert(filename);
962 assert(lvalue);
963 assert(rvalue);
964 assert(token);
965
966 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
967 if (r < 0) {
968 log_syntax(unit, LOG_ERR, filename, line, r,
969 "Failed to parse IPv6 token, ignoring: %s", rvalue);
970 return 0;
971 }
972
973 if (in_addr_is_null(AF_INET6, &buffer)) {
974 log_syntax(unit, LOG_ERR, filename, line, 0,
975 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
976 return 0;
977 }
978
979 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
980 log_syntax(unit, LOG_ERR, filename, line, 0,
981 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
982 return 0;
983 }
984
985 *token = buffer.in6;
986
987 return 0;
988 }
989
990 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
991 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
992 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
993 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
994 };
995
996 DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
997
998 int config_parse_ipv6_privacy_extensions(
999 const char* unit,
1000 const char *filename,
1001 unsigned line,
1002 const char *section,
1003 unsigned section_line,
1004 const char *lvalue,
1005 int ltype,
1006 const char *rvalue,
1007 void *data,
1008 void *userdata) {
1009
1010 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
1011 int k;
1012
1013 assert(filename);
1014 assert(lvalue);
1015 assert(rvalue);
1016 assert(ipv6_privacy_extensions);
1017
1018 /* Our enum shall be a superset of booleans, hence first try
1019 * to parse as boolean, and then as enum */
1020
1021 k = parse_boolean(rvalue);
1022 if (k > 0)
1023 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
1024 else if (k == 0)
1025 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
1026 else {
1027 IPv6PrivacyExtensions s;
1028
1029 s = ipv6_privacy_extensions_from_string(rvalue);
1030 if (s < 0) {
1031
1032 if (streq(rvalue, "kernel"))
1033 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
1034 else {
1035 log_syntax(unit, LOG_ERR, filename, line, 0,
1036 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1037 return 0;
1038 }
1039 }
1040
1041 *ipv6_privacy_extensions = s;
1042 }
1043
1044 return 0;
1045 }
1046
1047 int config_parse_hostname(
1048 const char *unit,
1049 const char *filename,
1050 unsigned line,
1051 const char *section,
1052 unsigned section_line,
1053 const char *lvalue,
1054 int ltype,
1055 const char *rvalue,
1056 void *data,
1057 void *userdata) {
1058
1059 _cleanup_free_ char *hn = NULL;
1060 char **hostname = data;
1061 int r;
1062
1063 assert(filename);
1064 assert(lvalue);
1065 assert(rvalue);
1066
1067 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
1068 if (r < 0)
1069 return r;
1070
1071 if (!hostname_is_valid(hn, false)) {
1072 log_syntax(unit, LOG_ERR, filename, line, 0,
1073 "Hostname is not valid, ignoring assignment: %s", rvalue);
1074 return 0;
1075 }
1076
1077 r = dns_name_is_valid(hn);
1078 if (r < 0) {
1079 log_syntax(unit, LOG_ERR, filename, line, r,
1080 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
1081 return 0;
1082 }
1083 if (r == 0) {
1084 log_syntax(unit, LOG_ERR, filename, line, 0,
1085 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
1086 return 0;
1087 }
1088
1089 return free_and_replace(*hostname, hn);
1090 }
1091
1092 int config_parse_timezone(
1093 const char *unit,
1094 const char *filename,
1095 unsigned line,
1096 const char *section,
1097 unsigned section_line,
1098 const char *lvalue,
1099 int ltype,
1100 const char *rvalue,
1101 void *data,
1102 void *userdata) {
1103
1104 _cleanup_free_ char *tz = NULL;
1105 char **datap = data;
1106 int r;
1107
1108 assert(filename);
1109 assert(lvalue);
1110 assert(rvalue);
1111
1112 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1113 if (r < 0)
1114 return r;
1115
1116 if (!timezone_is_valid(tz, LOG_ERR)) {
1117 log_syntax(unit, LOG_ERR, filename, line, 0,
1118 "Timezone is not valid, ignoring assignment: %s", rvalue);
1119 return 0;
1120 }
1121
1122 return free_and_replace(*datap, tz);
1123 }
1124
1125 int config_parse_dhcp_server_dns(
1126 const char *unit,
1127 const char *filename,
1128 unsigned line,
1129 const char *section,
1130 unsigned section_line,
1131 const char *lvalue,
1132 int ltype,
1133 const char *rvalue,
1134 void *data,
1135 void *userdata) {
1136
1137 Network *n = data;
1138 const char *p = rvalue;
1139 int r;
1140
1141 assert(filename);
1142 assert(lvalue);
1143 assert(rvalue);
1144
1145 for (;;) {
1146 _cleanup_free_ char *w = NULL;
1147 struct in_addr a, *m;
1148
1149 r = extract_first_word(&p, &w, NULL, 0);
1150 if (r == -ENOMEM)
1151 return log_oom();
1152 if (r < 0) {
1153 log_syntax(unit, LOG_ERR, filename, line, r,
1154 "Failed to extract word, ignoring: %s", rvalue);
1155 return 0;
1156 }
1157 if (r == 0)
1158 break;
1159
1160 if (inet_pton(AF_INET, w, &a) <= 0) {
1161 log_syntax(unit, LOG_ERR, filename, line, 0,
1162 "Failed to parse DNS server address, ignoring: %s", w);
1163 continue;
1164 }
1165
1166 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
1167 if (!m)
1168 return log_oom();
1169
1170 m[n->n_dhcp_server_dns++] = a;
1171 n->dhcp_server_dns = m;
1172 }
1173
1174 return 0;
1175 }
1176
1177 int config_parse_radv_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 = data;
1190 const char *p = rvalue;
1191 int r;
1192
1193 assert(filename);
1194 assert(lvalue);
1195 assert(rvalue);
1196
1197 for (;;) {
1198 _cleanup_free_ char *w = NULL;
1199 union in_addr_union a;
1200
1201 r = extract_first_word(&p, &w, NULL, 0);
1202 if (r == -ENOMEM)
1203 return log_oom();
1204 if (r < 0) {
1205 log_syntax(unit, LOG_ERR, filename, line, r,
1206 "Failed to extract word, ignoring: %s", rvalue);
1207 return 0;
1208 }
1209 if (r == 0)
1210 break;
1211
1212 if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
1213 struct in6_addr *m;
1214
1215 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1216 if (!m)
1217 return log_oom();
1218
1219 m[n->n_router_dns++] = a.in6;
1220 n->router_dns = m;
1221
1222 } else
1223 log_syntax(unit, LOG_ERR, filename, line, 0,
1224 "Failed to parse DNS server address, ignoring: %s", w);
1225 }
1226
1227 return 0;
1228 }
1229
1230 int config_parse_radv_search_domains(
1231 const char *unit,
1232 const char *filename,
1233 unsigned line,
1234 const char *section,
1235 unsigned section_line,
1236 const char *lvalue,
1237 int ltype,
1238 const char *rvalue,
1239 void *data,
1240 void *userdata) {
1241
1242 Network *n = data;
1243 const char *p = rvalue;
1244 int r;
1245
1246 assert(filename);
1247 assert(lvalue);
1248 assert(rvalue);
1249
1250 for (;;) {
1251 _cleanup_free_ char *w = NULL, *idna = NULL;
1252
1253 r = extract_first_word(&p, &w, NULL, 0);
1254 if (r == -ENOMEM)
1255 return log_oom();
1256 if (r < 0) {
1257 log_syntax(unit, LOG_ERR, filename, line, r,
1258 "Failed to extract word, ignoring: %s", rvalue);
1259 return 0;
1260 }
1261 if (r == 0)
1262 break;
1263
1264 r = dns_name_apply_idna(w, &idna);
1265 if (r < 0) {
1266 log_syntax(unit, LOG_ERR, filename, line, r,
1267 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1268 continue;
1269 } else if (r == 0)
1270 /* transfer ownership to simplify subsequent operations */
1271 idna = TAKE_PTR(w);
1272
1273 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops);
1274 if (r < 0)
1275 return r;
1276
1277 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1278 if (r < 0)
1279 return r;
1280 }
1281
1282 return 0;
1283 }
1284
1285 int config_parse_dhcp_server_ntp(
1286 const char *unit,
1287 const char *filename,
1288 unsigned line,
1289 const char *section,
1290 unsigned section_line,
1291 const char *lvalue,
1292 int ltype,
1293 const char *rvalue,
1294 void *data,
1295 void *userdata) {
1296
1297 Network *n = data;
1298 const char *p = rvalue;
1299 int r;
1300
1301 assert(filename);
1302 assert(lvalue);
1303 assert(rvalue);
1304
1305 for (;;) {
1306 _cleanup_free_ char *w = NULL;
1307 struct in_addr a, *m;
1308
1309 r = extract_first_word(&p, &w, NULL, 0);
1310 if (r == -ENOMEM)
1311 return log_oom();
1312 if (r < 0) {
1313 log_syntax(unit, LOG_ERR, filename, line, r,
1314 "Failed to extract word, ignoring: %s", rvalue);
1315 return 0;
1316 }
1317 if (r == 0)
1318 return 0;
1319
1320 if (inet_pton(AF_INET, w, &a) <= 0) {
1321 log_syntax(unit, LOG_ERR, filename, line, 0,
1322 "Failed to parse NTP server address, ignoring: %s", w);
1323 continue;
1324 }
1325
1326 m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
1327 if (!m)
1328 return log_oom();
1329
1330 m[n->n_dhcp_server_ntp++] = a;
1331 n->dhcp_server_ntp = m;
1332 }
1333 }
1334
1335 int config_parse_dns(
1336 const char *unit,
1337 const char *filename,
1338 unsigned line,
1339 const char *section,
1340 unsigned section_line,
1341 const char *lvalue,
1342 int ltype,
1343 const char *rvalue,
1344 void *data,
1345 void *userdata) {
1346
1347 Network *n = userdata;
1348 int r;
1349
1350 assert(filename);
1351 assert(lvalue);
1352 assert(rvalue);
1353
1354 for (;;) {
1355 _cleanup_free_ char *w = NULL;
1356 union in_addr_union a;
1357 struct in_addr_data *m;
1358 int family;
1359
1360 r = extract_first_word(&rvalue, &w, NULL, 0);
1361 if (r == -ENOMEM)
1362 return log_oom();
1363 if (r < 0) {
1364 log_syntax(unit, LOG_ERR, filename, line, r,
1365 "Invalid syntax, ignoring: %s", rvalue);
1366 break;
1367 }
1368 if (r == 0)
1369 break;
1370
1371 r = in_addr_from_string_auto(w, &family, &a);
1372 if (r < 0) {
1373 log_syntax(unit, LOG_ERR, filename, line, r,
1374 "Failed to parse dns server address, ignoring: %s", w);
1375 continue;
1376 }
1377
1378 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
1379 if (!m)
1380 return log_oom();
1381
1382 m[n->n_dns++] = (struct in_addr_data) {
1383 .family = family,
1384 .address = a,
1385 };
1386
1387 n->dns = m;
1388 }
1389
1390 return 0;
1391 }
1392
1393 int config_parse_dnssec_negative_trust_anchors(
1394 const char *unit,
1395 const char *filename,
1396 unsigned line,
1397 const char *section,
1398 unsigned section_line,
1399 const char *lvalue,
1400 int ltype,
1401 const char *rvalue,
1402 void *data,
1403 void *userdata) {
1404
1405 const char *p = rvalue;
1406 Network *n = data;
1407 int r;
1408
1409 assert(n);
1410 assert(lvalue);
1411 assert(rvalue);
1412
1413 if (isempty(rvalue)) {
1414 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1415 return 0;
1416 }
1417
1418 for (;;) {
1419 _cleanup_free_ char *w = NULL;
1420
1421 r = extract_first_word(&p, &w, NULL, 0);
1422 if (r < 0) {
1423 log_syntax(unit, LOG_ERR, filename, line, r,
1424 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1425 break;
1426 }
1427 if (r == 0)
1428 break;
1429
1430 r = dns_name_is_valid(w);
1431 if (r <= 0) {
1432 log_syntax(unit, LOG_ERR, filename, line, r,
1433 "%s is not a valid domain name, ignoring.", w);
1434 continue;
1435 }
1436
1437 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1438 if (r < 0)
1439 return log_oom();
1440
1441 r = set_put(n->dnssec_negative_trust_anchors, w);
1442 if (r < 0)
1443 return log_oom();
1444 if (r > 0)
1445 w = NULL;
1446 }
1447
1448 return 0;
1449 }
1450
1451 int config_parse_ntp(
1452 const char *unit,
1453 const char *filename,
1454 unsigned line,
1455 const char *section,
1456 unsigned section_line,
1457 const char *lvalue,
1458 int ltype,
1459 const char *rvalue,
1460 void *data,
1461 void *userdata) {
1462
1463 char ***l = data;
1464 int r;
1465
1466 assert(l);
1467 assert(lvalue);
1468 assert(rvalue);
1469
1470 if (isempty(rvalue)) {
1471 *l = strv_free(*l);
1472 return 0;
1473 }
1474
1475 for (;;) {
1476 _cleanup_free_ char *w = NULL;
1477
1478 r = extract_first_word(&rvalue, &w, NULL, 0);
1479 if (r == -ENOMEM)
1480 return log_oom();
1481 if (r < 0) {
1482 log_syntax(unit, LOG_ERR, filename, line, r,
1483 "Failed to extract NTP server name, ignoring: %s", rvalue);
1484 break;
1485 }
1486 if (r == 0)
1487 break;
1488
1489 r = dns_name_is_valid_or_address(w);
1490 if (r <= 0) {
1491 log_syntax(unit, LOG_ERR, filename, line, r,
1492 "%s is not a valid domain name or IP address, ignoring.", w);
1493 continue;
1494 }
1495
1496 if (strv_length(*l) > MAX_NTP_SERVERS) {
1497 log_syntax(unit, LOG_WARNING, filename, line, 0,
1498 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1499 MAX_NTP_SERVERS, w);
1500 break;
1501 }
1502
1503 r = strv_consume(l, TAKE_PTR(w));
1504 if (r < 0)
1505 return log_oom();
1506 }
1507
1508 return 0;
1509 }
1510
1511 int config_parse_dhcp_user_class(
1512 const char *unit,
1513 const char *filename,
1514 unsigned line,
1515 const char *section,
1516 unsigned section_line,
1517 const char *lvalue,
1518 int ltype,
1519 const char *rvalue,
1520 void *data,
1521 void *userdata) {
1522
1523 char ***l = data;
1524 int r;
1525
1526 assert(l);
1527 assert(lvalue);
1528 assert(rvalue);
1529
1530 if (isempty(rvalue)) {
1531 *l = strv_free(*l);
1532 return 0;
1533 }
1534
1535 for (;;) {
1536 _cleanup_free_ char *w = NULL;
1537
1538 r = extract_first_word(&rvalue, &w, NULL, 0);
1539 if (r == -ENOMEM)
1540 return log_oom();
1541 if (r < 0) {
1542 log_syntax(unit, LOG_ERR, filename, line, r,
1543 "Failed to split user classes option, ignoring: %s", rvalue);
1544 break;
1545 }
1546 if (r == 0)
1547 break;
1548
1549 if (strlen(w) > 255) {
1550 log_syntax(unit, LOG_ERR, filename, line, 0,
1551 "%s length is not in the range 1-255, ignoring.", w);
1552 continue;
1553 }
1554
1555 r = strv_push(l, w);
1556 if (r < 0)
1557 return log_oom();
1558
1559 w = NULL;
1560 }
1561
1562 return 0;
1563 }
1564
1565 int config_parse_section_route_table(
1566 const char *unit,
1567 const char *filename,
1568 unsigned line,
1569 const char *section,
1570 unsigned section_line,
1571 const char *lvalue,
1572 int ltype,
1573 const char *rvalue,
1574 void *data,
1575 void *userdata) {
1576
1577 Network *network = data;
1578 uint32_t rt;
1579 int r;
1580
1581 assert(filename);
1582 assert(lvalue);
1583 assert(rvalue);
1584 assert(data);
1585
1586 r = safe_atou32(rvalue, &rt);
1587 if (r < 0) {
1588 log_syntax(unit, LOG_ERR, filename, line, r,
1589 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
1590 return 0;
1591 }
1592
1593 if (streq_ptr(section, "DHCP")) {
1594 network->dhcp_route_table = rt;
1595 network->dhcp_route_table_set = true;
1596 } else { /* section is IPv6AcceptRA */
1597 network->ipv6_accept_ra_route_table = rt;
1598 network->ipv6_accept_ra_route_table_set = true;
1599 }
1600
1601 return 0;
1602 }
1603
1604 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
1605 "Failed to parse DHCP use domains setting");
1606
1607 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1608 [DHCP_USE_DOMAINS_NO] = "no",
1609 [DHCP_USE_DOMAINS_ROUTE] = "route",
1610 [DHCP_USE_DOMAINS_YES] = "yes",
1611 };
1612
1613 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
1614
1615 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1616
1617 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1618 [LLDP_MODE_NO] = "no",
1619 [LLDP_MODE_YES] = "yes",
1620 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1621 };
1622
1623 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
1624
1625 int config_parse_iaid(const char *unit,
1626 const char *filename,
1627 unsigned line,
1628 const char *section,
1629 unsigned section_line,
1630 const char *lvalue,
1631 int ltype,
1632 const char *rvalue,
1633 void *data,
1634 void *userdata) {
1635 Network *network = data;
1636 uint32_t iaid;
1637 int r;
1638
1639 assert(filename);
1640 assert(lvalue);
1641 assert(rvalue);
1642 assert(network);
1643
1644 r = safe_atou32(rvalue, &iaid);
1645 if (r < 0) {
1646 log_syntax(unit, LOG_ERR, filename, line, r,
1647 "Unable to read IAID, ignoring assignment: %s", rvalue);
1648 return 0;
1649 }
1650
1651 network->iaid = iaid;
1652 network->iaid_set = true;
1653
1654 return 0;
1655 }