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