]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #3156 from keszybz/duid-settings
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013 Tom Gundersen <teg@jklm.no>
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <ctype.h>
21 #include <net/if.h>
22
23 #include "alloc-util.h"
24 #include "conf-files.h"
25 #include "conf-parser.h"
26 #include "dns-domain.h"
27 #include "fd-util.h"
28 #include "hostname-util.h"
29 #include "network-internal.h"
30 #include "networkd-network.h"
31 #include "networkd.h"
32 #include "parse-util.h"
33 #include "set.h"
34 #include "stat-util.h"
35 #include "string-table.h"
36 #include "string-util.h"
37 #include "util.h"
38
39 static int network_load_one(Manager *manager, const char *filename) {
40 _cleanup_network_free_ Network *network = NULL;
41 _cleanup_fclose_ FILE *file = NULL;
42 char *d;
43 Route *route;
44 Address *address;
45 int r;
46
47 assert(manager);
48 assert(filename);
49
50 file = fopen(filename, "re");
51 if (!file) {
52 if (errno == ENOENT)
53 return 0;
54 else
55 return -errno;
56 }
57
58 if (null_or_empty_fd(fileno(file))) {
59 log_debug("Skipping empty file: %s", filename);
60 return 0;
61 }
62
63 network = new0(Network, 1);
64 if (!network)
65 return log_oom();
66
67 network->manager = manager;
68
69 LIST_HEAD_INIT(network->static_addresses);
70 LIST_HEAD_INIT(network->static_routes);
71 LIST_HEAD_INIT(network->static_fdb_entries);
72
73 network->stacked_netdevs = hashmap_new(&string_hash_ops);
74 if (!network->stacked_netdevs)
75 return log_oom();
76
77 network->addresses_by_section = hashmap_new(NULL);
78 if (!network->addresses_by_section)
79 return log_oom();
80
81 network->routes_by_section = hashmap_new(NULL);
82 if (!network->routes_by_section)
83 return log_oom();
84
85 network->fdb_entries_by_section = hashmap_new(NULL);
86 if (!network->fdb_entries_by_section)
87 return log_oom();
88
89 network->filename = strdup(filename);
90 if (!network->filename)
91 return log_oom();
92
93 network->name = strdup(basename(filename));
94 if (!network->name)
95 return log_oom();
96
97 d = strrchr(network->name, '.');
98 if (!d)
99 return -EINVAL;
100
101 assert(streq(d, ".network"));
102
103 *d = '\0';
104
105 network->dhcp = ADDRESS_FAMILY_NO;
106 network->dhcp_use_ntp = true;
107 network->dhcp_use_dns = true;
108 network->dhcp_use_hostname = true;
109 network->dhcp_use_routes = true;
110 network->dhcp_send_hostname = true;
111 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
112 network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
113
114 network->dhcp_server_emit_dns = true;
115 network->dhcp_server_emit_ntp = true;
116 network->dhcp_server_emit_timezone = true;
117
118 network->use_bpdu = true;
119 network->allow_port_to_be_root = true;
120 network->unicast_flood = true;
121
122 network->lldp_mode = LLDP_MODE_ROUTERS_ONLY;
123
124 network->llmnr = RESOLVE_SUPPORT_YES;
125 network->mdns = RESOLVE_SUPPORT_NO;
126 network->dnssec_mode = _DNSSEC_MODE_INVALID;
127
128 network->link_local = ADDRESS_FAMILY_IPV6;
129
130 network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
131 network->ipv6_accept_ra = -1;
132 network->ipv6_dad_transmits = -1;
133 network->ipv6_hop_limit = -1;
134 network->duid.type = _DUID_TYPE_INVALID;
135 network->proxy_arp = -1;
136
137 r = config_parse(NULL, filename, file,
138 "Match\0"
139 "Link\0"
140 "Network\0"
141 "Address\0"
142 "Route\0"
143 "DHCP\0"
144 "DHCPv4\0" /* compat */
145 "DHCPServer\0"
146 "Bridge\0"
147 "BridgeFDB\0",
148 config_item_perf_lookup, network_network_gperf_lookup,
149 false, false, true, network);
150 if (r < 0)
151 return r;
152
153 /* IPMasquerade=yes implies IPForward=yes */
154 if (network->ip_masquerade)
155 network->ip_forward |= ADDRESS_FAMILY_IPV4;
156
157 LIST_PREPEND(networks, manager->networks, network);
158
159 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
160 if (r < 0)
161 return r;
162
163 r = hashmap_put(manager->networks_by_name, network->name, network);
164 if (r < 0)
165 return r;
166
167 LIST_FOREACH(routes, route, network->static_routes) {
168 if (!route->family) {
169 log_warning("Route section without Gateway field configured in %s. "
170 "Ignoring", filename);
171 return 0;
172 }
173 }
174
175 LIST_FOREACH(addresses, address, network->static_addresses) {
176 if (!address->family) {
177 log_warning("Address section without Address field configured in %s. "
178 "Ignoring", filename);
179 return 0;
180 }
181 }
182
183 network = NULL;
184
185 return 0;
186 }
187
188 int network_load(Manager *manager) {
189 Network *network;
190 _cleanup_strv_free_ char **files = NULL;
191 char **f;
192 int r;
193
194 assert(manager);
195
196 while ((network = manager->networks))
197 network_free(network);
198
199 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
200 if (r < 0)
201 return log_error_errno(r, "Failed to enumerate network files: %m");
202
203 STRV_FOREACH_BACKWARDS(f, files) {
204 r = network_load_one(manager, *f);
205 if (r < 0)
206 return r;
207 }
208
209 return 0;
210 }
211
212 void network_free(Network *network) {
213 NetDev *netdev;
214 Route *route;
215 Address *address;
216 FdbEntry *fdb_entry;
217 Iterator i;
218
219 if (!network)
220 return;
221
222 free(network->filename);
223
224 free(network->match_mac);
225 strv_free(network->match_path);
226 strv_free(network->match_driver);
227 strv_free(network->match_type);
228 strv_free(network->match_name);
229
230 free(network->description);
231 free(network->dhcp_vendor_class_identifier);
232 free(network->dhcp_hostname);
233
234 free(network->mac);
235
236 strv_free(network->ntp);
237 strv_free(network->dns);
238 strv_free(network->search_domains);
239 strv_free(network->route_domains);
240 strv_free(network->bind_carrier);
241
242 netdev_unref(network->bridge);
243
244 netdev_unref(network->bond);
245
246 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
247 hashmap_remove(network->stacked_netdevs, netdev->ifname);
248 netdev_unref(netdev);
249 }
250 hashmap_free(network->stacked_netdevs);
251
252 while ((route = network->static_routes))
253 route_free(route);
254
255 while ((address = network->static_addresses))
256 address_free(address);
257
258 while ((fdb_entry = network->static_fdb_entries))
259 fdb_entry_free(fdb_entry);
260
261 hashmap_free(network->addresses_by_section);
262 hashmap_free(network->routes_by_section);
263 hashmap_free(network->fdb_entries_by_section);
264
265 if (network->manager) {
266 if (network->manager->networks)
267 LIST_REMOVE(networks, network->manager->networks, network);
268
269 if (network->manager->networks_by_name)
270 hashmap_remove(network->manager->networks_by_name, network->name);
271 }
272
273 free(network->name);
274
275 condition_free_list(network->match_host);
276 condition_free_list(network->match_virt);
277 condition_free_list(network->match_kernel);
278 condition_free_list(network->match_arch);
279
280 free(network->dhcp_server_timezone);
281 free(network->dhcp_server_dns);
282 free(network->dhcp_server_ntp);
283
284 set_free_free(network->dnssec_negative_trust_anchors);
285
286 free(network);
287 }
288
289 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
290 Network *network;
291
292 assert(manager);
293 assert(name);
294 assert(ret);
295
296 network = hashmap_get(manager->networks_by_name, name);
297 if (!network)
298 return -ENOENT;
299
300 *ret = network;
301
302 return 0;
303 }
304
305 int network_get(Manager *manager, struct udev_device *device,
306 const char *ifname, const struct ether_addr *address,
307 Network **ret) {
308 Network *network;
309 struct udev_device *parent;
310 const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
311
312 assert(manager);
313 assert(ret);
314
315 if (device) {
316 path = udev_device_get_property_value(device, "ID_PATH");
317
318 parent = udev_device_get_parent(device);
319 if (parent)
320 parent_driver = udev_device_get_driver(parent);
321
322 driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
323
324 devtype = udev_device_get_devtype(device);
325 }
326
327 LIST_FOREACH(networks, network, manager->networks) {
328 if (net_match_config(network->match_mac, network->match_path,
329 network->match_driver, network->match_type,
330 network->match_name, network->match_host,
331 network->match_virt, network->match_kernel,
332 network->match_arch,
333 address, path, parent_driver, driver,
334 devtype, ifname)) {
335 if (network->match_name && device) {
336 const char *attr;
337 uint8_t name_assign_type = NET_NAME_UNKNOWN;
338
339 attr = udev_device_get_sysattr_value(device, "name_assign_type");
340 if (attr)
341 (void) safe_atou8(attr, &name_assign_type);
342
343 if (name_assign_type == NET_NAME_ENUM)
344 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
345 ifname, network->filename);
346 else
347 log_debug("%s: found matching network '%s'", ifname, network->filename);
348 } else
349 log_debug("%s: found matching network '%s'", ifname, network->filename);
350
351 *ret = network;
352 return 0;
353 }
354 }
355
356 *ret = NULL;
357
358 return -ENOENT;
359 }
360
361 int network_apply(Manager *manager, Network *network, Link *link) {
362 int r;
363
364 assert(manager);
365 assert(network);
366 assert(link);
367
368 link->network = network;
369
370 if (network->ipv4ll_route) {
371 Route *route;
372
373 r = route_new_static(network, 0, &route);
374 if (r < 0)
375 return r;
376
377 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
378 if (r == 0)
379 return -EINVAL;
380 if (r < 0)
381 return -errno;
382
383 route->family = AF_INET;
384 route->dst_prefixlen = 16;
385 route->scope = RT_SCOPE_LINK;
386 route->priority = IPV4LL_ROUTE_METRIC;
387 route->protocol = RTPROT_STATIC;
388 }
389
390 if (!strv_isempty(network->dns) ||
391 !strv_isempty(network->ntp) ||
392 !strv_isempty(network->search_domains) ||
393 !strv_isempty(network->route_domains)) {
394 manager_dirty(manager);
395 link_dirty(link);
396 }
397
398 return 0;
399 }
400
401 bool network_has_static_ipv6_addresses(Network *network) {
402 Address *address;
403
404 assert(network);
405
406 LIST_FOREACH(addresses, address, network->static_addresses) {
407 if (address->family == AF_INET6)
408 return true;
409 }
410
411 return false;
412 }
413
414 int config_parse_netdev(const char *unit,
415 const char *filename,
416 unsigned line,
417 const char *section,
418 unsigned section_line,
419 const char *lvalue,
420 int ltype,
421 const char *rvalue,
422 void *data,
423 void *userdata) {
424 Network *network = userdata;
425 _cleanup_free_ char *kind_string = NULL;
426 char *p;
427 NetDev *netdev;
428 NetDevKind kind;
429 int r;
430
431 assert(filename);
432 assert(lvalue);
433 assert(rvalue);
434 assert(data);
435
436 kind_string = strdup(lvalue);
437 if (!kind_string)
438 return log_oom();
439
440 /* the keys are CamelCase versions of the kind */
441 for (p = kind_string; *p; p++)
442 *p = tolower(*p);
443
444 kind = netdev_kind_from_string(kind_string);
445 if (kind == _NETDEV_KIND_INVALID) {
446 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
447 return 0;
448 }
449
450 r = netdev_get(network->manager, rvalue, &netdev);
451 if (r < 0) {
452 log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
453 return 0;
454 }
455
456 if (netdev->kind != kind) {
457 log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
458 return 0;
459 }
460
461 switch (kind) {
462 case NETDEV_KIND_BRIDGE:
463 network->bridge = netdev;
464
465 break;
466 case NETDEV_KIND_BOND:
467 network->bond = netdev;
468
469 break;
470 case NETDEV_KIND_VLAN:
471 case NETDEV_KIND_MACVLAN:
472 case NETDEV_KIND_MACVTAP:
473 case NETDEV_KIND_IPVLAN:
474 case NETDEV_KIND_VXLAN:
475 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
476 if (r < 0) {
477 log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue);
478 return 0;
479 }
480
481 break;
482 default:
483 assert_not_reached("Can not parse NetDev");
484 }
485
486 netdev_ref(netdev);
487
488 return 0;
489 }
490
491 int config_parse_domains(
492 const char *unit,
493 const char *filename,
494 unsigned line,
495 const char *section,
496 unsigned section_line,
497 const char *lvalue,
498 int ltype,
499 const char *rvalue,
500 void *data,
501 void *userdata) {
502
503 const char *p;
504 Network *n = data;
505 int r;
506
507 assert(n);
508 assert(lvalue);
509 assert(rvalue);
510
511 if (isempty(rvalue)) {
512 n->search_domains = strv_free(n->search_domains);
513 n->route_domains = strv_free(n->route_domains);
514 return 0;
515 }
516
517 p = rvalue;
518 for (;;) {
519 _cleanup_free_ char *w = NULL, *normalized = NULL;
520 const char *domain;
521 bool is_route;
522
523 r = extract_first_word(&p, &w, NULL, 0);
524 if (r < 0) {
525 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
526 break;
527 }
528 if (r == 0)
529 break;
530
531 is_route = w[0] == '~';
532 domain = is_route ? w + 1 : w;
533
534 if (dns_name_is_root(domain) || streq(domain, "*")) {
535 /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
536 * routing domain, unconditionally. */
537 is_route = true;
538 domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
539
540 } else {
541 r = dns_name_normalize(domain, &normalized);
542 if (r < 0) {
543 log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
544 continue;
545 }
546
547 domain = normalized;
548
549 if (is_localhost(domain)) {
550 log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain);
551 continue;
552 }
553 }
554
555 if (is_route) {
556 r = strv_extend(&n->route_domains, domain);
557 if (r < 0)
558 return log_oom();
559
560 } else {
561 r = strv_extend(&n->search_domains, domain);
562 if (r < 0)
563 return log_oom();
564 }
565 }
566
567 strv_uniq(n->route_domains);
568 strv_uniq(n->search_domains);
569
570 return 0;
571 }
572
573 int config_parse_tunnel(const char *unit,
574 const char *filename,
575 unsigned line,
576 const char *section,
577 unsigned section_line,
578 const char *lvalue,
579 int ltype,
580 const char *rvalue,
581 void *data,
582 void *userdata) {
583 Network *network = userdata;
584 NetDev *netdev;
585 int r;
586
587 assert(filename);
588 assert(lvalue);
589 assert(rvalue);
590 assert(data);
591
592 r = netdev_get(network->manager, rvalue, &netdev);
593 if (r < 0) {
594 log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
595 return 0;
596 }
597
598 if (netdev->kind != NETDEV_KIND_IPIP &&
599 netdev->kind != NETDEV_KIND_SIT &&
600 netdev->kind != NETDEV_KIND_GRE &&
601 netdev->kind != NETDEV_KIND_GRETAP &&
602 netdev->kind != NETDEV_KIND_IP6GRE &&
603 netdev->kind != NETDEV_KIND_IP6GRETAP &&
604 netdev->kind != NETDEV_KIND_VTI &&
605 netdev->kind != NETDEV_KIND_VTI6 &&
606 netdev->kind != NETDEV_KIND_IP6TNL
607 ) {
608 log_syntax(unit, LOG_ERR, filename, line, 0,
609 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
610 return 0;
611 }
612
613 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
614 if (r < 0) {
615 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
616 return 0;
617 }
618
619 netdev_ref(netdev);
620
621 return 0;
622 }
623
624 int config_parse_ipv4ll(
625 const char* unit,
626 const char *filename,
627 unsigned line,
628 const char *section,
629 unsigned section_line,
630 const char *lvalue,
631 int ltype,
632 const char *rvalue,
633 void *data,
634 void *userdata) {
635
636 AddressFamilyBoolean *link_local = data;
637
638 assert(filename);
639 assert(lvalue);
640 assert(rvalue);
641 assert(data);
642
643 /* Note that this is mostly like
644 * config_parse_address_family_boolean(), except that it
645 * applies only to IPv4 */
646
647 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
648
649 return 0;
650 }
651
652 int config_parse_dhcp(
653 const char* unit,
654 const char *filename,
655 unsigned line,
656 const char *section,
657 unsigned section_line,
658 const char *lvalue,
659 int ltype,
660 const char *rvalue,
661 void *data,
662 void *userdata) {
663
664 AddressFamilyBoolean *dhcp = data, s;
665
666 assert(filename);
667 assert(lvalue);
668 assert(rvalue);
669 assert(data);
670
671 /* Note that this is mostly like
672 * config_parse_address_family_boolean(), except that it
673 * understands some old names for the enum values */
674
675 s = address_family_boolean_from_string(rvalue);
676 if (s < 0) {
677
678 /* Previously, we had a slightly different enum here,
679 * support its values for compatbility. */
680
681 if (streq(rvalue, "none"))
682 s = ADDRESS_FAMILY_NO;
683 else if (streq(rvalue, "v4"))
684 s = ADDRESS_FAMILY_IPV4;
685 else if (streq(rvalue, "v6"))
686 s = ADDRESS_FAMILY_IPV6;
687 else if (streq(rvalue, "both"))
688 s = ADDRESS_FAMILY_YES;
689 else {
690 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
691 return 0;
692 }
693 }
694
695 *dhcp = s;
696 return 0;
697 }
698
699 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
700 [DHCP_CLIENT_ID_MAC] = "mac",
701 [DHCP_CLIENT_ID_DUID] = "duid"
702 };
703
704 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
705 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
706
707 int config_parse_ipv6token(
708 const char* unit,
709 const char *filename,
710 unsigned line,
711 const char *section,
712 unsigned section_line,
713 const char *lvalue,
714 int ltype,
715 const char *rvalue,
716 void *data,
717 void *userdata) {
718
719 union in_addr_union buffer;
720 struct in6_addr *token = data;
721 int r;
722
723 assert(filename);
724 assert(lvalue);
725 assert(rvalue);
726 assert(token);
727
728 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
729 if (r < 0) {
730 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
731 return 0;
732 }
733
734 r = in_addr_is_null(AF_INET6, &buffer);
735 if (r != 0) {
736 log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
737 return 0;
738 }
739
740 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
741 log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
742 return 0;
743 }
744
745 *token = buffer.in6;
746
747 return 0;
748 }
749
750 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
751 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
752 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
753 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
754 };
755
756 DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
757
758 int config_parse_ipv6_privacy_extensions(
759 const char* unit,
760 const char *filename,
761 unsigned line,
762 const char *section,
763 unsigned section_line,
764 const char *lvalue,
765 int ltype,
766 const char *rvalue,
767 void *data,
768 void *userdata) {
769
770 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
771 int k;
772
773 assert(filename);
774 assert(lvalue);
775 assert(rvalue);
776 assert(ipv6_privacy_extensions);
777
778 /* Our enum shall be a superset of booleans, hence first try
779 * to parse as boolean, and then as enum */
780
781 k = parse_boolean(rvalue);
782 if (k > 0)
783 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
784 else if (k == 0)
785 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
786 else {
787 IPv6PrivacyExtensions s;
788
789 s = ipv6_privacy_extensions_from_string(rvalue);
790 if (s < 0) {
791
792 if (streq(rvalue, "kernel"))
793 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
794 else {
795 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
796 return 0;
797 }
798 }
799
800 *ipv6_privacy_extensions = s;
801 }
802
803 return 0;
804 }
805
806 int config_parse_hostname(
807 const char *unit,
808 const char *filename,
809 unsigned line,
810 const char *section,
811 unsigned section_line,
812 const char *lvalue,
813 int ltype,
814 const char *rvalue,
815 void *data,
816 void *userdata) {
817
818 char **hostname = data, *hn = NULL;
819 int r;
820
821 assert(filename);
822 assert(lvalue);
823 assert(rvalue);
824
825 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
826 if (r < 0)
827 return r;
828
829 if (!hostname_is_valid(hn, false)) {
830 log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
831 free(hn);
832 return 0;
833 }
834
835 free(*hostname);
836 *hostname = hostname_cleanup(hn);
837 return 0;
838 }
839
840 int config_parse_timezone(
841 const char *unit,
842 const char *filename,
843 unsigned line,
844 const char *section,
845 unsigned section_line,
846 const char *lvalue,
847 int ltype,
848 const char *rvalue,
849 void *data,
850 void *userdata) {
851
852 char **datap = data, *tz = NULL;
853 int r;
854
855 assert(filename);
856 assert(lvalue);
857 assert(rvalue);
858
859 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
860 if (r < 0)
861 return r;
862
863 if (!timezone_is_valid(tz)) {
864 log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
865 free(tz);
866 return 0;
867 }
868
869 free(*datap);
870 *datap = tz;
871
872 return 0;
873 }
874
875 int config_parse_dhcp_server_dns(
876 const char *unit,
877 const char *filename,
878 unsigned line,
879 const char *section,
880 unsigned section_line,
881 const char *lvalue,
882 int ltype,
883 const char *rvalue,
884 void *data,
885 void *userdata) {
886
887 Network *n = data;
888 const char *p = rvalue;
889 int r;
890
891 assert(filename);
892 assert(lvalue);
893 assert(rvalue);
894
895 for (;;) {
896 _cleanup_free_ char *w = NULL;
897 struct in_addr a, *m;
898
899 r = extract_first_word(&p, &w, NULL, 0);
900 if (r < 0) {
901 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
902 return 0;
903 }
904
905 if (r == 0)
906 return 0;
907
908 if (inet_pton(AF_INET, w, &a) <= 0) {
909 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
910 continue;
911 }
912
913 m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr));
914 if (!m)
915 return log_oom();
916
917 m[n->n_dhcp_server_dns++] = a;
918 n->dhcp_server_dns = m;
919 }
920 }
921
922 int config_parse_dhcp_server_ntp(
923 const char *unit,
924 const char *filename,
925 unsigned line,
926 const char *section,
927 unsigned section_line,
928 const char *lvalue,
929 int ltype,
930 const char *rvalue,
931 void *data,
932 void *userdata) {
933
934 Network *n = data;
935 const char *p = rvalue;
936 int r;
937
938 assert(filename);
939 assert(lvalue);
940 assert(rvalue);
941
942 for (;;) {
943 _cleanup_free_ char *w = NULL;
944 struct in_addr a, *m;
945
946 r = extract_first_word(&p, &w, NULL, 0);
947 if (r < 0) {
948 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
949 return 0;
950 }
951
952 if (r == 0)
953 return 0;
954
955 if (inet_pton(AF_INET, w, &a) <= 0) {
956 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w);
957 continue;
958 }
959
960 m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr));
961 if (!m)
962 return log_oom();
963
964 m[n->n_dhcp_server_ntp++] = a;
965 n->dhcp_server_ntp = m;
966 }
967 }
968
969 int config_parse_dnssec_negative_trust_anchors(
970 const char *unit,
971 const char *filename,
972 unsigned line,
973 const char *section,
974 unsigned section_line,
975 const char *lvalue,
976 int ltype,
977 const char *rvalue,
978 void *data,
979 void *userdata) {
980
981 const char *p = rvalue;
982 Network *n = data;
983 int r;
984
985 assert(n);
986 assert(lvalue);
987 assert(rvalue);
988
989 if (isempty(rvalue)) {
990 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
991 return 0;
992 }
993
994 for (;;) {
995 _cleanup_free_ char *w = NULL;
996
997 r = extract_first_word(&p, &w, NULL, 0);
998 if (r < 0) {
999 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1000 break;
1001 }
1002 if (r == 0)
1003 break;
1004
1005 r = dns_name_is_valid(w);
1006 if (r <= 0) {
1007 log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
1008 continue;
1009 }
1010
1011 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1012 if (r < 0)
1013 return log_oom();
1014
1015 r = set_put(n->dnssec_negative_trust_anchors, w);
1016 if (r < 0)
1017 return log_oom();
1018 if (r > 0)
1019 w = NULL;
1020 }
1021
1022 return 0;
1023 }
1024
1025 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
1026
1027 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1028 [DHCP_USE_DOMAINS_NO] = "no",
1029 [DHCP_USE_DOMAINS_ROUTE] = "route",
1030 [DHCP_USE_DOMAINS_YES] = "yes",
1031 };
1032
1033 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
1034
1035 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1036
1037 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1038 [LLDP_MODE_NO] = "no",
1039 [LLDP_MODE_YES] = "yes",
1040 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1041 };
1042
1043 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);