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