]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
nspawn: when running nspawn, set a $PATH including both bin + sbin by default (#8756)
[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
TG
111static int network_load_one(Manager *manager, const char *filename) {
112 _cleanup_network_free_ Network *network = NULL;
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;
49092e22 250
23bb31aa
ZJS
251 dropin_dirname = strjoina(network->name, ".network.d");
252
253 r = config_parse_many(filename, network_dirs, dropin_dirname,
254 "Match\0"
255 "Link\0"
256 "Network\0"
257 "Address\0"
95b74ef6 258 "IPv6AddressLabel\0"
bce67bbe 259 "RoutingPolicyRule\0"
23bb31aa
ZJS
260 "Route\0"
261 "DHCP\0"
262 "DHCPv4\0" /* compat */
263 "DHCPServer\0"
264 "IPv6AcceptRA\0"
a0e5c15d 265 "IPv6NDPProxyAddress\0"
23bb31aa
ZJS
266 "Bridge\0"
267 "BridgeFDB\0"
9d5d0090 268 "BridgeVLAN\0"
7d5cac19 269 "IPv6PrefixDelegation\0"
9d5d0090 270 "IPv6Prefix\0",
23bb31aa 271 config_item_perf_lookup, network_network_gperf_lookup,
bcde742e 272 CONFIG_PARSE_WARN, network);
36f822c4 273 if (r < 0)
f579559b 274 return r;
f579559b 275
add8d07d 276 network_apply_anonymize_if_set(network);
277
5a8bcb67
LP
278 /* IPMasquerade=yes implies IPForward=yes */
279 if (network->ip_masquerade)
769d324c 280 network->ip_forward |= ADDRESS_FAMILY_IPV4;
5a8bcb67 281
f579559b 282 LIST_PREPEND(networks, manager->networks, network);
b3070dc0 283
dbffab87
TG
284 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
285 if (r < 0)
286 return r;
287
288 r = hashmap_put(manager->networks_by_name, network->name, network);
289 if (r < 0)
290 return r;
291
3d3d4255 292 LIST_FOREACH(routes, route, network->static_routes) {
b3070dc0
TG
293 if (!route->family) {
294 log_warning("Route section without Gateway field configured in %s. "
295 "Ignoring", filename);
296 return 0;
297 }
b3070dc0
TG
298 }
299
3d3d4255 300 LIST_FOREACH(addresses, address, network->static_addresses) {
b3070dc0
TG
301 if (!address->family) {
302 log_warning("Address section without Address field configured in %s. "
303 "Ignoring", filename);
304 return 0;
305 }
306 }
307
f579559b
TG
308 network = NULL;
309
310 return 0;
311}
312
313int network_load(Manager *manager) {
314 Network *network;
477e73b5
ZJS
315 _cleanup_strv_free_ char **files = NULL;
316 char **f;
f579559b
TG
317 int r;
318
319 assert(manager);
320
321 while ((network = manager->networks))
322 network_free(network);
323
b5084605 324 r = conf_files_list_strv(&files, ".network", NULL, 0, network_dirs);
f647962d
MS
325 if (r < 0)
326 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b
TG
327
328 STRV_FOREACH_BACKWARDS(f, files) {
329 r = network_load_one(manager, *f);
330 if (r < 0)
331 return r;
332 }
333
f579559b
TG
334 return 0;
335}
336
f579559b 337void network_free(Network *network) {
a0e5c15d 338 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
bce67bbe
SS
339 RoutingPolicyRule *rule;
340 FdbEntry *fdb_entry;
95b74ef6 341 AddressLabel *label;
057abfd8 342 Prefix *prefix;
bce67bbe
SS
343 Address *address;
344 NetDev *netdev;
345 Route *route;
06f021a8 346 Iterator i;
f579559b
TG
347
348 if (!network)
349 return;
350
351 free(network->filename);
352
353 free(network->match_mac);
5256e00e
TG
354 strv_free(network->match_path);
355 strv_free(network->match_driver);
356 strv_free(network->match_type);
357 strv_free(network->match_name);
f579559b
TG
358
359 free(network->description);
edb85f0d 360 free(network->dhcp_vendor_class_identifier);
27cb34f5 361 free(network->dhcp_hostname);
f579559b 362
c106cc36
TG
363 free(network->mac);
364
b0e39c82 365 strv_free(network->ntp);
5512a963 366 free(network->dns);
3df9bec5
LP
367 strv_free(network->search_domains);
368 strv_free(network->route_domains);
0d4ad91d 369 strv_free(network->bind_carrier);
3bef724f 370
47e2dc31 371 netdev_unref(network->bridge);
47e2dc31 372 netdev_unref(network->bond);
6cb955c6 373 netdev_unref(network->vrf);
47e2dc31 374
2c36be2f
TG
375 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
376 hashmap_remove(network->stacked_netdevs, netdev->ifname);
326cb406 377 netdev_unref(netdev);
2c36be2f 378 }
6a0a2f86 379 hashmap_free(network->stacked_netdevs);
326cb406 380
f048a16b 381 while ((route = network->static_routes))
f579559b
TG
382 route_free(route);
383
f048a16b 384 while ((address = network->static_addresses))
f579559b
TG
385 address_free(address);
386
b98b483b
AR
387 while ((fdb_entry = network->static_fdb_entries))
388 fdb_entry_free(fdb_entry);
389
a0e5c15d
FK
390 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
391 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
392
95b74ef6
SS
393 while ((label = network->address_labels))
394 address_label_free(label);
395
057abfd8
PF
396 while ((prefix = network->static_prefixes))
397 prefix_free(prefix);
398
bce67bbe
SS
399 while ((rule = network->rules))
400 routing_policy_rule_free(rule);
401
6ae115c1
TG
402 hashmap_free(network->addresses_by_section);
403 hashmap_free(network->routes_by_section);
b98b483b 404 hashmap_free(network->fdb_entries_by_section);
95b74ef6 405 hashmap_free(network->address_labels_by_section);
057abfd8 406 hashmap_free(network->prefixes_by_section);
bce67bbe 407 hashmap_free(network->rules_by_section);
6ae115c1 408
dbffab87
TG
409 if (network->manager) {
410 if (network->manager->networks)
411 LIST_REMOVE(networks, network->manager->networks, network);
412
413 if (network->manager->networks_by_name)
414 hashmap_remove(network->manager->networks_by_name, network->name);
415 }
416
417 free(network->name);
f579559b 418
79e16ce3
LP
419 condition_free_list(network->match_host);
420 condition_free_list(network->match_virt);
5022f08a
LP
421 condition_free_list(network->match_kernel_cmdline);
422 condition_free_list(network->match_kernel_version);
79e16ce3
LP
423 condition_free_list(network->match_arch);
424
8eb9058d 425 free(network->dhcp_server_timezone);
1a04db0f
LP
426 free(network->dhcp_server_dns);
427 free(network->dhcp_server_ntp);
8eb9058d 428
8a516214
LP
429 set_free_free(network->dnssec_negative_trust_anchors);
430
f579559b
TG
431 free(network);
432}
433
dbffab87
TG
434int network_get_by_name(Manager *manager, const char *name, Network **ret) {
435 Network *network;
436
437 assert(manager);
438 assert(name);
439 assert(ret);
440
441 network = hashmap_get(manager->networks_by_name, name);
442 if (!network)
443 return -ENOENT;
444
445 *ret = network;
446
447 return 0;
448}
449
505f8da7
TG
450int network_get(Manager *manager, struct udev_device *device,
451 const char *ifname, const struct ether_addr *address,
452 Network **ret) {
f579559b 453 Network *network;
af3aa302 454 struct udev_device *parent;
24c083df 455 const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
f579559b
TG
456
457 assert(manager);
f579559b 458 assert(ret);
af3aa302 459
24c083df
TG
460 if (device) {
461 path = udev_device_get_property_value(device, "ID_PATH");
af3aa302 462
24c083df
TG
463 parent = udev_device_get_parent(device);
464 if (parent)
465 parent_driver = udev_device_get_driver(parent);
af3aa302 466
24c083df 467 driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
af3aa302 468
24c083df
TG
469 devtype = udev_device_get_devtype(device);
470 }
f579559b 471
f579559b
TG
472 LIST_FOREACH(networks, network, manager->networks) {
473 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
474 network->match_driver, network->match_type,
475 network->match_name, network->match_host,
5022f08a
LP
476 network->match_virt, network->match_kernel_cmdline,
477 network->match_kernel_version, network->match_arch,
af3aa302
TG
478 address, path, parent_driver, driver,
479 devtype, ifname)) {
24c083df 480 if (network->match_name && device) {
ca6038b8
TG
481 const char *attr;
482 uint8_t name_assign_type = NET_NAME_UNKNOWN;
483
32bc8adc 484 attr = udev_device_get_sysattr_value(device, "name_assign_type");
285760fe 485 if (attr)
dc751688 486 (void) safe_atou8(attr, &name_assign_type);
32bc8adc
TG
487
488 if (name_assign_type == NET_NAME_ENUM)
a2fae7bb
TG
489 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
490 ifname, network->filename);
32bc8adc 491 else
a2fae7bb 492 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 493 } else
a2fae7bb 494 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 495
f579559b
TG
496 *ret = network;
497 return 0;
498 }
499 }
500
501 *ret = NULL;
502
503 return -ENOENT;
504}
505
7d342c03 506int network_apply(Network *network, Link *link) {
f579559b
TG
507 int r;
508
c4a03a56
TG
509 assert(network);
510 assert(link);
511
f579559b
TG
512 link->network = network;
513
bfa695b5
TG
514 if (network->ipv4ll_route) {
515 Route *route;
516
0b180d75 517 r = route_new_static(network, NULL, 0, &route);
bfa695b5
TG
518 if (r < 0)
519 return r;
520
2ce40956 521 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
bfa695b5
TG
522 if (r == 0)
523 return -EINVAL;
524 if (r < 0)
525 return -errno;
526
527 route->family = AF_INET;
528 route->dst_prefixlen = 16;
529 route->scope = RT_SCOPE_LINK;
86655331 530 route->priority = IPV4LL_ROUTE_METRIC;
bfa695b5
TG
531 route->protocol = RTPROT_STATIC;
532 }
533
5512a963 534 if (network->n_dns > 0 ||
3df9bec5
LP
535 !strv_isempty(network->ntp) ||
536 !strv_isempty(network->search_domains) ||
2ad6b610 537 !strv_isempty(network->route_domains))
84de38c5 538 link_dirty(link);
3bef724f 539
f579559b
TG
540 return 0;
541}
02b59d57 542
439689c6
SS
543bool network_has_static_ipv6_addresses(Network *network) {
544 Address *address;
545
546 assert(network);
547
548 LIST_FOREACH(addresses, address, network->static_addresses) {
549 if (address->family == AF_INET6)
550 return true;
551 }
552
553 return false;
554}
555
69a93e7d 556int config_parse_netdev(const char *unit,
02b59d57
TG
557 const char *filename,
558 unsigned line,
559 const char *section,
560 unsigned section_line,
561 const char *lvalue,
562 int ltype,
563 const char *rvalue,
564 void *data,
565 void *userdata) {
566 Network *network = userdata;
31d0ac36
TG
567 _cleanup_free_ char *kind_string = NULL;
568 char *p;
1a436809 569 NetDev *netdev;
69a93e7d 570 NetDevKind kind;
02b59d57
TG
571 int r;
572
573 assert(filename);
574 assert(lvalue);
575 assert(rvalue);
576 assert(data);
577
69a93e7d
TG
578 kind_string = strdup(lvalue);
579 if (!kind_string)
580 return log_oom();
52433f6b 581
69a93e7d
TG
582 /* the keys are CamelCase versions of the kind */
583 for (p = kind_string; *p; p++)
584 *p = tolower(*p);
52433f6b 585
69a93e7d
TG
586 kind = netdev_kind_from_string(kind_string);
587 if (kind == _NETDEV_KIND_INVALID) {
12ca818f 588 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
589 return 0;
590 }
591
54abf461
TG
592 r = netdev_get(network->manager, rvalue, &netdev);
593 if (r < 0) {
12ca818f 594 log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
595 return 0;
596 }
597
69a93e7d 598 if (netdev->kind != kind) {
12ca818f 599 log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
600 return 0;
601 }
602
69a93e7d
TG
603 switch (kind) {
604 case NETDEV_KIND_BRIDGE:
605 network->bridge = netdev;
54abf461 606
69a93e7d
TG
607 break;
608 case NETDEV_KIND_BOND:
609 network->bond = netdev;
fe6b2d55 610
6cb955c6
AR
611 break;
612 case NETDEV_KIND_VRF:
613 network->vrf = netdev;
614
69a93e7d
TG
615 break;
616 case NETDEV_KIND_VLAN:
69a93e7d 617 case NETDEV_KIND_MACVLAN:
f33ff02b 618 case NETDEV_KIND_MACVTAP:
c4a5ddc9 619 case NETDEV_KIND_IPVLAN:
326cb406 620 case NETDEV_KIND_VXLAN:
92c918b0 621 case NETDEV_KIND_VCAN:
6a0a2f86 622 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406 623 if (r < 0) {
87ac8d99 624 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add NetDev '%s' to network: %m", rvalue);
326cb406
SS
625 return 0;
626 }
627
69a93e7d
TG
628 break;
629 default:
87ac8d99 630 assert_not_reached("Cannot parse NetDev");
fe6b2d55
TG
631 }
632
47e2dc31
TG
633 netdev_ref(netdev);
634
fe6b2d55
TG
635 return 0;
636}
7951dea2 637
3df9bec5
LP
638int config_parse_domains(
639 const char *unit,
640 const char *filename,
641 unsigned line,
642 const char *section,
643 unsigned section_line,
644 const char *lvalue,
645 int ltype,
646 const char *rvalue,
647 void *data,
648 void *userdata) {
649
650 const char *p;
651 Network *n = data;
6192b846
TG
652 int r;
653
3df9bec5
LP
654 assert(n);
655 assert(lvalue);
656 assert(rvalue);
6192b846 657
3df9bec5
LP
658 if (isempty(rvalue)) {
659 n->search_domains = strv_free(n->search_domains);
660 n->route_domains = strv_free(n->route_domains);
661 return 0;
662 }
67272d15 663
3df9bec5
LP
664 p = rvalue;
665 for (;;) {
666 _cleanup_free_ char *w = NULL, *normalized = NULL;
667 const char *domain;
668 bool is_route;
669
670 r = extract_first_word(&p, &w, NULL, 0);
671 if (r < 0) {
672 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
673 break;
674 }
675 if (r == 0)
676 break;
677
678 is_route = w[0] == '~';
679 domain = is_route ? w + 1 : w;
680
681 if (dns_name_is_root(domain) || streq(domain, "*")) {
682 /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
683 * routing domain, unconditionally. */
684 is_route = true;
685 domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
686
687 } else {
688 r = dns_name_normalize(domain, &normalized);
689 if (r < 0) {
690 log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
691 continue;
692 }
693
694 domain = normalized;
695
696 if (is_localhost(domain)) {
697 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 698 continue;
3df9bec5 699 }
37de2509 700 }
40274ed6 701
3df9bec5
LP
702 if (is_route) {
703 r = strv_extend(&n->route_domains, domain);
704 if (r < 0)
705 return log_oom();
40274ed6 706
3df9bec5
LP
707 } else {
708 r = strv_extend(&n->search_domains, domain);
709 if (r < 0)
710 return log_oom();
711 }
40274ed6 712 }
f15b6e5a 713
3df9bec5
LP
714 strv_uniq(n->route_domains);
715 strv_uniq(n->search_domains);
716
6192b846
TG
717 return 0;
718}
719
7951dea2
SS
720int config_parse_tunnel(const char *unit,
721 const char *filename,
722 unsigned line,
723 const char *section,
724 unsigned section_line,
725 const char *lvalue,
726 int ltype,
727 const char *rvalue,
728 void *data,
729 void *userdata) {
730 Network *network = userdata;
731 NetDev *netdev;
732 int r;
733
734 assert(filename);
735 assert(lvalue);
736 assert(rvalue);
737 assert(data);
738
739 r = netdev_get(network->manager, rvalue, &netdev);
740 if (r < 0) {
6a7a4e4d 741 log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
7951dea2
SS
742 return 0;
743 }
744
d29ad81e
ZJS
745 if (!IN_SET(netdev->kind,
746 NETDEV_KIND_IPIP,
747 NETDEV_KIND_SIT,
748 NETDEV_KIND_GRE,
749 NETDEV_KIND_GRETAP,
750 NETDEV_KIND_IP6GRE,
751 NETDEV_KIND_IP6GRETAP,
752 NETDEV_KIND_VTI,
753 NETDEV_KIND_VTI6,
754 NETDEV_KIND_IP6TNL)) {
12ca818f 755 log_syntax(unit, LOG_ERR, filename, line, 0,
7951dea2
SS
756 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
757 return 0;
758 }
759
6a0a2f86
TG
760 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
761 if (r < 0) {
6a7a4e4d 762 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
6a0a2f86
TG
763 return 0;
764 }
765
766 netdev_ref(netdev);
7951dea2
SS
767
768 return 0;
769}
bd8f6538 770
d0d6a4cd
TG
771int config_parse_ipv4ll(
772 const char* unit,
773 const char *filename,
774 unsigned line,
775 const char *section,
776 unsigned section_line,
777 const char *lvalue,
778 int ltype,
779 const char *rvalue,
780 void *data,
781 void *userdata) {
782
783 AddressFamilyBoolean *link_local = data;
784
785 assert(filename);
786 assert(lvalue);
787 assert(rvalue);
788 assert(data);
789
790 /* Note that this is mostly like
791 * config_parse_address_family_boolean(), except that it
792 * applies only to IPv4 */
793
5883ff60 794 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
d0d6a4cd
TG
795
796 return 0;
797}
798
bd8f6538
TG
799int config_parse_dhcp(
800 const char* unit,
801 const char *filename,
802 unsigned line,
803 const char *section,
804 unsigned section_line,
805 const char *lvalue,
806 int ltype,
807 const char *rvalue,
808 void *data,
809 void *userdata) {
810
cb9fc36a 811 AddressFamilyBoolean *dhcp = data, s;
bd8f6538
TG
812
813 assert(filename);
814 assert(lvalue);
815 assert(rvalue);
816 assert(data);
817
769d324c
LP
818 /* Note that this is mostly like
819 * config_parse_address_family_boolean(), except that it
820 * understands some old names for the enum values */
821
cb9fc36a
LP
822 s = address_family_boolean_from_string(rvalue);
823 if (s < 0) {
824
825 /* Previously, we had a slightly different enum here,
826 * support its values for compatbility. */
827
828 if (streq(rvalue, "none"))
829 s = ADDRESS_FAMILY_NO;
830 else if (streq(rvalue, "v4"))
831 s = ADDRESS_FAMILY_IPV4;
832 else if (streq(rvalue, "v6"))
833 s = ADDRESS_FAMILY_IPV6;
834 else if (streq(rvalue, "both"))
835 s = ADDRESS_FAMILY_YES;
836 else {
12ca818f 837 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
bd8f6538
TG
838 return 0;
839 }
bd8f6538
TG
840 }
841
cb9fc36a 842 *dhcp = s;
bd8f6538
TG
843 return 0;
844}
845
3e43b2cd
JJ
846static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
847 [DHCP_CLIENT_ID_MAC] = "mac",
dace710c
YW
848 [DHCP_CLIENT_ID_DUID] = "duid",
849 [DHCP_CLIENT_ID_DUID_ONLY] = "duid-only",
3e43b2cd
JJ
850};
851
499d555a
SS
852DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier);
853DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier, "Failed to parse client identifier type");
3e43b2cd 854
60c35566 855int config_parse_ipv6token(
7f77697a
TG
856 const char* unit,
857 const char *filename,
858 unsigned line,
859 const char *section,
860 unsigned section_line,
861 const char *lvalue,
862 int ltype,
863 const char *rvalue,
864 void *data,
865 void *userdata) {
866
867 union in_addr_union buffer;
868 struct in6_addr *token = data;
869 int r;
870
871 assert(filename);
872 assert(lvalue);
873 assert(rvalue);
874 assert(token);
875
876 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
877 if (r < 0) {
6a7a4e4d 878 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
7f77697a
TG
879 return 0;
880 }
881
882 r = in_addr_is_null(AF_INET6, &buffer);
12ca818f 883 if (r != 0) {
87ac8d99 884 log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
7f77697a
TG
885 return 0;
886 }
887
888 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
87ac8d99 889 log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
7f77697a
TG
890 return 0;
891 }
892
893 *token = buffer.in6;
894
895 return 0;
896}
8add5f79 897
49092e22 898static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1f0d9695
LP
899 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
900 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
901 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
49092e22
SS
902};
903
904DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
905
906int config_parse_ipv6_privacy_extensions(
907 const char* unit,
908 const char *filename,
909 unsigned line,
910 const char *section,
911 unsigned section_line,
912 const char *lvalue,
913 int ltype,
914 const char *rvalue,
915 void *data,
916 void *userdata) {
917
918 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
919 int k;
920
921 assert(filename);
922 assert(lvalue);
923 assert(rvalue);
924 assert(ipv6_privacy_extensions);
925
926 /* Our enum shall be a superset of booleans, hence first try
927 * to parse as boolean, and then as enum */
928
929 k = parse_boolean(rvalue);
930 if (k > 0)
1f0d9695 931 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
49092e22 932 else if (k == 0)
1f0d9695 933 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
49092e22 934 else {
1f0d9695 935 IPv6PrivacyExtensions s;
49092e22
SS
936
937 s = ipv6_privacy_extensions_from_string(rvalue);
1f0d9695
LP
938 if (s < 0) {
939
940 if (streq(rvalue, "kernel"))
941 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
942 else {
12ca818f 943 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1f0d9695
LP
944 return 0;
945 }
49092e22
SS
946 }
947
948 *ipv6_privacy_extensions = s;
949 }
950
951 return 0;
952}
a7d0ef44 953
1ac608c9
LP
954int config_parse_hostname(
955 const char *unit,
956 const char *filename,
957 unsigned line,
958 const char *section,
959 unsigned section_line,
960 const char *lvalue,
961 int ltype,
962 const char *rvalue,
963 void *data,
964 void *userdata) {
965
966 char **hostname = data, *hn = NULL;
a7d0ef44
SS
967 int r;
968
969 assert(filename);
970 assert(lvalue);
971 assert(rvalue);
972
1ac608c9 973 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
a7d0ef44
SS
974 if (r < 0)
975 return r;
976
1ac608c9 977 if (!hostname_is_valid(hn, false)) {
12ca818f 978 log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
a7d0ef44
SS
979 free(hn);
980 return 0;
981 }
982
1ac608c9 983 free(*hostname);
ae691c1d 984 *hostname = hostname_cleanup(hn);
a7d0ef44
SS
985 return 0;
986}
8eb9058d
LP
987
988int config_parse_timezone(
989 const char *unit,
990 const char *filename,
991 unsigned line,
992 const char *section,
993 unsigned section_line,
994 const char *lvalue,
995 int ltype,
996 const char *rvalue,
997 void *data,
998 void *userdata) {
999
64d6c229 1000 char **datap = data, *tz = NULL;
8eb9058d
LP
1001 int r;
1002
1003 assert(filename);
1004 assert(lvalue);
1005 assert(rvalue);
1006
1007 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1008 if (r < 0)
1009 return r;
1010
1011 if (!timezone_is_valid(tz)) {
12ca818f 1012 log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
8eb9058d
LP
1013 free(tz);
1014 return 0;
1015 }
1016
64d6c229
TA
1017 free(*datap);
1018 *datap = tz;
8eb9058d
LP
1019
1020 return 0;
1021}
1a04db0f
LP
1022
1023int config_parse_dhcp_server_dns(
1024 const char *unit,
1025 const char *filename,
1026 unsigned line,
1027 const char *section,
1028 unsigned section_line,
1029 const char *lvalue,
1030 int ltype,
1031 const char *rvalue,
1032 void *data,
1033 void *userdata) {
1034
1035 Network *n = data;
1036 const char *p = rvalue;
1037 int r;
1038
1039 assert(filename);
1040 assert(lvalue);
1041 assert(rvalue);
1042
1043 for (;;) {
1044 _cleanup_free_ char *w = NULL;
1045 struct in_addr a, *m;
1046
1047 r = extract_first_word(&p, &w, NULL, 0);
fa105ce6
LP
1048 if (r == -ENOMEM)
1049 return log_oom();
1a04db0f
LP
1050 if (r < 0) {
1051 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1052 return 0;
1053 }
1a04db0f 1054 if (r == 0)
fa105ce6 1055 break;
1a04db0f
LP
1056
1057 if (inet_pton(AF_INET, w, &a) <= 0) {
12ca818f 1058 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
1a04db0f
LP
1059 continue;
1060 }
1061
62d74c78 1062 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
1a04db0f
LP
1063 if (!m)
1064 return log_oom();
1065
1066 m[n->n_dhcp_server_dns++] = a;
1067 n->dhcp_server_dns = m;
1068 }
fa105ce6 1069
88295a05
PF
1070 return 0;
1071}
1072
1073int config_parse_radv_dns(
1074 const char *unit,
1075 const char *filename,
1076 unsigned line,
1077 const char *section,
1078 unsigned section_line,
1079 const char *lvalue,
1080 int ltype,
1081 const char *rvalue,
1082 void *data,
1083 void *userdata) {
1084
1085 Network *n = data;
1086 const char *p = rvalue;
1087 int r;
1088
1089 assert(filename);
1090 assert(lvalue);
1091 assert(rvalue);
1092
1093 for (;;) {
1094 _cleanup_free_ char *w = NULL;
1095 union in_addr_union a;
1096
1097 r = extract_first_word(&p, &w, NULL, 0);
1098 if (r == -ENOMEM)
1099 return log_oom();
1100 if (r < 0) {
1101 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1102 return 0;
1103 }
1104 if (r == 0)
1105 break;
1106
1107 if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
1108 struct in6_addr *m;
1109
62d74c78 1110 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
88295a05
PF
1111 if (!m)
1112 return log_oom();
1113
1114 m[n->n_router_dns++] = a.in6;
1115 n->router_dns = m;
1116
1117 } else
1118 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
1119
1120 }
1121
700f1186
PF
1122 return 0;
1123}
1124
1125int config_parse_radv_search_domains(
1126 const char *unit,
1127 const char *filename,
1128 unsigned line,
1129 const char *section,
1130 unsigned section_line,
1131 const char *lvalue,
1132 int ltype,
1133 const char *rvalue,
1134 void *data,
1135 void *userdata) {
1136
1137 Network *n = data;
1138 const char *p = rvalue;
1139 int r;
1140
1141 assert(filename);
1142 assert(lvalue);
1143 assert(rvalue);
1144
1145 for (;;) {
1146 _cleanup_free_ char *w = NULL;
1147 _cleanup_free_ char *idna = NULL;
1148
1149 r = extract_first_word(&p, &w, NULL, 0);
1150 if (r == -ENOMEM)
1151 return log_oom();
1152 if (r < 0) {
1153 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1154 return 0;
1155 }
1156 if (r == 0)
1157 break;
1158
1159 r = dns_name_apply_idna(w, &idna);
1160 if (r > 0) {
1161 r = strv_push(&n->router_search_domains, idna);
1162 if (r >= 0)
1163 idna = NULL;
1164 } else if (r == 0) {
1165 r = strv_push(&n->router_search_domains, w);
1166 if (r >= 0)
1167 w = NULL;
1168 }
1169 }
1170
fa105ce6 1171 return 0;
1a04db0f
LP
1172}
1173
1174int config_parse_dhcp_server_ntp(
1175 const char *unit,
1176 const char *filename,
1177 unsigned line,
1178 const char *section,
1179 unsigned section_line,
1180 const char *lvalue,
1181 int ltype,
1182 const char *rvalue,
1183 void *data,
1184 void *userdata) {
1185
1186 Network *n = data;
1187 const char *p = rvalue;
1188 int r;
1189
1190 assert(filename);
1191 assert(lvalue);
1192 assert(rvalue);
1193
1194 for (;;) {
1195 _cleanup_free_ char *w = NULL;
1196 struct in_addr a, *m;
1197
1198 r = extract_first_word(&p, &w, NULL, 0);
fa105ce6
LP
1199 if (r == -ENOMEM)
1200 return log_oom();
1a04db0f 1201 if (r < 0) {
12ca818f 1202 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1a04db0f
LP
1203 return 0;
1204 }
1a04db0f
LP
1205 if (r == 0)
1206 return 0;
1207
1208 if (inet_pton(AF_INET, w, &a) <= 0) {
12ca818f 1209 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w);
1a04db0f
LP
1210 continue;
1211 }
1212
62d74c78 1213 m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
1a04db0f
LP
1214 if (!m)
1215 return log_oom();
1216
1217 m[n->n_dhcp_server_ntp++] = a;
1218 n->dhcp_server_ntp = m;
1219 }
1220}
8a516214 1221
53253824
SS
1222int config_parse_dns(
1223 const char *unit,
1224 const char *filename,
1225 unsigned line,
1226 const char *section,
1227 unsigned section_line,
1228 const char *lvalue,
1229 int ltype,
1230 const char *rvalue,
1231 void *data,
1232 void *userdata) {
1233
1234 Network *n = userdata;
1235 int r;
1236
1237 assert(filename);
1238 assert(lvalue);
1239 assert(rvalue);
1240
1241 for (;;) {
1242 _cleanup_free_ char *w = NULL;
1243 union in_addr_union a;
5512a963 1244 struct in_addr_data *m;
53253824
SS
1245 int family;
1246
5512a963 1247 r = extract_first_word(&rvalue, &w, NULL, 0);
53253824
SS
1248 if (r == -ENOMEM)
1249 return log_oom();
1250 if (r < 0) {
1251 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1252 break;
1253 }
5512a963
LP
1254 if (r == 0)
1255 break;
53253824
SS
1256
1257 r = in_addr_from_string_auto(w, &family, &a);
1258 if (r < 0) {
5512a963 1259 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse dns server address, ignoring: %s", w);
53253824
SS
1260 continue;
1261 }
1262
62d74c78 1263 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
5512a963 1264 if (!m)
53253824
SS
1265 return log_oom();
1266
5512a963
LP
1267 m[n->n_dns++] = (struct in_addr_data) {
1268 .family = family,
1269 .address = a,
1270 };
1271
1272 n->dns = m;
53253824
SS
1273 }
1274
1275 return 0;
1276}
1277
8a516214
LP
1278int config_parse_dnssec_negative_trust_anchors(
1279 const char *unit,
1280 const char *filename,
1281 unsigned line,
1282 const char *section,
1283 unsigned section_line,
1284 const char *lvalue,
1285 int ltype,
1286 const char *rvalue,
1287 void *data,
1288 void *userdata) {
1289
1290 const char *p = rvalue;
1291 Network *n = data;
1292 int r;
1293
3df9bec5 1294 assert(n);
8a516214
LP
1295 assert(lvalue);
1296 assert(rvalue);
1297
1298 if (isempty(rvalue)) {
1299 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1300 return 0;
1301 }
1302
1303 for (;;) {
1304 _cleanup_free_ char *w = NULL;
1305
1306 r = extract_first_word(&p, &w, NULL, 0);
1307 if (r < 0) {
1308 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1309 break;
1310 }
1311 if (r == 0)
1312 break;
1313
1314 r = dns_name_is_valid(w);
1315 if (r <= 0) {
1316 log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
1317 continue;
1318 }
1319
cbbf38ae
LP
1320 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1321 if (r < 0)
1322 return log_oom();
1323
8a516214
LP
1324 r = set_put(n->dnssec_negative_trust_anchors, w);
1325 if (r < 0)
1326 return log_oom();
1327 if (r > 0)
1328 w = NULL;
1329 }
1330
1331 return 0;
1332}
b2a81c0b 1333
26575990
LP
1334int config_parse_ntp(
1335 const char *unit,
1336 const char *filename,
1337 unsigned line,
1338 const char *section,
1339 unsigned section_line,
1340 const char *lvalue,
1341 int ltype,
1342 const char *rvalue,
1343 void *data,
1344 void *userdata) {
1345
1346 char ***l = data;
1347 int r;
1348
1349 assert(l);
1350 assert(lvalue);
1351 assert(rvalue);
1352
1353 if (isempty(rvalue)) {
1354 *l = strv_free(*l);
1355 return 0;
1356 }
1357
1358 for (;;) {
1359 _cleanup_free_ char *w = NULL;
1360
1361 r = extract_first_word(&rvalue, &w, NULL, 0);
1362 if (r == -ENOMEM)
1363 return log_oom();
1364 if (r < 0) {
1365 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract NTP server name, ignoring: %s", rvalue);
1366 break;
1367 }
1368 if (r == 0)
1369 break;
1370
1371 r = dns_name_is_valid_or_address(w);
1372 if (r <= 0) {
1373 log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name or IP address, ignoring.", w);
1374 continue;
1375 }
1376
1377 r = strv_push(l, w);
1378 if (r < 0)
1379 return log_oom();
1380
1381 w = NULL;
1382 }
1383
1384 return 0;
1385}
1386
f594276b
JK
1387int config_parse_dhcp_route_table(const char *unit,
1388 const char *filename,
1389 unsigned line,
1390 const char *section,
1391 unsigned section_line,
1392 const char *lvalue,
1393 int ltype,
1394 const char *rvalue,
1395 void *data,
1396 void *userdata) {
fc1ba79d 1397 Network *network = data;
f594276b
JK
1398 uint32_t rt;
1399 int r;
1400
1401 assert(filename);
1402 assert(lvalue);
1403 assert(rvalue);
1404 assert(data);
1405
1406 r = safe_atou32(rvalue, &rt);
1407 if (r < 0) {
1408 log_syntax(unit, LOG_ERR, filename, line, r,
1409 "Unable to read RouteTable, ignoring assignment: %s", rvalue);
1410 return 0;
1411 }
1412
fc1ba79d
AR
1413 network->dhcp_route_table = rt;
1414 network->dhcp_route_table_set = true;
f594276b
JK
1415
1416 return 0;
1417}
1418
b2a81c0b
LP
1419DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
1420
1421static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1422 [DHCP_USE_DOMAINS_NO] = "no",
1423 [DHCP_USE_DOMAINS_ROUTE] = "route",
1424 [DHCP_USE_DOMAINS_YES] = "yes",
1425};
1426
1427DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
34437b4f
LP
1428
1429DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1430
1431static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1432 [LLDP_MODE_NO] = "no",
1433 [LLDP_MODE_YES] = "yes",
1434 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1435};
1436
1437DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);