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