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