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