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