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