]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-radv.c
Merge pull request #21355 from bluca/coverity
[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"
0f96a823 12#include "networkd-dhcp6.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"
3b6a3bde 18#include "networkd-route.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
30 if (network->dhcp6_pd < 0)
31 /* For backward compatibility. */
32 network->dhcp6_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6);
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
57 if (!link_ipv6ll_enabled(link))
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
ecee0abe 72 network_config_section_free(prefix->section);
e609cd06 73 set_free(prefix->tokens);
6e849e95 74
064dfb05 75 return mfree(prefix);
6e849e95
PF
76}
77
064dfb05
YW
78DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
79
a8d4a210 80static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
8e766630
LP
81 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
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
ecb0e85e
YW
90 r = network_config_section_new(filename, section_line, &n);
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
b9eea0a7 114 r = hashmap_ensure_put(&network->prefixes_by_section, &network_config_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
131 network_config_section_free(prefix->section);
132
064dfb05
YW
133 return mfree(prefix);
134}
135
136DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
137
a8d4a210 138static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
203d4df5 139 _cleanup_(network_config_section_freep) NetworkConfigSection *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
ecb0e85e
YW
148 r = network_config_section_new(filename, section_line, &n);
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
b2cb2e82 169 r = hashmap_ensure_put(&network->route_prefixes_by_section, &network_config_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
YW
412static int radv_find_uplink(Link *link, Link **ret) {
413 assert(link);
203d4df5 414
0943b3b7
YW
415 if (link->network->router_uplink_name)
416 return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
203d4df5 417
0943b3b7
YW
418 if (link->network->router_uplink_index > 0)
419 return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
420
421 if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
422 /* It is not necessary to propagate error in automatic selection. */
423 if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0)
424 *ret = NULL;
c9778516
YW
425 return 0;
426 }
203d4df5 427
0943b3b7 428 *ret = NULL;
203d4df5
SS
429 return 0;
430}
431
0943b3b7 432static int radv_configure(Link *link) {
0943b3b7
YW
433 Link *uplink = NULL;
434 RoutePrefix *q;
435 Prefix *p;
203d4df5
SS
436 int r;
437
0943b3b7
YW
438 assert(link);
439 assert(link->network);
203d4df5 440
0943b3b7
YW
441 if (link->radv)
442 return -EBUSY;
443
444 r = sd_radv_new(&link->radv);
203d4df5 445 if (r < 0)
0943b3b7 446 return r;
203d4df5 447
0943b3b7
YW
448 r = sd_radv_attach_event(link->radv, link->manager->event, 0);
449 if (r < 0)
450 return r;
203d4df5 451
0943b3b7
YW
452 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
453 if (r < 0)
454 return r;
203d4df5 455
0943b3b7
YW
456 r = sd_radv_set_ifindex(link->radv, link->ifindex);
457 if (r < 0)
458 return r;
203d4df5 459
0943b3b7
YW
460 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
461 if (r < 0)
462 return r;
203d4df5 463
0943b3b7
YW
464 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
465 if (r < 0)
466 return r;
c555a358 467
7003b114 468 r = sd_radv_set_router_lifetime(link->radv, link->network->router_lifetime_usec);
0943b3b7
YW
469 if (r < 0)
470 return r;
c555a358 471
7003b114 472 if (link->network->router_lifetime_usec > 0) {
0943b3b7
YW
473 r = sd_radv_set_preference(link->radv, link->network->router_preference);
474 if (r < 0)
475 return r;
476 }
c555a358 477
0943b3b7 478 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
2ac41679
YW
479 r = radv_set_prefix(link, p);
480 if (r < 0 && r != -EEXIST)
0943b3b7
YW
481 return r;
482 }
c555a358 483
0943b3b7 484 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
2ac41679
YW
485 r = radv_set_route_prefix(link, q);
486 if (r < 0 && r != -EEXIST)
0943b3b7
YW
487 return r;
488 }
c555a358 489
0943b3b7 490 (void) radv_find_uplink(link, &uplink);
c555a358 491
0943b3b7
YW
492 r = radv_set_dns(link, uplink);
493 if (r < 0)
494 return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
c555a358 495
0943b3b7
YW
496 r = radv_set_domains(link, uplink);
497 if (r < 0)
498 return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
c555a358 499
0943b3b7 500 return 0;
c555a358
PF
501}
502
0943b3b7
YW
503int radv_update_mac(Link *link) {
504 bool restart;
c555a358
PF
505 int r;
506
0943b3b7 507 assert(link);
c555a358 508
0943b3b7
YW
509 if (!link->radv)
510 return 0;
fd3ef936 511
0943b3b7 512 restart = sd_radv_is_running(link->radv);
c555a358 513
0943b3b7
YW
514 r = sd_radv_stop(link->radv);
515 if (r < 0)
516 return r;
fd3ef936 517
0943b3b7
YW
518 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
519 if (r < 0)
520 return r;
c555a358 521
0943b3b7
YW
522 if (restart) {
523 r = sd_radv_start(link->radv);
524 if (r < 0)
525 return r;
c555a358
PF
526 }
527
0943b3b7
YW
528 return 0;
529}
c555a358 530
0943b3b7
YW
531static int radv_is_ready_to_configure(Link *link) {
532 bool needs_uplink = false;
533 int r;
c555a358 534
0943b3b7
YW
535 assert(link);
536 assert(link->network);
349a981d 537
0943b3b7
YW
538 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
539 return false;
c555a358 540
0943b3b7
YW
541 if (in6_addr_is_null(&link->ipv6ll_address))
542 return false;
c555a358 543
0943b3b7
YW
544 if (link->network->router_emit_dns && !link->network->router_dns) {
545 _cleanup_free_ struct in6_addr *dns = NULL;
546 size_t n_dns;
c555a358 547
0943b3b7
YW
548 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
549 if (r < 0)
550 return r;
c555a358 551
0943b3b7
YW
552 needs_uplink = r == 0;
553 }
c555a358 554
0943b3b7
YW
555 if (link->network->router_emit_domains &&
556 !link->network->router_search_domains &&
557 !link->network->search_domains)
558 needs_uplink = true;
559
560 if (needs_uplink) {
561 Link *uplink = NULL;
c555a358 562
0943b3b7
YW
563 if (radv_find_uplink(link, &uplink) < 0)
564 return false;
c555a358 565
0943b3b7
YW
566 if (uplink && !uplink->network)
567 return false;
568 }
c555a358 569
0943b3b7
YW
570 return true;
571}
c555a358 572
0943b3b7
YW
573int request_process_radv(Request *req) {
574 Link *link;
575 int r;
349a981d 576
0943b3b7
YW
577 assert(req);
578 assert(req->link);
579 assert(req->type == REQUEST_TYPE_RADV);
c555a358 580
0943b3b7 581 link = req->link;
c555a358 582
0943b3b7
YW
583 r = radv_is_ready_to_configure(link);
584 if (r <= 0)
585 return r;
5e2a51d5 586
0943b3b7
YW
587 r = radv_configure(link);
588 if (r < 0)
589 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
c555a358 590
0943b3b7 591 if (link_has_carrier(link)) {
0f96a823 592 r = radv_start(link);
0943b3b7
YW
593 if (r < 0)
594 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
595 }
596
597 log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
598 link_has_carrier(link) ? " and started." : "");
599 return 1;
c555a358
PF
600}
601
0943b3b7
YW
602int link_request_radv(Link *link) {
603 int r;
63295b42 604
0943b3b7 605 assert(link);
63295b42 606
0943b3b7
YW
607 if (!link_radv_enabled(link))
608 return 0;
63295b42 609
0943b3b7 610 if (link->radv)
63295b42 611 return 0;
63295b42 612
0943b3b7
YW
613 r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL);
614 if (r < 0)
615 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
616
617 log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
63295b42
YW
618 return 0;
619}
620
0f96a823
YW
621int radv_start(Link *link) {
622 int r;
623
624 assert(link);
625 assert(link->network);
626
627 if (!link->radv)
628 return 0;
629
630 if (!link_has_carrier(link))
631 return 0;
632
633 if (in6_addr_is_null(&link->ipv6ll_address))
634 return 0;
635
636 if (sd_radv_is_running(link->radv))
637 return 0;
638
639 if (link->network->dhcp6_pd_announce) {
640 r = dhcp6_request_prefix_delegation(link);
641 if (r < 0)
642 return log_link_debug_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m");
643 }
644
645 log_link_debug(link, "Starting IPv6 Router Advertisements");
646 return sd_radv_start(link->radv);
647}
648
0943b3b7
YW
649int radv_add_prefix(
650 Link *link,
651 const struct in6_addr *prefix,
652 uint8_t prefix_len,
16bc8635
YW
653 usec_t lifetime_preferred_usec,
654 usec_t lifetime_valid_usec) {
0943b3b7
YW
655
656 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
95081e08 657 int r;
091214b6
PF
658
659 assert(link);
091214b6 660
0943b3b7
YW
661 if (!link->radv)
662 return 0;
bc9e40c9 663
0943b3b7 664 r = sd_radv_prefix_new(&p);
091214b6
PF
665 if (r < 0)
666 return r;
667
0943b3b7 668 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
091214b6
PF
669 if (r < 0)
670 return r;
671
c9e2c2da 672 r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
091214b6
PF
673 if (r < 0)
674 return r;
675
c9e2c2da 676 r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
091214b6
PF
677 if (r < 0)
678 return r;
679
95e104e0 680 r = sd_radv_add_prefix(link->radv, p);
0943b3b7 681 if (r < 0 && r != -EEXIST)
091214b6
PF
682 return r;
683
0943b3b7
YW
684 return 0;
685}
091214b6 686
2110040b
YW
687static int prefix_section_verify(Prefix *p) {
688 assert(p);
689
690 if (section_is_invalid(p->section))
691 return -EINVAL;
692
693 if (in6_addr_is_null(&p->prefix))
694 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
695 "%s: [IPv6Prefix] section without Prefix= field configured, "
696 "or specified prefix is the null address. "
697 "Ignoring [IPv6Prefix] section from line %u.",
698 p->section->filename, p->section->line);
699
700 if (p->prefixlen < 3 || p->prefixlen > 128)
701 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
702 "%s: Invalid prefix length %u is specified in [IPv6Prefix] section. "
703 "Valid range is 3…128. Ignoring [IPv6Prefix] section from line %u.",
704 p->section->filename, p->prefixlen, p->section->line);
705
706 if (p->prefixlen > 64) {
707 _cleanup_free_ char *str = NULL;
708
709 if (p->assign)
710 (void) in6_addr_prefix_to_string(&p->prefix, p->prefixlen, &str);
711
712 log_info("%s: Unusual prefix length %u (> 64) is specified in [IPv6Prefix] section from line %u%s%s.",
713 p->section->filename, p->prefixlen, p->section->line,
714 p->assign ? ", refusing to assign an address in " : "",
715 p->assign ? strna(str) : "");
716
717 p->assign = false;
718 }
719
720 if (p->valid_lifetime == 0)
721 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
722 "%s: The valid lifetime of prefix cannot be zero. "
723 "Ignoring [IPv6Prefix] section from line %u.",
724 p->section->filename, p->section->line);
725
726 if (p->preferred_lifetime > p->valid_lifetime)
727 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
728 "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
729 "Ignoring [IPv6Prefix] section from line %u.",
730 p->section->filename,
731 FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
732 FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
733 p->section->line);
734
735 return 0;
736}
737
0943b3b7 738void network_drop_invalid_prefixes(Network *network) {
2110040b 739 Prefix *p;
2075e596 740
0943b3b7
YW
741 assert(network);
742
2110040b
YW
743 HASHMAP_FOREACH(p, network->prefixes_by_section)
744 if (prefix_section_verify(p) < 0)
745 prefix_free(p);
746}
747
748static int route_prefix_section_verify(RoutePrefix *p) {
749 if (section_is_invalid(p->section))
750 return -EINVAL;
751
752 if (p->prefixlen > 128)
753 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
754 "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
755 "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
756 p->section->filename, p->prefixlen, p->section->line);
757
758 if (p->lifetime == 0)
759 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
760 "%s: The lifetime of route cannot be zero. "
761 "Ignoring [IPv6RoutePrefix] section from line %u.",
762 p->section->filename, p->section->line);
763
764 return 0;
0943b3b7
YW
765}
766
767void network_drop_invalid_route_prefixes(Network *network) {
2110040b 768 RoutePrefix *p;
0943b3b7
YW
769
770 assert(network);
771
2110040b
YW
772 HASHMAP_FOREACH(p, network->route_prefixes_by_section)
773 if (route_prefix_section_verify(p) < 0)
774 route_prefix_free(p);
0943b3b7
YW
775}
776
777int config_parse_prefix(
778 const char *unit,
779 const char *filename,
780 unsigned line,
781 const char *section,
782 unsigned section_line,
783 const char *lvalue,
784 int ltype,
785 const char *rvalue,
786 void *data,
787 void *userdata) {
788
0943b3b7 789 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679
YW
790 Network *network = userdata;
791 union in_addr_union a;
0943b3b7
YW
792 int r;
793
794 assert(filename);
795 assert(section);
796 assert(lvalue);
797 assert(rvalue);
2ac41679 798 assert(userdata);
0943b3b7
YW
799
800 r = prefix_new_static(network, filename, section_line, &p);
091214b6 801 if (r < 0)
0943b3b7 802 return log_oom();
091214b6 803
2ac41679 804 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 805 if (r < 0) {
2ac41679
YW
806 log_syntax(unit, LOG_WARNING, filename, line, r,
807 "Prefix is invalid, ignoring assignment: %s", rvalue);
0943b3b7 808 return 0;
8a08bbfc 809 }
cf72568a
YW
810
811 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 812 p->prefix = a.in6;
203d4df5 813
2ac41679 814 TAKE_PTR(p);
0943b3b7
YW
815 return 0;
816}
a254fab2 817
2ac41679 818int config_parse_prefix_boolean(
0943b3b7
YW
819 const char *unit,
820 const char *filename,
821 unsigned line,
822 const char *section,
823 unsigned section_line,
824 const char *lvalue,
825 int ltype,
826 const char *rvalue,
827 void *data,
828 void *userdata) {
a254fab2 829
0943b3b7 830 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 831 Network *network = userdata;
0943b3b7
YW
832 int r;
833
834 assert(filename);
835 assert(section);
836 assert(lvalue);
837 assert(rvalue);
2ac41679 838 assert(userdata);
0943b3b7
YW
839
840 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 841 if (r < 0)
0943b3b7
YW
842 return log_oom();
843
844 r = parse_boolean(rvalue);
845 if (r < 0) {
2ac41679
YW
846 log_syntax(unit, LOG_WARNING, filename, line, r,
847 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
0943b3b7
YW
848 return 0;
849 }
850
851 if (streq(lvalue, "OnLink"))
2ac41679 852 p->onlink = r;
0943b3b7 853 else if (streq(lvalue, "AddressAutoconfiguration"))
2ac41679
YW
854 p->address_auto_configuration = r;
855 else if (streq(lvalue, "Assign"))
856 p->assign = r;
857 else
858 assert_not_reached();
a254fab2 859
2ac41679 860 TAKE_PTR(p);
fd3ef936 861 return 0;
091214b6 862}
ca5ad760 863
0943b3b7
YW
864int config_parse_prefix_lifetime(
865 const char *unit,
866 const char *filename,
867 unsigned line,
868 const char *section,
869 unsigned section_line,
870 const char *lvalue,
871 int ltype,
872 const char *rvalue,
873 void *data,
874 void *userdata) {
875
0943b3b7 876 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 877 Network *network = userdata;
0943b3b7 878 usec_t usec;
be9363cc
YW
879 int r;
880
0943b3b7
YW
881 assert(filename);
882 assert(section);
883 assert(lvalue);
884 assert(rvalue);
2ac41679 885 assert(userdata);
be9363cc 886
0943b3b7 887 r = prefix_new_static(network, filename, section_line, &p);
a391901e 888 if (r < 0)
0943b3b7 889 return log_oom();
be9363cc 890
0943b3b7
YW
891 r = parse_sec(rvalue, &usec);
892 if (r < 0) {
2ac41679
YW
893 log_syntax(unit, LOG_WARNING, filename, line, r,
894 "Lifetime is invalid, ignoring assignment: %s", rvalue);
0943b3b7
YW
895 return 0;
896 }
be9363cc 897
2ac41679
YW
898 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
899 log_syntax(unit, LOG_WARNING, filename, line, 0,
900 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7 901 return 0;
be9363cc
YW
902 }
903
2ac41679
YW
904 if (streq(lvalue, "PreferredLifetimeSec"))
905 p->preferred_lifetime = usec;
906 else if (streq(lvalue, "ValidLifetimeSec"))
907 p->valid_lifetime = usec;
908 else
909 assert_not_reached();
63295b42 910
2ac41679 911 TAKE_PTR(p);
0943b3b7 912 return 0;
a254fab2
YW
913}
914
0943b3b7
YW
915int config_parse_prefix_metric(
916 const char *unit,
917 const char *filename,
918 unsigned line,
919 const char *section,
920 unsigned section_line,
921 const char *lvalue,
922 int ltype,
923 const char *rvalue,
924 void *data,
925 void *userdata) {
a254fab2 926
0943b3b7 927 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 928 Network *network = userdata;
0943b3b7 929 int r;
a254fab2 930
0943b3b7
YW
931 assert(filename);
932 assert(section);
933 assert(lvalue);
934 assert(rvalue);
2ac41679 935 assert(userdata);
a254fab2 936
0943b3b7 937 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 938 if (r < 0)
0943b3b7 939 return log_oom();
a254fab2 940
0943b3b7
YW
941 r = safe_atou32(rvalue, &p->route_metric);
942 if (r < 0) {
943 log_syntax(unit, LOG_WARNING, filename, line, r,
944 "Failed to parse %s=, ignoring assignment: %s",
945 lvalue, rvalue);
946 return 0;
a254fab2
YW
947 }
948
0943b3b7 949 TAKE_PTR(p);
0943b3b7 950 return 0;
a254fab2
YW
951}
952
e609cd06
YW
953int config_parse_prefix_token(
954 const char *unit,
955 const char *filename,
956 unsigned line,
957 const char *section,
958 unsigned section_line,
959 const char *lvalue,
960 int ltype,
961 const char *rvalue,
962 void *data,
963 void *userdata) {
964
965 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
966 Network *network = userdata;
967 int r;
968
969 assert(filename);
970 assert(section);
971 assert(lvalue);
972 assert(rvalue);
973 assert(userdata);
974
975 r = prefix_new_static(network, filename, section_line, &p);
976 if (r < 0)
977 return log_oom();
978
979 r = config_parse_address_generation_type(unit, filename, line, section, section_line,
980 lvalue, ltype, rvalue, &p->tokens, userdata);
981 if (r < 0)
982 return r;
983
984 TAKE_PTR(p);
985 return 0;
986}
987
0943b3b7
YW
988int config_parse_route_prefix(
989 const char *unit,
990 const char *filename,
991 unsigned line,
992 const char *section,
993 unsigned section_line,
994 const char *lvalue,
995 int ltype,
996 const char *rvalue,
997 void *data,
998 void *userdata) {
999
0943b3b7 1000 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
2ac41679
YW
1001 Network *network = userdata;
1002 union in_addr_union a;
a254fab2
YW
1003 int r;
1004
0943b3b7
YW
1005 assert(filename);
1006 assert(section);
1007 assert(lvalue);
1008 assert(rvalue);
2ac41679 1009 assert(userdata);
a254fab2 1010
0943b3b7
YW
1011 r = route_prefix_new_static(network, filename, section_line, &p);
1012 if (r < 0)
1013 return log_oom();
1014
2ac41679 1015 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 1016 if (r < 0) {
2ac41679
YW
1017 log_syntax(unit, LOG_WARNING, filename, line, r,
1018 "Route prefix is invalid, ignoring assignment: %s", rvalue);
a254fab2 1019 return 0;
0943b3b7 1020 }
cf72568a
YW
1021
1022 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 1023 p->prefix = a.in6;
a254fab2 1024
2ac41679 1025 TAKE_PTR(p);
a254fab2
YW
1026 return 0;
1027}
1028
0943b3b7
YW
1029int config_parse_route_prefix_lifetime(
1030 const char *unit,
1031 const char *filename,
1032 unsigned line,
1033 const char *section,
1034 unsigned section_line,
1035 const char *lvalue,
1036 int ltype,
1037 const char *rvalue,
1038 void *data,
1039 void *userdata) {
a8d4a210 1040
0943b3b7 1041 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
2ac41679 1042 Network *network = userdata;
0943b3b7 1043 usec_t usec;
1d596fde
YW
1044 int r;
1045
0943b3b7
YW
1046 assert(filename);
1047 assert(section);
1048 assert(lvalue);
1049 assert(rvalue);
2ac41679 1050 assert(userdata);
1d596fde 1051
0943b3b7 1052 r = route_prefix_new_static(network, filename, section_line, &p);
1d596fde 1053 if (r < 0)
0943b3b7 1054 return log_oom();
1d596fde 1055
0943b3b7
YW
1056 r = parse_sec(rvalue, &usec);
1057 if (r < 0) {
1058 log_syntax(unit, LOG_WARNING, filename, line, r,
1059 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
1060 return 0;
1061 }
1d596fde 1062
2ac41679
YW
1063 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1064 log_syntax(unit, LOG_WARNING, filename, line, 0,
1065 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7
YW
1066 return 0;
1067 }
1d596fde 1068
2ac41679 1069 p->lifetime = usec;
1d596fde 1070
2ac41679 1071 TAKE_PTR(p);
1d596fde
YW
1072 return 0;
1073}
1074
ca5ad760
YW
1075int config_parse_radv_dns(
1076 const char *unit,
1077 const char *filename,
1078 unsigned line,
1079 const char *section,
1080 unsigned section_line,
1081 const char *lvalue,
1082 int ltype,
1083 const char *rvalue,
1084 void *data,
1085 void *userdata) {
1086
1087 Network *n = data;
ca5ad760
YW
1088 int r;
1089
1090 assert(filename);
1091 assert(lvalue);
1092 assert(rvalue);
1093
a3c1a949
YW
1094 if (isempty(rvalue)) {
1095 n->n_router_dns = 0;
1096 n->router_dns = mfree(n->router_dns);
1097 return 0;
1098 }
1099
d96edb2c 1100 for (const char *p = rvalue;;) {
ca5ad760
YW
1101 _cleanup_free_ char *w = NULL;
1102 union in_addr_union a;
1103
1104 r = extract_first_word(&p, &w, NULL, 0);
1105 if (r == -ENOMEM)
1106 return log_oom();
1107 if (r < 0) {
d96edb2c 1108 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1109 "Failed to extract word, ignoring: %s", rvalue);
1110 return 0;
1111 }
1112 if (r == 0)
d96edb2c 1113 return 0;
ca5ad760 1114
fd3ef936
YW
1115 if (streq(w, "_link_local"))
1116 a = IN_ADDR_NULL;
1117 else {
1118 r = in_addr_from_string(AF_INET6, w, &a);
1119 if (r < 0) {
d96edb2c 1120 log_syntax(unit, LOG_WARNING, filename, line, r,
fd3ef936
YW
1121 "Failed to parse DNS server address, ignoring: %s", w);
1122 continue;
1123 }
ca5ad760 1124
fd3ef936 1125 if (in_addr_is_null(AF_INET6, &a)) {
d96edb2c 1126 log_syntax(unit, LOG_WARNING, filename, line, 0,
fd3ef936
YW
1127 "DNS server address is null, ignoring: %s", w);
1128 continue;
1129 }
1130 }
ca5ad760 1131
fd3ef936
YW
1132 struct in6_addr *m;
1133 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1134 if (!m)
1135 return log_oom();
ca5ad760 1136
fd3ef936
YW
1137 m[n->n_router_dns++] = a.in6;
1138 n->router_dns = m;
ca5ad760 1139 }
ca5ad760
YW
1140}
1141
1142int config_parse_radv_search_domains(
1143 const char *unit,
1144 const char *filename,
1145 unsigned line,
1146 const char *section,
1147 unsigned section_line,
1148 const char *lvalue,
1149 int ltype,
1150 const char *rvalue,
1151 void *data,
1152 void *userdata) {
1153
1154 Network *n = data;
ca5ad760
YW
1155 int r;
1156
1157 assert(filename);
1158 assert(lvalue);
1159 assert(rvalue);
1160
a3c1a949
YW
1161 if (isempty(rvalue)) {
1162 n->router_search_domains = ordered_set_free(n->router_search_domains);
1163 return 0;
1164 }
1165
d96edb2c 1166 for (const char *p = rvalue;;) {
ca5ad760
YW
1167 _cleanup_free_ char *w = NULL, *idna = NULL;
1168
1169 r = extract_first_word(&p, &w, NULL, 0);
1170 if (r == -ENOMEM)
1171 return log_oom();
1172 if (r < 0) {
d96edb2c 1173 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1174 "Failed to extract word, ignoring: %s", rvalue);
1175 return 0;
1176 }
1177 if (r == 0)
d96edb2c 1178 return 0;
ca5ad760
YW
1179
1180 r = dns_name_apply_idna(w, &idna);
1181 if (r < 0) {
d96edb2c 1182 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1183 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1184 continue;
1185 } else if (r == 0)
1186 /* transfer ownership to simplify subsequent operations */
1187 idna = TAKE_PTR(w);
1188
5e276772 1189 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
ca5ad760 1190 if (r < 0)
d96edb2c 1191 return log_oom();
ca5ad760
YW
1192
1193 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1194 if (r < 0)
d96edb2c 1195 return log_oom();
ca5ad760 1196 }
ca5ad760
YW
1197}
1198
1199static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
a8d4a210 1200 [RADV_PREFIX_DELEGATION_NONE] = "no",
ca5ad760 1201 [RADV_PREFIX_DELEGATION_STATIC] = "static",
a8d4a210
YW
1202 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
1203 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
ca5ad760
YW
1204};
1205
1206DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
1207 radv_prefix_delegation,
1208 RADVPrefixDelegation,
1209 RADV_PREFIX_DELEGATION_BOTH);
1210
27ff0490
YW
1211int config_parse_router_prefix_delegation(
1212 const char *unit,
1213 const char *filename,
1214 unsigned line,
1215 const char *section,
1216 unsigned section_line,
1217 const char *lvalue,
1218 int ltype,
1219 const char *rvalue,
1220 void *data,
1221 void *userdata) {
1222
1223 RADVPrefixDelegation val, *ra = data;
1224 int r;
1225
1226 assert(filename);
1227 assert(lvalue);
1228 assert(rvalue);
1229 assert(data);
1230
1231 if (streq(lvalue, "IPv6SendRA")) {
1232 r = parse_boolean(rvalue);
1233 if (r < 0) {
1234 log_syntax(unit, LOG_WARNING, filename, line, r,
1235 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1236 return 0;
1237 }
1238
1239 /* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
1240 * need to explicitly enable DHCPv6PrefixDelegation=. */
1241 *ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
1242 return 0;
1243 }
1244
1245 /* For backward compatibility */
1246 val = radv_prefix_delegation_from_string(rvalue);
1247 if (val < 0) {
b98680b2 1248 log_syntax(unit, LOG_WARNING, filename, line, val,
27ff0490
YW
1249 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1250 return 0;
1251 }
1252
1253 *ra = val;
1254 return 0;
1255}
a8d4a210 1256
4f1ac4a3
YW
1257int config_parse_router_lifetime(
1258 const char *unit,
1259 const char *filename,
1260 unsigned line,
1261 const char *section,
1262 unsigned section_line,
1263 const char *lvalue,
1264 int ltype,
1265 const char *rvalue,
1266 void *data,
1267 void *userdata) {
1268
1269 usec_t usec, *lifetime = data;
1270 int r;
1271
1272 assert(filename);
1273 assert(section);
1274 assert(lvalue);
1275 assert(rvalue);
1276 assert(data);
1277
1278 if (isempty(rvalue)) {
1279 *lifetime = RADV_DEFAULT_ROUTER_LIFETIME_USEC;
1280 return 0;
1281 }
1282
1283 r = parse_sec(rvalue, &usec);
1284 if (r < 0) {
1285 log_syntax(unit, LOG_WARNING, filename, line, r,
1286 "Failed to parse router lifetime, ignoring assignment: %s", rvalue);
1287 return 0;
1288 }
1289 if (usec > 0) {
1290 if (usec < RADV_MIN_ROUTER_LIFETIME_USEC) {
1291 log_syntax(unit, LOG_WARNING, filename, line, 0,
1292 "Router lifetime %s is too short, using %s.",
1293 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1294 FORMAT_TIMESPAN(RADV_MIN_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1295 usec = RADV_MIN_ROUTER_LIFETIME_USEC;
1296 } else if (usec > RADV_MAX_ROUTER_LIFETIME_USEC) {
1297 log_syntax(unit, LOG_WARNING, filename, line, 0,
1298 "Router lifetime %s is too large, using %s.",
1299 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1300 FORMAT_TIMESPAN(RADV_MAX_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1301 usec = RADV_MAX_ROUTER_LIFETIME_USEC;
1302 }
1303 }
1304
1305 *lifetime = usec;
1306 return 0;
1307}
1308
a8d4a210
YW
1309int config_parse_router_preference(
1310 const char *unit,
1311 const char *filename,
1312 unsigned line,
1313 const char *section,
1314 unsigned section_line,
1315 const char *lvalue,
1316 int ltype,
1317 const char *rvalue,
1318 void *data,
1319 void *userdata) {
1320
ca5ad760
YW
1321 Network *network = userdata;
1322
1323 assert(filename);
1324 assert(section);
1325 assert(lvalue);
1326 assert(rvalue);
1327 assert(data);
1328
1329 if (streq(rvalue, "high"))
1330 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
1331 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
1332 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
1333 else if (streq(rvalue, "low"))
1334 network->router_preference = SD_NDISC_PREFERENCE_LOW;
1335 else
d96edb2c
YW
1336 log_syntax(unit, LOG_WARNING, filename, line, 0,
1337 "Invalid router preference, ignoring assignment: %s", rvalue);
ca5ad760
YW
1338
1339 return 0;
1340}