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