]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
network: do not assign unused value
[thirdparty/systemd.git] / src / network / networkd-network.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f579559b 2
69a93e7d 3#include <ctype.h>
987efa17 4#include <net/if.h>
69a93e7d 5
b5efdb8a 6#include "alloc-util.h"
f579559b
TG
7#include "conf-files.h"
8#include "conf-parser.h"
37de2509 9#include "dns-domain.h"
3ffd4af2 10#include "fd-util.h"
07630cea 11#include "hostname-util.h"
88295a05 12#include "in-addr-util.h"
fc2f9534 13#include "network-internal.h"
23f53b99 14#include "networkd-manager.h"
3ffd4af2 15#include "networkd-network.h"
6bedfcbb 16#include "parse-util.h"
8a516214 17#include "set.h"
8fcde012 18#include "stat-util.h"
8b43440b 19#include "string-table.h"
07630cea 20#include "string-util.h"
700f1186 21#include "strv.h"
07630cea 22#include "util.h"
f579559b 23
f4859fc7
SS
24static void network_config_hash_func(const void *p, struct siphash *state) {
25 const NetworkConfigSection *c = p;
26
27 siphash24_compress(c->filename, strlen(c->filename), state);
28 siphash24_compress(&c->line, sizeof(c->line), state);
29}
30
31static int network_config_compare_func(const void *a, const void *b) {
32 const NetworkConfigSection *x = a, *y = b;
33 int r;
34
35 r = strcmp(x->filename, y->filename);
36 if (r != 0)
37 return r;
38
26cdf3e5 39 return CMP(x->line, y->line);
f4859fc7
SS
40}
41
42const struct hash_ops network_config_hash_ops = {
43 .hash = network_config_hash_func,
44 .compare = network_config_compare_func,
45};
46
47int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
48 NetworkConfigSection *cs;
49
50 cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
51 if (!cs)
52 return -ENOMEM;
53
54 strcpy(cs->filename, filename);
55 cs->line = line;
56
ae2a15bc 57 *s = TAKE_PTR(cs);
f4859fc7
SS
58
59 return 0;
60}
61
62void network_config_section_free(NetworkConfigSection *cs) {
bce67bbe 63 free(cs);
f4859fc7
SS
64}
65
add8d07d 66/* Set defaults following RFC7844 */
67void network_apply_anonymize_if_set(Network *network) {
68 if (!network->dhcp_anonymize)
69 return;
70 /* RFC7844 3.7
71 SHOULD NOT send the Host Name option */
72 network->dhcp_send_hostname = false;
73 /* RFC7844 section 3.:
74 MAY contain the Client Identifier option
75 Section 3.5:
76 clients MUST use client identifiers based solely
77 on the link-layer address */
78 /* NOTE: Using MAC, as it does not reveal extra information,
79 * and some servers might not answer if this option is not sent */
80 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
81 /* RFC 7844 3.10:
82 SHOULD NOT use the Vendor Class Identifier option */
727ba17f 83 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
add8d07d 84 /* RFC7844 section 3.6.:
85 The client intending to protect its privacy SHOULD only request a
86 minimal number of options in the PRL and SHOULD also randomly shuffle
87 the ordering of option codes in the PRL. If this random ordering
88 cannot be implemented, the client MAY order the option codes in the
89 PRL by option code number (lowest to highest).
90 */
91 /* NOTE: dhcp_use_mtu is false by default,
92 * though it was not initiallized to any value in network_load_one.
93 * Maybe there should be another var called *send*?
94 * (to use the MTU sent by the server but to do not send
95 * the option in the PRL). */
96 network->dhcp_use_mtu = false;
28522b0d 97 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
98 * but this is needed to use them. */
99 network->dhcp_use_routes = true;
add8d07d 100 /* RFC7844 section 3.6.
101 * same comments as previous option */
102 network->dhcp_use_timezone = false;
103}
104
212bd73c 105int network_load_one(Manager *manager, const char *filename) {
8e766630 106 _cleanup_(network_freep) Network *network = NULL;
f579559b 107 _cleanup_fclose_ FILE *file = NULL;
dbffab87 108 char *d;
047a0dac 109 const char *dropin_dirname;
b3070dc0
TG
110 Route *route;
111 Address *address;
f579559b
TG
112 int r;
113
bf1bc670
TA
114 assert(manager);
115 assert(filename);
116
f579559b
TG
117 file = fopen(filename, "re");
118 if (!file) {
119 if (errno == ENOENT)
120 return 0;
1e7a0e21
LP
121
122 return -errno;
f579559b
TG
123 }
124
ed88bcfb
ZJS
125 if (null_or_empty_fd(fileno(file))) {
126 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
127 return 0;
128 }
129
17f9c355 130 network = new(Network, 1);
f579559b
TG
131 if (!network)
132 return log_oom();
133
17f9c355
YW
134 *network = (Network) {
135 .manager = manager,
136
137 .required_for_online = true,
138 .dhcp = ADDRESS_FAMILY_NO,
139 .dhcp_use_ntp = true,
140 .dhcp_use_dns = true,
141 .dhcp_use_hostname = true,
142 .dhcp_use_routes = true,
143 /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
144 .dhcp_send_hostname = true,
145 /* To enable/disable RFC7844 Anonymity Profiles */
146 .dhcp_anonymize = false,
147 .dhcp_route_metric = DHCP_ROUTE_METRIC,
148 /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
149 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
150 .dhcp_route_table = RT_TABLE_MAIN,
151 .dhcp_route_table_set = false,
152 /* NOTE: from man: UseMTU=... Defaults to false*/
153 .dhcp_use_mtu = false,
154 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
155 .dhcp_use_timezone = false,
156 .rapid_commit = true,
157
158 .dhcp_server_emit_dns = true,
159 .dhcp_server_emit_ntp = true,
160 .dhcp_server_emit_router = true,
161 .dhcp_server_emit_timezone = true,
162
163 .router_emit_dns = true,
164 .router_emit_domains = true,
165
166 .use_bpdu = -1,
167 .hairpin = -1,
168 .fast_leave = -1,
169 .allow_port_to_be_root = -1,
170 .unicast_flood = -1,
171 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
172
173 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
174
175 .llmnr = RESOLVE_SUPPORT_YES,
176 .mdns = RESOLVE_SUPPORT_NO,
177 .dnssec_mode = _DNSSEC_MODE_INVALID,
178 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
179
180 .link_local = ADDRESS_FAMILY_IPV6,
181
182 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
183 .ipv6_accept_ra = -1,
184 .ipv6_dad_transmits = -1,
185 .ipv6_hop_limit = -1,
186 .ipv6_proxy_ndp = -1,
187 .duid.type = _DUID_TYPE_INVALID,
188 .proxy_arp = -1,
189 .arp = -1,
190 .multicast = -1,
191 .allmulticast = -1,
192 .ipv6_accept_ra_use_dns = true,
193 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
194 };
f579559b 195
d5099efc 196 network->stacked_netdevs = hashmap_new(&string_hash_ops);
6a0a2f86 197 if (!network->stacked_netdevs)
326cb406
SS
198 return log_oom();
199
f4859fc7 200 network->addresses_by_section = hashmap_new(&network_config_hash_ops);
6ae115c1
TG
201 if (!network->addresses_by_section)
202 return log_oom();
203
f4859fc7 204 network->routes_by_section = hashmap_new(&network_config_hash_ops);
6ae115c1
TG
205 if (!network->routes_by_section)
206 return log_oom();
207
1a75764a 208 network->fdb_entries_by_section = hashmap_new(&network_config_hash_ops);
b98b483b
AR
209 if (!network->fdb_entries_by_section)
210 return log_oom();
211
95b74ef6
SS
212 network->address_labels_by_section = hashmap_new(&network_config_hash_ops);
213 if (!network->address_labels_by_section)
057abfd8
PF
214 log_oom();
215
216 network->prefixes_by_section = hashmap_new(&network_config_hash_ops);
217 if (!network->prefixes_by_section)
95b74ef6
SS
218 return log_oom();
219
bce67bbe
SS
220 network->rules_by_section = hashmap_new(&network_config_hash_ops);
221 if (!network->rules_by_section)
222 return log_oom();
223
6ae115c1
TG
224 network->filename = strdup(filename);
225 if (!network->filename)
226 return log_oom();
227
dbffab87
TG
228 network->name = strdup(basename(filename));
229 if (!network->name)
230 return log_oom();
231
232 d = strrchr(network->name, '.');
233 if (!d)
234 return -EINVAL;
235
dbffab87
TG
236 *d = '\0';
237
23bb31aa
ZJS
238 dropin_dirname = strjoina(network->name, ".network.d");
239
240 r = config_parse_many(filename, network_dirs, dropin_dirname,
241 "Match\0"
242 "Link\0"
243 "Network\0"
244 "Address\0"
95b74ef6 245 "IPv6AddressLabel\0"
bce67bbe 246 "RoutingPolicyRule\0"
23bb31aa
ZJS
247 "Route\0"
248 "DHCP\0"
249 "DHCPv4\0" /* compat */
250 "DHCPServer\0"
251 "IPv6AcceptRA\0"
a0e5c15d 252 "IPv6NDPProxyAddress\0"
23bb31aa
ZJS
253 "Bridge\0"
254 "BridgeFDB\0"
9d5d0090 255 "BridgeVLAN\0"
7d5cac19 256 "IPv6PrefixDelegation\0"
06828bb6
HP
257 "IPv6Prefix\0"
258 "CAN\0",
23bb31aa 259 config_item_perf_lookup, network_network_gperf_lookup,
bcde742e 260 CONFIG_PARSE_WARN, network);
36f822c4 261 if (r < 0)
f579559b 262 return r;
f579559b 263
add8d07d 264 network_apply_anonymize_if_set(network);
265
5a8bcb67
LP
266 /* IPMasquerade=yes implies IPForward=yes */
267 if (network->ip_masquerade)
769d324c 268 network->ip_forward |= ADDRESS_FAMILY_IPV4;
5a8bcb67 269
7169cdc8
YW
270 if (network->mtu > 0 && network->dhcp_use_mtu) {
271 log_warning("MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set in %s. "
272 "Disabling UseMTU=.", filename);
273 network->dhcp_use_mtu = false;
274 }
275
f579559b 276 LIST_PREPEND(networks, manager->networks, network);
b3070dc0 277
dbffab87
TG
278 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
279 if (r < 0)
280 return r;
281
282 r = hashmap_put(manager->networks_by_name, network->name, network);
283 if (r < 0)
284 return r;
285
3d3d4255 286 LIST_FOREACH(routes, route, network->static_routes) {
b3070dc0
TG
287 if (!route->family) {
288 log_warning("Route section without Gateway field configured in %s. "
289 "Ignoring", filename);
290 return 0;
291 }
b3070dc0
TG
292 }
293
3d3d4255 294 LIST_FOREACH(addresses, address, network->static_addresses) {
b3070dc0
TG
295 if (!address->family) {
296 log_warning("Address section without Address field configured in %s. "
297 "Ignoring", filename);
298 return 0;
299 }
300 }
301
f579559b
TG
302 network = NULL;
303
304 return 0;
305}
306
307int network_load(Manager *manager) {
308 Network *network;
477e73b5
ZJS
309 _cleanup_strv_free_ char **files = NULL;
310 char **f;
f579559b
TG
311 int r;
312
313 assert(manager);
314
315 while ((network = manager->networks))
316 network_free(network);
317
b5084605 318 r = conf_files_list_strv(&files, ".network", NULL, 0, network_dirs);
f647962d
MS
319 if (r < 0)
320 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b
TG
321
322 STRV_FOREACH_BACKWARDS(f, files) {
323 r = network_load_one(manager, *f);
324 if (r < 0)
325 return r;
326 }
327
f579559b
TG
328 return 0;
329}
330
f579559b 331void network_free(Network *network) {
a0e5c15d 332 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
bce67bbe
SS
333 RoutingPolicyRule *rule;
334 FdbEntry *fdb_entry;
95b74ef6 335 AddressLabel *label;
057abfd8 336 Prefix *prefix;
bce67bbe 337 Address *address;
bce67bbe 338 Route *route;
f579559b
TG
339
340 if (!network)
341 return;
342
343 free(network->filename);
344
e90d0374 345 set_free_free(network->match_mac);
5256e00e
TG
346 strv_free(network->match_path);
347 strv_free(network->match_driver);
348 strv_free(network->match_type);
349 strv_free(network->match_name);
f579559b
TG
350
351 free(network->description);
edb85f0d 352 free(network->dhcp_vendor_class_identifier);
af1c0de0 353 strv_free(network->dhcp_user_class);
27cb34f5 354 free(network->dhcp_hostname);
f579559b 355
c106cc36
TG
356 free(network->mac);
357
b0e39c82 358 strv_free(network->ntp);
5512a963 359 free(network->dns);
3df9bec5
LP
360 strv_free(network->search_domains);
361 strv_free(network->route_domains);
0d4ad91d 362 strv_free(network->bind_carrier);
cdd7812b 363
28c3428d 364 strv_free(network->router_search_domains);
cdd7812b 365 free(network->router_dns);
3bef724f 366
47e2dc31 367 netdev_unref(network->bridge);
47e2dc31 368 netdev_unref(network->bond);
6cb955c6 369 netdev_unref(network->vrf);
47e2dc31 370
fa6f1e54 371 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
326cb406 372
f048a16b 373 while ((route = network->static_routes))
f579559b
TG
374 route_free(route);
375
f048a16b 376 while ((address = network->static_addresses))
f579559b
TG
377 address_free(address);
378
b98b483b
AR
379 while ((fdb_entry = network->static_fdb_entries))
380 fdb_entry_free(fdb_entry);
381
a0e5c15d
FK
382 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
383 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
384
95b74ef6
SS
385 while ((label = network->address_labels))
386 address_label_free(label);
387
057abfd8
PF
388 while ((prefix = network->static_prefixes))
389 prefix_free(prefix);
390
bce67bbe
SS
391 while ((rule = network->rules))
392 routing_policy_rule_free(rule);
393
6ae115c1
TG
394 hashmap_free(network->addresses_by_section);
395 hashmap_free(network->routes_by_section);
b98b483b 396 hashmap_free(network->fdb_entries_by_section);
95b74ef6 397 hashmap_free(network->address_labels_by_section);
057abfd8 398 hashmap_free(network->prefixes_by_section);
bce67bbe 399 hashmap_free(network->rules_by_section);
6ae115c1 400
dbffab87
TG
401 if (network->manager) {
402 if (network->manager->networks)
403 LIST_REMOVE(networks, network->manager->networks, network);
404
e512c6c1 405 if (network->manager->networks_by_name && network->name)
dbffab87 406 hashmap_remove(network->manager->networks_by_name, network->name);
27dfc982
YW
407
408 if (network->manager->duids_requesting_uuid)
409 set_remove(network->manager->duids_requesting_uuid, &network->duid);
dbffab87
TG
410 }
411
412 free(network->name);
f579559b 413
79e16ce3
LP
414 condition_free_list(network->match_host);
415 condition_free_list(network->match_virt);
5022f08a
LP
416 condition_free_list(network->match_kernel_cmdline);
417 condition_free_list(network->match_kernel_version);
79e16ce3
LP
418 condition_free_list(network->match_arch);
419
8eb9058d 420 free(network->dhcp_server_timezone);
1a04db0f
LP
421 free(network->dhcp_server_dns);
422 free(network->dhcp_server_ntp);
8eb9058d 423
8a516214
LP
424 set_free_free(network->dnssec_negative_trust_anchors);
425
f579559b
TG
426 free(network);
427}
428
dbffab87
TG
429int network_get_by_name(Manager *manager, const char *name, Network **ret) {
430 Network *network;
431
432 assert(manager);
433 assert(name);
434 assert(ret);
435
436 network = hashmap_get(manager->networks_by_name, name);
437 if (!network)
438 return -ENOENT;
439
440 *ret = network;
441
442 return 0;
443}
444
51517f9e 445int network_get(Manager *manager, sd_device *device,
505f8da7
TG
446 const char *ifname, const struct ether_addr *address,
447 Network **ret) {
24c083df 448 const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
51517f9e
YW
449 sd_device *parent;
450 Network *network;
f579559b
TG
451
452 assert(manager);
f579559b 453 assert(ret);
af3aa302 454
24c083df 455 if (device) {
51517f9e 456 (void) sd_device_get_property_value(device, "ID_PATH", &path);
af3aa302 457
51517f9e
YW
458 if (sd_device_get_parent(device, &parent) >= 0)
459 (void) sd_device_get_driver(parent, &parent_driver);
af3aa302 460
51517f9e 461 (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver);
af3aa302 462
51517f9e 463 (void) sd_device_get_devtype(device, &devtype);
24c083df 464 }
f579559b 465
f579559b
TG
466 LIST_FOREACH(networks, network, manager->networks) {
467 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
468 network->match_driver, network->match_type,
469 network->match_name, network->match_host,
5022f08a
LP
470 network->match_virt, network->match_kernel_cmdline,
471 network->match_kernel_version, network->match_arch,
af3aa302
TG
472 address, path, parent_driver, driver,
473 devtype, ifname)) {
24c083df 474 if (network->match_name && device) {
ca6038b8
TG
475 const char *attr;
476 uint8_t name_assign_type = NET_NAME_UNKNOWN;
477
51517f9e 478 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
dc751688 479 (void) safe_atou8(attr, &name_assign_type);
32bc8adc
TG
480
481 if (name_assign_type == NET_NAME_ENUM)
a2fae7bb
TG
482 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
483 ifname, network->filename);
32bc8adc 484 else
a2fae7bb 485 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 486 } else
a2fae7bb 487 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 488
f579559b
TG
489 *ret = network;
490 return 0;
491 }
492 }
493
494 *ret = NULL;
495
496 return -ENOENT;
497}
498
7d342c03 499int network_apply(Network *network, Link *link) {
f579559b
TG
500 int r;
501
c4a03a56
TG
502 assert(network);
503 assert(link);
504
f579559b
TG
505 link->network = network;
506
bfa695b5
TG
507 if (network->ipv4ll_route) {
508 Route *route;
509
0b180d75 510 r = route_new_static(network, NULL, 0, &route);
bfa695b5
TG
511 if (r < 0)
512 return r;
513
2ce40956 514 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
bfa695b5
TG
515 if (r == 0)
516 return -EINVAL;
517 if (r < 0)
518 return -errno;
519
520 route->family = AF_INET;
521 route->dst_prefixlen = 16;
522 route->scope = RT_SCOPE_LINK;
86655331 523 route->priority = IPV4LL_ROUTE_METRIC;
bfa695b5
TG
524 route->protocol = RTPROT_STATIC;
525 }
526
5512a963 527 if (network->n_dns > 0 ||
3df9bec5
LP
528 !strv_isempty(network->ntp) ||
529 !strv_isempty(network->search_domains) ||
2ad6b610 530 !strv_isempty(network->route_domains))
84de38c5 531 link_dirty(link);
3bef724f 532
f579559b
TG
533 return 0;
534}
02b59d57 535
439689c6
SS
536bool network_has_static_ipv6_addresses(Network *network) {
537 Address *address;
538
539 assert(network);
540
541 LIST_FOREACH(addresses, address, network->static_addresses) {
542 if (address->family == AF_INET6)
543 return true;
544 }
545
546 return false;
547}
548
69a93e7d 549int config_parse_netdev(const char *unit,
02b59d57
TG
550 const char *filename,
551 unsigned line,
552 const char *section,
553 unsigned section_line,
554 const char *lvalue,
555 int ltype,
556 const char *rvalue,
557 void *data,
558 void *userdata) {
559 Network *network = userdata;
31d0ac36
TG
560 _cleanup_free_ char *kind_string = NULL;
561 char *p;
1a436809 562 NetDev *netdev;
69a93e7d 563 NetDevKind kind;
02b59d57
TG
564 int r;
565
566 assert(filename);
567 assert(lvalue);
568 assert(rvalue);
569 assert(data);
570
69a93e7d
TG
571 kind_string = strdup(lvalue);
572 if (!kind_string)
573 return log_oom();
52433f6b 574
69a93e7d
TG
575 /* the keys are CamelCase versions of the kind */
576 for (p = kind_string; *p; p++)
577 *p = tolower(*p);
52433f6b 578
69a93e7d
TG
579 kind = netdev_kind_from_string(kind_string);
580 if (kind == _NETDEV_KIND_INVALID) {
12ca818f 581 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
582 return 0;
583 }
584
54abf461
TG
585 r = netdev_get(network->manager, rvalue, &netdev);
586 if (r < 0) {
12ca818f 587 log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
588 return 0;
589 }
590
69a93e7d 591 if (netdev->kind != kind) {
12ca818f 592 log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
593 return 0;
594 }
595
69a93e7d
TG
596 switch (kind) {
597 case NETDEV_KIND_BRIDGE:
25ed70f7 598 network->bridge = netdev_unref(network->bridge);
69a93e7d 599 network->bridge = netdev;
54abf461 600
69a93e7d
TG
601 break;
602 case NETDEV_KIND_BOND:
25ed70f7 603 network->bond = netdev_unref(network->bond);
69a93e7d 604 network->bond = netdev;
fe6b2d55 605
6cb955c6
AR
606 break;
607 case NETDEV_KIND_VRF:
25ed70f7 608 network->vrf = netdev_unref(network->vrf);
6cb955c6
AR
609 network->vrf = netdev;
610
69a93e7d
TG
611 break;
612 case NETDEV_KIND_VLAN:
69a93e7d 613 case NETDEV_KIND_MACVLAN:
f33ff02b 614 case NETDEV_KIND_MACVTAP:
c4a5ddc9 615 case NETDEV_KIND_IPVLAN:
326cb406 616 case NETDEV_KIND_VXLAN:
92c918b0 617 case NETDEV_KIND_VCAN:
6a0a2f86 618 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406 619 if (r < 0) {
87ac8d99 620 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add NetDev '%s' to network: %m", rvalue);
326cb406
SS
621 return 0;
622 }
623
69a93e7d
TG
624 break;
625 default:
87ac8d99 626 assert_not_reached("Cannot parse NetDev");
fe6b2d55
TG
627 }
628
47e2dc31
TG
629 netdev_ref(netdev);
630
fe6b2d55
TG
631 return 0;
632}
7951dea2 633
3df9bec5
LP
634int config_parse_domains(
635 const char *unit,
636 const char *filename,
637 unsigned line,
638 const char *section,
639 unsigned section_line,
640 const char *lvalue,
641 int ltype,
642 const char *rvalue,
643 void *data,
644 void *userdata) {
645
646 const char *p;
647 Network *n = data;
6192b846
TG
648 int r;
649
3df9bec5
LP
650 assert(n);
651 assert(lvalue);
652 assert(rvalue);
6192b846 653
3df9bec5
LP
654 if (isempty(rvalue)) {
655 n->search_domains = strv_free(n->search_domains);
656 n->route_domains = strv_free(n->route_domains);
657 return 0;
658 }
67272d15 659
3df9bec5
LP
660 p = rvalue;
661 for (;;) {
662 _cleanup_free_ char *w = NULL, *normalized = NULL;
663 const char *domain;
664 bool is_route;
665
666 r = extract_first_word(&p, &w, NULL, 0);
667 if (r < 0) {
668 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
669 break;
670 }
671 if (r == 0)
672 break;
673
674 is_route = w[0] == '~';
675 domain = is_route ? w + 1 : w;
676
677 if (dns_name_is_root(domain) || streq(domain, "*")) {
678 /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
679 * routing domain, unconditionally. */
680 is_route = true;
681 domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
682
683 } else {
684 r = dns_name_normalize(domain, &normalized);
685 if (r < 0) {
686 log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
687 continue;
688 }
689
690 domain = normalized;
691
692 if (is_localhost(domain)) {
693 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 694 continue;
3df9bec5 695 }
37de2509 696 }
40274ed6 697
3df9bec5
LP
698 if (is_route) {
699 r = strv_extend(&n->route_domains, domain);
700 if (r < 0)
701 return log_oom();
40274ed6 702
3df9bec5
LP
703 } else {
704 r = strv_extend(&n->search_domains, domain);
705 if (r < 0)
706 return log_oom();
707 }
40274ed6 708 }
f15b6e5a 709
3df9bec5
LP
710 strv_uniq(n->route_domains);
711 strv_uniq(n->search_domains);
712
6192b846
TG
713 return 0;
714}
715
7951dea2
SS
716int config_parse_tunnel(const char *unit,
717 const char *filename,
718 unsigned line,
719 const char *section,
720 unsigned section_line,
721 const char *lvalue,
722 int ltype,
723 const char *rvalue,
724 void *data,
725 void *userdata) {
726 Network *network = userdata;
727 NetDev *netdev;
728 int r;
729
730 assert(filename);
731 assert(lvalue);
732 assert(rvalue);
733 assert(data);
734
735 r = netdev_get(network->manager, rvalue, &netdev);
736 if (r < 0) {
6a7a4e4d 737 log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
7951dea2
SS
738 return 0;
739 }
740
d29ad81e
ZJS
741 if (!IN_SET(netdev->kind,
742 NETDEV_KIND_IPIP,
743 NETDEV_KIND_SIT,
744 NETDEV_KIND_GRE,
745 NETDEV_KIND_GRETAP,
746 NETDEV_KIND_IP6GRE,
747 NETDEV_KIND_IP6GRETAP,
748 NETDEV_KIND_VTI,
749 NETDEV_KIND_VTI6,
750 NETDEV_KIND_IP6TNL)) {
12ca818f 751 log_syntax(unit, LOG_ERR, filename, line, 0,
7951dea2
SS
752 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
753 return 0;
754 }
755
6a0a2f86
TG
756 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
757 if (r < 0) {
6a7a4e4d 758 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
6a0a2f86
TG
759 return 0;
760 }
761
762 netdev_ref(netdev);
7951dea2
SS
763
764 return 0;
765}
bd8f6538 766
d0d6a4cd
TG
767int config_parse_ipv4ll(
768 const char* unit,
769 const char *filename,
770 unsigned line,
771 const char *section,
772 unsigned section_line,
773 const char *lvalue,
774 int ltype,
775 const char *rvalue,
776 void *data,
777 void *userdata) {
778
779 AddressFamilyBoolean *link_local = data;
780
781 assert(filename);
782 assert(lvalue);
783 assert(rvalue);
784 assert(data);
785
786 /* Note that this is mostly like
787 * config_parse_address_family_boolean(), except that it
788 * applies only to IPv4 */
789
5883ff60 790 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
d0d6a4cd
TG
791
792 return 0;
793}
794
bd8f6538
TG
795int config_parse_dhcp(
796 const char* unit,
797 const char *filename,
798 unsigned line,
799 const char *section,
800 unsigned section_line,
801 const char *lvalue,
802 int ltype,
803 const char *rvalue,
804 void *data,
805 void *userdata) {
806
cb9fc36a 807 AddressFamilyBoolean *dhcp = data, s;
bd8f6538
TG
808
809 assert(filename);
810 assert(lvalue);
811 assert(rvalue);
812 assert(data);
813
769d324c
LP
814 /* Note that this is mostly like
815 * config_parse_address_family_boolean(), except that it
816 * understands some old names for the enum values */
817
cb9fc36a
LP
818 s = address_family_boolean_from_string(rvalue);
819 if (s < 0) {
820
821 /* Previously, we had a slightly different enum here,
822 * support its values for compatbility. */
823
824 if (streq(rvalue, "none"))
825 s = ADDRESS_FAMILY_NO;
826 else if (streq(rvalue, "v4"))
827 s = ADDRESS_FAMILY_IPV4;
828 else if (streq(rvalue, "v6"))
829 s = ADDRESS_FAMILY_IPV6;
830 else if (streq(rvalue, "both"))
831 s = ADDRESS_FAMILY_YES;
832 else {
12ca818f 833 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
bd8f6538
TG
834 return 0;
835 }
bd8f6538
TG
836 }
837
cb9fc36a 838 *dhcp = s;
bd8f6538
TG
839 return 0;
840}
841
3e43b2cd
JJ
842static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
843 [DHCP_CLIENT_ID_MAC] = "mac",
dace710c
YW
844 [DHCP_CLIENT_ID_DUID] = "duid",
845 [DHCP_CLIENT_ID_DUID_ONLY] = "duid-only",
3e43b2cd
JJ
846};
847
499d555a
SS
848DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier);
849DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier, "Failed to parse client identifier type");
3e43b2cd 850
60c35566 851int config_parse_ipv6token(
7f77697a
TG
852 const char* unit,
853 const char *filename,
854 unsigned line,
855 const char *section,
856 unsigned section_line,
857 const char *lvalue,
858 int ltype,
859 const char *rvalue,
860 void *data,
861 void *userdata) {
862
863 union in_addr_union buffer;
864 struct in6_addr *token = data;
865 int r;
866
867 assert(filename);
868 assert(lvalue);
869 assert(rvalue);
870 assert(token);
871
872 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
873 if (r < 0) {
6a7a4e4d 874 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
7f77697a
TG
875 return 0;
876 }
877
878 r = in_addr_is_null(AF_INET6, &buffer);
12ca818f 879 if (r != 0) {
87ac8d99 880 log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
7f77697a
TG
881 return 0;
882 }
883
884 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
87ac8d99 885 log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
7f77697a
TG
886 return 0;
887 }
888
889 *token = buffer.in6;
890
891 return 0;
892}
8add5f79 893
49092e22 894static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1f0d9695
LP
895 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
896 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
897 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
49092e22
SS
898};
899
900DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
901
902int config_parse_ipv6_privacy_extensions(
903 const char* unit,
904 const char *filename,
905 unsigned line,
906 const char *section,
907 unsigned section_line,
908 const char *lvalue,
909 int ltype,
910 const char *rvalue,
911 void *data,
912 void *userdata) {
913
914 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
915 int k;
916
917 assert(filename);
918 assert(lvalue);
919 assert(rvalue);
920 assert(ipv6_privacy_extensions);
921
922 /* Our enum shall be a superset of booleans, hence first try
923 * to parse as boolean, and then as enum */
924
925 k = parse_boolean(rvalue);
926 if (k > 0)
1f0d9695 927 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
49092e22 928 else if (k == 0)
1f0d9695 929 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
49092e22 930 else {
1f0d9695 931 IPv6PrivacyExtensions s;
49092e22
SS
932
933 s = ipv6_privacy_extensions_from_string(rvalue);
1f0d9695
LP
934 if (s < 0) {
935
936 if (streq(rvalue, "kernel"))
937 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
938 else {
12ca818f 939 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1f0d9695
LP
940 return 0;
941 }
49092e22
SS
942 }
943
944 *ipv6_privacy_extensions = s;
945 }
946
947 return 0;
948}
a7d0ef44 949
1ac608c9
LP
950int config_parse_hostname(
951 const char *unit,
952 const char *filename,
953 unsigned line,
954 const char *section,
955 unsigned section_line,
956 const char *lvalue,
957 int ltype,
958 const char *rvalue,
959 void *data,
960 void *userdata) {
961
6528693a
YW
962 _cleanup_free_ char *hn = NULL;
963 char **hostname = data;
a7d0ef44
SS
964 int r;
965
966 assert(filename);
967 assert(lvalue);
968 assert(rvalue);
969
1ac608c9 970 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
a7d0ef44
SS
971 if (r < 0)
972 return r;
973
1ac608c9 974 if (!hostname_is_valid(hn, false)) {
12ca818f 975 log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
a7d0ef44
SS
976 return 0;
977 }
978
6528693a
YW
979 r = dns_name_is_valid(hn);
980 if (r < 0) {
981 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
982 return 0;
983 }
984 if (r == 0) {
985 log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
986 return 0;
987 }
988
989 return free_and_replace(*hostname, hn);
a7d0ef44 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
19f9e4e2
YW
1004 _cleanup_free_ char *tz = NULL;
1005 char **datap = data;
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
089fb865 1016 if (!timezone_is_valid(tz, LOG_ERR)) {
12ca818f 1017 log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
8eb9058d
LP
1018 return 0;
1019 }
1020
19f9e4e2 1021 return free_and_replace(*datap, tz);
8eb9058d 1022}
1a04db0f
LP
1023
1024int config_parse_dhcp_server_dns(
1025 const char *unit,
1026 const char *filename,
1027 unsigned line,
1028 const char *section,
1029 unsigned section_line,
1030 const char *lvalue,
1031 int ltype,
1032 const char *rvalue,
1033 void *data,
1034 void *userdata) {
1035
1036 Network *n = data;
1037 const char *p = rvalue;
1038 int r;
1039
1040 assert(filename);
1041 assert(lvalue);
1042 assert(rvalue);
1043
1044 for (;;) {
1045 _cleanup_free_ char *w = NULL;
1046 struct in_addr a, *m;
1047
1048 r = extract_first_word(&p, &w, NULL, 0);
fa105ce6
LP
1049 if (r == -ENOMEM)
1050 return log_oom();
1a04db0f
LP
1051 if (r < 0) {
1052 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1053 return 0;
1054 }
1a04db0f 1055 if (r == 0)
fa105ce6 1056 break;
1a04db0f
LP
1057
1058 if (inet_pton(AF_INET, w, &a) <= 0) {
12ca818f 1059 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
1a04db0f
LP
1060 continue;
1061 }
1062
62d74c78 1063 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
1a04db0f
LP
1064 if (!m)
1065 return log_oom();
1066
1067 m[n->n_dhcp_server_dns++] = a;
1068 n->dhcp_server_dns = m;
1069 }
fa105ce6 1070
88295a05
PF
1071 return 0;
1072}
1073
1074int config_parse_radv_dns(
1075 const char *unit,
1076 const char *filename,
1077 unsigned line,
1078 const char *section,
1079 unsigned section_line,
1080 const char *lvalue,
1081 int ltype,
1082 const char *rvalue,
1083 void *data,
1084 void *userdata) {
1085
1086 Network *n = data;
1087 const char *p = rvalue;
1088 int r;
1089
1090 assert(filename);
1091 assert(lvalue);
1092 assert(rvalue);
1093
1094 for (;;) {
1095 _cleanup_free_ char *w = NULL;
1096 union in_addr_union a;
1097
1098 r = extract_first_word(&p, &w, NULL, 0);
1099 if (r == -ENOMEM)
1100 return log_oom();
1101 if (r < 0) {
1102 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1103 return 0;
1104 }
1105 if (r == 0)
1106 break;
1107
1108 if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
1109 struct in6_addr *m;
1110
62d74c78 1111 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
88295a05
PF
1112 if (!m)
1113 return log_oom();
1114
1115 m[n->n_router_dns++] = a.in6;
1116 n->router_dns = m;
1117
1118 } else
1119 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
1120
1121 }
1122
700f1186
PF
1123 return 0;
1124}
1125
1126int config_parse_radv_search_domains(
1127 const char *unit,
1128 const char *filename,
1129 unsigned line,
1130 const char *section,
1131 unsigned section_line,
1132 const char *lvalue,
1133 int ltype,
1134 const char *rvalue,
1135 void *data,
1136 void *userdata) {
1137
1138 Network *n = data;
1139 const char *p = rvalue;
1140 int r;
1141
1142 assert(filename);
1143 assert(lvalue);
1144 assert(rvalue);
1145
1146 for (;;) {
7a99f98b 1147 _cleanup_free_ char *w = NULL, *idna = NULL;
700f1186
PF
1148
1149 r = extract_first_word(&p, &w, NULL, 0);
1150 if (r == -ENOMEM)
1151 return log_oom();
1152 if (r < 0) {
1153 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1154 return 0;
1155 }
1156 if (r == 0)
1157 break;
1158
1159 r = dns_name_apply_idna(w, &idna);
7a99f98b
YW
1160 if (r < 0) {
1161 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1162 continue;
1163 }
700f1186
PF
1164 if (r > 0) {
1165 r = strv_push(&n->router_search_domains, idna);
1166 if (r >= 0)
1167 idna = NULL;
7a99f98b 1168 } else {
700f1186
PF
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);