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