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