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