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