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