]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
network: move NetworkConfigSection and related functions to networkd-util.[ch]
[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 /* Let's assume that anything above this number is a user misconfiguration. */
27 #define MAX_NTP_SERVERS 128
28
29 /* Set defaults following RFC7844 */
30 void network_apply_anonymize_if_set(Network *network) {
31 if (!network->dhcp_anonymize)
32 return;
33 /* RFC7844 3.7
34 SHOULD NOT send the Host Name option */
35 network->dhcp_send_hostname = false;
36 /* RFC7844 section 3.:
37 MAY contain the Client Identifier option
38 Section 3.5:
39 clients MUST use client identifiers based solely
40 on the link-layer address */
41 /* NOTE: Using MAC, as it does not reveal extra information,
42 * and some servers might not answer if this option is not sent */
43 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
44 /* RFC 7844 3.10:
45 SHOULD NOT use the Vendor Class Identifier option */
46 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
47 /* RFC7844 section 3.6.:
48 The client intending to protect its privacy SHOULD only request a
49 minimal number of options in the PRL and SHOULD also randomly shuffle
50 the ordering of option codes in the PRL. If this random ordering
51 cannot be implemented, the client MAY order the option codes in the
52 PRL by option code number (lowest to highest).
53 */
54 /* NOTE: dhcp_use_mtu is false by default,
55 * though it was not initiallized to any value in network_load_one.
56 * Maybe there should be another var called *send*?
57 * (to use the MTU sent by the server but to do not send
58 * the option in the PRL). */
59 network->dhcp_use_mtu = false;
60 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
61 * but this is needed to use them. */
62 network->dhcp_use_routes = true;
63 /* RFC7844 section 3.6.
64 * same comments as previous option */
65 network->dhcp_use_timezone = false;
66 }
67
68 static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
69 const char *kind_string;
70 NetDev *netdev;
71 int r;
72
73 assert(network);
74 assert(network->manager);
75 assert(network->filename);
76 assert(ret_netdev);
77
78 if (!name)
79 return 0;
80
81 if (kind == _NETDEV_KIND_TUNNEL)
82 kind_string = "tunnel";
83 else {
84 kind_string = netdev_kind_to_string(kind);
85 if (!kind_string)
86 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
87 "%s: Invalid NetDev kind of %s, ignoring assignment.",
88 network->filename, name);
89 }
90
91 r = netdev_get(network->manager, name, &netdev);
92 if (r < 0)
93 return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
94 network->filename, name);
95
96 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
97 IN_SET(netdev->kind,
98 NETDEV_KIND_IPIP,
99 NETDEV_KIND_SIT,
100 NETDEV_KIND_GRE,
101 NETDEV_KIND_GRETAP,
102 NETDEV_KIND_IP6GRE,
103 NETDEV_KIND_IP6GRETAP,
104 NETDEV_KIND_VTI,
105 NETDEV_KIND_VTI6,
106 NETDEV_KIND_IP6TNL)))
107 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
108 "%s: NetDev %s is not a %s, ignoring assignment",
109 network->filename, name, kind_string);
110
111 *ret_netdev = netdev_ref(netdev);
112 return 1;
113 }
114
115 static int network_resolve_stacked_netdevs(Network *network) {
116 void *name, *kind;
117 Iterator i;
118 int r;
119
120 assert(network);
121
122 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names, i) {
123 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
124
125 r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev);
126 if (r <= 0)
127 continue;
128
129 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
130 if (r < 0)
131 return log_oom();
132
133 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
134 if (r < 0)
135 return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m",
136 network->filename, (const char *) name);
137
138 netdev = NULL;
139 }
140
141 return 0;
142 }
143
144 static uint32_t network_get_stacked_netdevs_mtu(Network *network) {
145 uint32_t mtu = 0;
146 NetDev *dev;
147 Iterator i;
148
149 HASHMAP_FOREACH(dev, network->stacked_netdevs, i)
150 if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0)
151 /* See vlan_dev_change_mtu() in kernel.
152 * Note that the additional 4bytes may not be necessary for all devices. */
153 mtu = MAX(mtu, dev->mtu + 4);
154
155 else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu)
156 /* See macvlan_change_mtu() in kernel. */
157 mtu = dev->mtu;
158
159 return mtu;
160 }
161
162 static int network_verify(Network *network) {
163 Address *address, *address_next;
164 Route *route, *route_next;
165 uint32_t mtu;
166
167 assert(network);
168 assert(network->filename);
169
170 /* skip out early if configuration does not match the environment */
171 if (!net_match_config(NULL, NULL, NULL, NULL, NULL,
172 network->match_host, network->match_virt, network->match_kernel_cmdline,
173 network->match_kernel_version, network->match_arch,
174 NULL, NULL, NULL, NULL, NULL))
175 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
176 "%s: Conditions in the file do not match the system environment, skipping.",
177 network->filename);
178
179 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
180 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
181 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
182 (void) network_resolve_stacked_netdevs(network);
183
184 /* Free unnecessary entries. */
185 network->bond_name = mfree(network->bond_name);
186 network->bridge_name = mfree(network->bridge_name);
187 network->vrf_name = mfree(network->vrf_name);
188 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
189
190 if (network->bond) {
191 /* Bonding slave does not support addressing. */
192 if (network->ipv6_accept_ra > 0) {
193 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
194 network->filename);
195 network->ipv6_accept_ra = 0;
196 }
197 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
198 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
199 network->filename);
200 network->link_local = ADDRESS_FAMILY_NO;
201 }
202 if (network->dhcp != ADDRESS_FAMILY_NO) {
203 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
204 network->filename);
205 network->dhcp = ADDRESS_FAMILY_NO;
206 }
207 if (network->dhcp_server) {
208 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
209 network->filename);
210 network->dhcp_server = false;
211 }
212 if (network->n_static_addresses > 0) {
213 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
214 network->filename);
215 while ((address = network->static_addresses))
216 address_free(address);
217 }
218 if (network->n_static_routes > 0) {
219 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
220 network->filename);
221 while ((route = network->static_routes))
222 route_free(route);
223 }
224 }
225
226 if (network->link_local < 0)
227 network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
228
229 if (network->ipv6_accept_ra < 0 && network->bridge)
230 network->ipv6_accept_ra = false;
231
232 /* IPMasquerade=yes implies IPForward=yes */
233 if (network->ip_masquerade)
234 network->ip_forward |= ADDRESS_FAMILY_IPV4;
235
236 network->mtu_is_set = network->mtu > 0;
237 mtu = network_get_stacked_netdevs_mtu(network);
238 if (network->mtu < mtu) {
239 if (network->mtu_is_set)
240 log_notice("%s: Bumping MTUBytes= from %"PRIu32" to %"PRIu32" because of stacked device",
241 network->filename, network->mtu, mtu);
242 network->mtu = mtu;
243 }
244
245 if (network->mtu_is_set && network->dhcp_use_mtu) {
246 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
247 "Disabling UseMTU=.", network->filename);
248 network->dhcp_use_mtu = false;
249 }
250
251 LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
252 if (address->family == AF_UNSPEC) {
253 log_warning("%s: Address section without Address= field configured. "
254 "Ignoring [Address] section from line %u.",
255 network->filename, address->section->line);
256
257 address_free(address);
258 }
259
260 LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes) {
261 if (route->family == AF_UNSPEC) {
262 log_warning("%s: Route section without Gateway=, Destination=, Source=, "
263 "or PreferredSource= field configured. "
264 "Ignoring [Route] section from line %u.",
265 network->filename, route->section->line);
266
267 route_free(route);
268 continue;
269 }
270
271 if (network->n_static_addresses == 0 &&
272 in_addr_is_null(route->family, &route->gw) == 0 &&
273 route->gateway_onlink < 0) {
274 log_warning("%s: Gateway= without static address configured. "
275 "Enabling GatewayOnLink= option.",
276 network->filename);
277 route->gateway_onlink = true;
278 }
279 }
280
281 return 0;
282 }
283
284 int network_load_one(Manager *manager, const char *filename) {
285 _cleanup_free_ char *fname = NULL, *name = NULL;
286 _cleanup_(network_freep) Network *network = NULL;
287 _cleanup_fclose_ FILE *file = NULL;
288 const char *dropin_dirname;
289 char *d;
290 int r;
291
292 assert(manager);
293 assert(filename);
294
295 file = fopen(filename, "re");
296 if (!file) {
297 if (errno == ENOENT)
298 return 0;
299
300 return -errno;
301 }
302
303 if (null_or_empty_fd(fileno(file))) {
304 log_debug("Skipping empty file: %s", filename);
305 return 0;
306 }
307
308 fname = strdup(filename);
309 if (!fname)
310 return log_oom();
311
312 name = strdup(basename(filename));
313 if (!name)
314 return log_oom();
315
316 d = strrchr(name, '.');
317 if (!d)
318 return -EINVAL;
319
320 *d = '\0';
321
322 dropin_dirname = strjoina(name, ".network.d");
323
324 network = new(Network, 1);
325 if (!network)
326 return log_oom();
327
328 *network = (Network) {
329 .filename = TAKE_PTR(fname),
330 .name = TAKE_PTR(name),
331
332 .required_for_online = true,
333 .dhcp = ADDRESS_FAMILY_NO,
334 .dhcp_use_ntp = true,
335 .dhcp_use_dns = true,
336 .dhcp_use_hostname = true,
337 .dhcp_use_routes = true,
338 /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
339 .dhcp_send_hostname = true,
340 /* To enable/disable RFC7844 Anonymity Profiles */
341 .dhcp_anonymize = false,
342 .dhcp_route_metric = DHCP_ROUTE_METRIC,
343 /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
344 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
345 .dhcp_route_table = RT_TABLE_MAIN,
346 .dhcp_route_table_set = false,
347 /* NOTE: from man: UseMTU=... Defaults to false*/
348 .dhcp_use_mtu = false,
349 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
350 .dhcp_use_timezone = false,
351 .rapid_commit = true,
352
353 .dhcp_server_emit_dns = true,
354 .dhcp_server_emit_ntp = true,
355 .dhcp_server_emit_router = true,
356 .dhcp_server_emit_timezone = true,
357
358 .router_emit_dns = true,
359 .router_emit_domains = true,
360
361 .use_bpdu = -1,
362 .hairpin = -1,
363 .fast_leave = -1,
364 .allow_port_to_be_root = -1,
365 .unicast_flood = -1,
366 .multicast_to_unicast = -1,
367 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
368
369 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
370
371 .dns_default_route = -1,
372 .llmnr = RESOLVE_SUPPORT_YES,
373 .mdns = RESOLVE_SUPPORT_NO,
374 .dnssec_mode = _DNSSEC_MODE_INVALID,
375 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
376
377 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
378 .link_local = _ADDRESS_FAMILY_BOOLEAN_INVALID,
379
380 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
381 .ipv6_accept_ra = -1,
382 .ipv6_dad_transmits = -1,
383 .ipv6_hop_limit = -1,
384 .ipv6_proxy_ndp = -1,
385 .duid.type = _DUID_TYPE_INVALID,
386 .proxy_arp = -1,
387 .arp = -1,
388 .multicast = -1,
389 .allmulticast = -1,
390 .ipv6_accept_ra_use_dns = true,
391 .ipv6_accept_ra_use_autonomous_prefix = true,
392 .ipv6_accept_ra_use_onlink_prefix = true,
393 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
394 .ipv6_accept_ra_route_table_set = false,
395
396 .can_triple_sampling = -1,
397 };
398
399 r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
400 "Match\0"
401 "Link\0"
402 "Network\0"
403 "Address\0"
404 "Neighbor\0"
405 "IPv6AddressLabel\0"
406 "RoutingPolicyRule\0"
407 "Route\0"
408 "DHCP\0"
409 "DHCPv4\0" /* compat */
410 "DHCPServer\0"
411 "IPv6AcceptRA\0"
412 "IPv6NDPProxyAddress\0"
413 "Bridge\0"
414 "BridgeFDB\0"
415 "BridgeVLAN\0"
416 "IPv6PrefixDelegation\0"
417 "IPv6Prefix\0"
418 "CAN\0",
419 config_item_perf_lookup, network_network_gperf_lookup,
420 CONFIG_PARSE_WARN, network);
421 if (r < 0)
422 return r;
423
424 network_apply_anonymize_if_set(network);
425
426 LIST_PREPEND(networks, manager->networks, network);
427 network->manager = manager;
428
429 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
430 if (r < 0)
431 return r;
432
433 r = hashmap_put(manager->networks_by_name, network->name, network);
434 if (r < 0)
435 return r;
436
437 if (network_verify(network) < 0)
438 return 0;
439
440 network = NULL;
441 return 0;
442 }
443
444 int network_load(Manager *manager) {
445 Network *network;
446 _cleanup_strv_free_ char **files = NULL;
447 char **f;
448 int r;
449
450 assert(manager);
451
452 while ((network = manager->networks))
453 network_free(network);
454
455 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
456 if (r < 0)
457 return log_error_errno(r, "Failed to enumerate network files: %m");
458
459 STRV_FOREACH_BACKWARDS(f, files) {
460 r = network_load_one(manager, *f);
461 if (r < 0)
462 return r;
463 }
464
465 return 0;
466 }
467
468 void network_free(Network *network) {
469 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
470 RoutingPolicyRule *rule;
471 FdbEntry *fdb_entry;
472 Neighbor *neighbor;
473 AddressLabel *label;
474 Prefix *prefix;
475 Address *address;
476 Route *route;
477
478 if (!network)
479 return;
480
481 free(network->filename);
482
483 set_free_free(network->match_mac);
484 strv_free(network->match_path);
485 strv_free(network->match_driver);
486 strv_free(network->match_type);
487 strv_free(network->match_name);
488
489 free(network->description);
490 free(network->dhcp_vendor_class_identifier);
491 strv_free(network->dhcp_user_class);
492 free(network->dhcp_hostname);
493
494 free(network->mac);
495
496 strv_free(network->ntp);
497 free(network->dns);
498 ordered_set_free_free(network->search_domains);
499 ordered_set_free_free(network->route_domains);
500 strv_free(network->bind_carrier);
501
502 ordered_set_free_free(network->router_search_domains);
503 free(network->router_dns);
504
505 free(network->bridge_name);
506 free(network->bond_name);
507 free(network->vrf_name);
508 hashmap_free_free_key(network->stacked_netdev_names);
509 netdev_unref(network->bridge);
510 netdev_unref(network->bond);
511 netdev_unref(network->vrf);
512 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
513
514 while ((route = network->static_routes))
515 route_free(route);
516
517 while ((address = network->static_addresses))
518 address_free(address);
519
520 while ((fdb_entry = network->static_fdb_entries))
521 fdb_entry_free(fdb_entry);
522
523 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
524 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
525
526 while ((neighbor = network->neighbors))
527 neighbor_free(neighbor);
528
529 while ((label = network->address_labels))
530 address_label_free(label);
531
532 while ((prefix = network->static_prefixes))
533 prefix_free(prefix);
534
535 while ((rule = network->rules))
536 routing_policy_rule_free(rule);
537
538 hashmap_free(network->addresses_by_section);
539 hashmap_free(network->routes_by_section);
540 hashmap_free(network->fdb_entries_by_section);
541 hashmap_free(network->neighbors_by_section);
542 hashmap_free(network->address_labels_by_section);
543 hashmap_free(network->prefixes_by_section);
544 hashmap_free(network->rules_by_section);
545
546 if (network->manager) {
547 if (network->manager->networks)
548 LIST_REMOVE(networks, network->manager->networks, network);
549
550 if (network->manager->networks_by_name && network->name)
551 hashmap_remove(network->manager->networks_by_name, network->name);
552
553 if (network->manager->duids_requesting_uuid)
554 set_remove(network->manager->duids_requesting_uuid, &network->duid);
555 }
556
557 free(network->name);
558
559 condition_free_list(network->match_host);
560 condition_free_list(network->match_virt);
561 condition_free_list(network->match_kernel_cmdline);
562 condition_free_list(network->match_kernel_version);
563 condition_free_list(network->match_arch);
564
565 free(network->dhcp_server_timezone);
566 free(network->dhcp_server_dns);
567 free(network->dhcp_server_ntp);
568
569 set_free_free(network->dnssec_negative_trust_anchors);
570
571 free(network);
572 }
573
574 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
575 Network *network;
576
577 assert(manager);
578 assert(name);
579 assert(ret);
580
581 network = hashmap_get(manager->networks_by_name, name);
582 if (!network)
583 return -ENOENT;
584
585 *ret = network;
586
587 return 0;
588 }
589
590 int network_get(Manager *manager, sd_device *device,
591 const char *ifname, const struct ether_addr *address,
592 Network **ret) {
593 const char *path = NULL, *driver = NULL, *devtype = NULL;
594 Network *network;
595
596 assert(manager);
597 assert(ret);
598
599 if (device) {
600 (void) sd_device_get_property_value(device, "ID_PATH", &path);
601
602 (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver);
603
604 (void) sd_device_get_devtype(device, &devtype);
605 }
606
607 LIST_FOREACH(networks, network, manager->networks) {
608 if (net_match_config(network->match_mac, network->match_path,
609 network->match_driver, network->match_type,
610 network->match_name, network->match_host,
611 network->match_virt, network->match_kernel_cmdline,
612 network->match_kernel_version, network->match_arch,
613 address, path, driver, devtype, ifname)) {
614 if (network->match_name && device) {
615 const char *attr;
616 uint8_t name_assign_type = NET_NAME_UNKNOWN;
617
618 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
619 (void) safe_atou8(attr, &name_assign_type);
620
621 if (name_assign_type == NET_NAME_ENUM)
622 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
623 ifname, network->filename);
624 else
625 log_debug("%s: found matching network '%s'", ifname, network->filename);
626 } else
627 log_debug("%s: found matching network '%s'", ifname, network->filename);
628
629 *ret = network;
630 return 0;
631 }
632 }
633
634 *ret = NULL;
635
636 return -ENOENT;
637 }
638
639 int network_apply(Network *network, Link *link) {
640 int r;
641
642 assert(network);
643 assert(link);
644
645 link->network = network;
646
647 if (network->ipv4ll_route) {
648 Route *route;
649
650 r = route_new_static(network, NULL, 0, &route);
651 if (r < 0)
652 return r;
653
654 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
655 if (r == 0)
656 return -EINVAL;
657 if (r < 0)
658 return -errno;
659
660 route->family = AF_INET;
661 route->dst_prefixlen = 16;
662 route->scope = RT_SCOPE_LINK;
663 route->priority = IPV4LL_ROUTE_METRIC;
664 route->protocol = RTPROT_STATIC;
665 }
666
667 if (network->n_dns > 0 ||
668 !strv_isempty(network->ntp) ||
669 !ordered_set_isempty(network->search_domains) ||
670 !ordered_set_isempty(network->route_domains))
671 link_dirty(link);
672
673 return 0;
674 }
675
676 bool network_has_static_ipv6_addresses(Network *network) {
677 Address *address;
678
679 assert(network);
680
681 LIST_FOREACH(addresses, address, network->static_addresses) {
682 if (address->family == AF_INET6)
683 return true;
684 }
685
686 return false;
687 }
688
689 int config_parse_stacked_netdev(const char *unit,
690 const char *filename,
691 unsigned line,
692 const char *section,
693 unsigned section_line,
694 const char *lvalue,
695 int ltype,
696 const char *rvalue,
697 void *data,
698 void *userdata) {
699 _cleanup_free_ char *name = NULL;
700 NetDevKind kind = ltype;
701 Hashmap **h = data;
702 int r;
703
704 assert(filename);
705 assert(lvalue);
706 assert(rvalue);
707 assert(data);
708 assert(IN_SET(kind,
709 NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
710 NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, _NETDEV_KIND_TUNNEL));
711
712 if (!ifname_valid(rvalue)) {
713 log_syntax(unit, LOG_ERR, filename, line, 0,
714 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
715 return 0;
716 }
717
718 name = strdup(rvalue);
719 if (!name)
720 return log_oom();
721
722 r = hashmap_ensure_allocated(h, &string_hash_ops);
723 if (r < 0)
724 return log_oom();
725
726 r = hashmap_put(*h, name, INT_TO_PTR(kind));
727 if (r < 0)
728 log_syntax(unit, LOG_ERR, filename, line, r,
729 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
730 else if (r == 0)
731 log_syntax(unit, LOG_DEBUG, filename, line, r,
732 "NetDev '%s' specified twice, ignoring.", name);
733 else
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 if (strv_length(*l) > MAX_NTP_SERVERS) {
1462 log_syntax(unit, LOG_WARNING, filename, line, 0,
1463 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1464 MAX_NTP_SERVERS, w);
1465 break;
1466 }
1467
1468 r = strv_consume(l, TAKE_PTR(w));
1469 if (r < 0)
1470 return log_oom();
1471 }
1472
1473 return 0;
1474 }
1475
1476 int config_parse_dhcp_user_class(
1477 const char *unit,
1478 const char *filename,
1479 unsigned line,
1480 const char *section,
1481 unsigned section_line,
1482 const char *lvalue,
1483 int ltype,
1484 const char *rvalue,
1485 void *data,
1486 void *userdata) {
1487
1488 char ***l = data;
1489 int r;
1490
1491 assert(l);
1492 assert(lvalue);
1493 assert(rvalue);
1494
1495 if (isempty(rvalue)) {
1496 *l = strv_free(*l);
1497 return 0;
1498 }
1499
1500 for (;;) {
1501 _cleanup_free_ char *w = NULL;
1502
1503 r = extract_first_word(&rvalue, &w, NULL, 0);
1504 if (r == -ENOMEM)
1505 return log_oom();
1506 if (r < 0) {
1507 log_syntax(unit, LOG_ERR, filename, line, r,
1508 "Failed to split user classes option, ignoring: %s", rvalue);
1509 break;
1510 }
1511 if (r == 0)
1512 break;
1513
1514 if (strlen(w) > 255) {
1515 log_syntax(unit, LOG_ERR, filename, line, 0,
1516 "%s length is not in the range 1-255, ignoring.", w);
1517 continue;
1518 }
1519
1520 r = strv_push(l, w);
1521 if (r < 0)
1522 return log_oom();
1523
1524 w = NULL;
1525 }
1526
1527 return 0;
1528 }
1529
1530 int config_parse_section_route_table(
1531 const char *unit,
1532 const char *filename,
1533 unsigned line,
1534 const char *section,
1535 unsigned section_line,
1536 const char *lvalue,
1537 int ltype,
1538 const char *rvalue,
1539 void *data,
1540 void *userdata) {
1541
1542 Network *network = data;
1543 uint32_t rt;
1544 int r;
1545
1546 assert(filename);
1547 assert(lvalue);
1548 assert(rvalue);
1549 assert(data);
1550
1551 r = safe_atou32(rvalue, &rt);
1552 if (r < 0) {
1553 log_syntax(unit, LOG_ERR, filename, line, r,
1554 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
1555 return 0;
1556 }
1557
1558 if (streq_ptr(section, "DHCP")) {
1559 network->dhcp_route_table = rt;
1560 network->dhcp_route_table_set = true;
1561 } else { /* section is IPv6AcceptRA */
1562 network->ipv6_accept_ra_route_table = rt;
1563 network->ipv6_accept_ra_route_table_set = true;
1564 }
1565
1566 return 0;
1567 }
1568
1569 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
1570 "Failed to parse DHCP use domains setting");
1571
1572 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1573 [DHCP_USE_DOMAINS_NO] = "no",
1574 [DHCP_USE_DOMAINS_ROUTE] = "route",
1575 [DHCP_USE_DOMAINS_YES] = "yes",
1576 };
1577
1578 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
1579
1580 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1581
1582 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1583 [LLDP_MODE_NO] = "no",
1584 [LLDP_MODE_YES] = "yes",
1585 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1586 };
1587
1588 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
1589
1590 int config_parse_iaid(const char *unit,
1591 const char *filename,
1592 unsigned line,
1593 const char *section,
1594 unsigned section_line,
1595 const char *lvalue,
1596 int ltype,
1597 const char *rvalue,
1598 void *data,
1599 void *userdata) {
1600 Network *network = data;
1601 uint32_t iaid;
1602 int r;
1603
1604 assert(filename);
1605 assert(lvalue);
1606 assert(rvalue);
1607 assert(network);
1608
1609 r = safe_atou32(rvalue, &iaid);
1610 if (r < 0) {
1611 log_syntax(unit, LOG_ERR, filename, line, r,
1612 "Unable to read IAID, ignoring assignment: %s", rvalue);
1613 return 0;
1614 }
1615
1616 network->iaid = iaid;
1617 network->iaid_set = true;
1618
1619 return 0;
1620 }