]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
Merge pull request #495 from poettering/forwarding-fix
[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
f579559b
TG
25#include "conf-files.h"
26#include "conf-parser.h"
477e73b5 27#include "util.h"
958b66ea 28#include "hostname-util.h"
5a8bcb67
LP
29#include "networkd.h"
30#include "networkd-netdev.h"
31#include "networkd-link.h"
32#include "network-internal.h"
37de2509 33#include "dns-domain.h"
f579559b
TG
34
35static int network_load_one(Manager *manager, const char *filename) {
36 _cleanup_network_free_ Network *network = NULL;
37 _cleanup_fclose_ FILE *file = NULL;
dbffab87 38 char *d;
b3070dc0
TG
39 Route *route;
40 Address *address;
f579559b
TG
41 int r;
42
bf1bc670
TA
43 assert(manager);
44 assert(filename);
45
f579559b
TG
46 file = fopen(filename, "re");
47 if (!file) {
48 if (errno == ENOENT)
49 return 0;
50 else
ecb08ec6 51 return -errno;
f579559b
TG
52 }
53
ed88bcfb
ZJS
54 if (null_or_empty_fd(fileno(file))) {
55 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
56 return 0;
57 }
58
f579559b
TG
59 network = new0(Network, 1);
60 if (!network)
61 return log_oom();
62
5a3eb5a7
TG
63 network->manager = manager;
64
f048a16b
TG
65 LIST_HEAD_INIT(network->static_addresses);
66 LIST_HEAD_INIT(network->static_routes);
b98b483b 67 LIST_HEAD_INIT(network->static_fdb_entries);
f579559b 68
d5099efc 69 network->stacked_netdevs = hashmap_new(&string_hash_ops);
6a0a2f86 70 if (!network->stacked_netdevs)
326cb406
SS
71 return log_oom();
72
d5099efc 73 network->addresses_by_section = hashmap_new(NULL);
6ae115c1
TG
74 if (!network->addresses_by_section)
75 return log_oom();
76
d5099efc 77 network->routes_by_section = hashmap_new(NULL);
6ae115c1
TG
78 if (!network->routes_by_section)
79 return log_oom();
80
b98b483b
AR
81 network->fdb_entries_by_section = hashmap_new(NULL);
82 if (!network->fdb_entries_by_section)
83 return log_oom();
84
6ae115c1
TG
85 network->filename = strdup(filename);
86 if (!network->filename)
87 return log_oom();
88
dbffab87
TG
89 network->name = strdup(basename(filename));
90 if (!network->name)
91 return log_oom();
92
93 d = strrchr(network->name, '.');
94 if (!d)
95 return -EINVAL;
96
97 assert(streq(d, ".network"));
98
99 *d = '\0';
100
cb9fc36a 101 network->dhcp = ADDRESS_FAMILY_NO;
bcb7a07e 102 network->dhcp_ntp = true;
5be4d38e 103 network->dhcp_dns = true;
1346b1f0 104 network->dhcp_hostname = true;
e1ea665e 105 network->dhcp_routes = true;
4cc7a82c 106 network->dhcp_sendhost = true;
84b5b79a 107 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
3e43b2cd 108 network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
5be4d38e 109
bd8f6538
TG
110 network->llmnr = LLMNR_SUPPORT_YES;
111
d0d6a4cd
TG
112 network->link_local = ADDRESS_FAMILY_IPV6;
113
49092e22
SS
114 network->ipv6_privacy_extensions = _IPV6_PRIVACY_EXTENSIONS_INVALID;
115
e9f3d2d5 116 r = config_parse(NULL, filename, file,
c106cc36
TG
117 "Match\0"
118 "Link\0"
119 "Network\0"
120 "Address\0"
121 "Route\0"
122 "DHCP\0"
123 "DHCPv4\0"
b98b483b
AR
124 "Bridge\0"
125 "BridgeFDB\0",
e9f3d2d5 126 config_item_perf_lookup, network_network_gperf_lookup,
36f822c4
ZJS
127 false, false, true, network);
128 if (r < 0)
f579559b 129 return r;
f579559b 130
5a8bcb67
LP
131 /* IPMasquerade=yes implies IPForward=yes */
132 if (network->ip_masquerade)
769d324c 133 network->ip_forward |= ADDRESS_FAMILY_IPV4;
5a8bcb67 134
f579559b 135 LIST_PREPEND(networks, manager->networks, network);
b3070dc0 136
dbffab87
TG
137 r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
138 if (r < 0)
139 return r;
140
141 r = hashmap_put(manager->networks_by_name, network->name, network);
142 if (r < 0)
143 return r;
144
3d3d4255 145 LIST_FOREACH(routes, route, network->static_routes) {
b3070dc0
TG
146 if (!route->family) {
147 log_warning("Route section without Gateway field configured in %s. "
148 "Ignoring", filename);
149 return 0;
150 }
b3070dc0
TG
151 }
152
3d3d4255 153 LIST_FOREACH(addresses, address, network->static_addresses) {
b3070dc0
TG
154 if (!address->family) {
155 log_warning("Address section without Address field configured in %s. "
156 "Ignoring", filename);
157 return 0;
158 }
159 }
160
f579559b
TG
161 network = NULL;
162
163 return 0;
164}
165
166int network_load(Manager *manager) {
167 Network *network;
477e73b5
ZJS
168 _cleanup_strv_free_ char **files = NULL;
169 char **f;
f579559b
TG
170 int r;
171
172 assert(manager);
173
174 while ((network = manager->networks))
175 network_free(network);
176
2ad8416d 177 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
f647962d
MS
178 if (r < 0)
179 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b
TG
180
181 STRV_FOREACH_BACKWARDS(f, files) {
182 r = network_load_one(manager, *f);
183 if (r < 0)
184 return r;
185 }
186
f579559b
TG
187 return 0;
188}
189
f579559b 190void network_free(Network *network) {
47e2dc31 191 NetDev *netdev;
f579559b
TG
192 Route *route;
193 Address *address;
b98b483b 194 FdbEntry *fdb_entry;
06f021a8 195 Iterator i;
f579559b
TG
196
197 if (!network)
198 return;
199
200 free(network->filename);
201
202 free(network->match_mac);
5256e00e
TG
203 strv_free(network->match_path);
204 strv_free(network->match_driver);
205 strv_free(network->match_type);
206 strv_free(network->match_name);
f579559b
TG
207
208 free(network->description);
edb85f0d 209 free(network->dhcp_vendor_class_identifier);
f579559b 210
c106cc36
TG
211 free(network->mac);
212
b0e39c82
TG
213 strv_free(network->ntp);
214 strv_free(network->dns);
486d1a81 215 strv_free(network->domains);
0d4ad91d 216 strv_free(network->bind_carrier);
3bef724f 217
47e2dc31
TG
218 netdev_unref(network->bridge);
219
220 netdev_unref(network->bond);
221
2c36be2f
TG
222 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
223 hashmap_remove(network->stacked_netdevs, netdev->ifname);
326cb406 224 netdev_unref(netdev);
2c36be2f 225 }
6a0a2f86 226 hashmap_free(network->stacked_netdevs);
326cb406 227
f048a16b 228 while ((route = network->static_routes))
f579559b
TG
229 route_free(route);
230
f048a16b 231 while ((address = network->static_addresses))
f579559b
TG
232 address_free(address);
233
b98b483b
AR
234 while ((fdb_entry = network->static_fdb_entries))
235 fdb_entry_free(fdb_entry);
236
6ae115c1
TG
237 hashmap_free(network->addresses_by_section);
238 hashmap_free(network->routes_by_section);
b98b483b 239 hashmap_free(network->fdb_entries_by_section);
6ae115c1 240
dbffab87
TG
241 if (network->manager) {
242 if (network->manager->networks)
243 LIST_REMOVE(networks, network->manager->networks, network);
244
245 if (network->manager->networks_by_name)
246 hashmap_remove(network->manager->networks_by_name, network->name);
247 }
248
249 free(network->name);
f579559b 250
79e16ce3
LP
251 condition_free_list(network->match_host);
252 condition_free_list(network->match_virt);
253 condition_free_list(network->match_kernel);
254 condition_free_list(network->match_arch);
255
f579559b
TG
256 free(network);
257}
258
dbffab87
TG
259int network_get_by_name(Manager *manager, const char *name, Network **ret) {
260 Network *network;
261
262 assert(manager);
263 assert(name);
264 assert(ret);
265
266 network = hashmap_get(manager->networks_by_name, name);
267 if (!network)
268 return -ENOENT;
269
270 *ret = network;
271
272 return 0;
273}
274
505f8da7
TG
275int network_get(Manager *manager, struct udev_device *device,
276 const char *ifname, const struct ether_addr *address,
277 Network **ret) {
f579559b 278 Network *network;
af3aa302 279 struct udev_device *parent;
24c083df 280 const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
f579559b
TG
281
282 assert(manager);
f579559b 283 assert(ret);
af3aa302 284
24c083df
TG
285 if (device) {
286 path = udev_device_get_property_value(device, "ID_PATH");
af3aa302 287
24c083df
TG
288 parent = udev_device_get_parent(device);
289 if (parent)
290 parent_driver = udev_device_get_driver(parent);
af3aa302 291
24c083df 292 driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
af3aa302 293
24c083df
TG
294 devtype = udev_device_get_devtype(device);
295 }
f579559b 296
f579559b
TG
297 LIST_FOREACH(networks, network, manager->networks) {
298 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
299 network->match_driver, network->match_type,
300 network->match_name, network->match_host,
301 network->match_virt, network->match_kernel,
302 network->match_arch,
af3aa302
TG
303 address, path, parent_driver, driver,
304 devtype, ifname)) {
24c083df 305 if (network->match_name && device) {
ca6038b8
TG
306 const char *attr;
307 uint8_t name_assign_type = NET_NAME_UNKNOWN;
308
32bc8adc 309 attr = udev_device_get_sysattr_value(device, "name_assign_type");
285760fe 310 if (attr)
dc751688 311 (void) safe_atou8(attr, &name_assign_type);
32bc8adc
TG
312
313 if (name_assign_type == NET_NAME_ENUM)
ca6038b8 314 log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
32bc8adc
TG
315 IFNAMSIZ, ifname, network->filename);
316 else
317 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
318 } else
319 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
320
f579559b
TG
321 *ret = network;
322 return 0;
323 }
324 }
325
326 *ret = NULL;
327
328 return -ENOENT;
329}
330
331int network_apply(Manager *manager, Network *network, Link *link) {
f579559b
TG
332 int r;
333
f579559b
TG
334 link->network = network;
335
bfa695b5
TG
336 if (network->ipv4ll_route) {
337 Route *route;
338
339 r = route_new_static(network, 0, &route);
340 if (r < 0)
341 return r;
342
343 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
344 if (r == 0)
345 return -EINVAL;
346 if (r < 0)
347 return -errno;
348
349 route->family = AF_INET;
350 route->dst_prefixlen = 16;
351 route->scope = RT_SCOPE_LINK;
352 route->metrics = IPV4LL_ROUTE_METRIC;
353 route->protocol = RTPROT_STATIC;
354 }
355
bcb7a07e 356 if (network->dns || network->ntp) {
091a364c 357 r = link_save(link);
3bef724f
TG
358 if (r < 0)
359 return r;
360 }
361
f579559b
TG
362 return 0;
363}
02b59d57 364
69a93e7d 365int config_parse_netdev(const char *unit,
02b59d57
TG
366 const char *filename,
367 unsigned line,
368 const char *section,
369 unsigned section_line,
370 const char *lvalue,
371 int ltype,
372 const char *rvalue,
373 void *data,
374 void *userdata) {
375 Network *network = userdata;
31d0ac36
TG
376 _cleanup_free_ char *kind_string = NULL;
377 char *p;
1a436809 378 NetDev *netdev;
69a93e7d 379 NetDevKind kind;
02b59d57
TG
380 int r;
381
382 assert(filename);
383 assert(lvalue);
384 assert(rvalue);
385 assert(data);
386
69a93e7d
TG
387 kind_string = strdup(lvalue);
388 if (!kind_string)
389 return log_oom();
52433f6b 390
69a93e7d
TG
391 /* the keys are CamelCase versions of the kind */
392 for (p = kind_string; *p; p++)
393 *p = tolower(*p);
52433f6b 394
69a93e7d
TG
395 kind = netdev_kind_from_string(kind_string);
396 if (kind == _NETDEV_KIND_INVALID) {
52433f6b 397 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 398 "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
399 return 0;
400 }
401
54abf461
TG
402 r = netdev_get(network->manager, rvalue, &netdev);
403 if (r < 0) {
404 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 405 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
406 return 0;
407 }
408
69a93e7d 409 if (netdev->kind != kind) {
54abf461 410 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 411 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
412 return 0;
413 }
414
69a93e7d
TG
415 switch (kind) {
416 case NETDEV_KIND_BRIDGE:
417 network->bridge = netdev;
54abf461 418
69a93e7d
TG
419 break;
420 case NETDEV_KIND_BOND:
421 network->bond = netdev;
fe6b2d55 422
69a93e7d
TG
423 break;
424 case NETDEV_KIND_VLAN:
69a93e7d 425 case NETDEV_KIND_MACVLAN:
c4a5ddc9 426 case NETDEV_KIND_IPVLAN:
326cb406 427 case NETDEV_KIND_VXLAN:
6a0a2f86 428 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406
SS
429 if (r < 0) {
430 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
6a7a4e4d
LP
431 "Can not add VLAN '%s' to network: %m",
432 rvalue);
326cb406
SS
433 return 0;
434 }
435
69a93e7d
TG
436 break;
437 default:
438 assert_not_reached("Can not parse NetDev");
fe6b2d55
TG
439 }
440
47e2dc31
TG
441 netdev_ref(netdev);
442
fe6b2d55
TG
443 return 0;
444}
7951dea2 445
6192b846
TG
446int config_parse_domains(const char *unit,
447 const char *filename,
448 unsigned line,
449 const char *section,
450 unsigned section_line,
451 const char *lvalue,
452 int ltype,
453 const char *rvalue,
454 void *data,
455 void *userdata) {
67272d15 456 Network *network = userdata;
6192b846
TG
457 char ***domains = data;
458 char **domain;
459 int r;
460
461 r = config_parse_strv(unit, filename, line, section, section_line,
462 lvalue, ltype, rvalue, domains, userdata);
463 if (r < 0)
464 return r;
465
466 strv_uniq(*domains);
f15b6e5a 467 network->wildcard_domain = !!strv_find(*domains, "*");
67272d15 468
40274ed6
LP
469 STRV_FOREACH(domain, *domains) {
470 if (is_localhost(*domain))
471 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
37de2509
NO
472 else {
473 r = dns_name_is_valid(*domain);
474 if (r <= 0 && !streq(*domain, "*")) {
475 if (r < 0)
476 log_error_errno(r, "Failed to validate domain name: %s: %m", *domain);
477 if (r == 0)
478 log_warning("Domain name is not valid, ignoring assignment: %s", *domain);
479 } else
480 continue;
481 }
40274ed6
LP
482
483 strv_remove(*domains, *domain);
484
485 /* We removed one entry, make sure we don't skip the next one */
486 domain--;
487 }
f15b6e5a 488
6192b846
TG
489 return 0;
490}
491
7951dea2
SS
492int config_parse_tunnel(const char *unit,
493 const char *filename,
494 unsigned line,
495 const char *section,
496 unsigned section_line,
497 const char *lvalue,
498 int ltype,
499 const char *rvalue,
500 void *data,
501 void *userdata) {
502 Network *network = userdata;
503 NetDev *netdev;
504 int r;
505
506 assert(filename);
507 assert(lvalue);
508 assert(rvalue);
509 assert(data);
510
511 r = netdev_get(network->manager, rvalue, &netdev);
512 if (r < 0) {
6a7a4e4d 513 log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
7951dea2
SS
514 return 0;
515 }
516
517 if (netdev->kind != NETDEV_KIND_IPIP &&
518 netdev->kind != NETDEV_KIND_SIT &&
a613382b 519 netdev->kind != NETDEV_KIND_GRE &&
1af2536a 520 netdev->kind != NETDEV_KIND_GRETAP &&
b16492f8
SS
521 netdev->kind != NETDEV_KIND_IP6GRE &&
522 netdev->kind != NETDEV_KIND_IP6GRETAP &&
855ee1a1 523 netdev->kind != NETDEV_KIND_VTI &&
9011ce77 524 netdev->kind != NETDEV_KIND_VTI6 &&
855ee1a1
SS
525 netdev->kind != NETDEV_KIND_IP6TNL
526 ) {
7951dea2
SS
527 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
528 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
529 return 0;
530 }
531
6a0a2f86
TG
532 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
533 if (r < 0) {
6a7a4e4d 534 log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
6a0a2f86
TG
535 return 0;
536 }
537
538 netdev_ref(netdev);
7951dea2
SS
539
540 return 0;
541}
bd8f6538 542
d0d6a4cd
TG
543int config_parse_ipv4ll(
544 const char* unit,
545 const char *filename,
546 unsigned line,
547 const char *section,
548 unsigned section_line,
549 const char *lvalue,
550 int ltype,
551 const char *rvalue,
552 void *data,
553 void *userdata) {
554
555 AddressFamilyBoolean *link_local = data;
556
557 assert(filename);
558 assert(lvalue);
559 assert(rvalue);
560 assert(data);
561
562 /* Note that this is mostly like
563 * config_parse_address_family_boolean(), except that it
564 * applies only to IPv4 */
565
566 if (parse_boolean(rvalue))
567 *link_local |= ADDRESS_FAMILY_IPV4;
568 else
569 *link_local &= ~ADDRESS_FAMILY_IPV4;
570
571 return 0;
572}
573
bd8f6538
TG
574int config_parse_dhcp(
575 const char* unit,
576 const char *filename,
577 unsigned line,
578 const char *section,
579 unsigned section_line,
580 const char *lvalue,
581 int ltype,
582 const char *rvalue,
583 void *data,
584 void *userdata) {
585
cb9fc36a 586 AddressFamilyBoolean *dhcp = data, s;
bd8f6538
TG
587
588 assert(filename);
589 assert(lvalue);
590 assert(rvalue);
591 assert(data);
592
769d324c
LP
593 /* Note that this is mostly like
594 * config_parse_address_family_boolean(), except that it
595 * understands some old names for the enum values */
596
cb9fc36a
LP
597 s = address_family_boolean_from_string(rvalue);
598 if (s < 0) {
599
600 /* Previously, we had a slightly different enum here,
601 * support its values for compatbility. */
602
603 if (streq(rvalue, "none"))
604 s = ADDRESS_FAMILY_NO;
605 else if (streq(rvalue, "v4"))
606 s = ADDRESS_FAMILY_IPV4;
607 else if (streq(rvalue, "v6"))
608 s = ADDRESS_FAMILY_IPV6;
609 else if (streq(rvalue, "both"))
610 s = ADDRESS_FAMILY_YES;
611 else {
612 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
bd8f6538
TG
613 return 0;
614 }
bd8f6538
TG
615 }
616
cb9fc36a 617 *dhcp = s;
bd8f6538
TG
618 return 0;
619}
620
3e43b2cd
JJ
621static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
622 [DHCP_CLIENT_ID_MAC] = "mac",
623 [DHCP_CLIENT_ID_DUID] = "duid"
624};
625
626DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
627DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
628
bd8f6538
TG
629static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
630 [LLMNR_SUPPORT_NO] = "no",
631 [LLMNR_SUPPORT_YES] = "yes",
632 [LLMNR_SUPPORT_RESOLVE] = "resolve",
633};
634
635DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
636
637int config_parse_llmnr(
638 const char* unit,
639 const char *filename,
640 unsigned line,
641 const char *section,
642 unsigned section_line,
643 const char *lvalue,
644 int ltype,
645 const char *rvalue,
646 void *data,
647 void *userdata) {
648
649 LLMNRSupport *llmnr = data;
650 int k;
651
652 assert(filename);
653 assert(lvalue);
654 assert(rvalue);
7f77697a 655 assert(llmnr);
bd8f6538
TG
656
657 /* Our enum shall be a superset of booleans, hence first try
658 * to parse as boolean, and then as enum */
659
660 k = parse_boolean(rvalue);
661 if (k > 0)
662 *llmnr = LLMNR_SUPPORT_YES;
663 else if (k == 0)
664 *llmnr = LLMNR_SUPPORT_NO;
665 else {
666 LLMNRSupport s;
667
668 s = llmnr_support_from_string(rvalue);
669 if (s < 0){
670 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
671 return 0;
672 }
673
674 *llmnr = s;
675 }
676
677 return 0;
678}
7f77697a 679
60c35566 680int config_parse_ipv6token(
7f77697a
TG
681 const char* unit,
682 const char *filename,
683 unsigned line,
684 const char *section,
685 unsigned section_line,
686 const char *lvalue,
687 int ltype,
688 const char *rvalue,
689 void *data,
690 void *userdata) {
691
692 union in_addr_union buffer;
693 struct in6_addr *token = data;
694 int r;
695
696 assert(filename);
697 assert(lvalue);
698 assert(rvalue);
699 assert(token);
700
701 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
702 if (r < 0) {
6a7a4e4d 703 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
7f77697a
TG
704 return 0;
705 }
706
707 r = in_addr_is_null(AF_INET6, &buffer);
708 if (r < 0) {
6a7a4e4d 709 log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
7f77697a
TG
710 return 0;
711 }
712
713 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
e2acdb6b 714 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
7f77697a
TG
715 return 0;
716 }
717
718 *token = buffer.in6;
719
720 return 0;
721}
8add5f79
NO
722
723int config_parse_address_family_boolean_with_kernel(
724 const char* unit,
725 const char *filename,
726 unsigned line,
727 const char *section,
728 unsigned section_line,
729 const char *lvalue,
730 int ltype,
731 const char *rvalue,
732 void *data,
733 void *userdata) {
734
735 AddressFamilyBoolean *fwd = data, s;
736
737 assert(filename);
738 assert(lvalue);
739 assert(rvalue);
740 assert(data);
741
742 s = address_family_boolean_from_string(rvalue);
743 if (s < 0) {
744 if (streq(rvalue, "kernel"))
745 s = _ADDRESS_FAMILY_BOOLEAN_INVALID;
746 else {
747 log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding option, ignoring: %s", rvalue);
748 return 0;
749 }
750 }
751
752 *fwd = s;
753
754 return 0;
755}
49092e22
SS
756
757static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
758 [IPV6_PRIVACY_EXTENSIONS_DISABLE] = "no",
759 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "yes",
760 [IPV6_PRIVACY_EXTENSIONS_PREFER_TEMPORARY] = "prefer-temporary",
761};
762
763DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
764
765int config_parse_ipv6_privacy_extensions(
766 const char* unit,
767 const char *filename,
768 unsigned line,
769 const char *section,
770 unsigned section_line,
771 const char *lvalue,
772 int ltype,
773 const char *rvalue,
774 void *data,
775 void *userdata) {
776
777 IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
778 int k;
779
780 assert(filename);
781 assert(lvalue);
782 assert(rvalue);
783 assert(ipv6_privacy_extensions);
784
785 /* Our enum shall be a superset of booleans, hence first try
786 * to parse as boolean, and then as enum */
787
788 k = parse_boolean(rvalue);
789 if (k > 0)
790 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC;
791 else if (k == 0)
792 *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_DISABLE;
793 else {
794 IPv6PrivacyExtensions s;
795
796 s = ipv6_privacy_extensions_from_string(rvalue);
797 if (s < 0){
798 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
799 return 0;
800 }
801
802 *ipv6_privacy_extensions = s;
803 }
804
805 return 0;
806}