]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-radv.c
network/radv: introduce ReachableTimeSec= setting
[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
eca280c8 225 .lifetime = RADV_PREF64_DEFAULT_LIFETIME_USEC,
1925f829
SS
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) {
3b6a3bde
YW
246 if (!p->assign)
247 continue;
248
e609cd06 249 /* radv_generate_addresses() below requires the prefix length <= 64. */
2110040b 250 if (p->prefixlen > 64)
fcd7ad52 251 continue;
fcd7ad52 252
e700e482
YW
253 _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
254 r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &tokens_by_address);
3b6a3bde 255 if (r < 0)
e609cd06 256 return r;
00f1261d 257
e700e482
YW
258 IPv6Token *token;
259 struct in6_addr *a;
260 HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
ebd96906 261 _cleanup_(address_unrefp) 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 271 address->route_metric = p->route_metric;
e700e482 272 address->token = ipv6_token_ref(token);
e609cd06 273
f60e6558 274 r = link_request_static_address(link, address);
e609cd06
YW
275 if (r < 0)
276 return r;
277 }
3b6a3bde
YW
278 }
279
280 return 0;
281}
282
e14679ff
YW
283int link_reconfigure_radv_address(Address *address, Link *link) {
284 int r;
285
286 assert(address);
287 assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
288 assert(link);
289
290 r = regenerate_address(address, link);
291 if (r <= 0)
292 return r;
293
294 r = link_request_static_address(link, address);
295 if (r < 0)
296 return r;
297
298 if (link->static_address_messages != 0) {
299 link->static_addresses_configured = false;
300 link_set_state(link, LINK_STATE_CONFIGURING);
301 }
302
303 return 0;
304}
305
2ac41679
YW
306static int radv_set_prefix(Link *link, Prefix *prefix) {
307 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
308 int r;
309
310 assert(link);
311 assert(link->radv);
312 assert(prefix);
313
314 r = sd_radv_prefix_new(&p);
315 if (r < 0)
316 return r;
317
318 r = sd_radv_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
319 if (r < 0)
320 return r;
321
95e104e0 322 r = sd_radv_prefix_set_preferred_lifetime(p, prefix->preferred_lifetime, USEC_INFINITY);
2ac41679
YW
323 if (r < 0)
324 return r;
325
95e104e0 326 r = sd_radv_prefix_set_valid_lifetime(p, prefix->valid_lifetime, USEC_INFINITY);
2ac41679
YW
327 if (r < 0)
328 return r;
329
330 r = sd_radv_prefix_set_onlink(p, prefix->onlink);
331 if (r < 0)
332 return r;
333
334 r = sd_radv_prefix_set_address_autoconfiguration(p, prefix->address_auto_configuration);
335 if (r < 0)
336 return r;
337
95e104e0 338 return sd_radv_add_prefix(link->radv, p);
2ac41679
YW
339}
340
341static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
342 _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
343 int r;
344
345 assert(link);
346 assert(link->radv);
347 assert(prefix);
348
349 r = sd_radv_route_prefix_new(&p);
350 if (r < 0)
351 return r;
352
353 r = sd_radv_route_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
354 if (r < 0)
355 return r;
356
95e104e0 357 r = sd_radv_route_prefix_set_lifetime(p, prefix->lifetime, USEC_INFINITY);
2ac41679
YW
358 if (r < 0)
359 return r;
360
95e104e0 361 return sd_radv_add_route_prefix(link->radv, p);
2ac41679
YW
362}
363
1925f829
SS
364static int radv_set_pref64_prefix(Link *link, pref64Prefix *prefix) {
365 _cleanup_(sd_radv_pref64_prefix_unrefp) sd_radv_pref64_prefix *p = NULL;
366 int r;
367
368 assert(link);
369 assert(link->radv);
370 assert(prefix);
371
372 r = sd_radv_pref64_prefix_new(&p);
373 if (r < 0)
374 return r;
375
376 r = sd_radv_pref64_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen, prefix->lifetime);
377 if (r < 0)
378 return r;
379
380 return sd_radv_add_pref64_prefix(link->radv, p);
381}
382
0943b3b7
YW
383static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
384 _cleanup_free_ struct in6_addr *addresses = NULL;
385 size_t n_addresses = 0;
6e849e95 386
0943b3b7
YW
387 assert(network);
388 assert(ret_addresses);
389 assert(ret_size);
6e849e95 390
0943b3b7
YW
391 for (size_t i = 0; i < network->n_dns; i++) {
392 union in_addr_union *addr;
6e849e95 393
0943b3b7
YW
394 if (network->dns[i]->family != AF_INET6)
395 continue;
6e849e95 396
0943b3b7 397 addr = &network->dns[i]->address;
6e849e95 398
0943b3b7
YW
399 if (in_addr_is_null(AF_INET6, addr) ||
400 in_addr_is_link_local(AF_INET6, addr) ||
401 in_addr_is_localhost(AF_INET6, addr))
402 continue;
403
404 if (!GREEDY_REALLOC(addresses, n_addresses + 1))
405 return -ENOMEM;
406
407 addresses[n_addresses++] = addr->in6;
c9778516 408 }
6e849e95 409
0943b3b7
YW
410 *ret_addresses = TAKE_PTR(addresses);
411 *ret_size = n_addresses;
6e849e95 412
0943b3b7 413 return n_addresses;
6e849e95
PF
414}
415
0943b3b7
YW
416static int radv_set_dns(Link *link, Link *uplink) {
417 _cleanup_free_ struct in6_addr *dns = NULL;
0943b3b7 418 size_t n_dns;
c9778516 419 int r;
6e849e95 420
0943b3b7 421 if (!link->network->router_emit_dns)
6e849e95 422 return 0;
6e849e95 423
0943b3b7
YW
424 if (link->network->router_dns) {
425 struct in6_addr *p;
6e849e95 426
0943b3b7
YW
427 dns = new(struct in6_addr, link->network->n_router_dns);
428 if (!dns)
429 return -ENOMEM;
6e849e95 430
0943b3b7
YW
431 p = dns;
432 for (size_t i = 0; i < link->network->n_router_dns; i++)
433 if (in6_addr_is_null(&link->network->router_dns[i])) {
434 if (in6_addr_is_set(&link->ipv6ll_address))
435 *(p++) = link->ipv6ll_address;
436 } else
437 *(p++) = link->network->router_dns[i];
6e849e95 438
0943b3b7 439 n_dns = p - dns;
a8d4a210 440
0943b3b7
YW
441 goto set_dns;
442 }
6e849e95 443
0943b3b7
YW
444 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
445 if (r > 0)
446 goto set_dns;
6e849e95 447
0943b3b7
YW
448 if (uplink) {
449 assert(uplink->network);
450
451 r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
452 if (r > 0)
453 goto set_dns;
6e849e95
PF
454 }
455
6e849e95 456 return 0;
bd6379ec 457
0943b3b7
YW
458set_dns:
459 return sd_radv_set_rdnss(link->radv,
eca280c8 460 link->network->router_dns_lifetime_usec,
0943b3b7
YW
461 dns, n_dns);
462}
bd6379ec 463
0943b3b7 464static int radv_set_domains(Link *link, Link *uplink) {
0943b3b7 465 _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
165a654e 466 OrderedSet *search_domains;
bd6379ec 467
0943b3b7 468 if (!link->network->router_emit_domains)
bd6379ec 469 return 0;
bd6379ec 470
0943b3b7 471 search_domains = link->network->router_search_domains;
bd6379ec 472
0943b3b7
YW
473 if (search_domains)
474 goto set_domains;
0e1fb1d0 475
0943b3b7
YW
476 search_domains = link->network->search_domains;
477 if (search_domains)
478 goto set_domains;
0e1fb1d0 479
0943b3b7
YW
480 if (uplink) {
481 assert(uplink->network);
0e1fb1d0 482
0943b3b7
YW
483 search_domains = uplink->network->search_domains;
484 if (search_domains)
485 goto set_domains;
0e1fb1d0
YW
486 }
487
0e1fb1d0 488 return 0;
0e1fb1d0 489
0943b3b7
YW
490set_domains:
491 s = ordered_set_get_strv(search_domains);
492 if (!s)
493 return log_oom();
203d4df5 494
0943b3b7 495 return sd_radv_set_dnssl(link->radv,
eca280c8 496 link->network->router_dns_lifetime_usec,
0943b3b7 497 s);
203d4df5 498
0943b3b7 499}
203d4df5 500
0943b3b7 501static int radv_find_uplink(Link *link, Link **ret) {
f6032ff3
YW
502 int r;
503
0943b3b7 504 assert(link);
203d4df5 505
0943b3b7
YW
506 if (link->network->router_uplink_name)
507 return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
203d4df5 508
0943b3b7
YW
509 if (link->network->router_uplink_index > 0)
510 return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
511
512 if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
a27588d4
YW
513 if (link_dhcp_pd_is_enabled(link))
514 r = dhcp_pd_find_uplink(link, ret); /* When DHCP-PD is enabled, use its uplink. */
f6032ff3
YW
515 else
516 r = manager_find_uplink(link->manager, AF_INET6, link, ret);
517 if (r < 0)
518 /* It is not necessary to propagate error in automatic selection. */
0943b3b7 519 *ret = NULL;
c9778516
YW
520 return 0;
521 }
203d4df5 522
0943b3b7 523 *ret = NULL;
203d4df5
SS
524 return 0;
525}
526
0943b3b7 527static int radv_configure(Link *link) {
0943b3b7
YW
528 Link *uplink = NULL;
529 RoutePrefix *q;
1925f829 530 pref64Prefix *n;
0943b3b7 531 Prefix *p;
203d4df5
SS
532 int r;
533
0943b3b7
YW
534 assert(link);
535 assert(link->network);
203d4df5 536
0943b3b7
YW
537 if (link->radv)
538 return -EBUSY;
539
540 r = sd_radv_new(&link->radv);
203d4df5 541 if (r < 0)
0943b3b7 542 return r;
203d4df5 543
0943b3b7
YW
544 r = sd_radv_attach_event(link->radv, link->manager->event, 0);
545 if (r < 0)
546 return r;
203d4df5 547
3be64aa4
YW
548 if (link->hw_addr.length == ETH_ALEN) {
549 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
550 if (r < 0)
551 return r;
552 }
203d4df5 553
0943b3b7
YW
554 r = sd_radv_set_ifindex(link->radv, link->ifindex);
555 if (r < 0)
556 return r;
203d4df5 557
0943b3b7
YW
558 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
559 if (r < 0)
560 return r;
203d4df5 561
0943b3b7
YW
562 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
563 if (r < 0)
564 return r;
c555a358 565
7003b114 566 r = sd_radv_set_router_lifetime(link->radv, link->network->router_lifetime_usec);
0943b3b7
YW
567 if (r < 0)
568 return r;
c555a358 569
b26c3452
SS
570 r = sd_radv_set_hop_limit(link->radv, link->network->router_hop_limit);
571 if (r < 0)
572 return r;
573
7003b114 574 if (link->network->router_lifetime_usec > 0) {
0943b3b7
YW
575 r = sd_radv_set_preference(link->radv, link->network->router_preference);
576 if (r < 0)
577 return r;
578 }
c555a358 579
59d475ba
YW
580 r = sd_radv_set_reachable_time(link->radv, link->network->router_reachable_usec);
581 if (r < 0)
582 return r;
583
fdc4c67c 584 if (link->network->router_retransmit_usec > 0) {
eca280c8 585 r = sd_radv_set_retransmit(link->radv, link->network->router_retransmit_usec);
fdc4c67c
SS
586 if (r < 0)
587 return r;
588 }
589
0943b3b7 590 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
2ac41679
YW
591 r = radv_set_prefix(link, p);
592 if (r < 0 && r != -EEXIST)
0943b3b7
YW
593 return r;
594 }
c555a358 595
0943b3b7 596 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
2ac41679
YW
597 r = radv_set_route_prefix(link, q);
598 if (r < 0 && r != -EEXIST)
0943b3b7
YW
599 return r;
600 }
c555a358 601
1925f829
SS
602 HASHMAP_FOREACH(n, link->network->pref64_prefixes_by_section) {
603 r = radv_set_pref64_prefix(link, n);
604 if (r < 0 && r != -EEXIST)
605 return r;
606 }
607
0943b3b7 608 (void) radv_find_uplink(link, &uplink);
c555a358 609
0943b3b7
YW
610 r = radv_set_dns(link, uplink);
611 if (r < 0)
612 return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
c555a358 613
0943b3b7
YW
614 r = radv_set_domains(link, uplink);
615 if (r < 0)
616 return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
c555a358 617
6a6d27bc
SS
618 r = sd_radv_set_home_agent_information(link->radv, link->network->router_home_agent_information);
619 if (r < 0)
620 return r;
621
622 r = sd_radv_set_home_agent_preference(link->radv, link->network->router_home_agent_preference);
623 if (r < 0)
624 return r;
625
eca280c8 626 r = sd_radv_set_home_agent_lifetime(link->radv, link->network->home_agent_lifetime_usec);
6a6d27bc
SS
627 if (r < 0)
628 return r;
629
0943b3b7 630 return 0;
c555a358
PF
631}
632
0943b3b7 633int radv_update_mac(Link *link) {
0943b3b7 634 assert(link);
c555a358 635
0943b3b7
YW
636 if (!link->radv)
637 return 0;
fd3ef936 638
3be64aa4
YW
639 if (link->hw_addr.length != ETH_ALEN)
640 return 0;
641
201f7628 642 return sd_radv_set_mac(link->radv, &link->hw_addr.ether);
0943b3b7 643}
c555a358 644
0943b3b7
YW
645static int radv_is_ready_to_configure(Link *link) {
646 bool needs_uplink = false;
647 int r;
c555a358 648
0943b3b7
YW
649 assert(link);
650 assert(link->network);
349a981d 651
4b482e8b 652 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
0943b3b7 653 return false;
c555a358 654
0943b3b7
YW
655 if (in6_addr_is_null(&link->ipv6ll_address))
656 return false;
c555a358 657
218a8502
YW
658 if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
659 return false;
660
0943b3b7
YW
661 if (link->network->router_emit_dns && !link->network->router_dns) {
662 _cleanup_free_ struct in6_addr *dns = NULL;
663 size_t n_dns;
c555a358 664
0943b3b7
YW
665 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
666 if (r < 0)
667 return r;
c555a358 668
0943b3b7
YW
669 needs_uplink = r == 0;
670 }
c555a358 671
0943b3b7
YW
672 if (link->network->router_emit_domains &&
673 !link->network->router_search_domains &&
674 !link->network->search_domains)
675 needs_uplink = true;
676
677 if (needs_uplink) {
678 Link *uplink = NULL;
c555a358 679
0943b3b7
YW
680 if (radv_find_uplink(link, &uplink) < 0)
681 return false;
c555a358 682
0943b3b7
YW
683 if (uplink && !uplink->network)
684 return false;
685 }
c555a358 686
0943b3b7
YW
687 return true;
688}
c555a358 689
09d09207 690static int radv_process_request(Request *req, Link *link, void *userdata) {
0943b3b7 691 int r;
349a981d 692
ff51134c 693 assert(link);
c555a358 694
0943b3b7
YW
695 r = radv_is_ready_to_configure(link);
696 if (r <= 0)
697 return r;
5e2a51d5 698
0943b3b7
YW
699 r = radv_configure(link);
700 if (r < 0)
701 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
c555a358 702
0943b3b7 703 if (link_has_carrier(link)) {
0f96a823 704 r = radv_start(link);
0943b3b7
YW
705 if (r < 0)
706 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
707 }
708
709 log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
2ffb134e 710 link_has_carrier(link) ? " and started" : "");
0943b3b7 711 return 1;
c555a358
PF
712}
713
0943b3b7
YW
714int link_request_radv(Link *link) {
715 int r;
63295b42 716
0943b3b7 717 assert(link);
63295b42 718
0943b3b7
YW
719 if (!link_radv_enabled(link))
720 return 0;
63295b42 721
0943b3b7 722 if (link->radv)
63295b42 723 return 0;
63295b42 724
09d09207 725 r = link_queue_request(link, REQUEST_TYPE_RADV, radv_process_request, NULL);
0943b3b7
YW
726 if (r < 0)
727 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
728
729 log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
63295b42
YW
730 return 0;
731}
732
0f96a823
YW
733int radv_start(Link *link) {
734 int r;
735
736 assert(link);
737 assert(link->network);
738
739 if (!link->radv)
740 return 0;
741
742 if (!link_has_carrier(link))
743 return 0;
744
745 if (in6_addr_is_null(&link->ipv6ll_address))
746 return 0;
747
748 if (sd_radv_is_running(link->radv))
749 return 0;
750
a27588d4
YW
751 if (link->network->dhcp_pd_announce) {
752 r = dhcp_request_prefix_delegation(link);
0f96a823 753 if (r < 0)
a27588d4 754 return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m");
0f96a823
YW
755 }
756
50ba4e40
YW
757 r = sd_radv_set_link_local_address(link->radv, &link->ipv6ll_address);
758 if (r < 0)
759 return r;
760
0f96a823
YW
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
2110040b
YW
832 if (p->preferred_lifetime > p->valid_lifetime)
833 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
834 "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
835 "Ignoring [IPv6Prefix] section from line %u.",
836 p->section->filename,
837 FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
838 FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
839 p->section->line);
840
841 return 0;
842}
843
0943b3b7 844void network_drop_invalid_prefixes(Network *network) {
2110040b 845 Prefix *p;
2075e596 846
0943b3b7
YW
847 assert(network);
848
2110040b
YW
849 HASHMAP_FOREACH(p, network->prefixes_by_section)
850 if (prefix_section_verify(p) < 0)
851 prefix_free(p);
852}
853
854static int route_prefix_section_verify(RoutePrefix *p) {
855 if (section_is_invalid(p->section))
856 return -EINVAL;
857
858 if (p->prefixlen > 128)
859 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
860 "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
861 "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
862 p->section->filename, p->prefixlen, p->section->line);
863
2110040b 864 return 0;
0943b3b7
YW
865}
866
867void network_drop_invalid_route_prefixes(Network *network) {
2110040b 868 RoutePrefix *p;
0943b3b7
YW
869
870 assert(network);
871
2110040b
YW
872 HASHMAP_FOREACH(p, network->route_prefixes_by_section)
873 if (route_prefix_section_verify(p) < 0)
874 route_prefix_free(p);
0943b3b7
YW
875}
876
1925f829
SS
877void network_drop_invalid_pref64_prefixes(Network *network) {
878 pref64Prefix *p;
879
880 assert(network);
881
882 HASHMAP_FOREACH(p, network->pref64_prefixes_by_section)
883 if (section_is_invalid(p->section))
884 pref64_prefix_free(p);
885}
886
0943b3b7
YW
887int config_parse_prefix(
888 const char *unit,
889 const char *filename,
890 unsigned line,
891 const char *section,
892 unsigned section_line,
893 const char *lvalue,
894 int ltype,
895 const char *rvalue,
896 void *data,
897 void *userdata) {
898
0943b3b7 899 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 900 Network *network = ASSERT_PTR(userdata);
2ac41679 901 union in_addr_union a;
0943b3b7
YW
902 int r;
903
904 assert(filename);
905 assert(section);
906 assert(lvalue);
907 assert(rvalue);
0943b3b7
YW
908
909 r = prefix_new_static(network, filename, section_line, &p);
091214b6 910 if (r < 0)
0943b3b7 911 return log_oom();
091214b6 912
2ac41679 913 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 914 if (r < 0) {
2ac41679
YW
915 log_syntax(unit, LOG_WARNING, filename, line, r,
916 "Prefix is invalid, ignoring assignment: %s", rvalue);
0943b3b7 917 return 0;
8a08bbfc 918 }
cf72568a
YW
919
920 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 921 p->prefix = a.in6;
203d4df5 922
2ac41679 923 TAKE_PTR(p);
0943b3b7
YW
924 return 0;
925}
a254fab2 926
2ac41679 927int config_parse_prefix_boolean(
0943b3b7
YW
928 const char *unit,
929 const char *filename,
930 unsigned line,
931 const char *section,
932 unsigned section_line,
933 const char *lvalue,
934 int ltype,
935 const char *rvalue,
936 void *data,
937 void *userdata) {
a254fab2 938
0943b3b7 939 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 940 Network *network = ASSERT_PTR(userdata);
0943b3b7
YW
941 int r;
942
943 assert(filename);
944 assert(section);
945 assert(lvalue);
946 assert(rvalue);
0943b3b7
YW
947
948 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 949 if (r < 0)
0943b3b7
YW
950 return log_oom();
951
952 r = parse_boolean(rvalue);
953 if (r < 0) {
2ac41679
YW
954 log_syntax(unit, LOG_WARNING, filename, line, r,
955 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
0943b3b7
YW
956 return 0;
957 }
958
959 if (streq(lvalue, "OnLink"))
2ac41679 960 p->onlink = r;
0943b3b7 961 else if (streq(lvalue, "AddressAutoconfiguration"))
2ac41679
YW
962 p->address_auto_configuration = r;
963 else if (streq(lvalue, "Assign"))
964 p->assign = r;
965 else
966 assert_not_reached();
a254fab2 967
2ac41679 968 TAKE_PTR(p);
fd3ef936 969 return 0;
091214b6 970}
ca5ad760 971
0943b3b7
YW
972int config_parse_prefix_lifetime(
973 const char *unit,
974 const char *filename,
975 unsigned line,
976 const char *section,
977 unsigned section_line,
978 const char *lvalue,
979 int ltype,
980 const char *rvalue,
981 void *data,
982 void *userdata) {
983
0943b3b7 984 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 985 Network *network = ASSERT_PTR(userdata);
0943b3b7 986 usec_t usec;
be9363cc
YW
987 int r;
988
0943b3b7
YW
989 assert(filename);
990 assert(section);
991 assert(lvalue);
992 assert(rvalue);
be9363cc 993
0943b3b7 994 r = prefix_new_static(network, filename, section_line, &p);
a391901e 995 if (r < 0)
0943b3b7 996 return log_oom();
be9363cc 997
0943b3b7
YW
998 r = parse_sec(rvalue, &usec);
999 if (r < 0) {
2ac41679
YW
1000 log_syntax(unit, LOG_WARNING, filename, line, r,
1001 "Lifetime is invalid, ignoring assignment: %s", rvalue);
0943b3b7
YW
1002 return 0;
1003 }
be9363cc 1004
2ac41679
YW
1005 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1006 log_syntax(unit, LOG_WARNING, filename, line, 0,
1007 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7 1008 return 0;
be9363cc
YW
1009 }
1010
2ac41679
YW
1011 if (streq(lvalue, "PreferredLifetimeSec"))
1012 p->preferred_lifetime = usec;
1013 else if (streq(lvalue, "ValidLifetimeSec"))
1014 p->valid_lifetime = usec;
1015 else
1016 assert_not_reached();
63295b42 1017
2ac41679 1018 TAKE_PTR(p);
0943b3b7 1019 return 0;
a254fab2
YW
1020}
1021
0943b3b7
YW
1022int config_parse_prefix_metric(
1023 const char *unit,
1024 const char *filename,
1025 unsigned line,
1026 const char *section,
1027 unsigned section_line,
1028 const char *lvalue,
1029 int ltype,
1030 const char *rvalue,
1031 void *data,
1032 void *userdata) {
a254fab2 1033
0943b3b7 1034 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 1035 Network *network = ASSERT_PTR(userdata);
0943b3b7 1036 int r;
a254fab2 1037
0943b3b7
YW
1038 assert(filename);
1039 assert(section);
1040 assert(lvalue);
1041 assert(rvalue);
a254fab2 1042
0943b3b7 1043 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 1044 if (r < 0)
0943b3b7 1045 return log_oom();
a254fab2 1046
0943b3b7
YW
1047 r = safe_atou32(rvalue, &p->route_metric);
1048 if (r < 0) {
1049 log_syntax(unit, LOG_WARNING, filename, line, r,
1050 "Failed to parse %s=, ignoring assignment: %s",
1051 lvalue, rvalue);
1052 return 0;
a254fab2
YW
1053 }
1054
0943b3b7 1055 TAKE_PTR(p);
0943b3b7 1056 return 0;
a254fab2
YW
1057}
1058
e609cd06
YW
1059int config_parse_prefix_token(
1060 const char *unit,
1061 const char *filename,
1062 unsigned line,
1063 const char *section,
1064 unsigned section_line,
1065 const char *lvalue,
1066 int ltype,
1067 const char *rvalue,
1068 void *data,
1069 void *userdata) {
1070
1071 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
99534007 1072 Network *network = ASSERT_PTR(userdata);
e609cd06
YW
1073 int r;
1074
1075 assert(filename);
1076 assert(section);
1077 assert(lvalue);
1078 assert(rvalue);
e609cd06
YW
1079
1080 r = prefix_new_static(network, filename, section_line, &p);
1081 if (r < 0)
1082 return log_oom();
1083
1084 r = config_parse_address_generation_type(unit, filename, line, section, section_line,
1085 lvalue, ltype, rvalue, &p->tokens, userdata);
1086 if (r < 0)
1087 return r;
1088
1089 TAKE_PTR(p);
1090 return 0;
1091}
1092
0943b3b7
YW
1093int config_parse_route_prefix(
1094 const char *unit,
1095 const char *filename,
1096 unsigned line,
1097 const char *section,
1098 unsigned section_line,
1099 const char *lvalue,
1100 int ltype,
1101 const char *rvalue,
1102 void *data,
1103 void *userdata) {
1104
0943b3b7 1105 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
99534007 1106 Network *network = ASSERT_PTR(userdata);
2ac41679 1107 union in_addr_union a;
a254fab2
YW
1108 int r;
1109
0943b3b7
YW
1110 assert(filename);
1111 assert(section);
1112 assert(lvalue);
1113 assert(rvalue);
a254fab2 1114
0943b3b7
YW
1115 r = route_prefix_new_static(network, filename, section_line, &p);
1116 if (r < 0)
1117 return log_oom();
1118
2ac41679 1119 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 1120 if (r < 0) {
2ac41679
YW
1121 log_syntax(unit, LOG_WARNING, filename, line, r,
1122 "Route prefix is invalid, ignoring assignment: %s", rvalue);
a254fab2 1123 return 0;
0943b3b7 1124 }
cf72568a
YW
1125
1126 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 1127 p->prefix = a.in6;
a254fab2 1128
2ac41679 1129 TAKE_PTR(p);
a254fab2
YW
1130 return 0;
1131}
1132
0943b3b7
YW
1133int config_parse_route_prefix_lifetime(
1134 const char *unit,
1135 const char *filename,
1136 unsigned line,
1137 const char *section,
1138 unsigned section_line,
1139 const char *lvalue,
1140 int ltype,
1141 const char *rvalue,
1142 void *data,
1143 void *userdata) {
a8d4a210 1144
0943b3b7 1145 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
99534007 1146 Network *network = ASSERT_PTR(userdata);
0943b3b7 1147 usec_t usec;
1d596fde
YW
1148 int r;
1149
0943b3b7
YW
1150 assert(filename);
1151 assert(section);
1152 assert(lvalue);
1153 assert(rvalue);
1d596fde 1154
0943b3b7 1155 r = route_prefix_new_static(network, filename, section_line, &p);
1d596fde 1156 if (r < 0)
0943b3b7 1157 return log_oom();
1d596fde 1158
0943b3b7
YW
1159 r = parse_sec(rvalue, &usec);
1160 if (r < 0) {
1161 log_syntax(unit, LOG_WARNING, filename, line, r,
1162 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
1163 return 0;
1164 }
1d596fde 1165
2ac41679
YW
1166 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1167 log_syntax(unit, LOG_WARNING, filename, line, 0,
1168 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7
YW
1169 return 0;
1170 }
1d596fde 1171
2ac41679 1172 p->lifetime = usec;
1d596fde 1173
2ac41679 1174 TAKE_PTR(p);
1d596fde
YW
1175 return 0;
1176}
1177
1925f829
SS
1178int config_parse_pref64_prefix(
1179 const char *unit,
1180 const char *filename,
1181 unsigned line,
1182 const char *section,
1183 unsigned section_line,
1184 const char *lvalue,
1185 int ltype,
1186 const char *rvalue,
1187 void *data,
1188 void *userdata) {
1189
1190 _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
1191 Network *network = ASSERT_PTR(userdata);
1192 union in_addr_union a;
1193 uint8_t prefixlen;
1194 int r;
1195
1196 assert(filename);
1197 assert(section);
1198 assert(lvalue);
1199 assert(rvalue);
1200
1201 r = pref64_prefix_new_static(network, filename, section_line, &p);
1202 if (r < 0)
1203 return log_oom();
1204
1205 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &prefixlen);
1206 if (r < 0) {
1207 log_syntax(unit, LOG_WARNING, filename, line, r,
1208 "PREF64 prefix is invalid, ignoring assignment: %s", rvalue);
1209 return 0;
1210 }
1211
1212 if (!IN_SET(prefixlen, 96, 64, 56, 48, 40, 32)) {
1213 log_syntax(unit, LOG_WARNING, filename, line, 0,
1214 "PREF64 prefixlen is invalid, ignoring assignment: %s", rvalue);
1215 return 0;
1216 }
1217
7f5c82aa 1218 (void) in6_addr_mask(&a.in6, prefixlen);
1925f829
SS
1219 p->prefix = a.in6;
1220 p->prefixlen = prefixlen;
1221
1222 TAKE_PTR(p);
1223 return 0;
1224}
1225
1226int config_parse_pref64_prefix_lifetime(
1227 const char *unit,
1228 const char *filename,
1229 unsigned line,
1230 const char *section,
1231 unsigned section_line,
1232 const char *lvalue,
1233 int ltype,
1234 const char *rvalue,
1235 void *data,
1236 void *userdata) {
1237
1238 _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
1239 Network *network = ASSERT_PTR(userdata);
1240 usec_t usec;
1241 int r;
1242
1243 assert(filename);
1244 assert(section);
1245 assert(lvalue);
1246 assert(rvalue);
1247
1248 r = pref64_prefix_new_static(network, filename, section_line, &p);
1249 if (r < 0)
1250 return log_oom();
1251
1252 r = parse_sec(rvalue, &usec);
1253 if (r < 0) {
1254 log_syntax(unit, LOG_WARNING, filename, line, r,
1255 "PREF64 lifetime is invalid, ignoring assignment: %s", rvalue);
1256 return 0;
1257 }
1258
1259 if (usec == USEC_INFINITY || DIV_ROUND_UP(usec, 8 * USEC_PER_SEC) >= UINT64_C(1) << 13) {
1260 log_syntax(unit, LOG_WARNING, filename, line, 0,
1261 "PREF64 lifetime is too long, ignoring assignment: %s", rvalue);
1262 return 0;
1263 }
1264
1265 p->lifetime = usec;
1266
1267 TAKE_PTR(p);
1268 return 0;
1269}
1270
ca5ad760
YW
1271int config_parse_radv_dns(
1272 const char *unit,
1273 const char *filename,
1274 unsigned line,
1275 const char *section,
1276 unsigned section_line,
1277 const char *lvalue,
1278 int ltype,
1279 const char *rvalue,
1280 void *data,
1281 void *userdata) {
1282
1283 Network *n = data;
ca5ad760
YW
1284 int r;
1285
1286 assert(filename);
1287 assert(lvalue);
1288 assert(rvalue);
1289
a3c1a949
YW
1290 if (isempty(rvalue)) {
1291 n->n_router_dns = 0;
1292 n->router_dns = mfree(n->router_dns);
1293 return 0;
1294 }
1295
d96edb2c 1296 for (const char *p = rvalue;;) {
ca5ad760
YW
1297 _cleanup_free_ char *w = NULL;
1298 union in_addr_union a;
1299
1300 r = extract_first_word(&p, &w, NULL, 0);
1301 if (r == -ENOMEM)
1302 return log_oom();
1303 if (r < 0) {
d96edb2c 1304 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1305 "Failed to extract word, ignoring: %s", rvalue);
1306 return 0;
1307 }
1308 if (r == 0)
d96edb2c 1309 return 0;
ca5ad760 1310
fd3ef936
YW
1311 if (streq(w, "_link_local"))
1312 a = IN_ADDR_NULL;
1313 else {
1314 r = in_addr_from_string(AF_INET6, w, &a);
1315 if (r < 0) {
d96edb2c 1316 log_syntax(unit, LOG_WARNING, filename, line, r,
fd3ef936
YW
1317 "Failed to parse DNS server address, ignoring: %s", w);
1318 continue;
1319 }
ca5ad760 1320
fd3ef936 1321 if (in_addr_is_null(AF_INET6, &a)) {
d96edb2c 1322 log_syntax(unit, LOG_WARNING, filename, line, 0,
fd3ef936
YW
1323 "DNS server address is null, ignoring: %s", w);
1324 continue;
1325 }
1326 }
ca5ad760 1327
fd3ef936
YW
1328 struct in6_addr *m;
1329 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1330 if (!m)
1331 return log_oom();
ca5ad760 1332
fd3ef936
YW
1333 m[n->n_router_dns++] = a.in6;
1334 n->router_dns = m;
ca5ad760 1335 }
ca5ad760
YW
1336}
1337
1338int config_parse_radv_search_domains(
1339 const char *unit,
1340 const char *filename,
1341 unsigned line,
1342 const char *section,
1343 unsigned section_line,
1344 const char *lvalue,
1345 int ltype,
1346 const char *rvalue,
1347 void *data,
1348 void *userdata) {
1349
1350 Network *n = data;
ca5ad760
YW
1351 int r;
1352
1353 assert(filename);
1354 assert(lvalue);
1355 assert(rvalue);
1356
a3c1a949
YW
1357 if (isempty(rvalue)) {
1358 n->router_search_domains = ordered_set_free(n->router_search_domains);
1359 return 0;
1360 }
1361
d96edb2c 1362 for (const char *p = rvalue;;) {
ca5ad760
YW
1363 _cleanup_free_ char *w = NULL, *idna = NULL;
1364
1365 r = extract_first_word(&p, &w, NULL, 0);
1366 if (r == -ENOMEM)
1367 return log_oom();
1368 if (r < 0) {
d96edb2c 1369 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1370 "Failed to extract word, ignoring: %s", rvalue);
1371 return 0;
1372 }
1373 if (r == 0)
d96edb2c 1374 return 0;
ca5ad760
YW
1375
1376 r = dns_name_apply_idna(w, &idna);
1377 if (r < 0) {
d96edb2c 1378 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1379 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1380 continue;
1381 } else if (r == 0)
1382 /* transfer ownership to simplify subsequent operations */
1383 idna = TAKE_PTR(w);
1384
5e276772 1385 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
ca5ad760 1386 if (r < 0)
d96edb2c 1387 return log_oom();
ca5ad760
YW
1388
1389 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1390 if (r < 0)
d96edb2c 1391 return log_oom();
ca5ad760 1392 }
ca5ad760
YW
1393}
1394
1395static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
a8d4a210 1396 [RADV_PREFIX_DELEGATION_NONE] = "no",
ca5ad760 1397 [RADV_PREFIX_DELEGATION_STATIC] = "static",
a8d4a210
YW
1398 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
1399 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
ca5ad760
YW
1400};
1401
1402DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
1403 radv_prefix_delegation,
1404 RADVPrefixDelegation,
1405 RADV_PREFIX_DELEGATION_BOTH);
1406
27ff0490
YW
1407int config_parse_router_prefix_delegation(
1408 const char *unit,
1409 const char *filename,
1410 unsigned line,
1411 const char *section,
1412 unsigned section_line,
1413 const char *lvalue,
1414 int ltype,
1415 const char *rvalue,
1416 void *data,
1417 void *userdata) {
1418
99534007 1419 RADVPrefixDelegation val, *ra = ASSERT_PTR(data);
27ff0490
YW
1420 int r;
1421
1422 assert(filename);
1423 assert(lvalue);
1424 assert(rvalue);
27ff0490
YW
1425
1426 if (streq(lvalue, "IPv6SendRA")) {
1427 r = parse_boolean(rvalue);
1428 if (r < 0) {
1429 log_syntax(unit, LOG_WARNING, filename, line, r,
1430 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1431 return 0;
1432 }
1433
1434 /* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
1435 * need to explicitly enable DHCPv6PrefixDelegation=. */
1436 *ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
1437 return 0;
1438 }
1439
1440 /* For backward compatibility */
1441 val = radv_prefix_delegation_from_string(rvalue);
1442 if (val < 0) {
b98680b2 1443 log_syntax(unit, LOG_WARNING, filename, line, val,
27ff0490
YW
1444 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1445 return 0;
1446 }
1447
1448 *ra = val;
1449 return 0;
1450}
a8d4a210 1451
4f1ac4a3
YW
1452int config_parse_router_lifetime(
1453 const char *unit,
1454 const char *filename,
1455 unsigned line,
1456 const char *section,
1457 unsigned section_line,
1458 const char *lvalue,
1459 int ltype,
1460 const char *rvalue,
1461 void *data,
1462 void *userdata) {
1463
99534007 1464 usec_t usec, *lifetime = ASSERT_PTR(data);
4f1ac4a3
YW
1465 int r;
1466
1467 assert(filename);
1468 assert(section);
1469 assert(lvalue);
1470 assert(rvalue);
4f1ac4a3
YW
1471
1472 if (isempty(rvalue)) {
1473 *lifetime = RADV_DEFAULT_ROUTER_LIFETIME_USEC;
1474 return 0;
1475 }
1476
1477 r = parse_sec(rvalue, &usec);
1478 if (r < 0) {
1479 log_syntax(unit, LOG_WARNING, filename, line, r,
1480 "Failed to parse router lifetime, ignoring assignment: %s", rvalue);
1481 return 0;
1482 }
1483 if (usec > 0) {
1484 if (usec < RADV_MIN_ROUTER_LIFETIME_USEC) {
1485 log_syntax(unit, LOG_WARNING, filename, line, 0,
1486 "Router lifetime %s is too short, using %s.",
1487 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1488 FORMAT_TIMESPAN(RADV_MIN_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1489 usec = RADV_MIN_ROUTER_LIFETIME_USEC;
1490 } else if (usec > RADV_MAX_ROUTER_LIFETIME_USEC) {
1491 log_syntax(unit, LOG_WARNING, filename, line, 0,
1492 "Router lifetime %s is too large, using %s.",
1493 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1494 FORMAT_TIMESPAN(RADV_MAX_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1495 usec = RADV_MAX_ROUTER_LIFETIME_USEC;
1496 }
1497 }
1498
1499 *lifetime = usec;
1500 return 0;
1501}
1502
59d475ba 1503int config_parse_router_uint32_msec_usec(
fdc4c67c
SS
1504 const char *unit,
1505 const char *filename,
1506 unsigned line,
1507 const char *section,
1508 unsigned section_line,
1509 const char *lvalue,
1510 int ltype,
1511 const char *rvalue,
1512 void *data,
1513 void *userdata) {
1514
59d475ba 1515 usec_t usec, *router_usec = ASSERT_PTR(data);
fdc4c67c
SS
1516 int r;
1517
1518 assert(filename);
1519 assert(section);
1520 assert(lvalue);
1521 assert(rvalue);
1522
1523 if (isempty(rvalue)) {
59d475ba 1524 *router_usec = 0;
fdc4c67c
SS
1525 return 0;
1526 }
1527
1528 r = parse_sec(rvalue, &usec);
1529 if (r < 0) {
1530 log_syntax(unit, LOG_WARNING, filename, line, r,
1531 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1532 return 0;
1533 }
1534
1535 if (usec != USEC_INFINITY &&
59d475ba 1536 usec > RADV_MAX_UINT32_MSEC_USEC) {
fdc4c67c 1537 log_syntax(unit, LOG_WARNING, filename, line, 0,
eca280c8 1538 "Invalid [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
fdc4c67c
SS
1539 return 0;
1540 }
1541
59d475ba 1542 *router_usec = usec;
fdc4c67c
SS
1543 return 0;
1544}
1545
a8d4a210
YW
1546int config_parse_router_preference(
1547 const char *unit,
1548 const char *filename,
1549 unsigned line,
1550 const char *section,
1551 unsigned section_line,
1552 const char *lvalue,
1553 int ltype,
1554 const char *rvalue,
1555 void *data,
1556 void *userdata) {
1557
ca5ad760
YW
1558 Network *network = userdata;
1559
1560 assert(filename);
1561 assert(section);
1562 assert(lvalue);
1563 assert(rvalue);
1564 assert(data);
1565
1566 if (streq(rvalue, "high"))
1567 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
1568 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
1569 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
1570 else if (streq(rvalue, "low"))
1571 network->router_preference = SD_NDISC_PREFERENCE_LOW;
1572 else
d96edb2c
YW
1573 log_syntax(unit, LOG_WARNING, filename, line, 0,
1574 "Invalid router preference, ignoring assignment: %s", rvalue);
ca5ad760
YW
1575
1576 return 0;
1577}
6a6d27bc
SS
1578
1579int config_parse_router_home_agent_lifetime(
1580 const char *unit,
1581 const char *filename,
1582 unsigned line,
1583 const char *section,
1584 unsigned section_line,
1585 const char *lvalue,
1586 int ltype,
1587 const char *rvalue,
1588 void *data,
1589 void *userdata) {
1590
1591 usec_t usec, *home_agent_lifetime_usec = ASSERT_PTR(data);
1592 int r;
1593
1594 assert(filename);
1595 assert(section);
1596 assert(lvalue);
1597 assert(rvalue);
1598
1599 if (isempty(rvalue)) {
1600 *home_agent_lifetime_usec = 0;
1601 return 0;
1602 }
1603
1604 r = parse_sec(rvalue, &usec);
1605 if (r < 0) {
1606 log_syntax(unit, LOG_WARNING, filename, line, r,
1607 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1608 return 0;
1609 }
1610
73151fbe 1611 if (!timestamp_is_set(usec) ||
eca280c8 1612 usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC) {
6a6d27bc 1613 log_syntax(unit, LOG_WARNING, filename, line, 0,
eca280c8 1614 "Invalid [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
6a6d27bc
SS
1615 return 0;
1616 }
1617
1618 *home_agent_lifetime_usec = usec;
1619 return 0;
1620}