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