]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
Merge pull request #11776 from keszybz/networkd-ordered-sets
[thirdparty/systemd.git] / src / network / networkd-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <net/if.h>
5
6 #include "alloc-util.h"
7 #include "conf-files.h"
8 #include "conf-parser.h"
9 #include "dns-domain.h"
10 #include "fd-util.h"
11 #include "hostname-util.h"
12 #include "in-addr-util.h"
13 #include "missing_network.h"
14 #include "network-internal.h"
15 #include "networkd-manager.h"
16 #include "networkd-network.h"
17 #include "parse-util.h"
18 #include "set.h"
19 #include "stat-util.h"
20 #include "string-table.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "util.h"
24
25 static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
26 siphash24_compress(c->filename, strlen(c->filename), state);
27 siphash24_compress(&c->line, sizeof(c->line), state);
28 }
29
30 static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
31 int r;
32
33 r = strcmp(x->filename, y->filename);
34 if (r != 0)
35 return r;
36
37 return CMP(x->line, y->line);
38 }
39
40 DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
41
42 int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
43 NetworkConfigSection *cs;
44
45 cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
46 if (!cs)
47 return -ENOMEM;
48
49 strcpy(cs->filename, filename);
50 cs->line = line;
51
52 *s = TAKE_PTR(cs);
53
54 return 0;
55 }
56
57 void network_config_section_free(NetworkConfigSection *cs) {
58 free(cs);
59 }
60
61 /* Set defaults following RFC7844 */
62 void network_apply_anonymize_if_set(Network *network) {
63 if (!network->dhcp_anonymize)
64 return;
65 /* RFC7844 3.7
66 SHOULD NOT send the Host Name option */
67 network->dhcp_send_hostname = false;
68 /* RFC7844 section 3.:
69 MAY contain the Client Identifier option
70 Section 3.5:
71 clients MUST use client identifiers based solely
72 on the link-layer address */
73 /* NOTE: Using MAC, as it does not reveal extra information,
74 * and some servers might not answer if this option is not sent */
75 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
76 /* RFC 7844 3.10:
77 SHOULD NOT use the Vendor Class Identifier option */
78 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
79 /* RFC7844 section 3.6.:
80 The client intending to protect its privacy SHOULD only request a
81 minimal number of options in the PRL and SHOULD also randomly shuffle
82 the ordering of option codes in the PRL. If this random ordering
83 cannot be implemented, the client MAY order the option codes in the
84 PRL by option code number (lowest to highest).
85 */
86 /* NOTE: dhcp_use_mtu is false by default,
87 * though it was not initiallized to any value in network_load_one.
88 * Maybe there should be another var called *send*?
89 * (to use the MTU sent by the server but to do not send
90 * the option in the PRL). */
91 network->dhcp_use_mtu = false;
92 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
93 * but this is needed to use them. */
94 network->dhcp_use_routes = true;
95 /* RFC7844 section 3.6.
96 * same comments as previous option */
97 network->dhcp_use_timezone = false;
98 }
99
100 static int network_verify(Network *network) {
101 Address *address;
102 Route *route;
103
104 assert(network);
105 assert(network->filename);
106
107 if (network->bond) {
108 /* Bonding slave does not support addressing. */
109 if (network->ipv6_accept_ra > 0) {
110 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
111 network->filename);
112 network->ipv6_accept_ra = 0;
113 }
114 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
115 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
116 network->filename);
117 network->link_local = ADDRESS_FAMILY_NO;
118 }
119 if (network->dhcp != ADDRESS_FAMILY_NO) {
120 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
121 network->filename);
122 network->dhcp = ADDRESS_FAMILY_NO;
123 }
124 if (network->dhcp_server) {
125 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
126 network->filename);
127 network->dhcp_server = false;
128 }
129 if (network->n_static_addresses > 0) {
130 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
131 network->filename);
132 while ((address = network->static_addresses))
133 address_free(address);
134 }
135 if (network->n_static_routes > 0) {
136 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
137 network->filename);
138 while ((route = network->static_routes))
139 route_free(route);
140 }
141 }
142
143 if (network->link_local < 0)
144 network->link_local = ADDRESS_FAMILY_IPV6;
145
146 /* IPMasquerade=yes implies IPForward=yes */
147 if (network->ip_masquerade)
148 network->ip_forward |= ADDRESS_FAMILY_IPV4;
149
150 if (network->mtu > 0 && network->dhcp_use_mtu) {
151 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
152 "Disabling UseMTU=.", network->filename);
153 network->dhcp_use_mtu = false;
154 }
155
156 LIST_FOREACH(routes, route, network->static_routes)
157 if (!route->family)
158 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
159 "%s: Route section without Gateway field configured. "
160 "Ignoring %s.",
161 network->filename, network->filename);
162
163 LIST_FOREACH(addresses, address, network->static_addresses)
164 if (!address->family)
165 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
166 "%s: Address section without Address field configured. "
167 "Ignoring %s.",
168 network->filename, network->filename);
169
170 return 0;
171 }
172
173 int network_load_one(Manager *manager, const char *filename) {
174 _cleanup_free_ char *fname = NULL, *name = NULL;
175 _cleanup_(network_freep) Network *network = NULL;
176 _cleanup_fclose_ FILE *file = NULL;
177 const char *dropin_dirname;
178 char *d;
179 int r;
180
181 assert(manager);
182 assert(filename);
183
184 file = fopen(filename, "re");
185 if (!file) {
186 if (errno == ENOENT)
187 return 0;
188
189 return -errno;
190 }
191
192 if (null_or_empty_fd(fileno(file))) {
193 log_debug("Skipping empty file: %s", filename);
194 return 0;
195 }
196
197 fname = strdup(filename);
198 if (!fname)
199 return log_oom();
200
201 name = strdup(basename(filename));
202 if (!name)
203 return log_oom();
204
205 d = strrchr(name, '.');
206 if (!d)
207 return -EINVAL;
208
209 *d = '\0';
210
211 dropin_dirname = strjoina(name, ".network.d");
212
213 network = new(Network, 1);
214 if (!network)
215 return log_oom();
216
217 *network = (Network) {
218 .manager = manager,
219 .filename = TAKE_PTR(fname),
220 .name = TAKE_PTR(name),
221
222 .required_for_online = true,
223 .dhcp = ADDRESS_FAMILY_NO,
224 .dhcp_use_ntp = true,
225 .dhcp_use_dns = true,
226 .dhcp_use_hostname = true,
227 .dhcp_use_routes = true,
228 /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
229 .dhcp_send_hostname = true,
230 /* To enable/disable RFC7844 Anonymity Profiles */
231 .dhcp_anonymize = false,
232 .dhcp_route_metric = DHCP_ROUTE_METRIC,
233 /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
234 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
235 .dhcp_route_table = RT_TABLE_MAIN,
236 .dhcp_route_table_set = false,
237 /* NOTE: from man: UseMTU=... Defaults to false*/
238 .dhcp_use_mtu = false,
239 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
240 .dhcp_use_timezone = false,
241 .rapid_commit = true,
242
243 .dhcp_server_emit_dns = true,
244 .dhcp_server_emit_ntp = true,
245 .dhcp_server_emit_router = true,
246 .dhcp_server_emit_timezone = true,
247
248 .router_emit_dns = true,
249 .router_emit_domains = true,
250
251 .use_bpdu = -1,
252 .hairpin = -1,
253 .fast_leave = -1,
254 .allow_port_to_be_root = -1,
255 .unicast_flood = -1,
256 .multicast_to_unicast = -1,
257 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
258
259 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
260
261 .dns_default_route = -1,
262 .llmnr = RESOLVE_SUPPORT_YES,
263 .mdns = RESOLVE_SUPPORT_NO,
264 .dnssec_mode = _DNSSEC_MODE_INVALID,
265 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
266
267 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
268 .link_local = _ADDRESS_FAMILY_BOOLEAN_INVALID,
269
270 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
271 .ipv6_accept_ra = -1,
272 .ipv6_dad_transmits = -1,
273 .ipv6_hop_limit = -1,
274 .ipv6_proxy_ndp = -1,
275 .duid.type = _DUID_TYPE_INVALID,
276 .proxy_arp = -1,
277 .arp = -1,
278 .multicast = -1,
279 .allmulticast = -1,
280 .ipv6_accept_ra_use_dns = true,
281 .ipv6_accept_ra_use_autonomous_prefix = true,
282 .ipv6_accept_ra_use_onlink_prefix = true,
283 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
284 .ipv6_accept_ra_route_table_set = false,
285 };
286
287 r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
288 "Match\0"
289 "Link\0"
290 "Network\0"
291 "Address\0"
292 "Neighbor\0"
293 "IPv6AddressLabel\0"
294 "RoutingPolicyRule\0"
295 "Route\0"
296 "DHCP\0"
297 "DHCPv4\0" /* compat */
298 "DHCPServer\0"
299 "IPv6AcceptRA\0"
300 "IPv6NDPProxyAddress\0"
301 "Bridge\0"
302 "BridgeFDB\0"
303 "BridgeVLAN\0"
304 "IPv6PrefixDelegation\0"
305 "IPv6Prefix\0"
306 "CAN\0",
307 config_item_perf_lookup, network_network_gperf_lookup,
308 CONFIG_PARSE_WARN, network);
309 if (r < 0) {
310 /* Unset manager here. Otherwise, LIST_REMOVE() in network_free() fails. */
311 network->manager = NULL;
312 return r;
313 }
314
315 network_apply_anonymize_if_set(network);
316
317 LIST_PREPEND(networks, manager->networks, network);
318
319 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
320 if (r < 0)
321 return r;
322
323 r = hashmap_put(manager->networks_by_name, network->name, network);
324 if (r < 0)
325 return r;
326
327 if (network_verify(network) < 0)
328 return 0;
329
330 network = NULL;
331 return 0;
332 }
333
334 int network_load(Manager *manager) {
335 Network *network;
336 _cleanup_strv_free_ char **files = NULL;
337 char **f;
338 int r;
339
340 assert(manager);
341
342 while ((network = manager->networks))
343 network_free(network);
344
345 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
346 if (r < 0)
347 return log_error_errno(r, "Failed to enumerate network files: %m");
348
349 STRV_FOREACH_BACKWARDS(f, files) {
350 r = network_load_one(manager, *f);
351 if (r < 0)
352 return r;
353 }
354
355 return 0;
356 }
357
358 void network_free(Network *network) {
359 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
360 RoutingPolicyRule *rule;
361 FdbEntry *fdb_entry;
362 Neighbor *neighbor;
363 AddressLabel *label;
364 Prefix *prefix;
365 Address *address;
366 Route *route;
367
368 if (!network)
369 return;
370
371 free(network->filename);
372
373 set_free_free(network->match_mac);
374 strv_free(network->match_path);
375 strv_free(network->match_driver);
376 strv_free(network->match_type);
377 strv_free(network->match_name);
378
379 free(network->description);
380 free(network->dhcp_vendor_class_identifier);
381 strv_free(network->dhcp_user_class);
382 free(network->dhcp_hostname);
383
384 free(network->mac);
385
386 strv_free(network->ntp);
387 free(network->dns);
388 ordered_set_free_free(network->search_domains);
389 ordered_set_free_free(network->route_domains);
390 strv_free(network->bind_carrier);
391
392 ordered_set_free_free(network->router_search_domains);
393 free(network->router_dns);
394
395 netdev_unref(network->bridge);
396 netdev_unref(network->bond);
397 netdev_unref(network->vrf);
398
399 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
400
401 while ((route = network->static_routes))
402 route_free(route);
403
404 while ((address = network->static_addresses))
405 address_free(address);
406
407 while ((fdb_entry = network->static_fdb_entries))
408 fdb_entry_free(fdb_entry);
409
410 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
411 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
412
413 while ((neighbor = network->neighbors))
414 neighbor_free(neighbor);
415
416 while ((label = network->address_labels))
417 address_label_free(label);
418
419 while ((prefix = network->static_prefixes))
420 prefix_free(prefix);
421
422 while ((rule = network->rules))
423 routing_policy_rule_free(rule);
424
425 hashmap_free(network->addresses_by_section);
426 hashmap_free(network->routes_by_section);
427 hashmap_free(network->fdb_entries_by_section);
428 hashmap_free(network->neighbors_by_section);
429 hashmap_free(network->address_labels_by_section);
430 hashmap_free(network->prefixes_by_section);
431 hashmap_free(network->rules_by_section);
432
433 if (network->manager) {
434 if (network->manager->networks)
435 LIST_REMOVE(networks, network->manager->networks, network);
436
437 if (network->manager->networks_by_name && network->name)
438 hashmap_remove(network->manager->networks_by_name, network->name);
439
440 if (network->manager->duids_requesting_uuid)
441 set_remove(network->manager->duids_requesting_uuid, &network->duid);
442 }
443
444 free(network->name);
445
446 condition_free_list(network->match_host);
447 condition_free_list(network->match_virt);
448 condition_free_list(network->match_kernel_cmdline);
449 condition_free_list(network->match_kernel_version);
450 condition_free_list(network->match_arch);
451
452 free(network->dhcp_server_timezone);
453 free(network->dhcp_server_dns);
454 free(network->dhcp_server_ntp);
455
456 set_free_free(network->dnssec_negative_trust_anchors);
457
458 free(network);
459 }
460
461 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
462 Network *network;
463
464 assert(manager);
465 assert(name);
466 assert(ret);
467
468 network = hashmap_get(manager->networks_by_name, name);
469 if (!network)
470 return -ENOENT;
471
472 *ret = network;
473
474 return 0;
475 }
476
477 int network_get(Manager *manager, sd_device *device,
478 const char *ifname, const struct ether_addr *address,
479 Network **ret) {
480 const char *path = NULL, *driver = NULL, *devtype = NULL;
481 Network *network;
482
483 assert(manager);
484 assert(ret);
485
486 if (device) {
487 (void) sd_device_get_property_value(device, "ID_PATH", &path);
488
489 (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver);
490
491 (void) sd_device_get_devtype(device, &devtype);
492 }
493
494 LIST_FOREACH(networks, network, manager->networks) {
495 if (net_match_config(network->match_mac, network->match_path,
496 network->match_driver, network->match_type,
497 network->match_name, network->match_host,
498 network->match_virt, network->match_kernel_cmdline,
499 network->match_kernel_version, network->match_arch,
500 address, path, driver, devtype, ifname)) {
501 if (network->match_name && device) {
502 const char *attr;
503 uint8_t name_assign_type = NET_NAME_UNKNOWN;
504
505 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
506 (void) safe_atou8(attr, &name_assign_type);
507
508 if (name_assign_type == NET_NAME_ENUM)
509 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
510 ifname, network->filename);
511 else
512 log_debug("%s: found matching network '%s'", ifname, network->filename);
513 } else
514 log_debug("%s: found matching network '%s'", ifname, network->filename);
515
516 *ret = network;
517 return 0;
518 }
519 }
520
521 *ret = NULL;
522
523 return -ENOENT;
524 }
525
526 int network_apply(Network *network, Link *link) {
527 int r;
528
529 assert(network);
530 assert(link);
531
532 link->network = network;
533
534 if (network->ipv4ll_route) {
535 Route *route;
536
537 r = route_new_static(network, NULL, 0, &route);
538 if (r < 0)
539 return r;
540
541 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
542 if (r == 0)
543 return -EINVAL;
544 if (r < 0)
545 return -errno;
546
547 route->family = AF_INET;
548 route->dst_prefixlen = 16;
549 route->scope = RT_SCOPE_LINK;
550 route->priority = IPV4LL_ROUTE_METRIC;
551 route->protocol = RTPROT_STATIC;
552 }
553
554 if (network->n_dns > 0 ||
555 !strv_isempty(network->ntp) ||
556 !ordered_set_isempty(network->search_domains) ||
557 !ordered_set_isempty(network->route_domains))
558 link_dirty(link);
559
560 return 0;
561 }
562
563 bool network_has_static_ipv6_addresses(Network *network) {
564 Address *address;
565
566 assert(network);
567
568 LIST_FOREACH(addresses, address, network->static_addresses) {
569 if (address->family == AF_INET6)
570 return true;
571 }
572
573 return false;
574 }
575
576 int config_parse_netdev(const char *unit,
577 const char *filename,
578 unsigned line,
579 const char *section,
580 unsigned section_line,
581 const char *lvalue,
582 int ltype,
583 const char *rvalue,
584 void *data,
585 void *userdata) {
586 Network *network = userdata;
587 _cleanup_free_ char *kind_string = NULL;
588 char *p;
589 NetDev *netdev;
590 NetDevKind kind;
591 int r;
592
593 assert(filename);
594 assert(lvalue);
595 assert(rvalue);
596 assert(data);
597
598 kind_string = strdup(lvalue);
599 if (!kind_string)
600 return log_oom();
601
602 /* the keys are CamelCase versions of the kind */
603 for (p = kind_string; *p; p++)
604 *p = tolower(*p);
605
606 kind = netdev_kind_from_string(kind_string);
607 if (kind == _NETDEV_KIND_INVALID) {
608 log_syntax(unit, LOG_ERR, filename, line, 0,
609 "Invalid NetDev kind: %s", lvalue);
610 return 0;
611 }
612
613 r = netdev_get(network->manager, rvalue, &netdev);
614 if (r < 0) {
615 log_syntax(unit, LOG_ERR, filename, line, r,
616 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
617 return 0;
618 }
619
620 if (netdev->kind != kind) {
621 log_syntax(unit, LOG_ERR, filename, line, 0,
622 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
623 return 0;
624 }
625
626 switch (kind) {
627 case NETDEV_KIND_BRIDGE:
628 network->bridge = netdev_unref(network->bridge);
629 network->bridge = netdev;
630
631 break;
632 case NETDEV_KIND_BOND:
633 network->bond = netdev_unref(network->bond);
634 network->bond = netdev;
635
636 break;
637 case NETDEV_KIND_VRF:
638 network->vrf = netdev_unref(network->vrf);
639 network->vrf = netdev;
640
641 break;
642 case NETDEV_KIND_VLAN:
643 case NETDEV_KIND_MACVLAN:
644 case NETDEV_KIND_MACVTAP:
645 case NETDEV_KIND_IPVLAN:
646 case NETDEV_KIND_VXLAN:
647 case NETDEV_KIND_VCAN:
648 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
649 if (r < 0)
650 return log_oom();
651
652 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
653 if (r < 0) {
654 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add NetDev '%s' to network: %m", rvalue);
655 return 0;
656 }
657
658 break;
659 default:
660 assert_not_reached("Cannot parse NetDev");
661 }
662
663 netdev_ref(netdev);
664
665 return 0;
666 }
667
668 int config_parse_domains(
669 const char *unit,
670 const char *filename,
671 unsigned line,
672 const char *section,
673 unsigned section_line,
674 const char *lvalue,
675 int ltype,
676 const char *rvalue,
677 void *data,
678 void *userdata) {
679
680 const char *p;
681 Network *n = data;
682 int r;
683
684 assert(n);
685 assert(lvalue);
686 assert(rvalue);
687
688 if (isempty(rvalue)) {
689 n->search_domains = ordered_set_free_free(n->search_domains);
690 n->route_domains = ordered_set_free_free(n->route_domains);
691 return 0;
692 }
693
694 p = rvalue;
695 for (;;) {
696 _cleanup_free_ char *w = NULL, *normalized = NULL;
697 const char *domain;
698 bool is_route;
699
700 r = extract_first_word(&p, &w, NULL, 0);
701 if (r < 0) {
702 log_syntax(unit, LOG_ERR, filename, line, r,
703 "Failed to extract search or route domain, ignoring: %s", rvalue);
704 break;
705 }
706 if (r == 0)
707 break;
708
709 is_route = w[0] == '~';
710 domain = is_route ? w + 1 : w;
711
712 if (dns_name_is_root(domain) || streq(domain, "*")) {
713 /* If the root domain appears as is, or the special token "*" is found, we'll
714 * consider this as routing domain, unconditionally. */
715 is_route = true;
716 domain = "."; /* make sure we don't allow empty strings, thus write the root
717 * domain as "." */
718 } else {
719 r = dns_name_normalize(domain, 0, &normalized);
720 if (r < 0) {
721 log_syntax(unit, LOG_ERR, filename, line, r,
722 "'%s' is not a valid domain name, ignoring.", domain);
723 continue;
724 }
725
726 domain = normalized;
727
728 if (is_localhost(domain)) {
729 log_syntax(unit, LOG_ERR, filename, line, 0,
730 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
731 domain);
732 continue;
733 }
734 }
735
736 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
737 r = ordered_set_ensure_allocated(set, &string_hash_ops);
738 if (r < 0)
739 return r;
740
741 r = ordered_set_put_strdup(*set, domain);
742 if (r < 0)
743 return log_oom();
744 }
745
746 return 0;
747 }
748
749 int config_parse_tunnel(const char *unit,
750 const char *filename,
751 unsigned line,
752 const char *section,
753 unsigned section_line,
754 const char *lvalue,
755 int ltype,
756 const char *rvalue,
757 void *data,
758 void *userdata) {
759 Network *network = userdata;
760 NetDev *netdev;
761 int r;
762
763 assert(filename);
764 assert(lvalue);
765 assert(rvalue);
766 assert(data);
767
768 r = netdev_get(network->manager, rvalue, &netdev);
769 if (r < 0) {
770 log_syntax(unit, LOG_ERR, filename, line, r,
771 "Tunnel is invalid, ignoring assignment: %s", rvalue);
772 return 0;
773 }
774
775 if (!IN_SET(netdev->kind,
776 NETDEV_KIND_IPIP,
777 NETDEV_KIND_SIT,
778 NETDEV_KIND_GRE,
779 NETDEV_KIND_GRETAP,
780 NETDEV_KIND_IP6GRE,
781 NETDEV_KIND_IP6GRETAP,
782 NETDEV_KIND_VTI,
783 NETDEV_KIND_VTI6,
784 NETDEV_KIND_IP6TNL)) {
785 log_syntax(unit, LOG_ERR, filename, line, 0,
786 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
787 return 0;
788 }
789
790 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
791 if (r < 0)
792 return log_oom();
793
794 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
795 if (r < 0) {
796 log_syntax(unit, LOG_ERR, filename, line, r,
797 "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
798 return 0;
799 }
800
801 netdev_ref(netdev);
802
803 return 0;
804 }
805
806 int config_parse_ipv4ll(
807 const char* unit,
808 const char *filename,
809 unsigned line,
810 const char *section,
811 unsigned section_line,
812 const char *lvalue,
813 int ltype,
814 const char *rvalue,
815 void *data,
816 void *userdata) {
817
818 AddressFamilyBoolean *link_local = data;
819
820 assert(filename);
821 assert(lvalue);
822 assert(rvalue);
823 assert(data);
824
825 /* Note that this is mostly like
826 * config_parse_address_family_boolean(), except that it
827 * applies only to IPv4 */
828
829 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
830
831 return 0;
832 }
833
834 int config_parse_dhcp(
835 const char* unit,
836 const char *filename,
837 unsigned line,
838 const char *section,
839 unsigned section_line,
840 const char *lvalue,
841 int ltype,
842 const char *rvalue,
843 void *data,
844 void *userdata) {
845
846 AddressFamilyBoolean *dhcp = data, s;
847
848 assert(filename);
849 assert(lvalue);
850 assert(rvalue);
851 assert(data);
852
853 /* Note that this is mostly like
854 * config_parse_address_family_boolean(), except that it
855 * understands some old names for the enum values */
856
857 s = address_family_boolean_from_string(rvalue);
858 if (s < 0) {
859
860 /* Previously, we had a slightly different enum here,
861 * support its values for compatbility. */
862
863 if (streq(rvalue, "none"))
864 s = ADDRESS_FAMILY_NO;
865 else if (streq(rvalue, "v4"))
866 s = ADDRESS_FAMILY_IPV4;
867 else if (streq(rvalue, "v6"))
868 s = ADDRESS_FAMILY_IPV6;
869 else if (streq(rvalue, "both"))
870 s = ADDRESS_FAMILY_YES;
871 else {
872 log_syntax(unit, LOG_ERR, filename, line, 0,
873 "Failed to parse DHCP option, ignoring: %s", rvalue);
874 return 0;
875 }
876
877 log_syntax(unit, LOG_WARNING, filename, line, 0,
878 "DHCP=%s is deprecated, please use DHCP=%s instead.",
879 rvalue, address_family_boolean_to_string(s));
880 }
881
882 *dhcp = s;
883 return 0;
884 }
885
886 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
887 [DHCP_CLIENT_ID_MAC] = "mac",
888 [DHCP_CLIENT_ID_DUID] = "duid",
889 [DHCP_CLIENT_ID_DUID_ONLY] = "duid-only",
890 };
891
892 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier);
893 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier,
894 "Failed to parse client identifier type");
895
896 int config_parse_ipv6token(
897 const char* unit,
898 const char *filename,
899 unsigned line,
900 const char *section,
901 unsigned section_line,
902 const char *lvalue,
903 int ltype,
904 const char *rvalue,
905 void *data,
906 void *userdata) {
907
908 union in_addr_union buffer;
909 struct in6_addr *token = data;
910 int r;
911
912 assert(filename);
913 assert(lvalue);
914 assert(rvalue);
915 assert(token);
916
917 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
918 if (r < 0) {
919 log_syntax(unit, LOG_ERR, filename, line, r,
920 "Failed to parse IPv6 token, ignoring: %s", rvalue);
921 return 0;
922 }
923
924 if (in_addr_is_null(AF_INET6, &buffer)) {
925 log_syntax(unit, LOG_ERR, filename, line, 0,
926 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
927 return 0;
928 }
929
930 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
931 log_syntax(unit, LOG_ERR, filename, line, 0,
932 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
933 return 0;
934 }
935
936 *token = buffer.in6;
937
938 return 0;
939 }
940
941 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
942 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
943 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
944 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
945 };
946
947 DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
948
949 int config_parse_ipv6_privacy_extensions(
950 const char* unit,
951 const char *filename,
952 unsigned line,
953 const char *section,
954 unsigned section_line,
955 const char *lvalue,
956 int ltype,
957 const char *rvalue,
958 void *data,
959 void *userdata) {
960
961 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
962 int k;
963
964 assert(filename);
965 assert(lvalue);
966 assert(rvalue);
967 assert(ipv6_privacy_extensions);
968
969 /* Our enum shall be a superset of booleans, hence first try
970 * to parse as boolean, and then as enum */
971
972 k = parse_boolean(rvalue);
973 if (k > 0)
974 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
975 else if (k == 0)
976 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
977 else {
978 IPv6PrivacyExtensions s;
979
980 s = ipv6_privacy_extensions_from_string(rvalue);
981 if (s < 0) {
982
983 if (streq(rvalue, "kernel"))
984 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
985 else {
986 log_syntax(unit, LOG_ERR, filename, line, 0,
987 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
988 return 0;
989 }
990 }
991
992 *ipv6_privacy_extensions = s;
993 }
994
995 return 0;
996 }
997
998 int config_parse_hostname(
999 const char *unit,
1000 const char *filename,
1001 unsigned line,
1002 const char *section,
1003 unsigned section_line,
1004 const char *lvalue,
1005 int ltype,
1006 const char *rvalue,
1007 void *data,
1008 void *userdata) {
1009
1010 _cleanup_free_ char *hn = NULL;
1011 char **hostname = data;
1012 int r;
1013
1014 assert(filename);
1015 assert(lvalue);
1016 assert(rvalue);
1017
1018 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
1019 if (r < 0)
1020 return r;
1021
1022 if (!hostname_is_valid(hn, false)) {
1023 log_syntax(unit, LOG_ERR, filename, line, 0,
1024 "Hostname is not valid, ignoring assignment: %s", rvalue);
1025 return 0;
1026 }
1027
1028 r = dns_name_is_valid(hn);
1029 if (r < 0) {
1030 log_syntax(unit, LOG_ERR, filename, line, r,
1031 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
1032 return 0;
1033 }
1034 if (r == 0) {
1035 log_syntax(unit, LOG_ERR, filename, line, 0,
1036 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
1037 return 0;
1038 }
1039
1040 return free_and_replace(*hostname, hn);
1041 }
1042
1043 int config_parse_timezone(
1044 const char *unit,
1045 const char *filename,
1046 unsigned line,
1047 const char *section,
1048 unsigned section_line,
1049 const char *lvalue,
1050 int ltype,
1051 const char *rvalue,
1052 void *data,
1053 void *userdata) {
1054
1055 _cleanup_free_ char *tz = NULL;
1056 char **datap = data;
1057 int r;
1058
1059 assert(filename);
1060 assert(lvalue);
1061 assert(rvalue);
1062
1063 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1064 if (r < 0)
1065 return r;
1066
1067 if (!timezone_is_valid(tz, LOG_ERR)) {
1068 log_syntax(unit, LOG_ERR, filename, line, 0,
1069 "Timezone is not valid, ignoring assignment: %s", rvalue);
1070 return 0;
1071 }
1072
1073 return free_and_replace(*datap, tz);
1074 }
1075
1076 int config_parse_dhcp_server_dns(
1077 const char *unit,
1078 const char *filename,
1079 unsigned line,
1080 const char *section,
1081 unsigned section_line,
1082 const char *lvalue,
1083 int ltype,
1084 const char *rvalue,
1085 void *data,
1086 void *userdata) {
1087
1088 Network *n = data;
1089 const char *p = rvalue;
1090 int r;
1091
1092 assert(filename);
1093 assert(lvalue);
1094 assert(rvalue);
1095
1096 for (;;) {
1097 _cleanup_free_ char *w = NULL;
1098 struct in_addr a, *m;
1099
1100 r = extract_first_word(&p, &w, NULL, 0);
1101 if (r == -ENOMEM)
1102 return log_oom();
1103 if (r < 0) {
1104 log_syntax(unit, LOG_ERR, filename, line, r,
1105 "Failed to extract word, ignoring: %s", rvalue);
1106 return 0;
1107 }
1108 if (r == 0)
1109 break;
1110
1111 if (inet_pton(AF_INET, w, &a) <= 0) {
1112 log_syntax(unit, LOG_ERR, filename, line, 0,
1113 "Failed to parse DNS server address, ignoring: %s", w);
1114 continue;
1115 }
1116
1117 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
1118 if (!m)
1119 return log_oom();
1120
1121 m[n->n_dhcp_server_dns++] = a;
1122 n->dhcp_server_dns = m;
1123 }
1124
1125 return 0;
1126 }
1127
1128 int config_parse_radv_dns(
1129 const char *unit,
1130 const char *filename,
1131 unsigned line,
1132 const char *section,
1133 unsigned section_line,
1134 const char *lvalue,
1135 int ltype,
1136 const char *rvalue,
1137 void *data,
1138 void *userdata) {
1139
1140 Network *n = data;
1141 const char *p = rvalue;
1142 int r;
1143
1144 assert(filename);
1145 assert(lvalue);
1146 assert(rvalue);
1147
1148 for (;;) {
1149 _cleanup_free_ char *w = NULL;
1150 union in_addr_union a;
1151
1152 r = extract_first_word(&p, &w, NULL, 0);
1153 if (r == -ENOMEM)
1154 return log_oom();
1155 if (r < 0) {
1156 log_syntax(unit, LOG_ERR, filename, line, r,
1157 "Failed to extract word, ignoring: %s", rvalue);
1158 return 0;
1159 }
1160 if (r == 0)
1161 break;
1162
1163 if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
1164 struct in6_addr *m;
1165
1166 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1167 if (!m)
1168 return log_oom();
1169
1170 m[n->n_router_dns++] = a.in6;
1171 n->router_dns = m;
1172
1173 } else
1174 log_syntax(unit, LOG_ERR, filename, line, 0,
1175 "Failed to parse DNS server address, ignoring: %s", w);
1176 }
1177
1178 return 0;
1179 }
1180
1181 int config_parse_radv_search_domains(
1182 const char *unit,
1183 const char *filename,
1184 unsigned line,
1185 const char *section,
1186 unsigned section_line,
1187 const char *lvalue,
1188 int ltype,
1189 const char *rvalue,
1190 void *data,
1191 void *userdata) {
1192
1193 Network *n = data;
1194 const char *p = rvalue;
1195 int r;
1196
1197 assert(filename);
1198 assert(lvalue);
1199 assert(rvalue);
1200
1201 for (;;) {
1202 _cleanup_free_ char *w = NULL, *idna = NULL;
1203
1204 r = extract_first_word(&p, &w, NULL, 0);
1205 if (r == -ENOMEM)
1206 return log_oom();
1207 if (r < 0) {
1208 log_syntax(unit, LOG_ERR, filename, line, r,
1209 "Failed to extract word, ignoring: %s", rvalue);
1210 return 0;
1211 }
1212 if (r == 0)
1213 break;
1214
1215 r = dns_name_apply_idna(w, &idna);
1216 if (r < 0) {
1217 log_syntax(unit, LOG_ERR, filename, line, r,
1218 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1219 continue;
1220 } else if (r == 0)
1221 /* transfer ownership to simplify subsequent operations */
1222 idna = TAKE_PTR(w);
1223
1224 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops);
1225 if (r < 0)
1226 return r;
1227
1228 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1229 if (r < 0)
1230 return r;
1231 }
1232
1233 return 0;
1234 }
1235
1236 int config_parse_dhcp_server_ntp(
1237 const char *unit,
1238 const char *filename,
1239 unsigned line,
1240 const char *section,
1241 unsigned section_line,
1242 const char *lvalue,
1243 int ltype,
1244 const char *rvalue,
1245 void *data,
1246 void *userdata) {
1247
1248 Network *n = data;
1249 const char *p = rvalue;
1250 int r;
1251
1252 assert(filename);
1253 assert(lvalue);
1254 assert(rvalue);
1255
1256 for (;;) {
1257 _cleanup_free_ char *w = NULL;
1258 struct in_addr a, *m;
1259
1260 r = extract_first_word(&p, &w, NULL, 0);
1261 if (r == -ENOMEM)
1262 return log_oom();
1263 if (r < 0) {
1264 log_syntax(unit, LOG_ERR, filename, line, r,
1265 "Failed to extract word, ignoring: %s", rvalue);
1266 return 0;
1267 }
1268 if (r == 0)
1269 return 0;
1270
1271 if (inet_pton(AF_INET, w, &a) <= 0) {
1272 log_syntax(unit, LOG_ERR, filename, line, 0,
1273 "Failed to parse NTP server address, ignoring: %s", w);
1274 continue;
1275 }
1276
1277 m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
1278 if (!m)
1279 return log_oom();
1280
1281 m[n->n_dhcp_server_ntp++] = a;
1282 n->dhcp_server_ntp = m;
1283 }
1284 }
1285
1286 int config_parse_dns(
1287 const char *unit,
1288 const char *filename,
1289 unsigned line,
1290 const char *section,
1291 unsigned section_line,
1292 const char *lvalue,
1293 int ltype,
1294 const char *rvalue,
1295 void *data,
1296 void *userdata) {
1297
1298 Network *n = userdata;
1299 int r;
1300
1301 assert(filename);
1302 assert(lvalue);
1303 assert(rvalue);
1304
1305 for (;;) {
1306 _cleanup_free_ char *w = NULL;
1307 union in_addr_union a;
1308 struct in_addr_data *m;
1309 int family;
1310
1311 r = extract_first_word(&rvalue, &w, NULL, 0);
1312 if (r == -ENOMEM)
1313 return log_oom();
1314 if (r < 0) {
1315 log_syntax(unit, LOG_ERR, filename, line, r,
1316 "Invalid syntax, ignoring: %s", rvalue);
1317 break;
1318 }
1319 if (r == 0)
1320 break;
1321
1322 r = in_addr_from_string_auto(w, &family, &a);
1323 if (r < 0) {
1324 log_syntax(unit, LOG_ERR, filename, line, r,
1325 "Failed to parse dns server address, ignoring: %s", w);
1326 continue;
1327 }
1328
1329 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
1330 if (!m)
1331 return log_oom();
1332
1333 m[n->n_dns++] = (struct in_addr_data) {
1334 .family = family,
1335 .address = a,
1336 };
1337
1338 n->dns = m;
1339 }
1340
1341 return 0;
1342 }
1343
1344 int config_parse_dnssec_negative_trust_anchors(
1345 const char *unit,
1346 const char *filename,
1347 unsigned line,
1348 const char *section,
1349 unsigned section_line,
1350 const char *lvalue,
1351 int ltype,
1352 const char *rvalue,
1353 void *data,
1354 void *userdata) {
1355
1356 const char *p = rvalue;
1357 Network *n = data;
1358 int r;
1359
1360 assert(n);
1361 assert(lvalue);
1362 assert(rvalue);
1363
1364 if (isempty(rvalue)) {
1365 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1366 return 0;
1367 }
1368
1369 for (;;) {
1370 _cleanup_free_ char *w = NULL;
1371
1372 r = extract_first_word(&p, &w, NULL, 0);
1373 if (r < 0) {
1374 log_syntax(unit, LOG_ERR, filename, line, r,
1375 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1376 break;
1377 }
1378 if (r == 0)
1379 break;
1380
1381 r = dns_name_is_valid(w);
1382 if (r <= 0) {
1383 log_syntax(unit, LOG_ERR, filename, line, r,
1384 "%s is not a valid domain name, ignoring.", w);
1385 continue;
1386 }
1387
1388 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1389 if (r < 0)
1390 return log_oom();
1391
1392 r = set_put(n->dnssec_negative_trust_anchors, w);
1393 if (r < 0)
1394 return log_oom();
1395 if (r > 0)
1396 w = NULL;
1397 }
1398
1399 return 0;
1400 }
1401
1402 int config_parse_ntp(
1403 const char *unit,
1404 const char *filename,
1405 unsigned line,
1406 const char *section,
1407 unsigned section_line,
1408 const char *lvalue,
1409 int ltype,
1410 const char *rvalue,
1411 void *data,
1412 void *userdata) {
1413
1414 char ***l = data;
1415 int r;
1416
1417 assert(l);
1418 assert(lvalue);
1419 assert(rvalue);
1420
1421 if (isempty(rvalue)) {
1422 *l = strv_free(*l);
1423 return 0;
1424 }
1425
1426 for (;;) {
1427 _cleanup_free_ char *w = NULL;
1428
1429 r = extract_first_word(&rvalue, &w, NULL, 0);
1430 if (r == -ENOMEM)
1431 return log_oom();
1432 if (r < 0) {
1433 log_syntax(unit, LOG_ERR, filename, line, r,
1434 "Failed to extract NTP server name, ignoring: %s", rvalue);
1435 break;
1436 }
1437 if (r == 0)
1438 break;
1439
1440 r = dns_name_is_valid_or_address(w);
1441 if (r <= 0) {
1442 log_syntax(unit, LOG_ERR, filename, line, r,
1443 "%s is not a valid domain name or IP address, ignoring.", w);
1444 continue;
1445 }
1446
1447 r = strv_push(l, w);
1448 if (r < 0)
1449 return log_oom();
1450
1451 w = NULL;
1452 }
1453
1454 return 0;
1455 }
1456
1457 int config_parse_dhcp_user_class(
1458 const char *unit,
1459 const char *filename,
1460 unsigned line,
1461 const char *section,
1462 unsigned section_line,
1463 const char *lvalue,
1464 int ltype,
1465 const char *rvalue,
1466 void *data,
1467 void *userdata) {
1468
1469 char ***l = data;
1470 int r;
1471
1472 assert(l);
1473 assert(lvalue);
1474 assert(rvalue);
1475
1476 if (isempty(rvalue)) {
1477 *l = strv_free(*l);
1478 return 0;
1479 }
1480
1481 for (;;) {
1482 _cleanup_free_ char *w = NULL;
1483
1484 r = extract_first_word(&rvalue, &w, NULL, 0);
1485 if (r == -ENOMEM)
1486 return log_oom();
1487 if (r < 0) {
1488 log_syntax(unit, LOG_ERR, filename, line, r,
1489 "Failed to split user classes option, ignoring: %s", rvalue);
1490 break;
1491 }
1492 if (r == 0)
1493 break;
1494
1495 if (strlen(w) > 255) {
1496 log_syntax(unit, LOG_ERR, filename, line, r,
1497 "%s length is not in the range 1-255, ignoring.", w);
1498 continue;
1499 }
1500
1501 r = strv_push(l, w);
1502 if (r < 0)
1503 return log_oom();
1504
1505 w = NULL;
1506 }
1507
1508 return 0;
1509 }
1510
1511 int config_parse_section_route_table(
1512 const char *unit,
1513 const char *filename,
1514 unsigned line,
1515 const char *section,
1516 unsigned section_line,
1517 const char *lvalue,
1518 int ltype,
1519 const char *rvalue,
1520 void *data,
1521 void *userdata) {
1522
1523 Network *network = data;
1524 uint32_t rt;
1525 int r;
1526
1527 assert(filename);
1528 assert(lvalue);
1529 assert(rvalue);
1530 assert(data);
1531
1532 r = safe_atou32(rvalue, &rt);
1533 if (r < 0) {
1534 log_syntax(unit, LOG_ERR, filename, line, r,
1535 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
1536 return 0;
1537 }
1538
1539 if (streq_ptr(section, "DHCP")) {
1540 network->dhcp_route_table = rt;
1541 network->dhcp_route_table_set = true;
1542 } else { /* section is IPv6AcceptRA */
1543 network->ipv6_accept_ra_route_table = rt;
1544 network->ipv6_accept_ra_route_table_set = true;
1545 }
1546
1547 return 0;
1548 }
1549
1550 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
1551 "Failed to parse DHCP use domains setting");
1552
1553 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1554 [DHCP_USE_DOMAINS_NO] = "no",
1555 [DHCP_USE_DOMAINS_ROUTE] = "route",
1556 [DHCP_USE_DOMAINS_YES] = "yes",
1557 };
1558
1559 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
1560
1561 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1562
1563 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1564 [LLDP_MODE_NO] = "no",
1565 [LLDP_MODE_YES] = "yes",
1566 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1567 };
1568
1569 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
1570
1571 int config_parse_iaid(const char *unit,
1572 const char *filename,
1573 unsigned line,
1574 const char *section,
1575 unsigned section_line,
1576 const char *lvalue,
1577 int ltype,
1578 const char *rvalue,
1579 void *data,
1580 void *userdata) {
1581 Network *network = data;
1582 uint32_t iaid;
1583 int r;
1584
1585 assert(filename);
1586 assert(lvalue);
1587 assert(rvalue);
1588 assert(network);
1589
1590 r = safe_atou32(rvalue, &iaid);
1591 if (r < 0) {
1592 log_syntax(unit, LOG_ERR, filename, line, r,
1593 "Unable to read IAID, ignoring assignment: %s", rvalue);
1594 return 0;
1595 }
1596
1597 network->iaid = iaid;
1598 network->iaid_set = true;
1599
1600 return 0;
1601 }