]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #5276 from poettering/resolved-cname
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013 Tom Gundersen <teg@jklm.no>
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <ctype.h>
21 #include <net/if.h>
22
23 #include "alloc-util.h"
24 #include "conf-files.h"
25 #include "conf-parser.h"
26 #include "dns-domain.h"
27 #include "fd-util.h"
28 #include "hostname-util.h"
29 #include "network-internal.h"
30 #include "networkd-manager.h"
31 #include "networkd-network.h"
32 #include "parse-util.h"
33 #include "set.h"
34 #include "stat-util.h"
35 #include "string-table.h"
36 #include "string-util.h"
37 #include "util.h"
38
39 static int network_load_one(Manager *manager, const char *filename) {
40 _cleanup_network_free_ Network *network = NULL;
41 _cleanup_fclose_ FILE *file = NULL;
42 char *d;
43 const char *dropin_dirname;
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
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 LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
74
75 network->stacked_netdevs = hashmap_new(&string_hash_ops);
76 if (!network->stacked_netdevs)
77 return log_oom();
78
79 network->addresses_by_section = hashmap_new(NULL);
80 if (!network->addresses_by_section)
81 return log_oom();
82
83 network->routes_by_section = hashmap_new(NULL);
84 if (!network->routes_by_section)
85 return log_oom();
86
87 network->fdb_entries_by_section = hashmap_new(NULL);
88 if (!network->fdb_entries_by_section)
89 return log_oom();
90
91 network->filename = strdup(filename);
92 if (!network->filename)
93 return log_oom();
94
95 network->name = strdup(basename(filename));
96 if (!network->name)
97 return log_oom();
98
99 d = strrchr(network->name, '.');
100 if (!d)
101 return -EINVAL;
102
103 assert(streq(d, ".network"));
104
105 *d = '\0';
106
107 network->dhcp = ADDRESS_FAMILY_NO;
108 network->dhcp_use_ntp = true;
109 network->dhcp_use_dns = true;
110 network->dhcp_use_hostname = true;
111 network->dhcp_use_routes = true;
112 network->dhcp_send_hostname = true;
113 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
114 network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
115 network->dhcp_route_table = RT_TABLE_MAIN;
116
117 network->dhcp_server_emit_dns = true;
118 network->dhcp_server_emit_ntp = true;
119 network->dhcp_server_emit_router = true;
120 network->dhcp_server_emit_timezone = true;
121
122 network->use_bpdu = true;
123 network->allow_port_to_be_root = true;
124 network->unicast_flood = true;
125
126 network->lldp_mode = LLDP_MODE_ROUTERS_ONLY;
127
128 network->llmnr = RESOLVE_SUPPORT_YES;
129 network->mdns = RESOLVE_SUPPORT_NO;
130 network->dnssec_mode = _DNSSEC_MODE_INVALID;
131
132 network->link_local = ADDRESS_FAMILY_IPV6;
133
134 network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
135 network->ipv6_accept_ra = -1;
136 network->ipv6_dad_transmits = -1;
137 network->ipv6_hop_limit = -1;
138 network->duid.type = _DUID_TYPE_INVALID;
139 network->proxy_arp = -1;
140 network->arp = -1;
141 network->ipv6_accept_ra_use_dns = true;
142 network->ipv6_accept_ra_route_table = RT_TABLE_MAIN;
143
144 dropin_dirname = strjoina(network->name, ".network.d");
145
146 r = config_parse_many(filename, network_dirs, dropin_dirname,
147 "Match\0"
148 "Link\0"
149 "Network\0"
150 "Address\0"
151 "Route\0"
152 "DHCP\0"
153 "DHCPv4\0" /* compat */
154 "DHCPServer\0"
155 "IPv6AcceptRA\0"
156 "IPv6NDPProxyAddress\0"
157 "Bridge\0"
158 "BridgeFDB\0"
159 "BridgeVLAN\0",
160 config_item_perf_lookup, network_network_gperf_lookup,
161 false, network);
162 if (r < 0)
163 return r;
164
165 /* IPMasquerade=yes implies IPForward=yes */
166 if (network->ip_masquerade)
167 network->ip_forward |= ADDRESS_FAMILY_IPV4;
168
169 LIST_PREPEND(networks, manager->networks, network);
170
171 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
172 if (r < 0)
173 return r;
174
175 r = hashmap_put(manager->networks_by_name, network->name, network);
176 if (r < 0)
177 return r;
178
179 LIST_FOREACH(routes, route, network->static_routes) {
180 if (!route->family) {
181 log_warning("Route section without Gateway field configured in %s. "
182 "Ignoring", filename);
183 return 0;
184 }
185 }
186
187 LIST_FOREACH(addresses, address, network->static_addresses) {
188 if (!address->family) {
189 log_warning("Address section without Address field configured in %s. "
190 "Ignoring", filename);
191 return 0;
192 }
193 }
194
195 network = NULL;
196
197 return 0;
198 }
199
200 int network_load(Manager *manager) {
201 Network *network;
202 _cleanup_strv_free_ char **files = NULL;
203 char **f;
204 int r;
205
206 assert(manager);
207
208 while ((network = manager->networks))
209 network_free(network);
210
211 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
212 if (r < 0)
213 return log_error_errno(r, "Failed to enumerate network files: %m");
214
215 STRV_FOREACH_BACKWARDS(f, files) {
216 r = network_load_one(manager, *f);
217 if (r < 0)
218 return r;
219 }
220
221 return 0;
222 }
223
224 void network_free(Network *network) {
225 NetDev *netdev;
226 Route *route;
227 Address *address;
228 FdbEntry *fdb_entry;
229 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
230 Iterator i;
231
232 if (!network)
233 return;
234
235 free(network->filename);
236
237 free(network->match_mac);
238 strv_free(network->match_path);
239 strv_free(network->match_driver);
240 strv_free(network->match_type);
241 strv_free(network->match_name);
242
243 free(network->description);
244 free(network->dhcp_vendor_class_identifier);
245 free(network->dhcp_hostname);
246
247 free(network->mac);
248
249 strv_free(network->ntp);
250 free(network->dns);
251 strv_free(network->search_domains);
252 strv_free(network->route_domains);
253 strv_free(network->bind_carrier);
254
255 netdev_unref(network->bridge);
256 netdev_unref(network->bond);
257 netdev_unref(network->vrf);
258
259 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
260 hashmap_remove(network->stacked_netdevs, netdev->ifname);
261 netdev_unref(netdev);
262 }
263 hashmap_free(network->stacked_netdevs);
264
265 while ((route = network->static_routes))
266 route_free(route);
267
268 while ((address = network->static_addresses))
269 address_free(address);
270
271 while ((fdb_entry = network->static_fdb_entries))
272 fdb_entry_free(fdb_entry);
273
274 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
275 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
276
277 hashmap_free(network->addresses_by_section);
278 hashmap_free(network->routes_by_section);
279 hashmap_free(network->fdb_entries_by_section);
280
281 if (network->manager) {
282 if (network->manager->networks)
283 LIST_REMOVE(networks, network->manager->networks, network);
284
285 if (network->manager->networks_by_name)
286 hashmap_remove(network->manager->networks_by_name, network->name);
287 }
288
289 free(network->name);
290
291 condition_free_list(network->match_host);
292 condition_free_list(network->match_virt);
293 condition_free_list(network->match_kernel);
294 condition_free_list(network->match_arch);
295
296 free(network->dhcp_server_timezone);
297 free(network->dhcp_server_dns);
298 free(network->dhcp_server_ntp);
299
300 set_free_free(network->dnssec_negative_trust_anchors);
301
302 free(network);
303 }
304
305 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
306 Network *network;
307
308 assert(manager);
309 assert(name);
310 assert(ret);
311
312 network = hashmap_get(manager->networks_by_name, name);
313 if (!network)
314 return -ENOENT;
315
316 *ret = network;
317
318 return 0;
319 }
320
321 int network_get(Manager *manager, struct udev_device *device,
322 const char *ifname, const struct ether_addr *address,
323 Network **ret) {
324 Network *network;
325 struct udev_device *parent;
326 const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
327
328 assert(manager);
329 assert(ret);
330
331 if (device) {
332 path = udev_device_get_property_value(device, "ID_PATH");
333
334 parent = udev_device_get_parent(device);
335 if (parent)
336 parent_driver = udev_device_get_driver(parent);
337
338 driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
339
340 devtype = udev_device_get_devtype(device);
341 }
342
343 LIST_FOREACH(networks, network, manager->networks) {
344 if (net_match_config(network->match_mac, network->match_path,
345 network->match_driver, network->match_type,
346 network->match_name, network->match_host,
347 network->match_virt, network->match_kernel,
348 network->match_arch,
349 address, path, parent_driver, driver,
350 devtype, ifname)) {
351 if (network->match_name && device) {
352 const char *attr;
353 uint8_t name_assign_type = NET_NAME_UNKNOWN;
354
355 attr = udev_device_get_sysattr_value(device, "name_assign_type");
356 if (attr)
357 (void) safe_atou8(attr, &name_assign_type);
358
359 if (name_assign_type == NET_NAME_ENUM)
360 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
361 ifname, network->filename);
362 else
363 log_debug("%s: found matching network '%s'", ifname, network->filename);
364 } else
365 log_debug("%s: found matching network '%s'", ifname, network->filename);
366
367 *ret = network;
368 return 0;
369 }
370 }
371
372 *ret = NULL;
373
374 return -ENOENT;
375 }
376
377 int network_apply(Network *network, Link *link) {
378 int r;
379
380 assert(network);
381 assert(link);
382
383 link->network = network;
384
385 if (network->ipv4ll_route) {
386 Route *route;
387
388 r = route_new_static(network, 0, &route);
389 if (r < 0)
390 return r;
391
392 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
393 if (r == 0)
394 return -EINVAL;
395 if (r < 0)
396 return -errno;
397
398 route->family = AF_INET;
399 route->dst_prefixlen = 16;
400 route->scope = RT_SCOPE_LINK;
401 route->priority = IPV4LL_ROUTE_METRIC;
402 route->protocol = RTPROT_STATIC;
403 }
404
405 if (network->n_dns > 0 ||
406 !strv_isempty(network->ntp) ||
407 !strv_isempty(network->search_domains) ||
408 !strv_isempty(network->route_domains))
409 link_dirty(link);
410
411 return 0;
412 }
413
414 bool network_has_static_ipv6_addresses(Network *network) {
415 Address *address;
416
417 assert(network);
418
419 LIST_FOREACH(addresses, address, network->static_addresses) {
420 if (address->family == AF_INET6)
421 return true;
422 }
423
424 return false;
425 }
426
427 int config_parse_netdev(const char *unit,
428 const char *filename,
429 unsigned line,
430 const char *section,
431 unsigned section_line,
432 const char *lvalue,
433 int ltype,
434 const char *rvalue,
435 void *data,
436 void *userdata) {
437 Network *network = userdata;
438 _cleanup_free_ char *kind_string = NULL;
439 char *p;
440 NetDev *netdev;
441 NetDevKind kind;
442 int r;
443
444 assert(filename);
445 assert(lvalue);
446 assert(rvalue);
447 assert(data);
448
449 kind_string = strdup(lvalue);
450 if (!kind_string)
451 return log_oom();
452
453 /* the keys are CamelCase versions of the kind */
454 for (p = kind_string; *p; p++)
455 *p = tolower(*p);
456
457 kind = netdev_kind_from_string(kind_string);
458 if (kind == _NETDEV_KIND_INVALID) {
459 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
460 return 0;
461 }
462
463 r = netdev_get(network->manager, rvalue, &netdev);
464 if (r < 0) {
465 log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
466 return 0;
467 }
468
469 if (netdev->kind != kind) {
470 log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
471 return 0;
472 }
473
474 switch (kind) {
475 case NETDEV_KIND_BRIDGE:
476 network->bridge = netdev;
477
478 break;
479 case NETDEV_KIND_BOND:
480 network->bond = netdev;
481
482 break;
483 case NETDEV_KIND_VRF:
484 network->vrf = netdev;
485
486 break;
487 case NETDEV_KIND_VLAN:
488 case NETDEV_KIND_MACVLAN:
489 case NETDEV_KIND_MACVTAP:
490 case NETDEV_KIND_IPVLAN:
491 case NETDEV_KIND_VXLAN:
492 case NETDEV_KIND_VCAN:
493 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
494 if (r < 0) {
495 log_syntax(unit, LOG_ERR, filename, line, r, "Can not add NetDev '%s' to network: %m", rvalue);
496 return 0;
497 }
498
499 break;
500 default:
501 assert_not_reached("Can not parse NetDev");
502 }
503
504 netdev_ref(netdev);
505
506 return 0;
507 }
508
509 int config_parse_domains(
510 const char *unit,
511 const char *filename,
512 unsigned line,
513 const char *section,
514 unsigned section_line,
515 const char *lvalue,
516 int ltype,
517 const char *rvalue,
518 void *data,
519 void *userdata) {
520
521 const char *p;
522 Network *n = data;
523 int r;
524
525 assert(n);
526 assert(lvalue);
527 assert(rvalue);
528
529 if (isempty(rvalue)) {
530 n->search_domains = strv_free(n->search_domains);
531 n->route_domains = strv_free(n->route_domains);
532 return 0;
533 }
534
535 p = rvalue;
536 for (;;) {
537 _cleanup_free_ char *w = NULL, *normalized = NULL;
538 const char *domain;
539 bool is_route;
540
541 r = extract_first_word(&p, &w, NULL, 0);
542 if (r < 0) {
543 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
544 break;
545 }
546 if (r == 0)
547 break;
548
549 is_route = w[0] == '~';
550 domain = is_route ? w + 1 : w;
551
552 if (dns_name_is_root(domain) || streq(domain, "*")) {
553 /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
554 * routing domain, unconditionally. */
555 is_route = true;
556 domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
557
558 } else {
559 r = dns_name_normalize(domain, &normalized);
560 if (r < 0) {
561 log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
562 continue;
563 }
564
565 domain = normalized;
566
567 if (is_localhost(domain)) {
568 log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain);
569 continue;
570 }
571 }
572
573 if (is_route) {
574 r = strv_extend(&n->route_domains, domain);
575 if (r < 0)
576 return log_oom();
577
578 } else {
579 r = strv_extend(&n->search_domains, domain);
580 if (r < 0)
581 return log_oom();
582 }
583 }
584
585 strv_uniq(n->route_domains);
586 strv_uniq(n->search_domains);
587
588 return 0;
589 }
590
591 int config_parse_tunnel(const char *unit,
592 const char *filename,
593 unsigned line,
594 const char *section,
595 unsigned section_line,
596 const char *lvalue,
597 int ltype,
598 const char *rvalue,
599 void *data,
600 void *userdata) {
601 Network *network = userdata;
602 NetDev *netdev;
603 int r;
604
605 assert(filename);
606 assert(lvalue);
607 assert(rvalue);
608 assert(data);
609
610 r = netdev_get(network->manager, rvalue, &netdev);
611 if (r < 0) {
612 log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
613 return 0;
614 }
615
616 if (netdev->kind != NETDEV_KIND_IPIP &&
617 netdev->kind != NETDEV_KIND_SIT &&
618 netdev->kind != NETDEV_KIND_GRE &&
619 netdev->kind != NETDEV_KIND_GRETAP &&
620 netdev->kind != NETDEV_KIND_IP6GRE &&
621 netdev->kind != NETDEV_KIND_IP6GRETAP &&
622 netdev->kind != NETDEV_KIND_VTI &&
623 netdev->kind != NETDEV_KIND_VTI6 &&
624 netdev->kind != NETDEV_KIND_IP6TNL
625 ) {
626 log_syntax(unit, LOG_ERR, filename, line, 0,
627 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
628 return 0;
629 }
630
631 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
632 if (r < 0) {
633 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
634 return 0;
635 }
636
637 netdev_ref(netdev);
638
639 return 0;
640 }
641
642 int config_parse_ipv4ll(
643 const char* unit,
644 const char *filename,
645 unsigned line,
646 const char *section,
647 unsigned section_line,
648 const char *lvalue,
649 int ltype,
650 const char *rvalue,
651 void *data,
652 void *userdata) {
653
654 AddressFamilyBoolean *link_local = data;
655
656 assert(filename);
657 assert(lvalue);
658 assert(rvalue);
659 assert(data);
660
661 /* Note that this is mostly like
662 * config_parse_address_family_boolean(), except that it
663 * applies only to IPv4 */
664
665 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
666
667 return 0;
668 }
669
670 int config_parse_dhcp(
671 const char* unit,
672 const char *filename,
673 unsigned line,
674 const char *section,
675 unsigned section_line,
676 const char *lvalue,
677 int ltype,
678 const char *rvalue,
679 void *data,
680 void *userdata) {
681
682 AddressFamilyBoolean *dhcp = data, s;
683
684 assert(filename);
685 assert(lvalue);
686 assert(rvalue);
687 assert(data);
688
689 /* Note that this is mostly like
690 * config_parse_address_family_boolean(), except that it
691 * understands some old names for the enum values */
692
693 s = address_family_boolean_from_string(rvalue);
694 if (s < 0) {
695
696 /* Previously, we had a slightly different enum here,
697 * support its values for compatbility. */
698
699 if (streq(rvalue, "none"))
700 s = ADDRESS_FAMILY_NO;
701 else if (streq(rvalue, "v4"))
702 s = ADDRESS_FAMILY_IPV4;
703 else if (streq(rvalue, "v6"))
704 s = ADDRESS_FAMILY_IPV6;
705 else if (streq(rvalue, "both"))
706 s = ADDRESS_FAMILY_YES;
707 else {
708 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
709 return 0;
710 }
711 }
712
713 *dhcp = s;
714 return 0;
715 }
716
717 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
718 [DHCP_CLIENT_ID_MAC] = "mac",
719 [DHCP_CLIENT_ID_DUID] = "duid"
720 };
721
722 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
723 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
724
725 int config_parse_ipv6token(
726 const char* unit,
727 const char *filename,
728 unsigned line,
729 const char *section,
730 unsigned section_line,
731 const char *lvalue,
732 int ltype,
733 const char *rvalue,
734 void *data,
735 void *userdata) {
736
737 union in_addr_union buffer;
738 struct in6_addr *token = data;
739 int r;
740
741 assert(filename);
742 assert(lvalue);
743 assert(rvalue);
744 assert(token);
745
746 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
747 if (r < 0) {
748 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
749 return 0;
750 }
751
752 r = in_addr_is_null(AF_INET6, &buffer);
753 if (r != 0) {
754 log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
755 return 0;
756 }
757
758 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
759 log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
760 return 0;
761 }
762
763 *token = buffer.in6;
764
765 return 0;
766 }
767
768 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
769 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
770 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
771 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
772 };
773
774 DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
775
776 int config_parse_ipv6_privacy_extensions(
777 const char* unit,
778 const char *filename,
779 unsigned line,
780 const char *section,
781 unsigned section_line,
782 const char *lvalue,
783 int ltype,
784 const char *rvalue,
785 void *data,
786 void *userdata) {
787
788 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
789 int k;
790
791 assert(filename);
792 assert(lvalue);
793 assert(rvalue);
794 assert(ipv6_privacy_extensions);
795
796 /* Our enum shall be a superset of booleans, hence first try
797 * to parse as boolean, and then as enum */
798
799 k = parse_boolean(rvalue);
800 if (k > 0)
801 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
802 else if (k == 0)
803 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
804 else {
805 IPv6PrivacyExtensions s;
806
807 s = ipv6_privacy_extensions_from_string(rvalue);
808 if (s < 0) {
809
810 if (streq(rvalue, "kernel"))
811 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
812 else {
813 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
814 return 0;
815 }
816 }
817
818 *ipv6_privacy_extensions = s;
819 }
820
821 return 0;
822 }
823
824 int config_parse_hostname(
825 const char *unit,
826 const char *filename,
827 unsigned line,
828 const char *section,
829 unsigned section_line,
830 const char *lvalue,
831 int ltype,
832 const char *rvalue,
833 void *data,
834 void *userdata) {
835
836 char **hostname = data, *hn = NULL;
837 int r;
838
839 assert(filename);
840 assert(lvalue);
841 assert(rvalue);
842
843 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
844 if (r < 0)
845 return r;
846
847 if (!hostname_is_valid(hn, false)) {
848 log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
849 free(hn);
850 return 0;
851 }
852
853 free(*hostname);
854 *hostname = hostname_cleanup(hn);
855 return 0;
856 }
857
858 int config_parse_timezone(
859 const char *unit,
860 const char *filename,
861 unsigned line,
862 const char *section,
863 unsigned section_line,
864 const char *lvalue,
865 int ltype,
866 const char *rvalue,
867 void *data,
868 void *userdata) {
869
870 char **datap = data, *tz = NULL;
871 int r;
872
873 assert(filename);
874 assert(lvalue);
875 assert(rvalue);
876
877 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
878 if (r < 0)
879 return r;
880
881 if (!timezone_is_valid(tz)) {
882 log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
883 free(tz);
884 return 0;
885 }
886
887 free(*datap);
888 *datap = tz;
889
890 return 0;
891 }
892
893 int config_parse_dhcp_server_dns(
894 const char *unit,
895 const char *filename,
896 unsigned line,
897 const char *section,
898 unsigned section_line,
899 const char *lvalue,
900 int ltype,
901 const char *rvalue,
902 void *data,
903 void *userdata) {
904
905 Network *n = data;
906 const char *p = rvalue;
907 int r;
908
909 assert(filename);
910 assert(lvalue);
911 assert(rvalue);
912
913 for (;;) {
914 _cleanup_free_ char *w = NULL;
915 struct in_addr a, *m;
916
917 r = extract_first_word(&p, &w, NULL, 0);
918 if (r == -ENOMEM)
919 return log_oom();
920 if (r < 0) {
921 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
922 return 0;
923 }
924 if (r == 0)
925 break;
926
927 if (inet_pton(AF_INET, w, &a) <= 0) {
928 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
929 continue;
930 }
931
932 m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr));
933 if (!m)
934 return log_oom();
935
936 m[n->n_dhcp_server_dns++] = a;
937 n->dhcp_server_dns = m;
938 }
939
940 return 0;
941 }
942
943 int config_parse_dhcp_server_ntp(
944 const char *unit,
945 const char *filename,
946 unsigned line,
947 const char *section,
948 unsigned section_line,
949 const char *lvalue,
950 int ltype,
951 const char *rvalue,
952 void *data,
953 void *userdata) {
954
955 Network *n = data;
956 const char *p = rvalue;
957 int r;
958
959 assert(filename);
960 assert(lvalue);
961 assert(rvalue);
962
963 for (;;) {
964 _cleanup_free_ char *w = NULL;
965 struct in_addr a, *m;
966
967 r = extract_first_word(&p, &w, NULL, 0);
968 if (r == -ENOMEM)
969 return log_oom();
970 if (r < 0) {
971 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
972 return 0;
973 }
974 if (r == 0)
975 return 0;
976
977 if (inet_pton(AF_INET, w, &a) <= 0) {
978 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w);
979 continue;
980 }
981
982 m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr));
983 if (!m)
984 return log_oom();
985
986 m[n->n_dhcp_server_ntp++] = a;
987 n->dhcp_server_ntp = m;
988 }
989 }
990
991 int config_parse_dns(
992 const char *unit,
993 const char *filename,
994 unsigned line,
995 const char *section,
996 unsigned section_line,
997 const char *lvalue,
998 int ltype,
999 const char *rvalue,
1000 void *data,
1001 void *userdata) {
1002
1003 Network *n = userdata;
1004 int r;
1005
1006 assert(filename);
1007 assert(lvalue);
1008 assert(rvalue);
1009
1010 for (;;) {
1011 _cleanup_free_ char *w = NULL;
1012 union in_addr_union a;
1013 struct in_addr_data *m;
1014 int family;
1015
1016 r = extract_first_word(&rvalue, &w, NULL, 0);
1017 if (r == -ENOMEM)
1018 return log_oom();
1019 if (r < 0) {
1020 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1021 break;
1022 }
1023 if (r == 0)
1024 break;
1025
1026 r = in_addr_from_string_auto(w, &family, &a);
1027 if (r < 0) {
1028 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse dns server address, ignoring: %s", w);
1029 continue;
1030 }
1031
1032 m = realloc(n->dns, (n->n_dns + 1) * sizeof(struct in_addr_data));
1033 if (!m)
1034 return log_oom();
1035
1036 m[n->n_dns++] = (struct in_addr_data) {
1037 .family = family,
1038 .address = a,
1039 };
1040
1041 n->dns = m;
1042 }
1043
1044 return 0;
1045 }
1046
1047 int config_parse_dnssec_negative_trust_anchors(
1048 const char *unit,
1049 const char *filename,
1050 unsigned line,
1051 const char *section,
1052 unsigned section_line,
1053 const char *lvalue,
1054 int ltype,
1055 const char *rvalue,
1056 void *data,
1057 void *userdata) {
1058
1059 const char *p = rvalue;
1060 Network *n = data;
1061 int r;
1062
1063 assert(n);
1064 assert(lvalue);
1065 assert(rvalue);
1066
1067 if (isempty(rvalue)) {
1068 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1069 return 0;
1070 }
1071
1072 for (;;) {
1073 _cleanup_free_ char *w = NULL;
1074
1075 r = extract_first_word(&p, &w, NULL, 0);
1076 if (r < 0) {
1077 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1078 break;
1079 }
1080 if (r == 0)
1081 break;
1082
1083 r = dns_name_is_valid(w);
1084 if (r <= 0) {
1085 log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
1086 continue;
1087 }
1088
1089 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1090 if (r < 0)
1091 return log_oom();
1092
1093 r = set_put(n->dnssec_negative_trust_anchors, w);
1094 if (r < 0)
1095 return log_oom();
1096 if (r > 0)
1097 w = NULL;
1098 }
1099
1100 return 0;
1101 }
1102
1103 int config_parse_ntp(
1104 const char *unit,
1105 const char *filename,
1106 unsigned line,
1107 const char *section,
1108 unsigned section_line,
1109 const char *lvalue,
1110 int ltype,
1111 const char *rvalue,
1112 void *data,
1113 void *userdata) {
1114
1115 char ***l = data;
1116 int r;
1117
1118 assert(l);
1119 assert(lvalue);
1120 assert(rvalue);
1121
1122 if (isempty(rvalue)) {
1123 *l = strv_free(*l);
1124 return 0;
1125 }
1126
1127 for (;;) {
1128 _cleanup_free_ char *w = NULL;
1129
1130 r = extract_first_word(&rvalue, &w, NULL, 0);
1131 if (r == -ENOMEM)
1132 return log_oom();
1133 if (r < 0) {
1134 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract NTP server name, ignoring: %s", rvalue);
1135 break;
1136 }
1137 if (r == 0)
1138 break;
1139
1140 r = dns_name_is_valid_or_address(w);
1141 if (r <= 0) {
1142 log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name or IP address, ignoring.", w);
1143 continue;
1144 }
1145
1146 r = strv_push(l, w);
1147 if (r < 0)
1148 return log_oom();
1149
1150 w = NULL;
1151 }
1152
1153 return 0;
1154 }
1155
1156 int config_parse_dhcp_route_table(const char *unit,
1157 const char *filename,
1158 unsigned line,
1159 const char *section,
1160 unsigned section_line,
1161 const char *lvalue,
1162 int ltype,
1163 const char *rvalue,
1164 void *data,
1165 void *userdata) {
1166 uint32_t rt;
1167 int r;
1168
1169 assert(filename);
1170 assert(lvalue);
1171 assert(rvalue);
1172 assert(data);
1173
1174 r = safe_atou32(rvalue, &rt);
1175 if (r < 0) {
1176 log_syntax(unit, LOG_ERR, filename, line, r,
1177 "Unable to read RouteTable, ignoring assignment: %s", rvalue);
1178 return 0;
1179 }
1180
1181 *((uint32_t *)data) = rt;
1182
1183 return 0;
1184 }
1185
1186 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
1187
1188 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1189 [DHCP_USE_DOMAINS_NO] = "no",
1190 [DHCP_USE_DOMAINS_ROUTE] = "route",
1191 [DHCP_USE_DOMAINS_YES] = "yes",
1192 };
1193
1194 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
1195
1196 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1197
1198 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1199 [LLDP_MODE_NO] = "no",
1200 [LLDP_MODE_YES] = "yes",
1201 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1202 };
1203
1204 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);