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