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