]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
networkd: Add EmitRouter= option for DHCP Server (#3251)
[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"
3ffd4af2 30#include "networkd-network.h"
fc2f9534 31#include "networkd.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
TG
38
39static int network_load_one(Manager *manager, const char *filename) {
40 _cleanup_network_free_ Network *network = NULL;
41 _cleanup_fclose_ FILE *file = NULL;
dbffab87 42 char *d;
b3070dc0
TG
43 Route *route;
44 Address *address;
f579559b
TG
45 int r;
46
bf1bc670
TA
47 assert(manager);
48 assert(filename);
49
f579559b
TG
50 file = fopen(filename, "re");
51 if (!file) {
52 if (errno == ENOENT)
53 return 0;
54 else
ecb08ec6 55 return -errno;
f579559b
TG
56 }
57
ed88bcfb
ZJS
58 if (null_or_empty_fd(fileno(file))) {
59 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
60 return 0;
61 }
62
f579559b
TG
63 network = new0(Network, 1);
64 if (!network)
65 return log_oom();
66
5a3eb5a7
TG
67 network->manager = manager;
68
f048a16b
TG
69 LIST_HEAD_INIT(network->static_addresses);
70 LIST_HEAD_INIT(network->static_routes);
b98b483b 71 LIST_HEAD_INIT(network->static_fdb_entries);
f579559b 72
d5099efc 73 network->stacked_netdevs = hashmap_new(&string_hash_ops);
6a0a2f86 74 if (!network->stacked_netdevs)
326cb406
SS
75 return log_oom();
76
d5099efc 77 network->addresses_by_section = hashmap_new(NULL);
6ae115c1
TG
78 if (!network->addresses_by_section)
79 return log_oom();
80
d5099efc 81 network->routes_by_section = hashmap_new(NULL);
6ae115c1
TG
82 if (!network->routes_by_section)
83 return log_oom();
84
b98b483b
AR
85 network->fdb_entries_by_section = hashmap_new(NULL);
86 if (!network->fdb_entries_by_section)
87 return log_oom();
88
6ae115c1
TG
89 network->filename = strdup(filename);
90 if (!network->filename)
91 return log_oom();
92
dbffab87
TG
93 network->name = strdup(basename(filename));
94 if (!network->name)
95 return log_oom();
96
97 d = strrchr(network->name, '.');
98 if (!d)
99 return -EINVAL;
100
101 assert(streq(d, ".network"));
102
103 *d = '\0';
104
cb9fc36a 105 network->dhcp = ADDRESS_FAMILY_NO;
27cb34f5
LP
106 network->dhcp_use_ntp = true;
107 network->dhcp_use_dns = true;
108 network->dhcp_use_hostname = true;
109 network->dhcp_use_routes = true;
110 network->dhcp_send_hostname = true;
84b5b79a 111 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
3e43b2cd 112 network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
5be4d38e 113
539f2a73
LP
114 network->dhcp_server_emit_dns = true;
115 network->dhcp_server_emit_ntp = true;
77ff6022 116 network->dhcp_server_emit_router = true;
539f2a73
LP
117 network->dhcp_server_emit_timezone = true;
118
84c34096 119 network->use_bpdu = true;
23da66bb 120 network->allow_port_to_be_root = true;
072f9e4a 121 network->unicast_flood = true;
84c34096 122
7cececb2
LP
123 network->lldp_mode = LLDP_MODE_ROUTERS_ONLY;
124
a7e5da6e 125 network->llmnr = RESOLVE_SUPPORT_YES;
aaa297d4 126 network->mdns = RESOLVE_SUPPORT_NO;
ad6c0475 127 network->dnssec_mode = _DNSSEC_MODE_INVALID;
bd8f6538 128
d0d6a4cd
TG
129 network->link_local = ADDRESS_FAMILY_IPV6;
130
1f0d9695 131 network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
4f2e437a 132 network->ipv6_accept_ra = -1;
8749cbcd 133 network->ipv6_dad_transmits = -1;
b69c3180 134 network->ipv6_hop_limit = -1;
8341a5c3 135 network->duid.type = _DUID_TYPE_INVALID;
23d8b221 136 network->proxy_arp = -1;
49092e22 137
e9f3d2d5 138 r = config_parse(NULL, filename, file,
c106cc36
TG
139 "Match\0"
140 "Link\0"
141 "Network\0"
142 "Address\0"
143 "Route\0"
144 "DHCP\0"
8eb9058d
LP
145 "DHCPv4\0" /* compat */
146 "DHCPServer\0"
b98b483b
AR
147 "Bridge\0"
148 "BridgeFDB\0",
e9f3d2d5 149 config_item_perf_lookup, network_network_gperf_lookup,
36f822c4
ZJS
150 false, false, true, network);
151 if (r < 0)
f579559b 152 return r;
f579559b 153
5a8bcb67
LP
154 /* IPMasquerade=yes implies IPForward=yes */
155 if (network->ip_masquerade)
769d324c 156 network->ip_forward |= ADDRESS_FAMILY_IPV4;
5a8bcb67 157
f579559b 158 LIST_PREPEND(networks, manager->networks, network);
b3070dc0 159
dbffab87
TG
160 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
161 if (r < 0)
162 return r;
163
164 r = hashmap_put(manager->networks_by_name, network->name, network);
165 if (r < 0)
166 return r;
167
3d3d4255 168 LIST_FOREACH(routes, route, network->static_routes) {
b3070dc0
TG
169 if (!route->family) {
170 log_warning("Route section without Gateway field configured in %s. "
171 "Ignoring", filename);
172 return 0;
173 }
b3070dc0
TG
174 }
175
3d3d4255 176 LIST_FOREACH(addresses, address, network->static_addresses) {
b3070dc0
TG
177 if (!address->family) {
178 log_warning("Address section without Address field configured in %s. "
179 "Ignoring", filename);
180 return 0;
181 }
182 }
183
f579559b
TG
184 network = NULL;
185
186 return 0;
187}
188
189int network_load(Manager *manager) {
190 Network *network;
477e73b5
ZJS
191 _cleanup_strv_free_ char **files = NULL;
192 char **f;
f579559b
TG
193 int r;
194
195 assert(manager);
196
197 while ((network = manager->networks))
198 network_free(network);
199
2ad8416d 200 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
f647962d
MS
201 if (r < 0)
202 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b
TG
203
204 STRV_FOREACH_BACKWARDS(f, files) {
205 r = network_load_one(manager, *f);
206 if (r < 0)
207 return r;
208 }
209
f579559b
TG
210 return 0;
211}
212
f579559b 213void network_free(Network *network) {
47e2dc31 214 NetDev *netdev;
f579559b
TG
215 Route *route;
216 Address *address;
b98b483b 217 FdbEntry *fdb_entry;
06f021a8 218 Iterator i;
f579559b
TG
219
220 if (!network)
221 return;
222
223 free(network->filename);
224
225 free(network->match_mac);
5256e00e
TG
226 strv_free(network->match_path);
227 strv_free(network->match_driver);
228 strv_free(network->match_type);
229 strv_free(network->match_name);
f579559b
TG
230
231 free(network->description);
edb85f0d 232 free(network->dhcp_vendor_class_identifier);
27cb34f5 233 free(network->dhcp_hostname);
f579559b 234
c106cc36
TG
235 free(network->mac);
236
b0e39c82
TG
237 strv_free(network->ntp);
238 strv_free(network->dns);
3df9bec5
LP
239 strv_free(network->search_domains);
240 strv_free(network->route_domains);
0d4ad91d 241 strv_free(network->bind_carrier);
3bef724f 242
47e2dc31
TG
243 netdev_unref(network->bridge);
244
245 netdev_unref(network->bond);
246
2c36be2f
TG
247 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
248 hashmap_remove(network->stacked_netdevs, netdev->ifname);
326cb406 249 netdev_unref(netdev);
2c36be2f 250 }
6a0a2f86 251 hashmap_free(network->stacked_netdevs);
326cb406 252
f048a16b 253 while ((route = network->static_routes))
f579559b
TG
254 route_free(route);
255
f048a16b 256 while ((address = network->static_addresses))
f579559b
TG
257 address_free(address);
258
b98b483b
AR
259 while ((fdb_entry = network->static_fdb_entries))
260 fdb_entry_free(fdb_entry);
261
6ae115c1
TG
262 hashmap_free(network->addresses_by_section);
263 hashmap_free(network->routes_by_section);
b98b483b 264 hashmap_free(network->fdb_entries_by_section);
6ae115c1 265
dbffab87
TG
266 if (network->manager) {
267 if (network->manager->networks)
268 LIST_REMOVE(networks, network->manager->networks, network);
269
270 if (network->manager->networks_by_name)
271 hashmap_remove(network->manager->networks_by_name, network->name);
272 }
273
274 free(network->name);
f579559b 275
79e16ce3
LP
276 condition_free_list(network->match_host);
277 condition_free_list(network->match_virt);
278 condition_free_list(network->match_kernel);
279 condition_free_list(network->match_arch);
280
8eb9058d 281 free(network->dhcp_server_timezone);
1a04db0f
LP
282 free(network->dhcp_server_dns);
283 free(network->dhcp_server_ntp);
8eb9058d 284
8a516214
LP
285 set_free_free(network->dnssec_negative_trust_anchors);
286
f579559b
TG
287 free(network);
288}
289
dbffab87
TG
290int network_get_by_name(Manager *manager, const char *name, Network **ret) {
291 Network *network;
292
293 assert(manager);
294 assert(name);
295 assert(ret);
296
297 network = hashmap_get(manager->networks_by_name, name);
298 if (!network)
299 return -ENOENT;
300
301 *ret = network;
302
303 return 0;
304}
305
505f8da7
TG
306int network_get(Manager *manager, struct udev_device *device,
307 const char *ifname, const struct ether_addr *address,
308 Network **ret) {
f579559b 309 Network *network;
af3aa302 310 struct udev_device *parent;
24c083df 311 const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
f579559b
TG
312
313 assert(manager);
f579559b 314 assert(ret);
af3aa302 315
24c083df
TG
316 if (device) {
317 path = udev_device_get_property_value(device, "ID_PATH");
af3aa302 318
24c083df
TG
319 parent = udev_device_get_parent(device);
320 if (parent)
321 parent_driver = udev_device_get_driver(parent);
af3aa302 322
24c083df 323 driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
af3aa302 324
24c083df
TG
325 devtype = udev_device_get_devtype(device);
326 }
f579559b 327
f579559b
TG
328 LIST_FOREACH(networks, network, manager->networks) {
329 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
330 network->match_driver, network->match_type,
331 network->match_name, network->match_host,
332 network->match_virt, network->match_kernel,
333 network->match_arch,
af3aa302
TG
334 address, path, parent_driver, driver,
335 devtype, ifname)) {
24c083df 336 if (network->match_name && device) {
ca6038b8
TG
337 const char *attr;
338 uint8_t name_assign_type = NET_NAME_UNKNOWN;
339
32bc8adc 340 attr = udev_device_get_sysattr_value(device, "name_assign_type");
285760fe 341 if (attr)
dc751688 342 (void) safe_atou8(attr, &name_assign_type);
32bc8adc
TG
343
344 if (name_assign_type == NET_NAME_ENUM)
a2fae7bb
TG
345 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
346 ifname, network->filename);
32bc8adc 347 else
a2fae7bb 348 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 349 } else
a2fae7bb 350 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 351
f579559b
TG
352 *ret = network;
353 return 0;
354 }
355 }
356
357 *ret = NULL;
358
359 return -ENOENT;
360}
361
362int network_apply(Manager *manager, Network *network, Link *link) {
f579559b
TG
363 int r;
364
c4a03a56
TG
365 assert(manager);
366 assert(network);
367 assert(link);
368
f579559b
TG
369 link->network = network;
370
bfa695b5
TG
371 if (network->ipv4ll_route) {
372 Route *route;
373
374 r = route_new_static(network, 0, &route);
375 if (r < 0)
376 return r;
377
2ce40956 378 r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
bfa695b5
TG
379 if (r == 0)
380 return -EINVAL;
381 if (r < 0)
382 return -errno;
383
384 route->family = AF_INET;
385 route->dst_prefixlen = 16;
386 route->scope = RT_SCOPE_LINK;
86655331 387 route->priority = IPV4LL_ROUTE_METRIC;
bfa695b5
TG
388 route->protocol = RTPROT_STATIC;
389 }
390
3df9bec5
LP
391 if (!strv_isempty(network->dns) ||
392 !strv_isempty(network->ntp) ||
393 !strv_isempty(network->search_domains) ||
394 !strv_isempty(network->route_domains)) {
84de38c5
TG
395 manager_dirty(manager);
396 link_dirty(link);
3bef724f
TG
397 }
398
f579559b
TG
399 return 0;
400}
02b59d57 401
439689c6
SS
402bool network_has_static_ipv6_addresses(Network *network) {
403 Address *address;
404
405 assert(network);
406
407 LIST_FOREACH(addresses, address, network->static_addresses) {
408 if (address->family == AF_INET6)
409 return true;
410 }
411
412 return false;
413}
414
69a93e7d 415int config_parse_netdev(const char *unit,
02b59d57
TG
416 const char *filename,
417 unsigned line,
418 const char *section,
419 unsigned section_line,
420 const char *lvalue,
421 int ltype,
422 const char *rvalue,
423 void *data,
424 void *userdata) {
425 Network *network = userdata;
31d0ac36
TG
426 _cleanup_free_ char *kind_string = NULL;
427 char *p;
1a436809 428 NetDev *netdev;
69a93e7d 429 NetDevKind kind;
02b59d57
TG
430 int r;
431
432 assert(filename);
433 assert(lvalue);
434 assert(rvalue);
435 assert(data);
436
69a93e7d
TG
437 kind_string = strdup(lvalue);
438 if (!kind_string)
439 return log_oom();
52433f6b 440
69a93e7d
TG
441 /* the keys are CamelCase versions of the kind */
442 for (p = kind_string; *p; p++)
443 *p = tolower(*p);
52433f6b 444
69a93e7d
TG
445 kind = netdev_kind_from_string(kind_string);
446 if (kind == _NETDEV_KIND_INVALID) {
12ca818f 447 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
448 return 0;
449 }
450
54abf461
TG
451 r = netdev_get(network->manager, rvalue, &netdev);
452 if (r < 0) {
12ca818f 453 log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
454 return 0;
455 }
456
69a93e7d 457 if (netdev->kind != kind) {
12ca818f 458 log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
459 return 0;
460 }
461
69a93e7d
TG
462 switch (kind) {
463 case NETDEV_KIND_BRIDGE:
464 network->bridge = netdev;
54abf461 465
69a93e7d
TG
466 break;
467 case NETDEV_KIND_BOND:
468 network->bond = netdev;
fe6b2d55 469
69a93e7d
TG
470 break;
471 case NETDEV_KIND_VLAN:
69a93e7d 472 case NETDEV_KIND_MACVLAN:
f33ff02b 473 case NETDEV_KIND_MACVTAP:
c4a5ddc9 474 case NETDEV_KIND_IPVLAN:
326cb406 475 case NETDEV_KIND_VXLAN:
6a0a2f86 476 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406 477 if (r < 0) {
12ca818f 478 log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue);
326cb406
SS
479 return 0;
480 }
481
69a93e7d
TG
482 break;
483 default:
484 assert_not_reached("Can not parse NetDev");
fe6b2d55
TG
485 }
486
47e2dc31
TG
487 netdev_ref(netdev);
488
fe6b2d55
TG
489 return 0;
490}
7951dea2 491
3df9bec5
LP
492int config_parse_domains(
493 const char *unit,
494 const char *filename,
495 unsigned line,
496 const char *section,
497 unsigned section_line,
498 const char *lvalue,
499 int ltype,
500 const char *rvalue,
501 void *data,
502 void *userdata) {
503
504 const char *p;
505 Network *n = data;
6192b846
TG
506 int r;
507
3df9bec5
LP
508 assert(n);
509 assert(lvalue);
510 assert(rvalue);
6192b846 511
3df9bec5
LP
512 if (isempty(rvalue)) {
513 n->search_domains = strv_free(n->search_domains);
514 n->route_domains = strv_free(n->route_domains);
515 return 0;
516 }
67272d15 517
3df9bec5
LP
518 p = rvalue;
519 for (;;) {
520 _cleanup_free_ char *w = NULL, *normalized = NULL;
521 const char *domain;
522 bool is_route;
523
524 r = extract_first_word(&p, &w, NULL, 0);
525 if (r < 0) {
526 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
527 break;
528 }
529 if (r == 0)
530 break;
531
532 is_route = w[0] == '~';
533 domain = is_route ? w + 1 : w;
534
535 if (dns_name_is_root(domain) || streq(domain, "*")) {
536 /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
537 * routing domain, unconditionally. */
538 is_route = true;
539 domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
540
541 } else {
542 r = dns_name_normalize(domain, &normalized);
543 if (r < 0) {
544 log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
545 continue;
546 }
547
548 domain = normalized;
549
550 if (is_localhost(domain)) {
551 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 552 continue;
3df9bec5 553 }
37de2509 554 }
40274ed6 555
3df9bec5
LP
556 if (is_route) {
557 r = strv_extend(&n->route_domains, domain);
558 if (r < 0)
559 return log_oom();
40274ed6 560
3df9bec5
LP
561 } else {
562 r = strv_extend(&n->search_domains, domain);
563 if (r < 0)
564 return log_oom();
565 }
40274ed6 566 }
f15b6e5a 567
3df9bec5
LP
568 strv_uniq(n->route_domains);
569 strv_uniq(n->search_domains);
570
6192b846
TG
571 return 0;
572}
573
7951dea2
SS
574int config_parse_tunnel(const char *unit,
575 const char *filename,
576 unsigned line,
577 const char *section,
578 unsigned section_line,
579 const char *lvalue,
580 int ltype,
581 const char *rvalue,
582 void *data,
583 void *userdata) {
584 Network *network = userdata;
585 NetDev *netdev;
586 int r;
587
588 assert(filename);
589 assert(lvalue);
590 assert(rvalue);
591 assert(data);
592
593 r = netdev_get(network->manager, rvalue, &netdev);
594 if (r < 0) {
6a7a4e4d 595 log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
7951dea2
SS
596 return 0;
597 }
598
599 if (netdev->kind != NETDEV_KIND_IPIP &&
600 netdev->kind != NETDEV_KIND_SIT &&
a613382b 601 netdev->kind != NETDEV_KIND_GRE &&
1af2536a 602 netdev->kind != NETDEV_KIND_GRETAP &&
b16492f8
SS
603 netdev->kind != NETDEV_KIND_IP6GRE &&
604 netdev->kind != NETDEV_KIND_IP6GRETAP &&
855ee1a1 605 netdev->kind != NETDEV_KIND_VTI &&
9011ce77 606 netdev->kind != NETDEV_KIND_VTI6 &&
855ee1a1
SS
607 netdev->kind != NETDEV_KIND_IP6TNL
608 ) {
12ca818f 609 log_syntax(unit, LOG_ERR, filename, line, 0,
7951dea2
SS
610 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
611 return 0;
612 }
613
6a0a2f86
TG
614 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
615 if (r < 0) {
6a7a4e4d 616 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
6a0a2f86
TG
617 return 0;
618 }
619
620 netdev_ref(netdev);
7951dea2
SS
621
622 return 0;
623}
bd8f6538 624
d0d6a4cd
TG
625int config_parse_ipv4ll(
626 const char* unit,
627 const char *filename,
628 unsigned line,
629 const char *section,
630 unsigned section_line,
631 const char *lvalue,
632 int ltype,
633 const char *rvalue,
634 void *data,
635 void *userdata) {
636
637 AddressFamilyBoolean *link_local = data;
638
639 assert(filename);
640 assert(lvalue);
641 assert(rvalue);
642 assert(data);
643
644 /* Note that this is mostly like
645 * config_parse_address_family_boolean(), except that it
646 * applies only to IPv4 */
647
5883ff60 648 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue));
d0d6a4cd
TG
649
650 return 0;
651}
652
bd8f6538
TG
653int config_parse_dhcp(
654 const char* unit,
655 const char *filename,
656 unsigned line,
657 const char *section,
658 unsigned section_line,
659 const char *lvalue,
660 int ltype,
661 const char *rvalue,
662 void *data,
663 void *userdata) {
664
cb9fc36a 665 AddressFamilyBoolean *dhcp = data, s;
bd8f6538
TG
666
667 assert(filename);
668 assert(lvalue);
669 assert(rvalue);
670 assert(data);
671
769d324c
LP
672 /* Note that this is mostly like
673 * config_parse_address_family_boolean(), except that it
674 * understands some old names for the enum values */
675
cb9fc36a
LP
676 s = address_family_boolean_from_string(rvalue);
677 if (s < 0) {
678
679 /* Previously, we had a slightly different enum here,
680 * support its values for compatbility. */
681
682 if (streq(rvalue, "none"))
683 s = ADDRESS_FAMILY_NO;
684 else if (streq(rvalue, "v4"))
685 s = ADDRESS_FAMILY_IPV4;
686 else if (streq(rvalue, "v6"))
687 s = ADDRESS_FAMILY_IPV6;
688 else if (streq(rvalue, "both"))
689 s = ADDRESS_FAMILY_YES;
690 else {
12ca818f 691 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
bd8f6538
TG
692 return 0;
693 }
bd8f6538
TG
694 }
695
cb9fc36a 696 *dhcp = s;
bd8f6538
TG
697 return 0;
698}
699
3e43b2cd
JJ
700static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
701 [DHCP_CLIENT_ID_MAC] = "mac",
702 [DHCP_CLIENT_ID_DUID] = "duid"
703};
704
705DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
706DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
707
60c35566 708int config_parse_ipv6token(
7f77697a
TG
709 const char* unit,
710 const char *filename,
711 unsigned line,
712 const char *section,
713 unsigned section_line,
714 const char *lvalue,
715 int ltype,
716 const char *rvalue,
717 void *data,
718 void *userdata) {
719
720 union in_addr_union buffer;
721 struct in6_addr *token = data;
722 int r;
723
724 assert(filename);
725 assert(lvalue);
726 assert(rvalue);
727 assert(token);
728
729 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
730 if (r < 0) {
6a7a4e4d 731 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
7f77697a
TG
732 return 0;
733 }
734
735 r = in_addr_is_null(AF_INET6, &buffer);
12ca818f 736 if (r != 0) {
6a7a4e4d 737 log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
7f77697a
TG
738 return 0;
739 }
740
741 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
12ca818f 742 log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
7f77697a
TG
743 return 0;
744 }
745
746 *token = buffer.in6;
747
748 return 0;
749}
8add5f79 750
49092e22 751static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1f0d9695
LP
752 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
753 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
754 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
49092e22
SS
755};
756
757DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
758
759int config_parse_ipv6_privacy_extensions(
760 const char* unit,
761 const char *filename,
762 unsigned line,
763 const char *section,
764 unsigned section_line,
765 const char *lvalue,
766 int ltype,
767 const char *rvalue,
768 void *data,
769 void *userdata) {
770
771 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
772 int k;
773
774 assert(filename);
775 assert(lvalue);
776 assert(rvalue);
777 assert(ipv6_privacy_extensions);
778
779 /* Our enum shall be a superset of booleans, hence first try
780 * to parse as boolean, and then as enum */
781
782 k = parse_boolean(rvalue);
783 if (k > 0)
1f0d9695 784 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
49092e22 785 else if (k == 0)
1f0d9695 786 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
49092e22 787 else {
1f0d9695 788 IPv6PrivacyExtensions s;
49092e22
SS
789
790 s = ipv6_privacy_extensions_from_string(rvalue);
1f0d9695
LP
791 if (s < 0) {
792
793 if (streq(rvalue, "kernel"))
794 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
795 else {
12ca818f 796 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
1f0d9695
LP
797 return 0;
798 }
49092e22
SS
799 }
800
801 *ipv6_privacy_extensions = s;
802 }
803
804 return 0;
805}
a7d0ef44 806
1ac608c9
LP
807int config_parse_hostname(
808 const char *unit,
809 const char *filename,
810 unsigned line,
811 const char *section,
812 unsigned section_line,
813 const char *lvalue,
814 int ltype,
815 const char *rvalue,
816 void *data,
817 void *userdata) {
818
819 char **hostname = data, *hn = NULL;
a7d0ef44
SS
820 int r;
821
822 assert(filename);
823 assert(lvalue);
824 assert(rvalue);
825
1ac608c9 826 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
a7d0ef44
SS
827 if (r < 0)
828 return r;
829
1ac608c9 830 if (!hostname_is_valid(hn, false)) {
12ca818f 831 log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
a7d0ef44
SS
832 free(hn);
833 return 0;
834 }
835
1ac608c9 836 free(*hostname);
ae691c1d 837 *hostname = hostname_cleanup(hn);
a7d0ef44
SS
838 return 0;
839}
8eb9058d
LP
840
841int config_parse_timezone(
842 const char *unit,
843 const char *filename,
844 unsigned line,
845 const char *section,
846 unsigned section_line,
847 const char *lvalue,
848 int ltype,
849 const char *rvalue,
850 void *data,
851 void *userdata) {
852
64d6c229 853 char **datap = data, *tz = NULL;
8eb9058d
LP
854 int r;
855
856 assert(filename);
857 assert(lvalue);
858 assert(rvalue);
859
860 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
861 if (r < 0)
862 return r;
863
864 if (!timezone_is_valid(tz)) {
12ca818f 865 log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
8eb9058d
LP
866 free(tz);
867 return 0;
868 }
869
64d6c229
TA
870 free(*datap);
871 *datap = tz;
8eb9058d
LP
872
873 return 0;
874}
1a04db0f
LP
875
876int config_parse_dhcp_server_dns(
877 const char *unit,
878 const char *filename,
879 unsigned line,
880 const char *section,
881 unsigned section_line,
882 const char *lvalue,
883 int ltype,
884 const char *rvalue,
885 void *data,
886 void *userdata) {
887
888 Network *n = data;
889 const char *p = rvalue;
890 int r;
891
892 assert(filename);
893 assert(lvalue);
894 assert(rvalue);
895
896 for (;;) {
897 _cleanup_free_ char *w = NULL;
898 struct in_addr a, *m;
899
900 r = extract_first_word(&p, &w, NULL, 0);
901 if (r < 0) {
902 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
903 return 0;
904 }
905
906 if (r == 0)
907 return 0;
908
909 if (inet_pton(AF_INET, w, &a) <= 0) {
12ca818f 910 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
1a04db0f
LP
911 continue;
912 }
913
914 m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr));
915 if (!m)
916 return log_oom();
917
918 m[n->n_dhcp_server_dns++] = a;
919 n->dhcp_server_dns = m;
920 }
921}
922
923int config_parse_dhcp_server_ntp(
924 const char *unit,
925 const char *filename,
926 unsigned line,
927 const char *section,
928 unsigned section_line,
929 const char *lvalue,
930 int ltype,
931 const char *rvalue,
932 void *data,
933 void *userdata) {
934
935 Network *n = data;
936 const char *p = rvalue;
937 int r;
938
939 assert(filename);
940 assert(lvalue);
941 assert(rvalue);
942
943 for (;;) {
944 _cleanup_free_ char *w = NULL;
945 struct in_addr a, *m;
946
947 r = extract_first_word(&p, &w, NULL, 0);
948 if (r < 0) {
12ca818f 949 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
1a04db0f
LP
950 return 0;
951 }
952
953 if (r == 0)
954 return 0;
955
956 if (inet_pton(AF_INET, w, &a) <= 0) {
12ca818f 957 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w);
1a04db0f
LP
958 continue;
959 }
960
961 m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr));
962 if (!m)
963 return log_oom();
964
965 m[n->n_dhcp_server_ntp++] = a;
966 n->dhcp_server_ntp = m;
967 }
968}
8a516214
LP
969
970int config_parse_dnssec_negative_trust_anchors(
971 const char *unit,
972 const char *filename,
973 unsigned line,
974 const char *section,
975 unsigned section_line,
976 const char *lvalue,
977 int ltype,
978 const char *rvalue,
979 void *data,
980 void *userdata) {
981
982 const char *p = rvalue;
983 Network *n = data;
984 int r;
985
3df9bec5 986 assert(n);
8a516214
LP
987 assert(lvalue);
988 assert(rvalue);
989
990 if (isempty(rvalue)) {
991 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
992 return 0;
993 }
994
995 for (;;) {
996 _cleanup_free_ char *w = NULL;
997
998 r = extract_first_word(&p, &w, NULL, 0);
999 if (r < 0) {
1000 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
1001 break;
1002 }
1003 if (r == 0)
1004 break;
1005
1006 r = dns_name_is_valid(w);
1007 if (r <= 0) {
1008 log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
1009 continue;
1010 }
1011
cbbf38ae
LP
1012 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1013 if (r < 0)
1014 return log_oom();
1015
8a516214
LP
1016 r = set_put(n->dnssec_negative_trust_anchors, w);
1017 if (r < 0)
1018 return log_oom();
1019 if (r > 0)
1020 w = NULL;
1021 }
1022
1023 return 0;
1024}
b2a81c0b
LP
1025
1026DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
1027
1028static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
1029 [DHCP_USE_DOMAINS_NO] = "no",
1030 [DHCP_USE_DOMAINS_ROUTE] = "route",
1031 [DHCP_USE_DOMAINS_YES] = "yes",
1032};
1033
1034DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
34437b4f
LP
1035
1036DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
1037
1038static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
1039 [LLDP_MODE_NO] = "no",
1040 [LLDP_MODE_YES] = "yes",
1041 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
1042};
1043
1044DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);