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