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