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