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