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