]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-radv.c
network: sd-radv - Allow to configure Mobile IPv6 Home Agent
[thirdparty/systemd.git] / src / network / networkd-radv.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
091214b6 2/***
810adae9 3 Copyright © 2017 Intel Corporation. All rights reserved.
091214b6
PF
4***/
5
6#include <netinet/icmp6.h>
7#include <arpa/inet.h>
8
ca5ad760 9#include "dns-domain.h"
f09a4747 10#include "networkd-address-generation.h"
3b6a3bde 11#include "networkd-address.h"
d5ebcf65 12#include "networkd-dhcp-prefix-delegation.h"
b5ce4047 13#include "networkd-link.h"
c555a358 14#include "networkd-manager.h"
b5ce4047 15#include "networkd-network.h"
a254fab2 16#include "networkd-queue.h"
091214b6 17#include "networkd-radv.h"
344b3cff 18#include "networkd-route-util.h"
6e849e95 19#include "parse-util.h"
4f1ac4a3 20#include "radv-internal.h"
6e849e95 21#include "string-util.h"
6b1dec66 22#include "string-table.h"
51517f9e 23#include "strv.h"
6e849e95 24
0943b3b7
YW
25void network_adjust_radv(Network *network) {
26 assert(network);
27
28 /* After this function is called, network->router_prefix_delegation can be treated as a boolean. */
29
a27588d4 30 if (network->dhcp_pd < 0)
0943b3b7 31 /* For backward compatibility. */
a27588d4 32 network->dhcp_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6);
0943b3b7
YW
33
34 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
35 if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE)
f81ac115 36 log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link-local addressing is disabled. "
0943b3b7
YW
37 "Disabling IPv6PrefixDelegation=.", network->filename);
38
39 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
40 }
41
42 if (network->router_prefix_delegation == RADV_PREFIX_DELEGATION_NONE) {
43 network->n_router_dns = 0;
44 network->router_dns = mfree(network->router_dns);
45 network->router_search_domains = ordered_set_free(network->router_search_domains);
46 }
47
48 if (!FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_STATIC)) {
49 network->prefixes_by_section = hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
50 network->route_prefixes_by_section = hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
1925f829 51 network->pref64_prefixes_by_section = hashmap_free_with_destructor(network->pref64_prefixes_by_section, pref64_prefix_free);
0943b3b7
YW
52 }
53}
54
0f96a823 55bool link_radv_enabled(Link *link) {
0943b3b7
YW
56 assert(link);
57
bd7e0a3f 58 if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
0943b3b7
YW
59 return false;
60
218a8502
YW
61 if (link->hw_addr.length != ETH_ALEN)
62 return false;
63
0943b3b7
YW
64 return link->network->router_prefix_delegation;
65}
66
064dfb05 67Prefix *prefix_free(Prefix *prefix) {
6e849e95 68 if (!prefix)
064dfb05 69 return NULL;
6e849e95
PF
70
71 if (prefix->network) {
ecb0e85e
YW
72 assert(prefix->section);
73 hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
6e849e95
PF
74 }
75
307fe3cd 76 config_section_free(prefix->section);
e609cd06 77 set_free(prefix->tokens);
6e849e95 78
064dfb05 79 return mfree(prefix);
6e849e95
PF
80}
81
307fe3cd 82DEFINE_SECTION_CLEANUP_FUNCTIONS(Prefix, prefix_free);
064dfb05 83
a8d4a210 84static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
307fe3cd 85 _cleanup_(config_section_freep) ConfigSection *n = NULL;
8e766630 86 _cleanup_(prefix_freep) Prefix *prefix = NULL;
6e849e95
PF
87 int r;
88
89 assert(network);
90 assert(ret);
ecb0e85e
YW
91 assert(filename);
92 assert(section_line > 0);
6e849e95 93
307fe3cd 94 r = config_section_new(filename, section_line, &n);
ecb0e85e
YW
95 if (r < 0)
96 return r;
6e849e95 97
ecb0e85e
YW
98 prefix = hashmap_get(network->prefixes_by_section, n);
99 if (prefix) {
100 *ret = TAKE_PTR(prefix);
101 return 0;
6e849e95
PF
102 }
103
2ac41679
YW
104 prefix = new(Prefix, 1);
105 if (!prefix)
106 return -ENOMEM;
107
108 *prefix = (Prefix) {
109 .network = network,
110 .section = TAKE_PTR(n),
6e849e95 111
c9e2c2da
YW
112 .preferred_lifetime = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
113 .valid_lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
2ac41679
YW
114 .onlink = true,
115 .address_auto_configuration = true,
116 };
6e849e95 117
307fe3cd 118 r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
ecb0e85e
YW
119 if (r < 0)
120 return r;
6e849e95 121
1cc6c93a 122 *ret = TAKE_PTR(prefix);
6e849e95
PF
123 return 0;
124}
125
064dfb05 126RoutePrefix *route_prefix_free(RoutePrefix *prefix) {
203d4df5 127 if (!prefix)
064dfb05 128 return NULL;
203d4df5
SS
129
130 if (prefix->network) {
ecb0e85e
YW
131 assert(prefix->section);
132 hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
203d4df5
SS
133 }
134
307fe3cd 135 config_section_free(prefix->section);
203d4df5 136
064dfb05
YW
137 return mfree(prefix);
138}
139
307fe3cd 140DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutePrefix, route_prefix_free);
064dfb05 141
a8d4a210 142static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
307fe3cd 143 _cleanup_(config_section_freep) ConfigSection *n = NULL;
95081e08 144 _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
203d4df5
SS
145 int r;
146
147 assert(network);
148 assert(ret);
ecb0e85e
YW
149 assert(filename);
150 assert(section_line > 0);
203d4df5 151
307fe3cd 152 r = config_section_new(filename, section_line, &n);
ecb0e85e
YW
153 if (r < 0)
154 return r;
203d4df5 155
ecb0e85e
YW
156 prefix = hashmap_get(network->route_prefixes_by_section, n);
157 if (prefix) {
158 *ret = TAKE_PTR(prefix);
159 return 0;
203d4df5
SS
160 }
161
2ac41679
YW
162 prefix = new(RoutePrefix, 1);
163 if (!prefix)
164 return -ENOMEM;
203d4df5 165
2ac41679
YW
166 *prefix = (RoutePrefix) {
167 .network = network,
168 .section = TAKE_PTR(n),
169
c9e2c2da 170 .lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
2ac41679 171 };
203d4df5 172
307fe3cd 173 r = hashmap_ensure_put(&network->route_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
ecb0e85e
YW
174 if (r < 0)
175 return r;
203d4df5
SS
176
177 *ret = TAKE_PTR(prefix);
203d4df5
SS
178 return 0;
179}
180
1925f829
SS
181pref64Prefix *pref64_prefix_free(pref64Prefix *prefix) {
182 if (!prefix)
183 return NULL;
184
185 if (prefix->network) {
186 assert(prefix->section);
187 hashmap_remove(prefix->network->pref64_prefixes_by_section, prefix->section);
188 }
189
190 config_section_free(prefix->section);
191
192 return mfree(prefix);
193}
194
195DEFINE_SECTION_CLEANUP_FUNCTIONS(pref64Prefix, pref64_prefix_free);
196
197static int pref64_prefix_new_static(Network *network, const char *filename, unsigned section_line, pref64Prefix **ret) {
198 _cleanup_(config_section_freep) ConfigSection *n = NULL;
199 _cleanup_(pref64_prefix_freep) pref64Prefix *prefix = NULL;
200 int r;
201
202 assert(network);
203 assert(ret);
204 assert(filename);
205 assert(section_line > 0);
206
207 r = config_section_new(filename, section_line, &n);
208 if (r < 0)
209 return r;
210
211 prefix = hashmap_get(network->pref64_prefixes_by_section, n);
212 if (prefix) {
213 *ret = TAKE_PTR(prefix);
214 return 0;
215 }
216
217 prefix = new(pref64Prefix, 1);
218 if (!prefix)
219 return -ENOMEM;
220
221 *prefix = (pref64Prefix) {
222 .network = network,
223 .section = TAKE_PTR(n),
224
225 .lifetime = RADV_DEFAULT_PRE64_LIFETIME_USEC,
226 };
227
228 r = hashmap_ensure_put(&network->pref64_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
229 if (r < 0)
230 return r;
231
232 *ret = TAKE_PTR(prefix);
233 return 0;
234}
235
3b6a3bde
YW
236int link_request_radv_addresses(Link *link) {
237 Prefix *p;
238 int r;
239
240 assert(link);
241
242 if (!link_radv_enabled(link))
243 return 0;
244
245 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
e609cd06 246 _cleanup_set_free_ Set *addresses = NULL;
2ac41679 247 struct in6_addr *a;
3b6a3bde
YW
248
249 if (!p->assign)
250 continue;
251
e609cd06 252 /* radv_generate_addresses() below requires the prefix length <= 64. */
2110040b 253 if (p->prefixlen > 64)
fcd7ad52 254 continue;
fcd7ad52 255
2ac41679 256 r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
3b6a3bde 257 if (r < 0)
e609cd06 258 return r;
00f1261d 259
e609cd06
YW
260 SET_FOREACH(a, addresses) {
261 _cleanup_(address_freep) Address *address = NULL;
3b6a3bde 262
e609cd06
YW
263 r = address_new(&address);
264 if (r < 0)
265 return -ENOMEM;
3b6a3bde 266
e609cd06
YW
267 address->source = NETWORK_CONFIG_SOURCE_STATIC;
268 address->family = AF_INET6;
269 address->in_addr.in6 = *a;
2ac41679 270 address->prefixlen = p->prefixlen;
e609cd06
YW
271 address->route_metric = p->route_metric;
272
f60e6558 273 r = link_request_static_address(link, address);
e609cd06
YW
274 if (r < 0)
275 return r;
276 }
3b6a3bde
YW
277 }
278
279 return 0;
280}
281
2ac41679
YW
282static uint32_t usec_to_lifetime(usec_t usec) {
283 uint64_t t;
284
285 if (usec == USEC_INFINITY)
286 return UINT32_MAX;
287
288 t = DIV_ROUND_UP(usec, USEC_PER_SEC);
289 if (t >= UINT32_MAX)
290 return UINT32_MAX;
291
292 return (uint32_t) t;
293}
294
295static int radv_set_prefix(Link *link, Prefix *prefix) {
296 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
297 int r;
298
299 assert(link);
300 assert(link->radv);
301 assert(prefix);
302
303 r = sd_radv_prefix_new(&p);
304 if (r < 0)
305 return r;
306
307 r = sd_radv_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
308 if (r < 0)
309 return r;
310
95e104e0 311 r = sd_radv_prefix_set_preferred_lifetime(p, prefix->preferred_lifetime, USEC_INFINITY);
2ac41679
YW
312 if (r < 0)
313 return r;
314
95e104e0 315 r = sd_radv_prefix_set_valid_lifetime(p, prefix->valid_lifetime, USEC_INFINITY);
2ac41679
YW
316 if (r < 0)
317 return r;
318
319 r = sd_radv_prefix_set_onlink(p, prefix->onlink);
320 if (r < 0)
321 return r;
322
323 r = sd_radv_prefix_set_address_autoconfiguration(p, prefix->address_auto_configuration);
324 if (r < 0)
325 return r;
326
95e104e0 327 return sd_radv_add_prefix(link->radv, p);
2ac41679
YW
328}
329
330static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
331 _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
332 int r;
333
334 assert(link);
335 assert(link->radv);
336 assert(prefix);
337
338 r = sd_radv_route_prefix_new(&p);
339 if (r < 0)
340 return r;
341
342 r = sd_radv_route_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
343 if (r < 0)
344 return r;
345
95e104e0 346 r = sd_radv_route_prefix_set_lifetime(p, prefix->lifetime, USEC_INFINITY);
2ac41679
YW
347 if (r < 0)
348 return r;
349
95e104e0 350 return sd_radv_add_route_prefix(link->radv, p);
2ac41679
YW
351}
352
1925f829
SS
353static int radv_set_pref64_prefix(Link *link, pref64Prefix *prefix) {
354 _cleanup_(sd_radv_pref64_prefix_unrefp) sd_radv_pref64_prefix *p = NULL;
355 int r;
356
357 assert(link);
358 assert(link->radv);
359 assert(prefix);
360
361 r = sd_radv_pref64_prefix_new(&p);
362 if (r < 0)
363 return r;
364
365 r = sd_radv_pref64_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen, prefix->lifetime);
366 if (r < 0)
367 return r;
368
369 return sd_radv_add_pref64_prefix(link->radv, p);
370}
371
0943b3b7
YW
372static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
373 _cleanup_free_ struct in6_addr *addresses = NULL;
374 size_t n_addresses = 0;
6e849e95 375
0943b3b7
YW
376 assert(network);
377 assert(ret_addresses);
378 assert(ret_size);
6e849e95 379
0943b3b7
YW
380 for (size_t i = 0; i < network->n_dns; i++) {
381 union in_addr_union *addr;
6e849e95 382
0943b3b7
YW
383 if (network->dns[i]->family != AF_INET6)
384 continue;
6e849e95 385
0943b3b7 386 addr = &network->dns[i]->address;
6e849e95 387
0943b3b7
YW
388 if (in_addr_is_null(AF_INET6, addr) ||
389 in_addr_is_link_local(AF_INET6, addr) ||
390 in_addr_is_localhost(AF_INET6, addr))
391 continue;
392
393 if (!GREEDY_REALLOC(addresses, n_addresses + 1))
394 return -ENOMEM;
395
396 addresses[n_addresses++] = addr->in6;
c9778516 397 }
6e849e95 398
0943b3b7
YW
399 *ret_addresses = TAKE_PTR(addresses);
400 *ret_size = n_addresses;
6e849e95 401
0943b3b7 402 return n_addresses;
6e849e95
PF
403}
404
0943b3b7
YW
405static int radv_set_dns(Link *link, Link *uplink) {
406 _cleanup_free_ struct in6_addr *dns = NULL;
0943b3b7 407 size_t n_dns;
c9778516 408 int r;
6e849e95 409
0943b3b7 410 if (!link->network->router_emit_dns)
6e849e95 411 return 0;
6e849e95 412
0943b3b7
YW
413 if (link->network->router_dns) {
414 struct in6_addr *p;
6e849e95 415
0943b3b7
YW
416 dns = new(struct in6_addr, link->network->n_router_dns);
417 if (!dns)
418 return -ENOMEM;
6e849e95 419
0943b3b7
YW
420 p = dns;
421 for (size_t i = 0; i < link->network->n_router_dns; i++)
422 if (in6_addr_is_null(&link->network->router_dns[i])) {
423 if (in6_addr_is_set(&link->ipv6ll_address))
424 *(p++) = link->ipv6ll_address;
425 } else
426 *(p++) = link->network->router_dns[i];
6e849e95 427
0943b3b7 428 n_dns = p - dns;
a8d4a210 429
0943b3b7
YW
430 goto set_dns;
431 }
6e849e95 432
0943b3b7
YW
433 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
434 if (r > 0)
435 goto set_dns;
6e849e95 436
0943b3b7
YW
437 if (uplink) {
438 assert(uplink->network);
439
440 r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
441 if (r > 0)
442 goto set_dns;
6e849e95
PF
443 }
444
6e849e95 445 return 0;
bd6379ec 446
0943b3b7
YW
447set_dns:
448 return sd_radv_set_rdnss(link->radv,
165a654e 449 usec_to_lifetime(link->network->router_dns_lifetime_usec),
0943b3b7
YW
450 dns, n_dns);
451}
bd6379ec 452
0943b3b7 453static int radv_set_domains(Link *link, Link *uplink) {
0943b3b7 454 _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
165a654e 455 OrderedSet *search_domains;
bd6379ec 456
0943b3b7 457 if (!link->network->router_emit_domains)
bd6379ec 458 return 0;
bd6379ec 459
0943b3b7 460 search_domains = link->network->router_search_domains;
bd6379ec 461
0943b3b7
YW
462 if (search_domains)
463 goto set_domains;
0e1fb1d0 464
0943b3b7
YW
465 search_domains = link->network->search_domains;
466 if (search_domains)
467 goto set_domains;
0e1fb1d0 468
0943b3b7
YW
469 if (uplink) {
470 assert(uplink->network);
0e1fb1d0 471
0943b3b7
YW
472 search_domains = uplink->network->search_domains;
473 if (search_domains)
474 goto set_domains;
0e1fb1d0
YW
475 }
476
0e1fb1d0 477 return 0;
0e1fb1d0 478
0943b3b7
YW
479set_domains:
480 s = ordered_set_get_strv(search_domains);
481 if (!s)
482 return log_oom();
203d4df5 483
0943b3b7 484 return sd_radv_set_dnssl(link->radv,
165a654e 485 usec_to_lifetime(link->network->router_dns_lifetime_usec),
0943b3b7 486 s);
203d4df5 487
0943b3b7 488}
203d4df5 489
0943b3b7 490static int radv_find_uplink(Link *link, Link **ret) {
f6032ff3
YW
491 int r;
492
0943b3b7 493 assert(link);
203d4df5 494
0943b3b7
YW
495 if (link->network->router_uplink_name)
496 return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
203d4df5 497
0943b3b7
YW
498 if (link->network->router_uplink_index > 0)
499 return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
500
501 if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
a27588d4
YW
502 if (link_dhcp_pd_is_enabled(link))
503 r = dhcp_pd_find_uplink(link, ret); /* When DHCP-PD is enabled, use its uplink. */
f6032ff3
YW
504 else
505 r = manager_find_uplink(link->manager, AF_INET6, link, ret);
506 if (r < 0)
507 /* It is not necessary to propagate error in automatic selection. */
0943b3b7 508 *ret = NULL;
c9778516
YW
509 return 0;
510 }
203d4df5 511
0943b3b7 512 *ret = NULL;
203d4df5
SS
513 return 0;
514}
515
0943b3b7 516static int radv_configure(Link *link) {
0943b3b7
YW
517 Link *uplink = NULL;
518 RoutePrefix *q;
1925f829 519 pref64Prefix *n;
0943b3b7 520 Prefix *p;
203d4df5
SS
521 int r;
522
0943b3b7
YW
523 assert(link);
524 assert(link->network);
203d4df5 525
0943b3b7
YW
526 if (link->radv)
527 return -EBUSY;
528
529 r = sd_radv_new(&link->radv);
203d4df5 530 if (r < 0)
0943b3b7 531 return r;
203d4df5 532
0943b3b7
YW
533 r = sd_radv_attach_event(link->radv, link->manager->event, 0);
534 if (r < 0)
535 return r;
203d4df5 536
3be64aa4
YW
537 if (link->hw_addr.length == ETH_ALEN) {
538 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
539 if (r < 0)
540 return r;
541 }
203d4df5 542
0943b3b7
YW
543 r = sd_radv_set_ifindex(link->radv, link->ifindex);
544 if (r < 0)
545 return r;
203d4df5 546
0943b3b7
YW
547 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
548 if (r < 0)
549 return r;
203d4df5 550
0943b3b7
YW
551 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
552 if (r < 0)
553 return r;
c555a358 554
7003b114 555 r = sd_radv_set_router_lifetime(link->radv, link->network->router_lifetime_usec);
0943b3b7
YW
556 if (r < 0)
557 return r;
c555a358 558
b26c3452
SS
559 r = sd_radv_set_hop_limit(link->radv, link->network->router_hop_limit);
560 if (r < 0)
561 return r;
562
7003b114 563 if (link->network->router_lifetime_usec > 0) {
0943b3b7
YW
564 r = sd_radv_set_preference(link->radv, link->network->router_preference);
565 if (r < 0)
566 return r;
567 }
c555a358 568
fdc4c67c
SS
569 if (link->network->router_retransmit_usec > 0) {
570 r = sd_radv_set_retransmit(link->radv, DIV_ROUND_UP(link->network->router_retransmit_usec, USEC_PER_MSEC));
571 if (r < 0)
572 return r;
573 }
574
0943b3b7 575 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
2ac41679
YW
576 r = radv_set_prefix(link, p);
577 if (r < 0 && r != -EEXIST)
0943b3b7
YW
578 return r;
579 }
c555a358 580
0943b3b7 581 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
2ac41679
YW
582 r = radv_set_route_prefix(link, q);
583 if (r < 0 && r != -EEXIST)
0943b3b7
YW
584 return r;
585 }
c555a358 586
1925f829
SS
587 HASHMAP_FOREACH(n, link->network->pref64_prefixes_by_section) {
588 r = radv_set_pref64_prefix(link, n);
589 if (r < 0 && r != -EEXIST)
590 return r;
591 }
592
0943b3b7 593 (void) radv_find_uplink(link, &uplink);
c555a358 594
0943b3b7
YW
595 r = radv_set_dns(link, uplink);
596 if (r < 0)
597 return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
c555a358 598
0943b3b7
YW
599 r = radv_set_domains(link, uplink);
600 if (r < 0)
601 return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
c555a358 602
6a6d27bc
SS
603 r = sd_radv_set_home_agent_information(link->radv, link->network->router_home_agent_information);
604 if (r < 0)
605 return r;
606
607 r = sd_radv_set_home_agent_preference(link->radv, link->network->router_home_agent_preference);
608 if (r < 0)
609 return r;
610
611 r = sd_radv_set_home_agent_lifetime(link->radv, DIV_ROUND_UP(link->network->home_agent_lifetime_usec, USEC_PER_SEC));
612 if (r < 0)
613 return r;
614
0943b3b7 615 return 0;
c555a358
PF
616}
617
0943b3b7
YW
618int radv_update_mac(Link *link) {
619 bool restart;
c555a358
PF
620 int r;
621
0943b3b7 622 assert(link);
c555a358 623
0943b3b7
YW
624 if (!link->radv)
625 return 0;
fd3ef936 626
3be64aa4
YW
627 if (link->hw_addr.length != ETH_ALEN)
628 return 0;
629
0943b3b7 630 restart = sd_radv_is_running(link->radv);
c555a358 631
0943b3b7
YW
632 r = sd_radv_stop(link->radv);
633 if (r < 0)
634 return r;
fd3ef936 635
0943b3b7
YW
636 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
637 if (r < 0)
638 return r;
c555a358 639
0943b3b7
YW
640 if (restart) {
641 r = sd_radv_start(link->radv);
642 if (r < 0)
643 return r;
c555a358
PF
644 }
645
0943b3b7
YW
646 return 0;
647}
c555a358 648
0943b3b7
YW
649static int radv_is_ready_to_configure(Link *link) {
650 bool needs_uplink = false;
651 int r;
c555a358 652
0943b3b7
YW
653 assert(link);
654 assert(link->network);
349a981d 655
4b482e8b 656 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
0943b3b7 657 return false;
c555a358 658
0943b3b7
YW
659 if (in6_addr_is_null(&link->ipv6ll_address))
660 return false;
c555a358 661
218a8502
YW
662 if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
663 return false;
664
0943b3b7
YW
665 if (link->network->router_emit_dns && !link->network->router_dns) {
666 _cleanup_free_ struct in6_addr *dns = NULL;
667 size_t n_dns;
c555a358 668
0943b3b7
YW
669 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
670 if (r < 0)
671 return r;
c555a358 672
0943b3b7
YW
673 needs_uplink = r == 0;
674 }
c555a358 675
0943b3b7
YW
676 if (link->network->router_emit_domains &&
677 !link->network->router_search_domains &&
678 !link->network->search_domains)
679 needs_uplink = true;
680
681 if (needs_uplink) {
682 Link *uplink = NULL;
c555a358 683
0943b3b7
YW
684 if (radv_find_uplink(link, &uplink) < 0)
685 return false;
c555a358 686
0943b3b7
YW
687 if (uplink && !uplink->network)
688 return false;
689 }
c555a358 690
0943b3b7
YW
691 return true;
692}
c555a358 693
09d09207 694static int radv_process_request(Request *req, Link *link, void *userdata) {
0943b3b7 695 int r;
349a981d 696
ff51134c 697 assert(link);
c555a358 698
0943b3b7
YW
699 r = radv_is_ready_to_configure(link);
700 if (r <= 0)
701 return r;
5e2a51d5 702
0943b3b7
YW
703 r = radv_configure(link);
704 if (r < 0)
705 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
c555a358 706
0943b3b7 707 if (link_has_carrier(link)) {
0f96a823 708 r = radv_start(link);
0943b3b7
YW
709 if (r < 0)
710 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
711 }
712
713 log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
2ffb134e 714 link_has_carrier(link) ? " and started" : "");
0943b3b7 715 return 1;
c555a358
PF
716}
717
0943b3b7
YW
718int link_request_radv(Link *link) {
719 int r;
63295b42 720
0943b3b7 721 assert(link);
63295b42 722
0943b3b7
YW
723 if (!link_radv_enabled(link))
724 return 0;
63295b42 725
0943b3b7 726 if (link->radv)
63295b42 727 return 0;
63295b42 728
09d09207 729 r = link_queue_request(link, REQUEST_TYPE_RADV, radv_process_request, NULL);
0943b3b7
YW
730 if (r < 0)
731 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
732
733 log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
63295b42
YW
734 return 0;
735}
736
0f96a823
YW
737int radv_start(Link *link) {
738 int r;
739
740 assert(link);
741 assert(link->network);
742
743 if (!link->radv)
744 return 0;
745
746 if (!link_has_carrier(link))
747 return 0;
748
749 if (in6_addr_is_null(&link->ipv6ll_address))
750 return 0;
751
752 if (sd_radv_is_running(link->radv))
753 return 0;
754
a27588d4
YW
755 if (link->network->dhcp_pd_announce) {
756 r = dhcp_request_prefix_delegation(link);
0f96a823 757 if (r < 0)
a27588d4 758 return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m");
0f96a823
YW
759 }
760
761 log_link_debug(link, "Starting IPv6 Router Advertisements");
762 return sd_radv_start(link->radv);
763}
764
0943b3b7
YW
765int radv_add_prefix(
766 Link *link,
767 const struct in6_addr *prefix,
768 uint8_t prefix_len,
16bc8635
YW
769 usec_t lifetime_preferred_usec,
770 usec_t lifetime_valid_usec) {
0943b3b7
YW
771
772 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
95081e08 773 int r;
091214b6
PF
774
775 assert(link);
091214b6 776
0943b3b7
YW
777 if (!link->radv)
778 return 0;
bc9e40c9 779
0943b3b7 780 r = sd_radv_prefix_new(&p);
091214b6
PF
781 if (r < 0)
782 return r;
783
0943b3b7 784 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
091214b6
PF
785 if (r < 0)
786 return r;
787
c9e2c2da 788 r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
091214b6
PF
789 if (r < 0)
790 return r;
791
c9e2c2da 792 r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
091214b6
PF
793 if (r < 0)
794 return r;
795
95e104e0 796 r = sd_radv_add_prefix(link->radv, p);
0943b3b7 797 if (r < 0 && r != -EEXIST)
091214b6
PF
798 return r;
799
0943b3b7
YW
800 return 0;
801}
091214b6 802
2110040b
YW
803static int prefix_section_verify(Prefix *p) {
804 assert(p);
805
806 if (section_is_invalid(p->section))
807 return -EINVAL;
808
809 if (in6_addr_is_null(&p->prefix))
810 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
811 "%s: [IPv6Prefix] section without Prefix= field configured, "
812 "or specified prefix is the null address. "
813 "Ignoring [IPv6Prefix] section from line %u.",
814 p->section->filename, p->section->line);
815
816 if (p->prefixlen < 3 || p->prefixlen > 128)
817 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
818 "%s: Invalid prefix length %u is specified in [IPv6Prefix] section. "
819 "Valid range is 3…128. Ignoring [IPv6Prefix] section from line %u.",
820 p->section->filename, p->prefixlen, p->section->line);
821
822 if (p->prefixlen > 64) {
c71384a9
ZJS
823 log_info("%s:%u: Unusual prefix length %u (> 64) is specified in [IPv6Prefix] section from line %s%s.",
824 p->section->filename, p->section->line,
825 p->prefixlen,
2110040b 826 p->assign ? ", refusing to assign an address in " : "",
c71384a9 827 p->assign ? IN6_ADDR_PREFIX_TO_STRING(&p->prefix, p->prefixlen) : "");
2110040b
YW
828
829 p->assign = false;
830 }
831
832 if (p->valid_lifetime == 0)
833 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
834 "%s: The valid lifetime of prefix cannot be zero. "
835 "Ignoring [IPv6Prefix] section from line %u.",
836 p->section->filename, p->section->line);
837
838 if (p->preferred_lifetime > p->valid_lifetime)
839 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
840 "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
841 "Ignoring [IPv6Prefix] section from line %u.",
842 p->section->filename,
843 FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
844 FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
845 p->section->line);
846
847 return 0;
848}
849
0943b3b7 850void network_drop_invalid_prefixes(Network *network) {
2110040b 851 Prefix *p;
2075e596 852
0943b3b7
YW
853 assert(network);
854
2110040b
YW
855 HASHMAP_FOREACH(p, network->prefixes_by_section)
856 if (prefix_section_verify(p) < 0)
857 prefix_free(p);
858}
859
860static int route_prefix_section_verify(RoutePrefix *p) {
861 if (section_is_invalid(p->section))
862 return -EINVAL;
863
864 if (p->prefixlen > 128)
865 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
866 "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
867 "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
868 p->section->filename, p->prefixlen, p->section->line);
869
870 if (p->lifetime == 0)
871 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
872 "%s: The lifetime of route cannot be zero. "
873 "Ignoring [IPv6RoutePrefix] section from line %u.",
874 p->section->filename, p->section->line);
875
876 return 0;
0943b3b7
YW
877}
878
879void network_drop_invalid_route_prefixes(Network *network) {
2110040b 880 RoutePrefix *p;
0943b3b7
YW
881
882 assert(network);
883
2110040b
YW
884 HASHMAP_FOREACH(p, network->route_prefixes_by_section)
885 if (route_prefix_section_verify(p) < 0)
886 route_prefix_free(p);
0943b3b7
YW
887}
888
1925f829
SS
889void network_drop_invalid_pref64_prefixes(Network *network) {
890 pref64Prefix *p;
891
892 assert(network);
893
894 HASHMAP_FOREACH(p, network->pref64_prefixes_by_section)
895 if (section_is_invalid(p->section))
896 pref64_prefix_free(p);
897}
898
0943b3b7
YW
899int config_parse_prefix(
900 const char *unit,
901 const char *filename,
902 unsigned line,
903 const char *section,
904 unsigned section_line,
905 const char *lvalue,
906 int ltype,
907 const char *rvalue,
908 void *data,
909 void *userdata) {
910
0943b3b7 911 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 912 Network *network = ASSERT_PTR(userdata);
2ac41679 913 union in_addr_union a;
0943b3b7
YW
914 int r;
915
916 assert(filename);
917 assert(section);
918 assert(lvalue);
919 assert(rvalue);
0943b3b7
YW
920
921 r = prefix_new_static(network, filename, section_line, &p);
091214b6 922 if (r < 0)
0943b3b7 923 return log_oom();
091214b6 924
2ac41679 925 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 926 if (r < 0) {
2ac41679
YW
927 log_syntax(unit, LOG_WARNING, filename, line, r,
928 "Prefix is invalid, ignoring assignment: %s", rvalue);
0943b3b7 929 return 0;
8a08bbfc 930 }
cf72568a
YW
931
932 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 933 p->prefix = a.in6;
203d4df5 934
2ac41679 935 TAKE_PTR(p);
0943b3b7
YW
936 return 0;
937}
a254fab2 938
2ac41679 939int config_parse_prefix_boolean(
0943b3b7
YW
940 const char *unit,
941 const char *filename,
942 unsigned line,
943 const char *section,
944 unsigned section_line,
945 const char *lvalue,
946 int ltype,
947 const char *rvalue,
948 void *data,
949 void *userdata) {
a254fab2 950
0943b3b7 951 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 952 Network *network = ASSERT_PTR(userdata);
0943b3b7
YW
953 int r;
954
955 assert(filename);
956 assert(section);
957 assert(lvalue);
958 assert(rvalue);
0943b3b7
YW
959
960 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 961 if (r < 0)
0943b3b7
YW
962 return log_oom();
963
964 r = parse_boolean(rvalue);
965 if (r < 0) {
2ac41679
YW
966 log_syntax(unit, LOG_WARNING, filename, line, r,
967 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
0943b3b7
YW
968 return 0;
969 }
970
971 if (streq(lvalue, "OnLink"))
2ac41679 972 p->onlink = r;
0943b3b7 973 else if (streq(lvalue, "AddressAutoconfiguration"))
2ac41679
YW
974 p->address_auto_configuration = r;
975 else if (streq(lvalue, "Assign"))
976 p->assign = r;
977 else
978 assert_not_reached();
a254fab2 979
2ac41679 980 TAKE_PTR(p);
fd3ef936 981 return 0;
091214b6 982}
ca5ad760 983
0943b3b7
YW
984int config_parse_prefix_lifetime(
985 const char *unit,
986 const char *filename,
987 unsigned line,
988 const char *section,
989 unsigned section_line,
990 const char *lvalue,
991 int ltype,
992 const char *rvalue,
993 void *data,
994 void *userdata) {
995
0943b3b7 996 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 997 Network *network = ASSERT_PTR(userdata);
0943b3b7 998 usec_t usec;
be9363cc
YW
999 int r;
1000
0943b3b7
YW
1001 assert(filename);
1002 assert(section);
1003 assert(lvalue);
1004 assert(rvalue);
be9363cc 1005
0943b3b7 1006 r = prefix_new_static(network, filename, section_line, &p);
a391901e 1007 if (r < 0)
0943b3b7 1008 return log_oom();
be9363cc 1009
0943b3b7
YW
1010 r = parse_sec(rvalue, &usec);
1011 if (r < 0) {
2ac41679
YW
1012 log_syntax(unit, LOG_WARNING, filename, line, r,
1013 "Lifetime is invalid, ignoring assignment: %s", rvalue);
0943b3b7
YW
1014 return 0;
1015 }
be9363cc 1016
2ac41679
YW
1017 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1018 log_syntax(unit, LOG_WARNING, filename, line, 0,
1019 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7 1020 return 0;
be9363cc
YW
1021 }
1022
2ac41679
YW
1023 if (streq(lvalue, "PreferredLifetimeSec"))
1024 p->preferred_lifetime = usec;
1025 else if (streq(lvalue, "ValidLifetimeSec"))
1026 p->valid_lifetime = usec;
1027 else
1028 assert_not_reached();
63295b42 1029
2ac41679 1030 TAKE_PTR(p);
0943b3b7 1031 return 0;
a254fab2
YW
1032}
1033
0943b3b7
YW
1034int config_parse_prefix_metric(
1035 const char *unit,
1036 const char *filename,
1037 unsigned line,
1038 const char *section,
1039 unsigned section_line,
1040 const char *lvalue,
1041 int ltype,
1042 const char *rvalue,
1043 void *data,
1044 void *userdata) {
a254fab2 1045
0943b3b7 1046 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 1047 Network *network = ASSERT_PTR(userdata);
0943b3b7 1048 int r;
a254fab2 1049
0943b3b7
YW
1050 assert(filename);
1051 assert(section);
1052 assert(lvalue);
1053 assert(rvalue);
a254fab2 1054
0943b3b7 1055 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 1056 if (r < 0)
0943b3b7 1057 return log_oom();
a254fab2 1058
0943b3b7
YW
1059 r = safe_atou32(rvalue, &p->route_metric);
1060 if (r < 0) {
1061 log_syntax(unit, LOG_WARNING, filename, line, r,
1062 "Failed to parse %s=, ignoring assignment: %s",
1063 lvalue, rvalue);
1064 return 0;
a254fab2
YW
1065 }
1066
0943b3b7 1067 TAKE_PTR(p);
0943b3b7 1068 return 0;
a254fab2
YW
1069}
1070
e609cd06
YW
1071int config_parse_prefix_token(
1072 const char *unit,
1073 const char *filename,
1074 unsigned line,
1075 const char *section,
1076 unsigned section_line,
1077 const char *lvalue,
1078 int ltype,
1079 const char *rvalue,
1080 void *data,
1081 void *userdata) {
1082
1083 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 1084 Network *network = ASSERT_PTR(userdata);
e609cd06
YW
1085 int r;
1086
1087 assert(filename);
1088 assert(section);
1089 assert(lvalue);
1090 assert(rvalue);
e609cd06
YW
1091
1092 r = prefix_new_static(network, filename, section_line, &p);
1093 if (r < 0)
1094 return log_oom();
1095
1096 r = config_parse_address_generation_type(unit, filename, line, section, section_line,
1097 lvalue, ltype, rvalue, &p->tokens, userdata);
1098 if (r < 0)
1099 return r;
1100
1101 TAKE_PTR(p);
1102 return 0;
1103}
1104
0943b3b7
YW
1105int config_parse_route_prefix(
1106 const char *unit,
1107 const char *filename,
1108 unsigned line,
1109 const char *section,
1110 unsigned section_line,
1111 const char *lvalue,
1112 int ltype,
1113 const char *rvalue,
1114 void *data,
1115 void *userdata) {
1116
0943b3b7 1117 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
99534007 1118 Network *network = ASSERT_PTR(userdata);
2ac41679 1119 union in_addr_union a;
a254fab2
YW
1120 int r;
1121
0943b3b7
YW
1122 assert(filename);
1123 assert(section);
1124 assert(lvalue);
1125 assert(rvalue);
a254fab2 1126
0943b3b7
YW
1127 r = route_prefix_new_static(network, filename, section_line, &p);
1128 if (r < 0)
1129 return log_oom();
1130
2ac41679 1131 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 1132 if (r < 0) {
2ac41679
YW
1133 log_syntax(unit, LOG_WARNING, filename, line, r,
1134 "Route prefix is invalid, ignoring assignment: %s", rvalue);
a254fab2 1135 return 0;
0943b3b7 1136 }
cf72568a
YW
1137
1138 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 1139 p->prefix = a.in6;
a254fab2 1140
2ac41679 1141 TAKE_PTR(p);
a254fab2
YW
1142 return 0;
1143}
1144
0943b3b7
YW
1145int config_parse_route_prefix_lifetime(
1146 const char *unit,
1147 const char *filename,
1148 unsigned line,
1149 const char *section,
1150 unsigned section_line,
1151 const char *lvalue,
1152 int ltype,
1153 const char *rvalue,
1154 void *data,
1155 void *userdata) {
a8d4a210 1156
0943b3b7 1157 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
99534007 1158 Network *network = ASSERT_PTR(userdata);
0943b3b7 1159 usec_t usec;
1d596fde
YW
1160 int r;
1161
0943b3b7
YW
1162 assert(filename);
1163 assert(section);
1164 assert(lvalue);
1165 assert(rvalue);
1d596fde 1166
0943b3b7 1167 r = route_prefix_new_static(network, filename, section_line, &p);
1d596fde 1168 if (r < 0)
0943b3b7 1169 return log_oom();
1d596fde 1170
0943b3b7
YW
1171 r = parse_sec(rvalue, &usec);
1172 if (r < 0) {
1173 log_syntax(unit, LOG_WARNING, filename, line, r,
1174 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
1175 return 0;
1176 }
1d596fde 1177
2ac41679
YW
1178 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1179 log_syntax(unit, LOG_WARNING, filename, line, 0,
1180 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7
YW
1181 return 0;
1182 }
1d596fde 1183
2ac41679 1184 p->lifetime = usec;
1d596fde 1185
2ac41679 1186 TAKE_PTR(p);
1d596fde
YW
1187 return 0;
1188}
1189
1925f829
SS
1190int config_parse_pref64_prefix(
1191 const char *unit,
1192 const char *filename,
1193 unsigned line,
1194 const char *section,
1195 unsigned section_line,
1196 const char *lvalue,
1197 int ltype,
1198 const char *rvalue,
1199 void *data,
1200 void *userdata) {
1201
1202 _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
1203 Network *network = ASSERT_PTR(userdata);
1204 union in_addr_union a;
1205 uint8_t prefixlen;
1206 int r;
1207
1208 assert(filename);
1209 assert(section);
1210 assert(lvalue);
1211 assert(rvalue);
1212
1213 r = pref64_prefix_new_static(network, filename, section_line, &p);
1214 if (r < 0)
1215 return log_oom();
1216
1217 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &prefixlen);
1218 if (r < 0) {
1219 log_syntax(unit, LOG_WARNING, filename, line, r,
1220 "PREF64 prefix is invalid, ignoring assignment: %s", rvalue);
1221 return 0;
1222 }
1223
1224 if (!IN_SET(prefixlen, 96, 64, 56, 48, 40, 32)) {
1225 log_syntax(unit, LOG_WARNING, filename, line, 0,
1226 "PREF64 prefixlen is invalid, ignoring assignment: %s", rvalue);
1227 return 0;
1228 }
1229
1230 (void) in6_addr_mask(&a.in6,prefixlen);
1231 p->prefix = a.in6;
1232 p->prefixlen = prefixlen;
1233
1234 TAKE_PTR(p);
1235 return 0;
1236}
1237
1238int config_parse_pref64_prefix_lifetime(
1239 const char *unit,
1240 const char *filename,
1241 unsigned line,
1242 const char *section,
1243 unsigned section_line,
1244 const char *lvalue,
1245 int ltype,
1246 const char *rvalue,
1247 void *data,
1248 void *userdata) {
1249
1250 _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
1251 Network *network = ASSERT_PTR(userdata);
1252 usec_t usec;
1253 int r;
1254
1255 assert(filename);
1256 assert(section);
1257 assert(lvalue);
1258 assert(rvalue);
1259
1260 r = pref64_prefix_new_static(network, filename, section_line, &p);
1261 if (r < 0)
1262 return log_oom();
1263
1264 r = parse_sec(rvalue, &usec);
1265 if (r < 0) {
1266 log_syntax(unit, LOG_WARNING, filename, line, r,
1267 "PREF64 lifetime is invalid, ignoring assignment: %s", rvalue);
1268 return 0;
1269 }
1270
1271 if (usec == USEC_INFINITY || DIV_ROUND_UP(usec, 8 * USEC_PER_SEC) >= UINT64_C(1) << 13) {
1272 log_syntax(unit, LOG_WARNING, filename, line, 0,
1273 "PREF64 lifetime is too long, ignoring assignment: %s", rvalue);
1274 return 0;
1275 }
1276
1277 p->lifetime = usec;
1278
1279 TAKE_PTR(p);
1280 return 0;
1281}
1282
ca5ad760
YW
1283int config_parse_radv_dns(
1284 const char *unit,
1285 const char *filename,
1286 unsigned line,
1287 const char *section,
1288 unsigned section_line,
1289 const char *lvalue,
1290 int ltype,
1291 const char *rvalue,
1292 void *data,
1293 void *userdata) {
1294
1295 Network *n = data;
ca5ad760
YW
1296 int r;
1297
1298 assert(filename);
1299 assert(lvalue);
1300 assert(rvalue);
1301
a3c1a949
YW
1302 if (isempty(rvalue)) {
1303 n->n_router_dns = 0;
1304 n->router_dns = mfree(n->router_dns);
1305 return 0;
1306 }
1307
d96edb2c 1308 for (const char *p = rvalue;;) {
ca5ad760
YW
1309 _cleanup_free_ char *w = NULL;
1310 union in_addr_union a;
1311
1312 r = extract_first_word(&p, &w, NULL, 0);
1313 if (r == -ENOMEM)
1314 return log_oom();
1315 if (r < 0) {
d96edb2c 1316 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1317 "Failed to extract word, ignoring: %s", rvalue);
1318 return 0;
1319 }
1320 if (r == 0)
d96edb2c 1321 return 0;
ca5ad760 1322
fd3ef936
YW
1323 if (streq(w, "_link_local"))
1324 a = IN_ADDR_NULL;
1325 else {
1326 r = in_addr_from_string(AF_INET6, w, &a);
1327 if (r < 0) {
d96edb2c 1328 log_syntax(unit, LOG_WARNING, filename, line, r,
fd3ef936
YW
1329 "Failed to parse DNS server address, ignoring: %s", w);
1330 continue;
1331 }
ca5ad760 1332
fd3ef936 1333 if (in_addr_is_null(AF_INET6, &a)) {
d96edb2c 1334 log_syntax(unit, LOG_WARNING, filename, line, 0,
fd3ef936
YW
1335 "DNS server address is null, ignoring: %s", w);
1336 continue;
1337 }
1338 }
ca5ad760 1339
fd3ef936
YW
1340 struct in6_addr *m;
1341 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1342 if (!m)
1343 return log_oom();
ca5ad760 1344
fd3ef936
YW
1345 m[n->n_router_dns++] = a.in6;
1346 n->router_dns = m;
ca5ad760 1347 }
ca5ad760
YW
1348}
1349
1350int config_parse_radv_search_domains(
1351 const char *unit,
1352 const char *filename,
1353 unsigned line,
1354 const char *section,
1355 unsigned section_line,
1356 const char *lvalue,
1357 int ltype,
1358 const char *rvalue,
1359 void *data,
1360 void *userdata) {
1361
1362 Network *n = data;
ca5ad760
YW
1363 int r;
1364
1365 assert(filename);
1366 assert(lvalue);
1367 assert(rvalue);
1368
a3c1a949
YW
1369 if (isempty(rvalue)) {
1370 n->router_search_domains = ordered_set_free(n->router_search_domains);
1371 return 0;
1372 }
1373
d96edb2c 1374 for (const char *p = rvalue;;) {
ca5ad760
YW
1375 _cleanup_free_ char *w = NULL, *idna = NULL;
1376
1377 r = extract_first_word(&p, &w, NULL, 0);
1378 if (r == -ENOMEM)
1379 return log_oom();
1380 if (r < 0) {
d96edb2c 1381 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1382 "Failed to extract word, ignoring: %s", rvalue);
1383 return 0;
1384 }
1385 if (r == 0)
d96edb2c 1386 return 0;
ca5ad760
YW
1387
1388 r = dns_name_apply_idna(w, &idna);
1389 if (r < 0) {
d96edb2c 1390 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1391 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1392 continue;
1393 } else if (r == 0)
1394 /* transfer ownership to simplify subsequent operations */
1395 idna = TAKE_PTR(w);
1396
5e276772 1397 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
ca5ad760 1398 if (r < 0)
d96edb2c 1399 return log_oom();
ca5ad760
YW
1400
1401 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1402 if (r < 0)
d96edb2c 1403 return log_oom();
ca5ad760 1404 }
ca5ad760
YW
1405}
1406
1407static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
a8d4a210 1408 [RADV_PREFIX_DELEGATION_NONE] = "no",
ca5ad760 1409 [RADV_PREFIX_DELEGATION_STATIC] = "static",
a8d4a210
YW
1410 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
1411 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
ca5ad760
YW
1412};
1413
1414DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
1415 radv_prefix_delegation,
1416 RADVPrefixDelegation,
1417 RADV_PREFIX_DELEGATION_BOTH);
1418
27ff0490
YW
1419int config_parse_router_prefix_delegation(
1420 const char *unit,
1421 const char *filename,
1422 unsigned line,
1423 const char *section,
1424 unsigned section_line,
1425 const char *lvalue,
1426 int ltype,
1427 const char *rvalue,
1428 void *data,
1429 void *userdata) {
1430
99534007 1431 RADVPrefixDelegation val, *ra = ASSERT_PTR(data);
27ff0490
YW
1432 int r;
1433
1434 assert(filename);
1435 assert(lvalue);
1436 assert(rvalue);
27ff0490
YW
1437
1438 if (streq(lvalue, "IPv6SendRA")) {
1439 r = parse_boolean(rvalue);
1440 if (r < 0) {
1441 log_syntax(unit, LOG_WARNING, filename, line, r,
1442 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1443 return 0;
1444 }
1445
1446 /* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
1447 * need to explicitly enable DHCPv6PrefixDelegation=. */
1448 *ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
1449 return 0;
1450 }
1451
1452 /* For backward compatibility */
1453 val = radv_prefix_delegation_from_string(rvalue);
1454 if (val < 0) {
b98680b2 1455 log_syntax(unit, LOG_WARNING, filename, line, val,
27ff0490
YW
1456 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1457 return 0;
1458 }
1459
1460 *ra = val;
1461 return 0;
1462}
a8d4a210 1463
4f1ac4a3
YW
1464int config_parse_router_lifetime(
1465 const char *unit,
1466 const char *filename,
1467 unsigned line,
1468 const char *section,
1469 unsigned section_line,
1470 const char *lvalue,
1471 int ltype,
1472 const char *rvalue,
1473 void *data,
1474 void *userdata) {
1475
99534007 1476 usec_t usec, *lifetime = ASSERT_PTR(data);
4f1ac4a3
YW
1477 int r;
1478
1479 assert(filename);
1480 assert(section);
1481 assert(lvalue);
1482 assert(rvalue);
4f1ac4a3
YW
1483
1484 if (isempty(rvalue)) {
1485 *lifetime = RADV_DEFAULT_ROUTER_LIFETIME_USEC;
1486 return 0;
1487 }
1488
1489 r = parse_sec(rvalue, &usec);
1490 if (r < 0) {
1491 log_syntax(unit, LOG_WARNING, filename, line, r,
1492 "Failed to parse router lifetime, ignoring assignment: %s", rvalue);
1493 return 0;
1494 }
1495 if (usec > 0) {
1496 if (usec < RADV_MIN_ROUTER_LIFETIME_USEC) {
1497 log_syntax(unit, LOG_WARNING, filename, line, 0,
1498 "Router lifetime %s is too short, using %s.",
1499 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1500 FORMAT_TIMESPAN(RADV_MIN_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1501 usec = RADV_MIN_ROUTER_LIFETIME_USEC;
1502 } else if (usec > RADV_MAX_ROUTER_LIFETIME_USEC) {
1503 log_syntax(unit, LOG_WARNING, filename, line, 0,
1504 "Router lifetime %s is too large, using %s.",
1505 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1506 FORMAT_TIMESPAN(RADV_MAX_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1507 usec = RADV_MAX_ROUTER_LIFETIME_USEC;
1508 }
1509 }
1510
1511 *lifetime = usec;
1512 return 0;
1513}
1514
fdc4c67c
SS
1515int config_parse_router_retransmit(
1516 const char *unit,
1517 const char *filename,
1518 unsigned line,
1519 const char *section,
1520 unsigned section_line,
1521 const char *lvalue,
1522 int ltype,
1523 const char *rvalue,
1524 void *data,
1525 void *userdata) {
1526
1527 usec_t usec, *router_retransmit_usec = ASSERT_PTR(data);
1528 int r;
1529
1530 assert(filename);
1531 assert(section);
1532 assert(lvalue);
1533 assert(rvalue);
1534
1535 if (isempty(rvalue)) {
1536 *router_retransmit_usec = 0;
1537 return 0;
1538 }
1539
1540 r = parse_sec(rvalue, &usec);
1541 if (r < 0) {
1542 log_syntax(unit, LOG_WARNING, filename, line, r,
1543 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1544 return 0;
1545 }
1546
1547 if (usec != USEC_INFINITY &&
1548 DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
1549 log_syntax(unit, LOG_WARNING, filename, line, 0,
1550 "Invalid %s= must be in the range 0...%"PRIu32"Sec, ignoring: %s", lvalue, UINT32_MAX, rvalue);
1551 return 0;
1552 }
1553
1554 *router_retransmit_usec = usec;
1555 return 0;
1556}
1557
a8d4a210
YW
1558int config_parse_router_preference(
1559 const char *unit,
1560 const char *filename,
1561 unsigned line,
1562 const char *section,
1563 unsigned section_line,
1564 const char *lvalue,
1565 int ltype,
1566 const char *rvalue,
1567 void *data,
1568 void *userdata) {
1569
ca5ad760
YW
1570 Network *network = userdata;
1571
1572 assert(filename);
1573 assert(section);
1574 assert(lvalue);
1575 assert(rvalue);
1576 assert(data);
1577
1578 if (streq(rvalue, "high"))
1579 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
1580 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
1581 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
1582 else if (streq(rvalue, "low"))
1583 network->router_preference = SD_NDISC_PREFERENCE_LOW;
1584 else
d96edb2c
YW
1585 log_syntax(unit, LOG_WARNING, filename, line, 0,
1586 "Invalid router preference, ignoring assignment: %s", rvalue);
ca5ad760
YW
1587
1588 return 0;
1589}
6a6d27bc
SS
1590
1591int config_parse_router_home_agent_lifetime(
1592 const char *unit,
1593 const char *filename,
1594 unsigned line,
1595 const char *section,
1596 unsigned section_line,
1597 const char *lvalue,
1598 int ltype,
1599 const char *rvalue,
1600 void *data,
1601 void *userdata) {
1602
1603 usec_t usec, *home_agent_lifetime_usec = ASSERT_PTR(data);
1604 int r;
1605
1606 assert(filename);
1607 assert(section);
1608 assert(lvalue);
1609 assert(rvalue);
1610
1611 if (isempty(rvalue)) {
1612 *home_agent_lifetime_usec = 0;
1613 return 0;
1614 }
1615
1616 r = parse_sec(rvalue, &usec);
1617 if (r < 0) {
1618 log_syntax(unit, LOG_WARNING, filename, line, r,
1619 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1620 return 0;
1621 }
1622
1623 if (usec == USEC_INFINITY || usec == 0 ||
1624 DIV_ROUND_UP(usec, USEC_PER_SEC) > RADV_MAX_HOME_AGENT_LIFETIME_USEC) {
1625 log_syntax(unit, LOG_WARNING, filename, line, 0,
1626 "Invalid %s= must be in the range 1...%lu seconds, ignoring: %s", lvalue,
1627 RADV_MAX_HOME_AGENT_LIFETIME_USEC / USEC_PER_SEC, rvalue);
1628 return 0;
1629 }
1630
1631 *home_agent_lifetime_usec = usec;
1632 return 0;
1633}