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