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