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