]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-radv.c
Merge pull request #21838 from lnussel/logind-refactor
[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)
36 log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
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);
51 }
52}
53
0f96a823 54bool link_radv_enabled(Link *link) {
0943b3b7
YW
55 assert(link);
56
e096cab2 57 if (!link_may_have_ipv6ll(link))
0943b3b7
YW
58 return false;
59
60 return link->network->router_prefix_delegation;
61}
62
064dfb05 63Prefix *prefix_free(Prefix *prefix) {
6e849e95 64 if (!prefix)
064dfb05 65 return NULL;
6e849e95
PF
66
67 if (prefix->network) {
ecb0e85e
YW
68 assert(prefix->section);
69 hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
6e849e95
PF
70 }
71
307fe3cd 72 config_section_free(prefix->section);
e609cd06 73 set_free(prefix->tokens);
6e849e95 74
064dfb05 75 return mfree(prefix);
6e849e95
PF
76}
77
307fe3cd 78DEFINE_SECTION_CLEANUP_FUNCTIONS(Prefix, prefix_free);
064dfb05 79
a8d4a210 80static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
307fe3cd 81 _cleanup_(config_section_freep) ConfigSection *n = NULL;
8e766630 82 _cleanup_(prefix_freep) Prefix *prefix = NULL;
6e849e95
PF
83 int r;
84
85 assert(network);
86 assert(ret);
ecb0e85e
YW
87 assert(filename);
88 assert(section_line > 0);
6e849e95 89
307fe3cd 90 r = config_section_new(filename, section_line, &n);
ecb0e85e
YW
91 if (r < 0)
92 return r;
6e849e95 93
ecb0e85e
YW
94 prefix = hashmap_get(network->prefixes_by_section, n);
95 if (prefix) {
96 *ret = TAKE_PTR(prefix);
97 return 0;
6e849e95
PF
98 }
99
2ac41679
YW
100 prefix = new(Prefix, 1);
101 if (!prefix)
102 return -ENOMEM;
103
104 *prefix = (Prefix) {
105 .network = network,
106 .section = TAKE_PTR(n),
6e849e95 107
c9e2c2da
YW
108 .preferred_lifetime = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
109 .valid_lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
2ac41679
YW
110 .onlink = true,
111 .address_auto_configuration = true,
112 };
6e849e95 113
307fe3cd 114 r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
ecb0e85e
YW
115 if (r < 0)
116 return r;
6e849e95 117
1cc6c93a 118 *ret = TAKE_PTR(prefix);
6e849e95
PF
119 return 0;
120}
121
064dfb05 122RoutePrefix *route_prefix_free(RoutePrefix *prefix) {
203d4df5 123 if (!prefix)
064dfb05 124 return NULL;
203d4df5
SS
125
126 if (prefix->network) {
ecb0e85e
YW
127 assert(prefix->section);
128 hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
203d4df5
SS
129 }
130
307fe3cd 131 config_section_free(prefix->section);
203d4df5 132
064dfb05
YW
133 return mfree(prefix);
134}
135
307fe3cd 136DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutePrefix, route_prefix_free);
064dfb05 137
a8d4a210 138static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
307fe3cd 139 _cleanup_(config_section_freep) ConfigSection *n = NULL;
95081e08 140 _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
203d4df5
SS
141 int r;
142
143 assert(network);
144 assert(ret);
ecb0e85e
YW
145 assert(filename);
146 assert(section_line > 0);
203d4df5 147
307fe3cd 148 r = config_section_new(filename, section_line, &n);
ecb0e85e
YW
149 if (r < 0)
150 return r;
203d4df5 151
ecb0e85e
YW
152 prefix = hashmap_get(network->route_prefixes_by_section, n);
153 if (prefix) {
154 *ret = TAKE_PTR(prefix);
155 return 0;
203d4df5
SS
156 }
157
2ac41679
YW
158 prefix = new(RoutePrefix, 1);
159 if (!prefix)
160 return -ENOMEM;
203d4df5 161
2ac41679
YW
162 *prefix = (RoutePrefix) {
163 .network = network,
164 .section = TAKE_PTR(n),
165
c9e2c2da 166 .lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
2ac41679 167 };
203d4df5 168
307fe3cd 169 r = hashmap_ensure_put(&network->route_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
ecb0e85e
YW
170 if (r < 0)
171 return r;
203d4df5
SS
172
173 *ret = TAKE_PTR(prefix);
203d4df5
SS
174 return 0;
175}
176
3b6a3bde
YW
177int link_request_radv_addresses(Link *link) {
178 Prefix *p;
179 int r;
180
181 assert(link);
182
183 if (!link_radv_enabled(link))
184 return 0;
185
186 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
e609cd06 187 _cleanup_set_free_ Set *addresses = NULL;
2ac41679 188 struct in6_addr *a;
3b6a3bde
YW
189
190 if (!p->assign)
191 continue;
192
e609cd06 193 /* radv_generate_addresses() below requires the prefix length <= 64. */
2110040b 194 if (p->prefixlen > 64)
fcd7ad52 195 continue;
fcd7ad52 196
2ac41679 197 r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
3b6a3bde 198 if (r < 0)
e609cd06 199 return r;
00f1261d 200
e609cd06
YW
201 SET_FOREACH(a, addresses) {
202 _cleanup_(address_freep) Address *address = NULL;
3b6a3bde 203
e609cd06
YW
204 r = address_new(&address);
205 if (r < 0)
206 return -ENOMEM;
3b6a3bde 207
e609cd06
YW
208 address->source = NETWORK_CONFIG_SOURCE_STATIC;
209 address->family = AF_INET6;
210 address->in_addr.in6 = *a;
2ac41679 211 address->prefixlen = p->prefixlen;
e609cd06
YW
212 address->route_metric = p->route_metric;
213
214 r = link_request_static_address(link, TAKE_PTR(address), true);
215 if (r < 0)
216 return r;
217 }
3b6a3bde
YW
218 }
219
220 return 0;
221}
222
2ac41679
YW
223static uint32_t usec_to_lifetime(usec_t usec) {
224 uint64_t t;
225
226 if (usec == USEC_INFINITY)
227 return UINT32_MAX;
228
229 t = DIV_ROUND_UP(usec, USEC_PER_SEC);
230 if (t >= UINT32_MAX)
231 return UINT32_MAX;
232
233 return (uint32_t) t;
234}
235
236static int radv_set_prefix(Link *link, Prefix *prefix) {
237 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
238 int r;
239
240 assert(link);
241 assert(link->radv);
242 assert(prefix);
243
244 r = sd_radv_prefix_new(&p);
245 if (r < 0)
246 return r;
247
248 r = sd_radv_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
249 if (r < 0)
250 return r;
251
95e104e0 252 r = sd_radv_prefix_set_preferred_lifetime(p, prefix->preferred_lifetime, USEC_INFINITY);
2ac41679
YW
253 if (r < 0)
254 return r;
255
95e104e0 256 r = sd_radv_prefix_set_valid_lifetime(p, prefix->valid_lifetime, USEC_INFINITY);
2ac41679
YW
257 if (r < 0)
258 return r;
259
260 r = sd_radv_prefix_set_onlink(p, prefix->onlink);
261 if (r < 0)
262 return r;
263
264 r = sd_radv_prefix_set_address_autoconfiguration(p, prefix->address_auto_configuration);
265 if (r < 0)
266 return r;
267
95e104e0 268 return sd_radv_add_prefix(link->radv, p);
2ac41679
YW
269}
270
271static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
272 _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
273 int r;
274
275 assert(link);
276 assert(link->radv);
277 assert(prefix);
278
279 r = sd_radv_route_prefix_new(&p);
280 if (r < 0)
281 return r;
282
283 r = sd_radv_route_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
284 if (r < 0)
285 return r;
286
95e104e0 287 r = sd_radv_route_prefix_set_lifetime(p, prefix->lifetime, USEC_INFINITY);
2ac41679
YW
288 if (r < 0)
289 return r;
290
95e104e0 291 return sd_radv_add_route_prefix(link->radv, p);
2ac41679
YW
292}
293
0943b3b7
YW
294static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
295 _cleanup_free_ struct in6_addr *addresses = NULL;
296 size_t n_addresses = 0;
6e849e95 297
0943b3b7
YW
298 assert(network);
299 assert(ret_addresses);
300 assert(ret_size);
6e849e95 301
0943b3b7
YW
302 for (size_t i = 0; i < network->n_dns; i++) {
303 union in_addr_union *addr;
6e849e95 304
0943b3b7
YW
305 if (network->dns[i]->family != AF_INET6)
306 continue;
6e849e95 307
0943b3b7 308 addr = &network->dns[i]->address;
6e849e95 309
0943b3b7
YW
310 if (in_addr_is_null(AF_INET6, addr) ||
311 in_addr_is_link_local(AF_INET6, addr) ||
312 in_addr_is_localhost(AF_INET6, addr))
313 continue;
314
315 if (!GREEDY_REALLOC(addresses, n_addresses + 1))
316 return -ENOMEM;
317
318 addresses[n_addresses++] = addr->in6;
c9778516 319 }
6e849e95 320
0943b3b7
YW
321 *ret_addresses = TAKE_PTR(addresses);
322 *ret_size = n_addresses;
6e849e95 323
0943b3b7 324 return n_addresses;
6e849e95
PF
325}
326
0943b3b7
YW
327static int radv_set_dns(Link *link, Link *uplink) {
328 _cleanup_free_ struct in6_addr *dns = NULL;
0943b3b7 329 size_t n_dns;
c9778516 330 int r;
6e849e95 331
0943b3b7 332 if (!link->network->router_emit_dns)
6e849e95 333 return 0;
6e849e95 334
0943b3b7
YW
335 if (link->network->router_dns) {
336 struct in6_addr *p;
6e849e95 337
0943b3b7
YW
338 dns = new(struct in6_addr, link->network->n_router_dns);
339 if (!dns)
340 return -ENOMEM;
6e849e95 341
0943b3b7
YW
342 p = dns;
343 for (size_t i = 0; i < link->network->n_router_dns; i++)
344 if (in6_addr_is_null(&link->network->router_dns[i])) {
345 if (in6_addr_is_set(&link->ipv6ll_address))
346 *(p++) = link->ipv6ll_address;
347 } else
348 *(p++) = link->network->router_dns[i];
6e849e95 349
0943b3b7 350 n_dns = p - dns;
a8d4a210 351
0943b3b7
YW
352 goto set_dns;
353 }
6e849e95 354
0943b3b7
YW
355 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
356 if (r > 0)
357 goto set_dns;
6e849e95 358
0943b3b7
YW
359 if (uplink) {
360 assert(uplink->network);
361
362 r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
363 if (r > 0)
364 goto set_dns;
6e849e95
PF
365 }
366
6e849e95 367 return 0;
bd6379ec 368
0943b3b7
YW
369set_dns:
370 return sd_radv_set_rdnss(link->radv,
165a654e 371 usec_to_lifetime(link->network->router_dns_lifetime_usec),
0943b3b7
YW
372 dns, n_dns);
373}
bd6379ec 374
0943b3b7 375static int radv_set_domains(Link *link, Link *uplink) {
0943b3b7 376 _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
165a654e 377 OrderedSet *search_domains;
bd6379ec 378
0943b3b7 379 if (!link->network->router_emit_domains)
bd6379ec 380 return 0;
bd6379ec 381
0943b3b7 382 search_domains = link->network->router_search_domains;
bd6379ec 383
0943b3b7
YW
384 if (search_domains)
385 goto set_domains;
0e1fb1d0 386
0943b3b7
YW
387 search_domains = link->network->search_domains;
388 if (search_domains)
389 goto set_domains;
0e1fb1d0 390
0943b3b7
YW
391 if (uplink) {
392 assert(uplink->network);
0e1fb1d0 393
0943b3b7
YW
394 search_domains = uplink->network->search_domains;
395 if (search_domains)
396 goto set_domains;
0e1fb1d0
YW
397 }
398
0e1fb1d0 399 return 0;
0e1fb1d0 400
0943b3b7
YW
401set_domains:
402 s = ordered_set_get_strv(search_domains);
403 if (!s)
404 return log_oom();
203d4df5 405
0943b3b7 406 return sd_radv_set_dnssl(link->radv,
165a654e 407 usec_to_lifetime(link->network->router_dns_lifetime_usec),
0943b3b7 408 s);
203d4df5 409
0943b3b7 410}
203d4df5 411
0943b3b7 412static int radv_find_uplink(Link *link, Link **ret) {
f6032ff3
YW
413 int r;
414
0943b3b7 415 assert(link);
203d4df5 416
0943b3b7
YW
417 if (link->network->router_uplink_name)
418 return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
203d4df5 419
0943b3b7
YW
420 if (link->network->router_uplink_index > 0)
421 return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
422
423 if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
a27588d4
YW
424 if (link_dhcp_pd_is_enabled(link))
425 r = dhcp_pd_find_uplink(link, ret); /* When DHCP-PD is enabled, use its uplink. */
f6032ff3
YW
426 else
427 r = manager_find_uplink(link->manager, AF_INET6, link, ret);
428 if (r < 0)
429 /* It is not necessary to propagate error in automatic selection. */
0943b3b7 430 *ret = NULL;
c9778516
YW
431 return 0;
432 }
203d4df5 433
0943b3b7 434 *ret = NULL;
203d4df5
SS
435 return 0;
436}
437
0943b3b7 438static int radv_configure(Link *link) {
0943b3b7
YW
439 Link *uplink = NULL;
440 RoutePrefix *q;
441 Prefix *p;
203d4df5
SS
442 int r;
443
0943b3b7
YW
444 assert(link);
445 assert(link->network);
203d4df5 446
0943b3b7
YW
447 if (link->radv)
448 return -EBUSY;
449
450 r = sd_radv_new(&link->radv);
203d4df5 451 if (r < 0)
0943b3b7 452 return r;
203d4df5 453
0943b3b7
YW
454 r = sd_radv_attach_event(link->radv, link->manager->event, 0);
455 if (r < 0)
456 return r;
203d4df5 457
0943b3b7
YW
458 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
459 if (r < 0)
460 return r;
203d4df5 461
0943b3b7
YW
462 r = sd_radv_set_ifindex(link->radv, link->ifindex);
463 if (r < 0)
464 return r;
203d4df5 465
0943b3b7
YW
466 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
467 if (r < 0)
468 return r;
203d4df5 469
0943b3b7
YW
470 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
471 if (r < 0)
472 return r;
c555a358 473
7003b114 474 r = sd_radv_set_router_lifetime(link->radv, link->network->router_lifetime_usec);
0943b3b7
YW
475 if (r < 0)
476 return r;
c555a358 477
7003b114 478 if (link->network->router_lifetime_usec > 0) {
0943b3b7
YW
479 r = sd_radv_set_preference(link->radv, link->network->router_preference);
480 if (r < 0)
481 return r;
482 }
c555a358 483
0943b3b7 484 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
2ac41679
YW
485 r = radv_set_prefix(link, p);
486 if (r < 0 && r != -EEXIST)
0943b3b7
YW
487 return r;
488 }
c555a358 489
0943b3b7 490 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
2ac41679
YW
491 r = radv_set_route_prefix(link, q);
492 if (r < 0 && r != -EEXIST)
0943b3b7
YW
493 return r;
494 }
c555a358 495
0943b3b7 496 (void) radv_find_uplink(link, &uplink);
c555a358 497
0943b3b7
YW
498 r = radv_set_dns(link, uplink);
499 if (r < 0)
500 return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
c555a358 501
0943b3b7
YW
502 r = radv_set_domains(link, uplink);
503 if (r < 0)
504 return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
c555a358 505
0943b3b7 506 return 0;
c555a358
PF
507}
508
0943b3b7
YW
509int radv_update_mac(Link *link) {
510 bool restart;
c555a358
PF
511 int r;
512
0943b3b7 513 assert(link);
c555a358 514
0943b3b7
YW
515 if (!link->radv)
516 return 0;
fd3ef936 517
0943b3b7 518 restart = sd_radv_is_running(link->radv);
c555a358 519
0943b3b7
YW
520 r = sd_radv_stop(link->radv);
521 if (r < 0)
522 return r;
fd3ef936 523
0943b3b7
YW
524 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
525 if (r < 0)
526 return r;
c555a358 527
0943b3b7
YW
528 if (restart) {
529 r = sd_radv_start(link->radv);
530 if (r < 0)
531 return r;
c555a358
PF
532 }
533
0943b3b7
YW
534 return 0;
535}
c555a358 536
0943b3b7
YW
537static int radv_is_ready_to_configure(Link *link) {
538 bool needs_uplink = false;
539 int r;
c555a358 540
0943b3b7
YW
541 assert(link);
542 assert(link->network);
349a981d 543
0943b3b7
YW
544 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
545 return false;
c555a358 546
0943b3b7
YW
547 if (in6_addr_is_null(&link->ipv6ll_address))
548 return false;
c555a358 549
0943b3b7
YW
550 if (link->network->router_emit_dns && !link->network->router_dns) {
551 _cleanup_free_ struct in6_addr *dns = NULL;
552 size_t n_dns;
c555a358 553
0943b3b7
YW
554 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
555 if (r < 0)
556 return r;
c555a358 557
0943b3b7
YW
558 needs_uplink = r == 0;
559 }
c555a358 560
0943b3b7
YW
561 if (link->network->router_emit_domains &&
562 !link->network->router_search_domains &&
563 !link->network->search_domains)
564 needs_uplink = true;
565
566 if (needs_uplink) {
567 Link *uplink = NULL;
c555a358 568
0943b3b7
YW
569 if (radv_find_uplink(link, &uplink) < 0)
570 return false;
c555a358 571
0943b3b7
YW
572 if (uplink && !uplink->network)
573 return false;
574 }
c555a358 575
0943b3b7
YW
576 return true;
577}
c555a358 578
0943b3b7
YW
579int request_process_radv(Request *req) {
580 Link *link;
581 int r;
349a981d 582
0943b3b7
YW
583 assert(req);
584 assert(req->link);
585 assert(req->type == REQUEST_TYPE_RADV);
c555a358 586
0943b3b7 587 link = req->link;
c555a358 588
0943b3b7
YW
589 r = radv_is_ready_to_configure(link);
590 if (r <= 0)
591 return r;
5e2a51d5 592
0943b3b7
YW
593 r = radv_configure(link);
594 if (r < 0)
595 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
c555a358 596
0943b3b7 597 if (link_has_carrier(link)) {
0f96a823 598 r = radv_start(link);
0943b3b7
YW
599 if (r < 0)
600 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
601 }
602
603 log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
604 link_has_carrier(link) ? " and started." : "");
605 return 1;
c555a358
PF
606}
607
0943b3b7
YW
608int link_request_radv(Link *link) {
609 int r;
63295b42 610
0943b3b7 611 assert(link);
63295b42 612
0943b3b7
YW
613 if (!link_radv_enabled(link))
614 return 0;
63295b42 615
0943b3b7 616 if (link->radv)
63295b42 617 return 0;
63295b42 618
0943b3b7
YW
619 r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL);
620 if (r < 0)
621 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
622
623 log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
63295b42
YW
624 return 0;
625}
626
0f96a823
YW
627int radv_start(Link *link) {
628 int r;
629
630 assert(link);
631 assert(link->network);
632
633 if (!link->radv)
634 return 0;
635
636 if (!link_has_carrier(link))
637 return 0;
638
639 if (in6_addr_is_null(&link->ipv6ll_address))
640 return 0;
641
642 if (sd_radv_is_running(link->radv))
643 return 0;
644
a27588d4
YW
645 if (link->network->dhcp_pd_announce) {
646 r = dhcp_request_prefix_delegation(link);
0f96a823 647 if (r < 0)
a27588d4 648 return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m");
0f96a823
YW
649 }
650
651 log_link_debug(link, "Starting IPv6 Router Advertisements");
652 return sd_radv_start(link->radv);
653}
654
0943b3b7
YW
655int radv_add_prefix(
656 Link *link,
657 const struct in6_addr *prefix,
658 uint8_t prefix_len,
16bc8635
YW
659 usec_t lifetime_preferred_usec,
660 usec_t lifetime_valid_usec) {
0943b3b7
YW
661
662 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
95081e08 663 int r;
091214b6
PF
664
665 assert(link);
091214b6 666
0943b3b7
YW
667 if (!link->radv)
668 return 0;
bc9e40c9 669
0943b3b7 670 r = sd_radv_prefix_new(&p);
091214b6
PF
671 if (r < 0)
672 return r;
673
0943b3b7 674 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
091214b6
PF
675 if (r < 0)
676 return r;
677
c9e2c2da 678 r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
091214b6
PF
679 if (r < 0)
680 return r;
681
c9e2c2da 682 r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
091214b6
PF
683 if (r < 0)
684 return r;
685
95e104e0 686 r = sd_radv_add_prefix(link->radv, p);
0943b3b7 687 if (r < 0 && r != -EEXIST)
091214b6
PF
688 return r;
689
0943b3b7
YW
690 return 0;
691}
091214b6 692
2110040b
YW
693static int prefix_section_verify(Prefix *p) {
694 assert(p);
695
696 if (section_is_invalid(p->section))
697 return -EINVAL;
698
699 if (in6_addr_is_null(&p->prefix))
700 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
701 "%s: [IPv6Prefix] section without Prefix= field configured, "
702 "or specified prefix is the null address. "
703 "Ignoring [IPv6Prefix] section from line %u.",
704 p->section->filename, p->section->line);
705
706 if (p->prefixlen < 3 || p->prefixlen > 128)
707 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
708 "%s: Invalid prefix length %u is specified in [IPv6Prefix] section. "
709 "Valid range is 3…128. Ignoring [IPv6Prefix] section from line %u.",
710 p->section->filename, p->prefixlen, p->section->line);
711
712 if (p->prefixlen > 64) {
713 _cleanup_free_ char *str = NULL;
714
715 if (p->assign)
716 (void) in6_addr_prefix_to_string(&p->prefix, p->prefixlen, &str);
717
718 log_info("%s: Unusual prefix length %u (> 64) is specified in [IPv6Prefix] section from line %u%s%s.",
719 p->section->filename, p->prefixlen, p->section->line,
720 p->assign ? ", refusing to assign an address in " : "",
721 p->assign ? strna(str) : "");
722
723 p->assign = false;
724 }
725
726 if (p->valid_lifetime == 0)
727 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
728 "%s: The valid lifetime of prefix cannot be zero. "
729 "Ignoring [IPv6Prefix] section from line %u.",
730 p->section->filename, p->section->line);
731
732 if (p->preferred_lifetime > p->valid_lifetime)
733 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
734 "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
735 "Ignoring [IPv6Prefix] section from line %u.",
736 p->section->filename,
737 FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
738 FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
739 p->section->line);
740
741 return 0;
742}
743
0943b3b7 744void network_drop_invalid_prefixes(Network *network) {
2110040b 745 Prefix *p;
2075e596 746
0943b3b7
YW
747 assert(network);
748
2110040b
YW
749 HASHMAP_FOREACH(p, network->prefixes_by_section)
750 if (prefix_section_verify(p) < 0)
751 prefix_free(p);
752}
753
754static int route_prefix_section_verify(RoutePrefix *p) {
755 if (section_is_invalid(p->section))
756 return -EINVAL;
757
758 if (p->prefixlen > 128)
759 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
760 "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
761 "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
762 p->section->filename, p->prefixlen, p->section->line);
763
764 if (p->lifetime == 0)
765 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
766 "%s: The lifetime of route cannot be zero. "
767 "Ignoring [IPv6RoutePrefix] section from line %u.",
768 p->section->filename, p->section->line);
769
770 return 0;
0943b3b7
YW
771}
772
773void network_drop_invalid_route_prefixes(Network *network) {
2110040b 774 RoutePrefix *p;
0943b3b7
YW
775
776 assert(network);
777
2110040b
YW
778 HASHMAP_FOREACH(p, network->route_prefixes_by_section)
779 if (route_prefix_section_verify(p) < 0)
780 route_prefix_free(p);
0943b3b7
YW
781}
782
783int config_parse_prefix(
784 const char *unit,
785 const char *filename,
786 unsigned line,
787 const char *section,
788 unsigned section_line,
789 const char *lvalue,
790 int ltype,
791 const char *rvalue,
792 void *data,
793 void *userdata) {
794
0943b3b7 795 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679
YW
796 Network *network = userdata;
797 union in_addr_union a;
0943b3b7
YW
798 int r;
799
800 assert(filename);
801 assert(section);
802 assert(lvalue);
803 assert(rvalue);
2ac41679 804 assert(userdata);
0943b3b7
YW
805
806 r = prefix_new_static(network, filename, section_line, &p);
091214b6 807 if (r < 0)
0943b3b7 808 return log_oom();
091214b6 809
2ac41679 810 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 811 if (r < 0) {
2ac41679
YW
812 log_syntax(unit, LOG_WARNING, filename, line, r,
813 "Prefix is invalid, ignoring assignment: %s", rvalue);
0943b3b7 814 return 0;
8a08bbfc 815 }
cf72568a
YW
816
817 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 818 p->prefix = a.in6;
203d4df5 819
2ac41679 820 TAKE_PTR(p);
0943b3b7
YW
821 return 0;
822}
a254fab2 823
2ac41679 824int config_parse_prefix_boolean(
0943b3b7
YW
825 const char *unit,
826 const char *filename,
827 unsigned line,
828 const char *section,
829 unsigned section_line,
830 const char *lvalue,
831 int ltype,
832 const char *rvalue,
833 void *data,
834 void *userdata) {
a254fab2 835
0943b3b7 836 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 837 Network *network = userdata;
0943b3b7
YW
838 int r;
839
840 assert(filename);
841 assert(section);
842 assert(lvalue);
843 assert(rvalue);
2ac41679 844 assert(userdata);
0943b3b7
YW
845
846 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 847 if (r < 0)
0943b3b7
YW
848 return log_oom();
849
850 r = parse_boolean(rvalue);
851 if (r < 0) {
2ac41679
YW
852 log_syntax(unit, LOG_WARNING, filename, line, r,
853 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
0943b3b7
YW
854 return 0;
855 }
856
857 if (streq(lvalue, "OnLink"))
2ac41679 858 p->onlink = r;
0943b3b7 859 else if (streq(lvalue, "AddressAutoconfiguration"))
2ac41679
YW
860 p->address_auto_configuration = r;
861 else if (streq(lvalue, "Assign"))
862 p->assign = r;
863 else
864 assert_not_reached();
a254fab2 865
2ac41679 866 TAKE_PTR(p);
fd3ef936 867 return 0;
091214b6 868}
ca5ad760 869
0943b3b7
YW
870int config_parse_prefix_lifetime(
871 const char *unit,
872 const char *filename,
873 unsigned line,
874 const char *section,
875 unsigned section_line,
876 const char *lvalue,
877 int ltype,
878 const char *rvalue,
879 void *data,
880 void *userdata) {
881
0943b3b7 882 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 883 Network *network = userdata;
0943b3b7 884 usec_t usec;
be9363cc
YW
885 int r;
886
0943b3b7
YW
887 assert(filename);
888 assert(section);
889 assert(lvalue);
890 assert(rvalue);
2ac41679 891 assert(userdata);
be9363cc 892
0943b3b7 893 r = prefix_new_static(network, filename, section_line, &p);
a391901e 894 if (r < 0)
0943b3b7 895 return log_oom();
be9363cc 896
0943b3b7
YW
897 r = parse_sec(rvalue, &usec);
898 if (r < 0) {
2ac41679
YW
899 log_syntax(unit, LOG_WARNING, filename, line, r,
900 "Lifetime is invalid, ignoring assignment: %s", rvalue);
0943b3b7
YW
901 return 0;
902 }
be9363cc 903
2ac41679
YW
904 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
905 log_syntax(unit, LOG_WARNING, filename, line, 0,
906 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7 907 return 0;
be9363cc
YW
908 }
909
2ac41679
YW
910 if (streq(lvalue, "PreferredLifetimeSec"))
911 p->preferred_lifetime = usec;
912 else if (streq(lvalue, "ValidLifetimeSec"))
913 p->valid_lifetime = usec;
914 else
915 assert_not_reached();
63295b42 916
2ac41679 917 TAKE_PTR(p);
0943b3b7 918 return 0;
a254fab2
YW
919}
920
0943b3b7
YW
921int config_parse_prefix_metric(
922 const char *unit,
923 const char *filename,
924 unsigned line,
925 const char *section,
926 unsigned section_line,
927 const char *lvalue,
928 int ltype,
929 const char *rvalue,
930 void *data,
931 void *userdata) {
a254fab2 932
0943b3b7 933 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 934 Network *network = userdata;
0943b3b7 935 int r;
a254fab2 936
0943b3b7
YW
937 assert(filename);
938 assert(section);
939 assert(lvalue);
940 assert(rvalue);
2ac41679 941 assert(userdata);
a254fab2 942
0943b3b7 943 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 944 if (r < 0)
0943b3b7 945 return log_oom();
a254fab2 946
0943b3b7
YW
947 r = safe_atou32(rvalue, &p->route_metric);
948 if (r < 0) {
949 log_syntax(unit, LOG_WARNING, filename, line, r,
950 "Failed to parse %s=, ignoring assignment: %s",
951 lvalue, rvalue);
952 return 0;
a254fab2
YW
953 }
954
0943b3b7 955 TAKE_PTR(p);
0943b3b7 956 return 0;
a254fab2
YW
957}
958
e609cd06
YW
959int config_parse_prefix_token(
960 const char *unit,
961 const char *filename,
962 unsigned line,
963 const char *section,
964 unsigned section_line,
965 const char *lvalue,
966 int ltype,
967 const char *rvalue,
968 void *data,
969 void *userdata) {
970
971 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
972 Network *network = userdata;
973 int r;
974
975 assert(filename);
976 assert(section);
977 assert(lvalue);
978 assert(rvalue);
979 assert(userdata);
980
981 r = prefix_new_static(network, filename, section_line, &p);
982 if (r < 0)
983 return log_oom();
984
985 r = config_parse_address_generation_type(unit, filename, line, section, section_line,
986 lvalue, ltype, rvalue, &p->tokens, userdata);
987 if (r < 0)
988 return r;
989
990 TAKE_PTR(p);
991 return 0;
992}
993
0943b3b7
YW
994int config_parse_route_prefix(
995 const char *unit,
996 const char *filename,
997 unsigned line,
998 const char *section,
999 unsigned section_line,
1000 const char *lvalue,
1001 int ltype,
1002 const char *rvalue,
1003 void *data,
1004 void *userdata) {
1005
0943b3b7 1006 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
2ac41679
YW
1007 Network *network = userdata;
1008 union in_addr_union a;
a254fab2
YW
1009 int r;
1010
0943b3b7
YW
1011 assert(filename);
1012 assert(section);
1013 assert(lvalue);
1014 assert(rvalue);
2ac41679 1015 assert(userdata);
a254fab2 1016
0943b3b7
YW
1017 r = route_prefix_new_static(network, filename, section_line, &p);
1018 if (r < 0)
1019 return log_oom();
1020
2ac41679 1021 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 1022 if (r < 0) {
2ac41679
YW
1023 log_syntax(unit, LOG_WARNING, filename, line, r,
1024 "Route prefix is invalid, ignoring assignment: %s", rvalue);
a254fab2 1025 return 0;
0943b3b7 1026 }
cf72568a
YW
1027
1028 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 1029 p->prefix = a.in6;
a254fab2 1030
2ac41679 1031 TAKE_PTR(p);
a254fab2
YW
1032 return 0;
1033}
1034
0943b3b7
YW
1035int config_parse_route_prefix_lifetime(
1036 const char *unit,
1037 const char *filename,
1038 unsigned line,
1039 const char *section,
1040 unsigned section_line,
1041 const char *lvalue,
1042 int ltype,
1043 const char *rvalue,
1044 void *data,
1045 void *userdata) {
a8d4a210 1046
0943b3b7 1047 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
2ac41679 1048 Network *network = userdata;
0943b3b7 1049 usec_t usec;
1d596fde
YW
1050 int r;
1051
0943b3b7
YW
1052 assert(filename);
1053 assert(section);
1054 assert(lvalue);
1055 assert(rvalue);
2ac41679 1056 assert(userdata);
1d596fde 1057
0943b3b7 1058 r = route_prefix_new_static(network, filename, section_line, &p);
1d596fde 1059 if (r < 0)
0943b3b7 1060 return log_oom();
1d596fde 1061
0943b3b7
YW
1062 r = parse_sec(rvalue, &usec);
1063 if (r < 0) {
1064 log_syntax(unit, LOG_WARNING, filename, line, r,
1065 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
1066 return 0;
1067 }
1d596fde 1068
2ac41679
YW
1069 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1070 log_syntax(unit, LOG_WARNING, filename, line, 0,
1071 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7
YW
1072 return 0;
1073 }
1d596fde 1074
2ac41679 1075 p->lifetime = usec;
1d596fde 1076
2ac41679 1077 TAKE_PTR(p);
1d596fde
YW
1078 return 0;
1079}
1080
ca5ad760
YW
1081int config_parse_radv_dns(
1082 const char *unit,
1083 const char *filename,
1084 unsigned line,
1085 const char *section,
1086 unsigned section_line,
1087 const char *lvalue,
1088 int ltype,
1089 const char *rvalue,
1090 void *data,
1091 void *userdata) {
1092
1093 Network *n = data;
ca5ad760
YW
1094 int r;
1095
1096 assert(filename);
1097 assert(lvalue);
1098 assert(rvalue);
1099
a3c1a949
YW
1100 if (isempty(rvalue)) {
1101 n->n_router_dns = 0;
1102 n->router_dns = mfree(n->router_dns);
1103 return 0;
1104 }
1105
d96edb2c 1106 for (const char *p = rvalue;;) {
ca5ad760
YW
1107 _cleanup_free_ char *w = NULL;
1108 union in_addr_union a;
1109
1110 r = extract_first_word(&p, &w, NULL, 0);
1111 if (r == -ENOMEM)
1112 return log_oom();
1113 if (r < 0) {
d96edb2c 1114 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1115 "Failed to extract word, ignoring: %s", rvalue);
1116 return 0;
1117 }
1118 if (r == 0)
d96edb2c 1119 return 0;
ca5ad760 1120
fd3ef936
YW
1121 if (streq(w, "_link_local"))
1122 a = IN_ADDR_NULL;
1123 else {
1124 r = in_addr_from_string(AF_INET6, w, &a);
1125 if (r < 0) {
d96edb2c 1126 log_syntax(unit, LOG_WARNING, filename, line, r,
fd3ef936
YW
1127 "Failed to parse DNS server address, ignoring: %s", w);
1128 continue;
1129 }
ca5ad760 1130
fd3ef936 1131 if (in_addr_is_null(AF_INET6, &a)) {
d96edb2c 1132 log_syntax(unit, LOG_WARNING, filename, line, 0,
fd3ef936
YW
1133 "DNS server address is null, ignoring: %s", w);
1134 continue;
1135 }
1136 }
ca5ad760 1137
fd3ef936
YW
1138 struct in6_addr *m;
1139 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1140 if (!m)
1141 return log_oom();
ca5ad760 1142
fd3ef936
YW
1143 m[n->n_router_dns++] = a.in6;
1144 n->router_dns = m;
ca5ad760 1145 }
ca5ad760
YW
1146}
1147
1148int config_parse_radv_search_domains(
1149 const char *unit,
1150 const char *filename,
1151 unsigned line,
1152 const char *section,
1153 unsigned section_line,
1154 const char *lvalue,
1155 int ltype,
1156 const char *rvalue,
1157 void *data,
1158 void *userdata) {
1159
1160 Network *n = data;
ca5ad760
YW
1161 int r;
1162
1163 assert(filename);
1164 assert(lvalue);
1165 assert(rvalue);
1166
a3c1a949
YW
1167 if (isempty(rvalue)) {
1168 n->router_search_domains = ordered_set_free(n->router_search_domains);
1169 return 0;
1170 }
1171
d96edb2c 1172 for (const char *p = rvalue;;) {
ca5ad760
YW
1173 _cleanup_free_ char *w = NULL, *idna = NULL;
1174
1175 r = extract_first_word(&p, &w, NULL, 0);
1176 if (r == -ENOMEM)
1177 return log_oom();
1178 if (r < 0) {
d96edb2c 1179 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1180 "Failed to extract word, ignoring: %s", rvalue);
1181 return 0;
1182 }
1183 if (r == 0)
d96edb2c 1184 return 0;
ca5ad760
YW
1185
1186 r = dns_name_apply_idna(w, &idna);
1187 if (r < 0) {
d96edb2c 1188 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1189 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1190 continue;
1191 } else if (r == 0)
1192 /* transfer ownership to simplify subsequent operations */
1193 idna = TAKE_PTR(w);
1194
5e276772 1195 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
ca5ad760 1196 if (r < 0)
d96edb2c 1197 return log_oom();
ca5ad760
YW
1198
1199 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1200 if (r < 0)
d96edb2c 1201 return log_oom();
ca5ad760 1202 }
ca5ad760
YW
1203}
1204
1205static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
a8d4a210 1206 [RADV_PREFIX_DELEGATION_NONE] = "no",
ca5ad760 1207 [RADV_PREFIX_DELEGATION_STATIC] = "static",
a8d4a210
YW
1208 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
1209 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
ca5ad760
YW
1210};
1211
1212DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
1213 radv_prefix_delegation,
1214 RADVPrefixDelegation,
1215 RADV_PREFIX_DELEGATION_BOTH);
1216
27ff0490
YW
1217int config_parse_router_prefix_delegation(
1218 const char *unit,
1219 const char *filename,
1220 unsigned line,
1221 const char *section,
1222 unsigned section_line,
1223 const char *lvalue,
1224 int ltype,
1225 const char *rvalue,
1226 void *data,
1227 void *userdata) {
1228
1229 RADVPrefixDelegation val, *ra = data;
1230 int r;
1231
1232 assert(filename);
1233 assert(lvalue);
1234 assert(rvalue);
1235 assert(data);
1236
1237 if (streq(lvalue, "IPv6SendRA")) {
1238 r = parse_boolean(rvalue);
1239 if (r < 0) {
1240 log_syntax(unit, LOG_WARNING, filename, line, r,
1241 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1242 return 0;
1243 }
1244
1245 /* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
1246 * need to explicitly enable DHCPv6PrefixDelegation=. */
1247 *ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
1248 return 0;
1249 }
1250
1251 /* For backward compatibility */
1252 val = radv_prefix_delegation_from_string(rvalue);
1253 if (val < 0) {
b98680b2 1254 log_syntax(unit, LOG_WARNING, filename, line, val,
27ff0490
YW
1255 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1256 return 0;
1257 }
1258
1259 *ra = val;
1260 return 0;
1261}
a8d4a210 1262
4f1ac4a3
YW
1263int config_parse_router_lifetime(
1264 const char *unit,
1265 const char *filename,
1266 unsigned line,
1267 const char *section,
1268 unsigned section_line,
1269 const char *lvalue,
1270 int ltype,
1271 const char *rvalue,
1272 void *data,
1273 void *userdata) {
1274
1275 usec_t usec, *lifetime = data;
1276 int r;
1277
1278 assert(filename);
1279 assert(section);
1280 assert(lvalue);
1281 assert(rvalue);
1282 assert(data);
1283
1284 if (isempty(rvalue)) {
1285 *lifetime = RADV_DEFAULT_ROUTER_LIFETIME_USEC;
1286 return 0;
1287 }
1288
1289 r = parse_sec(rvalue, &usec);
1290 if (r < 0) {
1291 log_syntax(unit, LOG_WARNING, filename, line, r,
1292 "Failed to parse router lifetime, ignoring assignment: %s", rvalue);
1293 return 0;
1294 }
1295 if (usec > 0) {
1296 if (usec < RADV_MIN_ROUTER_LIFETIME_USEC) {
1297 log_syntax(unit, LOG_WARNING, filename, line, 0,
1298 "Router lifetime %s is too short, using %s.",
1299 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1300 FORMAT_TIMESPAN(RADV_MIN_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1301 usec = RADV_MIN_ROUTER_LIFETIME_USEC;
1302 } else if (usec > RADV_MAX_ROUTER_LIFETIME_USEC) {
1303 log_syntax(unit, LOG_WARNING, filename, line, 0,
1304 "Router lifetime %s is too large, using %s.",
1305 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1306 FORMAT_TIMESPAN(RADV_MAX_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1307 usec = RADV_MAX_ROUTER_LIFETIME_USEC;
1308 }
1309 }
1310
1311 *lifetime = usec;
1312 return 0;
1313}
1314
a8d4a210
YW
1315int config_parse_router_preference(
1316 const char *unit,
1317 const char *filename,
1318 unsigned line,
1319 const char *section,
1320 unsigned section_line,
1321 const char *lvalue,
1322 int ltype,
1323 const char *rvalue,
1324 void *data,
1325 void *userdata) {
1326
ca5ad760
YW
1327 Network *network = userdata;
1328
1329 assert(filename);
1330 assert(section);
1331 assert(lvalue);
1332 assert(rvalue);
1333 assert(data);
1334
1335 if (streq(rvalue, "high"))
1336 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
1337 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
1338 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
1339 else if (streq(rvalue, "low"))
1340 network->router_preference = SD_NDISC_PREFERENCE_LOW;
1341 else
d96edb2c
YW
1342 log_syntax(unit, LOG_WARNING, filename, line, 0,
1343 "Invalid router preference, ignoring assignment: %s", rvalue);
ca5ad760
YW
1344
1345 return 0;
1346}