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