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