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