]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-radv.c
Merge pull request #28328 from yuwata/network-ndisc-limit-captive-portal
[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 return 0;
604 }
605
606 int radv_update_mac(Link *link) {
607 bool restart;
608 int r;
609
610 assert(link);
611
612 if (!link->radv)
613 return 0;
614
615 if (link->hw_addr.length != ETH_ALEN)
616 return 0;
617
618 restart = sd_radv_is_running(link->radv);
619
620 r = sd_radv_stop(link->radv);
621 if (r < 0)
622 return r;
623
624 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
625 if (r < 0)
626 return r;
627
628 if (restart) {
629 r = sd_radv_start(link->radv);
630 if (r < 0)
631 return r;
632 }
633
634 return 0;
635 }
636
637 static int radv_is_ready_to_configure(Link *link) {
638 bool needs_uplink = false;
639 int r;
640
641 assert(link);
642 assert(link->network);
643
644 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
645 return false;
646
647 if (in6_addr_is_null(&link->ipv6ll_address))
648 return false;
649
650 if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
651 return false;
652
653 if (link->network->router_emit_dns && !link->network->router_dns) {
654 _cleanup_free_ struct in6_addr *dns = NULL;
655 size_t n_dns;
656
657 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
658 if (r < 0)
659 return r;
660
661 needs_uplink = r == 0;
662 }
663
664 if (link->network->router_emit_domains &&
665 !link->network->router_search_domains &&
666 !link->network->search_domains)
667 needs_uplink = true;
668
669 if (needs_uplink) {
670 Link *uplink = NULL;
671
672 if (radv_find_uplink(link, &uplink) < 0)
673 return false;
674
675 if (uplink && !uplink->network)
676 return false;
677 }
678
679 return true;
680 }
681
682 static int radv_process_request(Request *req, Link *link, void *userdata) {
683 int r;
684
685 assert(link);
686
687 r = radv_is_ready_to_configure(link);
688 if (r <= 0)
689 return r;
690
691 r = radv_configure(link);
692 if (r < 0)
693 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
694
695 if (link_has_carrier(link)) {
696 r = radv_start(link);
697 if (r < 0)
698 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
699 }
700
701 log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
702 link_has_carrier(link) ? " and started" : "");
703 return 1;
704 }
705
706 int link_request_radv(Link *link) {
707 int r;
708
709 assert(link);
710
711 if (!link_radv_enabled(link))
712 return 0;
713
714 if (link->radv)
715 return 0;
716
717 r = link_queue_request(link, REQUEST_TYPE_RADV, radv_process_request, NULL);
718 if (r < 0)
719 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
720
721 log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
722 return 0;
723 }
724
725 int radv_start(Link *link) {
726 int r;
727
728 assert(link);
729 assert(link->network);
730
731 if (!link->radv)
732 return 0;
733
734 if (!link_has_carrier(link))
735 return 0;
736
737 if (in6_addr_is_null(&link->ipv6ll_address))
738 return 0;
739
740 if (sd_radv_is_running(link->radv))
741 return 0;
742
743 if (link->network->dhcp_pd_announce) {
744 r = dhcp_request_prefix_delegation(link);
745 if (r < 0)
746 return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m");
747 }
748
749 log_link_debug(link, "Starting IPv6 Router Advertisements");
750 return sd_radv_start(link->radv);
751 }
752
753 int radv_add_prefix(
754 Link *link,
755 const struct in6_addr *prefix,
756 uint8_t prefix_len,
757 usec_t lifetime_preferred_usec,
758 usec_t lifetime_valid_usec) {
759
760 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
761 int r;
762
763 assert(link);
764
765 if (!link->radv)
766 return 0;
767
768 r = sd_radv_prefix_new(&p);
769 if (r < 0)
770 return r;
771
772 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
773 if (r < 0)
774 return r;
775
776 r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
777 if (r < 0)
778 return r;
779
780 r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
781 if (r < 0)
782 return r;
783
784 r = sd_radv_add_prefix(link->radv, p);
785 if (r < 0 && r != -EEXIST)
786 return r;
787
788 return 0;
789 }
790
791 static int prefix_section_verify(Prefix *p) {
792 assert(p);
793
794 if (section_is_invalid(p->section))
795 return -EINVAL;
796
797 if (in6_addr_is_null(&p->prefix))
798 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
799 "%s: [IPv6Prefix] section without Prefix= field configured, "
800 "or specified prefix is the null address. "
801 "Ignoring [IPv6Prefix] section from line %u.",
802 p->section->filename, p->section->line);
803
804 if (p->prefixlen < 3 || p->prefixlen > 128)
805 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
806 "%s: Invalid prefix length %u is specified in [IPv6Prefix] section. "
807 "Valid range is 3…128. Ignoring [IPv6Prefix] section from line %u.",
808 p->section->filename, p->prefixlen, p->section->line);
809
810 if (p->prefixlen > 64) {
811 log_info("%s:%u: Unusual prefix length %u (> 64) is specified in [IPv6Prefix] section from line %s%s.",
812 p->section->filename, p->section->line,
813 p->prefixlen,
814 p->assign ? ", refusing to assign an address in " : "",
815 p->assign ? IN6_ADDR_PREFIX_TO_STRING(&p->prefix, p->prefixlen) : "");
816
817 p->assign = false;
818 }
819
820 if (p->valid_lifetime == 0)
821 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
822 "%s: The valid lifetime of prefix cannot be zero. "
823 "Ignoring [IPv6Prefix] section from line %u.",
824 p->section->filename, p->section->line);
825
826 if (p->preferred_lifetime > p->valid_lifetime)
827 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
828 "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
829 "Ignoring [IPv6Prefix] section from line %u.",
830 p->section->filename,
831 FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
832 FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
833 p->section->line);
834
835 return 0;
836 }
837
838 void network_drop_invalid_prefixes(Network *network) {
839 Prefix *p;
840
841 assert(network);
842
843 HASHMAP_FOREACH(p, network->prefixes_by_section)
844 if (prefix_section_verify(p) < 0)
845 prefix_free(p);
846 }
847
848 static int route_prefix_section_verify(RoutePrefix *p) {
849 if (section_is_invalid(p->section))
850 return -EINVAL;
851
852 if (p->prefixlen > 128)
853 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
854 "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
855 "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
856 p->section->filename, p->prefixlen, p->section->line);
857
858 if (p->lifetime == 0)
859 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
860 "%s: The lifetime of route cannot be zero. "
861 "Ignoring [IPv6RoutePrefix] section from line %u.",
862 p->section->filename, p->section->line);
863
864 return 0;
865 }
866
867 void network_drop_invalid_route_prefixes(Network *network) {
868 RoutePrefix *p;
869
870 assert(network);
871
872 HASHMAP_FOREACH(p, network->route_prefixes_by_section)
873 if (route_prefix_section_verify(p) < 0)
874 route_prefix_free(p);
875 }
876
877 void network_drop_invalid_pref64_prefixes(Network *network) {
878 pref64Prefix *p;
879
880 assert(network);
881
882 HASHMAP_FOREACH(p, network->pref64_prefixes_by_section)
883 if (section_is_invalid(p->section))
884 pref64_prefix_free(p);
885 }
886
887 int config_parse_prefix(
888 const char *unit,
889 const char *filename,
890 unsigned line,
891 const char *section,
892 unsigned section_line,
893 const char *lvalue,
894 int ltype,
895 const char *rvalue,
896 void *data,
897 void *userdata) {
898
899 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
900 Network *network = ASSERT_PTR(userdata);
901 union in_addr_union a;
902 int r;
903
904 assert(filename);
905 assert(section);
906 assert(lvalue);
907 assert(rvalue);
908
909 r = prefix_new_static(network, filename, section_line, &p);
910 if (r < 0)
911 return log_oom();
912
913 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
914 if (r < 0) {
915 log_syntax(unit, LOG_WARNING, filename, line, r,
916 "Prefix is invalid, ignoring assignment: %s", rvalue);
917 return 0;
918 }
919
920 (void) in6_addr_mask(&a.in6, p->prefixlen);
921 p->prefix = a.in6;
922
923 TAKE_PTR(p);
924 return 0;
925 }
926
927 int config_parse_prefix_boolean(
928 const char *unit,
929 const char *filename,
930 unsigned line,
931 const char *section,
932 unsigned section_line,
933 const char *lvalue,
934 int ltype,
935 const char *rvalue,
936 void *data,
937 void *userdata) {
938
939 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
940 Network *network = ASSERT_PTR(userdata);
941 int r;
942
943 assert(filename);
944 assert(section);
945 assert(lvalue);
946 assert(rvalue);
947
948 r = prefix_new_static(network, filename, section_line, &p);
949 if (r < 0)
950 return log_oom();
951
952 r = parse_boolean(rvalue);
953 if (r < 0) {
954 log_syntax(unit, LOG_WARNING, filename, line, r,
955 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
956 return 0;
957 }
958
959 if (streq(lvalue, "OnLink"))
960 p->onlink = r;
961 else if (streq(lvalue, "AddressAutoconfiguration"))
962 p->address_auto_configuration = r;
963 else if (streq(lvalue, "Assign"))
964 p->assign = r;
965 else
966 assert_not_reached();
967
968 TAKE_PTR(p);
969 return 0;
970 }
971
972 int config_parse_prefix_lifetime(
973 const char *unit,
974 const char *filename,
975 unsigned line,
976 const char *section,
977 unsigned section_line,
978 const char *lvalue,
979 int ltype,
980 const char *rvalue,
981 void *data,
982 void *userdata) {
983
984 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
985 Network *network = ASSERT_PTR(userdata);
986 usec_t usec;
987 int r;
988
989 assert(filename);
990 assert(section);
991 assert(lvalue);
992 assert(rvalue);
993
994 r = prefix_new_static(network, filename, section_line, &p);
995 if (r < 0)
996 return log_oom();
997
998 r = parse_sec(rvalue, &usec);
999 if (r < 0) {
1000 log_syntax(unit, LOG_WARNING, filename, line, r,
1001 "Lifetime is invalid, ignoring assignment: %s", rvalue);
1002 return 0;
1003 }
1004
1005 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1006 log_syntax(unit, LOG_WARNING, filename, line, 0,
1007 "Lifetime is too long, ignoring assignment: %s", rvalue);
1008 return 0;
1009 }
1010
1011 if (streq(lvalue, "PreferredLifetimeSec"))
1012 p->preferred_lifetime = usec;
1013 else if (streq(lvalue, "ValidLifetimeSec"))
1014 p->valid_lifetime = usec;
1015 else
1016 assert_not_reached();
1017
1018 TAKE_PTR(p);
1019 return 0;
1020 }
1021
1022 int config_parse_prefix_metric(
1023 const char *unit,
1024 const char *filename,
1025 unsigned line,
1026 const char *section,
1027 unsigned section_line,
1028 const char *lvalue,
1029 int ltype,
1030 const char *rvalue,
1031 void *data,
1032 void *userdata) {
1033
1034 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
1035 Network *network = ASSERT_PTR(userdata);
1036 int r;
1037
1038 assert(filename);
1039 assert(section);
1040 assert(lvalue);
1041 assert(rvalue);
1042
1043 r = prefix_new_static(network, filename, section_line, &p);
1044 if (r < 0)
1045 return log_oom();
1046
1047 r = safe_atou32(rvalue, &p->route_metric);
1048 if (r < 0) {
1049 log_syntax(unit, LOG_WARNING, filename, line, r,
1050 "Failed to parse %s=, ignoring assignment: %s",
1051 lvalue, rvalue);
1052 return 0;
1053 }
1054
1055 TAKE_PTR(p);
1056 return 0;
1057 }
1058
1059 int config_parse_prefix_token(
1060 const char *unit,
1061 const char *filename,
1062 unsigned line,
1063 const char *section,
1064 unsigned section_line,
1065 const char *lvalue,
1066 int ltype,
1067 const char *rvalue,
1068 void *data,
1069 void *userdata) {
1070
1071 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
1072 Network *network = ASSERT_PTR(userdata);
1073 int r;
1074
1075 assert(filename);
1076 assert(section);
1077 assert(lvalue);
1078 assert(rvalue);
1079
1080 r = prefix_new_static(network, filename, section_line, &p);
1081 if (r < 0)
1082 return log_oom();
1083
1084 r = config_parse_address_generation_type(unit, filename, line, section, section_line,
1085 lvalue, ltype, rvalue, &p->tokens, userdata);
1086 if (r < 0)
1087 return r;
1088
1089 TAKE_PTR(p);
1090 return 0;
1091 }
1092
1093 int config_parse_route_prefix(
1094 const char *unit,
1095 const char *filename,
1096 unsigned line,
1097 const char *section,
1098 unsigned section_line,
1099 const char *lvalue,
1100 int ltype,
1101 const char *rvalue,
1102 void *data,
1103 void *userdata) {
1104
1105 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
1106 Network *network = ASSERT_PTR(userdata);
1107 union in_addr_union a;
1108 int r;
1109
1110 assert(filename);
1111 assert(section);
1112 assert(lvalue);
1113 assert(rvalue);
1114
1115 r = route_prefix_new_static(network, filename, section_line, &p);
1116 if (r < 0)
1117 return log_oom();
1118
1119 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
1120 if (r < 0) {
1121 log_syntax(unit, LOG_WARNING, filename, line, r,
1122 "Route prefix is invalid, ignoring assignment: %s", rvalue);
1123 return 0;
1124 }
1125
1126 (void) in6_addr_mask(&a.in6, p->prefixlen);
1127 p->prefix = a.in6;
1128
1129 TAKE_PTR(p);
1130 return 0;
1131 }
1132
1133 int config_parse_route_prefix_lifetime(
1134 const char *unit,
1135 const char *filename,
1136 unsigned line,
1137 const char *section,
1138 unsigned section_line,
1139 const char *lvalue,
1140 int ltype,
1141 const char *rvalue,
1142 void *data,
1143 void *userdata) {
1144
1145 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
1146 Network *network = ASSERT_PTR(userdata);
1147 usec_t usec;
1148 int r;
1149
1150 assert(filename);
1151 assert(section);
1152 assert(lvalue);
1153 assert(rvalue);
1154
1155 r = route_prefix_new_static(network, filename, section_line, &p);
1156 if (r < 0)
1157 return log_oom();
1158
1159 r = parse_sec(rvalue, &usec);
1160 if (r < 0) {
1161 log_syntax(unit, LOG_WARNING, filename, line, r,
1162 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
1163 return 0;
1164 }
1165
1166 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1167 log_syntax(unit, LOG_WARNING, filename, line, 0,
1168 "Lifetime is too long, ignoring assignment: %s", rvalue);
1169 return 0;
1170 }
1171
1172 p->lifetime = usec;
1173
1174 TAKE_PTR(p);
1175 return 0;
1176 }
1177
1178 int config_parse_pref64_prefix(
1179 const char *unit,
1180 const char *filename,
1181 unsigned line,
1182 const char *section,
1183 unsigned section_line,
1184 const char *lvalue,
1185 int ltype,
1186 const char *rvalue,
1187 void *data,
1188 void *userdata) {
1189
1190 _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
1191 Network *network = ASSERT_PTR(userdata);
1192 union in_addr_union a;
1193 uint8_t prefixlen;
1194 int r;
1195
1196 assert(filename);
1197 assert(section);
1198 assert(lvalue);
1199 assert(rvalue);
1200
1201 r = pref64_prefix_new_static(network, filename, section_line, &p);
1202 if (r < 0)
1203 return log_oom();
1204
1205 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &prefixlen);
1206 if (r < 0) {
1207 log_syntax(unit, LOG_WARNING, filename, line, r,
1208 "PREF64 prefix is invalid, ignoring assignment: %s", rvalue);
1209 return 0;
1210 }
1211
1212 if (!IN_SET(prefixlen, 96, 64, 56, 48, 40, 32)) {
1213 log_syntax(unit, LOG_WARNING, filename, line, 0,
1214 "PREF64 prefixlen is invalid, ignoring assignment: %s", rvalue);
1215 return 0;
1216 }
1217
1218 (void) in6_addr_mask(&a.in6,prefixlen);
1219 p->prefix = a.in6;
1220 p->prefixlen = prefixlen;
1221
1222 TAKE_PTR(p);
1223 return 0;
1224 }
1225
1226 int config_parse_pref64_prefix_lifetime(
1227 const char *unit,
1228 const char *filename,
1229 unsigned line,
1230 const char *section,
1231 unsigned section_line,
1232 const char *lvalue,
1233 int ltype,
1234 const char *rvalue,
1235 void *data,
1236 void *userdata) {
1237
1238 _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
1239 Network *network = ASSERT_PTR(userdata);
1240 usec_t usec;
1241 int r;
1242
1243 assert(filename);
1244 assert(section);
1245 assert(lvalue);
1246 assert(rvalue);
1247
1248 r = pref64_prefix_new_static(network, filename, section_line, &p);
1249 if (r < 0)
1250 return log_oom();
1251
1252 r = parse_sec(rvalue, &usec);
1253 if (r < 0) {
1254 log_syntax(unit, LOG_WARNING, filename, line, r,
1255 "PREF64 lifetime is invalid, ignoring assignment: %s", rvalue);
1256 return 0;
1257 }
1258
1259 if (usec == USEC_INFINITY || DIV_ROUND_UP(usec, 8 * USEC_PER_SEC) >= UINT64_C(1) << 13) {
1260 log_syntax(unit, LOG_WARNING, filename, line, 0,
1261 "PREF64 lifetime is too long, ignoring assignment: %s", rvalue);
1262 return 0;
1263 }
1264
1265 p->lifetime = usec;
1266
1267 TAKE_PTR(p);
1268 return 0;
1269 }
1270
1271 int config_parse_radv_dns(
1272 const char *unit,
1273 const char *filename,
1274 unsigned line,
1275 const char *section,
1276 unsigned section_line,
1277 const char *lvalue,
1278 int ltype,
1279 const char *rvalue,
1280 void *data,
1281 void *userdata) {
1282
1283 Network *n = data;
1284 int r;
1285
1286 assert(filename);
1287 assert(lvalue);
1288 assert(rvalue);
1289
1290 if (isempty(rvalue)) {
1291 n->n_router_dns = 0;
1292 n->router_dns = mfree(n->router_dns);
1293 return 0;
1294 }
1295
1296 for (const char *p = rvalue;;) {
1297 _cleanup_free_ char *w = NULL;
1298 union in_addr_union a;
1299
1300 r = extract_first_word(&p, &w, NULL, 0);
1301 if (r == -ENOMEM)
1302 return log_oom();
1303 if (r < 0) {
1304 log_syntax(unit, LOG_WARNING, filename, line, r,
1305 "Failed to extract word, ignoring: %s", rvalue);
1306 return 0;
1307 }
1308 if (r == 0)
1309 return 0;
1310
1311 if (streq(w, "_link_local"))
1312 a = IN_ADDR_NULL;
1313 else {
1314 r = in_addr_from_string(AF_INET6, w, &a);
1315 if (r < 0) {
1316 log_syntax(unit, LOG_WARNING, filename, line, r,
1317 "Failed to parse DNS server address, ignoring: %s", w);
1318 continue;
1319 }
1320
1321 if (in_addr_is_null(AF_INET6, &a)) {
1322 log_syntax(unit, LOG_WARNING, filename, line, 0,
1323 "DNS server address is null, ignoring: %s", w);
1324 continue;
1325 }
1326 }
1327
1328 struct in6_addr *m;
1329 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1330 if (!m)
1331 return log_oom();
1332
1333 m[n->n_router_dns++] = a.in6;
1334 n->router_dns = m;
1335 }
1336 }
1337
1338 int config_parse_radv_search_domains(
1339 const char *unit,
1340 const char *filename,
1341 unsigned line,
1342 const char *section,
1343 unsigned section_line,
1344 const char *lvalue,
1345 int ltype,
1346 const char *rvalue,
1347 void *data,
1348 void *userdata) {
1349
1350 Network *n = data;
1351 int r;
1352
1353 assert(filename);
1354 assert(lvalue);
1355 assert(rvalue);
1356
1357 if (isempty(rvalue)) {
1358 n->router_search_domains = ordered_set_free(n->router_search_domains);
1359 return 0;
1360 }
1361
1362 for (const char *p = rvalue;;) {
1363 _cleanup_free_ char *w = NULL, *idna = NULL;
1364
1365 r = extract_first_word(&p, &w, NULL, 0);
1366 if (r == -ENOMEM)
1367 return log_oom();
1368 if (r < 0) {
1369 log_syntax(unit, LOG_WARNING, filename, line, r,
1370 "Failed to extract word, ignoring: %s", rvalue);
1371 return 0;
1372 }
1373 if (r == 0)
1374 return 0;
1375
1376 r = dns_name_apply_idna(w, &idna);
1377 if (r < 0) {
1378 log_syntax(unit, LOG_WARNING, filename, line, r,
1379 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1380 continue;
1381 } else if (r == 0)
1382 /* transfer ownership to simplify subsequent operations */
1383 idna = TAKE_PTR(w);
1384
1385 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
1386 if (r < 0)
1387 return log_oom();
1388
1389 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1390 if (r < 0)
1391 return log_oom();
1392 }
1393 }
1394
1395 static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
1396 [RADV_PREFIX_DELEGATION_NONE] = "no",
1397 [RADV_PREFIX_DELEGATION_STATIC] = "static",
1398 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
1399 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
1400 };
1401
1402 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
1403 radv_prefix_delegation,
1404 RADVPrefixDelegation,
1405 RADV_PREFIX_DELEGATION_BOTH);
1406
1407 int config_parse_router_prefix_delegation(
1408 const char *unit,
1409 const char *filename,
1410 unsigned line,
1411 const char *section,
1412 unsigned section_line,
1413 const char *lvalue,
1414 int ltype,
1415 const char *rvalue,
1416 void *data,
1417 void *userdata) {
1418
1419 RADVPrefixDelegation val, *ra = ASSERT_PTR(data);
1420 int r;
1421
1422 assert(filename);
1423 assert(lvalue);
1424 assert(rvalue);
1425
1426 if (streq(lvalue, "IPv6SendRA")) {
1427 r = parse_boolean(rvalue);
1428 if (r < 0) {
1429 log_syntax(unit, LOG_WARNING, filename, line, r,
1430 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1431 return 0;
1432 }
1433
1434 /* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
1435 * need to explicitly enable DHCPv6PrefixDelegation=. */
1436 *ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
1437 return 0;
1438 }
1439
1440 /* For backward compatibility */
1441 val = radv_prefix_delegation_from_string(rvalue);
1442 if (val < 0) {
1443 log_syntax(unit, LOG_WARNING, filename, line, val,
1444 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1445 return 0;
1446 }
1447
1448 *ra = val;
1449 return 0;
1450 }
1451
1452 int config_parse_router_lifetime(
1453 const char *unit,
1454 const char *filename,
1455 unsigned line,
1456 const char *section,
1457 unsigned section_line,
1458 const char *lvalue,
1459 int ltype,
1460 const char *rvalue,
1461 void *data,
1462 void *userdata) {
1463
1464 usec_t usec, *lifetime = ASSERT_PTR(data);
1465 int r;
1466
1467 assert(filename);
1468 assert(section);
1469 assert(lvalue);
1470 assert(rvalue);
1471
1472 if (isempty(rvalue)) {
1473 *lifetime = RADV_DEFAULT_ROUTER_LIFETIME_USEC;
1474 return 0;
1475 }
1476
1477 r = parse_sec(rvalue, &usec);
1478 if (r < 0) {
1479 log_syntax(unit, LOG_WARNING, filename, line, r,
1480 "Failed to parse router lifetime, ignoring assignment: %s", rvalue);
1481 return 0;
1482 }
1483 if (usec > 0) {
1484 if (usec < RADV_MIN_ROUTER_LIFETIME_USEC) {
1485 log_syntax(unit, LOG_WARNING, filename, line, 0,
1486 "Router lifetime %s is too short, using %s.",
1487 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1488 FORMAT_TIMESPAN(RADV_MIN_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1489 usec = RADV_MIN_ROUTER_LIFETIME_USEC;
1490 } else if (usec > RADV_MAX_ROUTER_LIFETIME_USEC) {
1491 log_syntax(unit, LOG_WARNING, filename, line, 0,
1492 "Router lifetime %s is too large, using %s.",
1493 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1494 FORMAT_TIMESPAN(RADV_MAX_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1495 usec = RADV_MAX_ROUTER_LIFETIME_USEC;
1496 }
1497 }
1498
1499 *lifetime = usec;
1500 return 0;
1501 }
1502
1503 int config_parse_router_retransmit(
1504 const char *unit,
1505 const char *filename,
1506 unsigned line,
1507 const char *section,
1508 unsigned section_line,
1509 const char *lvalue,
1510 int ltype,
1511 const char *rvalue,
1512 void *data,
1513 void *userdata) {
1514
1515 usec_t usec, *router_retransmit_usec = ASSERT_PTR(data);
1516 int r;
1517
1518 assert(filename);
1519 assert(section);
1520 assert(lvalue);
1521 assert(rvalue);
1522
1523 if (isempty(rvalue)) {
1524 *router_retransmit_usec = 0;
1525 return 0;
1526 }
1527
1528 r = parse_sec(rvalue, &usec);
1529 if (r < 0) {
1530 log_syntax(unit, LOG_WARNING, filename, line, r,
1531 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1532 return 0;
1533 }
1534
1535 if (usec != USEC_INFINITY &&
1536 DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
1537 log_syntax(unit, LOG_WARNING, filename, line, 0,
1538 "Invalid %s= must be in the range 0...%"PRIu32"Sec, ignoring: %s", lvalue, UINT32_MAX, rvalue);
1539 return 0;
1540 }
1541
1542 *router_retransmit_usec = usec;
1543 return 0;
1544 }
1545
1546 int config_parse_router_preference(
1547 const char *unit,
1548 const char *filename,
1549 unsigned line,
1550 const char *section,
1551 unsigned section_line,
1552 const char *lvalue,
1553 int ltype,
1554 const char *rvalue,
1555 void *data,
1556 void *userdata) {
1557
1558 Network *network = userdata;
1559
1560 assert(filename);
1561 assert(section);
1562 assert(lvalue);
1563 assert(rvalue);
1564 assert(data);
1565
1566 if (streq(rvalue, "high"))
1567 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
1568 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
1569 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
1570 else if (streq(rvalue, "low"))
1571 network->router_preference = SD_NDISC_PREFERENCE_LOW;
1572 else
1573 log_syntax(unit, LOG_WARNING, filename, line, 0,
1574 "Invalid router preference, ignoring assignment: %s", rvalue);
1575
1576 return 0;
1577 }