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