]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-radv.c
network: make network_get_ipv6_dns() always set return value on success
[thirdparty/systemd.git] / src / network / networkd-radv.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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-link.h"
11 #include "networkd-manager.h"
12 #include "networkd-network.h"
13 #include "networkd-radv.h"
14 #include "parse-util.h"
15 #include "string-util.h"
16 #include "string-table.h"
17 #include "strv.h"
18
19 Prefix *prefix_free(Prefix *prefix) {
20 if (!prefix)
21 return NULL;
22
23 if (prefix->network) {
24 assert(prefix->section);
25 hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
26 }
27
28 network_config_section_free(prefix->section);
29 sd_radv_prefix_unref(prefix->radv_prefix);
30
31 return mfree(prefix);
32 }
33
34 DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
35
36 static int prefix_new(Prefix **ret) {
37 _cleanup_(prefix_freep) Prefix *prefix = NULL;
38
39 prefix = new0(Prefix, 1);
40 if (!prefix)
41 return -ENOMEM;
42
43 if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
44 return -ENOMEM;
45
46 *ret = TAKE_PTR(prefix);
47
48 return 0;
49 }
50
51 static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
52 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
53 _cleanup_(prefix_freep) Prefix *prefix = NULL;
54 int r;
55
56 assert(network);
57 assert(ret);
58 assert(filename);
59 assert(section_line > 0);
60
61 r = network_config_section_new(filename, section_line, &n);
62 if (r < 0)
63 return r;
64
65 prefix = hashmap_get(network->prefixes_by_section, n);
66 if (prefix) {
67 *ret = TAKE_PTR(prefix);
68 return 0;
69 }
70
71 r = prefix_new(&prefix);
72 if (r < 0)
73 return r;
74
75 prefix->network = network;
76 prefix->section = TAKE_PTR(n);
77
78 r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops);
79 if (r < 0)
80 return r;
81
82 r = hashmap_put(network->prefixes_by_section, prefix->section, prefix);
83 if (r < 0)
84 return r;
85
86 *ret = TAKE_PTR(prefix);
87
88 return 0;
89 }
90
91 RoutePrefix *route_prefix_free(RoutePrefix *prefix) {
92 if (!prefix)
93 return NULL;
94
95 if (prefix->network) {
96 assert(prefix->section);
97 hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
98 }
99
100 network_config_section_free(prefix->section);
101 sd_radv_route_prefix_unref(prefix->radv_route_prefix);
102
103 return mfree(prefix);
104 }
105
106 DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
107
108 static int route_prefix_new(RoutePrefix **ret) {
109 _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
110
111 prefix = new0(RoutePrefix, 1);
112 if (!prefix)
113 return -ENOMEM;
114
115 if (sd_radv_route_prefix_new(&prefix->radv_route_prefix) < 0)
116 return -ENOMEM;
117
118 *ret = TAKE_PTR(prefix);
119
120 return 0;
121 }
122
123 static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
124 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
125 _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
126 int r;
127
128 assert(network);
129 assert(ret);
130 assert(filename);
131 assert(section_line > 0);
132
133 r = network_config_section_new(filename, section_line, &n);
134 if (r < 0)
135 return r;
136
137 prefix = hashmap_get(network->route_prefixes_by_section, n);
138 if (prefix) {
139 *ret = TAKE_PTR(prefix);
140 return 0;
141 }
142
143 r = route_prefix_new(&prefix);
144 if (r < 0)
145 return r;
146
147 prefix->network = network;
148 prefix->section = TAKE_PTR(n);
149
150 r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops);
151 if (r < 0)
152 return r;
153
154 r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix);
155 if (r < 0)
156 return r;
157
158 *ret = TAKE_PTR(prefix);
159
160 return 0;
161 }
162
163 void network_verify_prefixes(Network *network) {
164 Prefix *prefix;
165
166 assert(network);
167
168 HASHMAP_FOREACH(prefix, network->prefixes_by_section)
169 if (section_is_invalid(prefix->section))
170 prefix_free(prefix);
171 }
172
173 void network_verify_route_prefixes(Network *network) {
174 RoutePrefix *prefix;
175
176 assert(network);
177
178 HASHMAP_FOREACH(prefix, network->route_prefixes_by_section)
179 if (section_is_invalid(prefix->section))
180 route_prefix_free(prefix);
181 }
182
183 int config_parse_prefix(
184 const char *unit,
185 const char *filename,
186 unsigned line,
187 const char *section,
188 unsigned section_line,
189 const char *lvalue,
190 int ltype,
191 const char *rvalue,
192 void *data,
193 void *userdata) {
194
195 Network *network = userdata;
196 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
197 uint8_t prefixlen = 64;
198 union in_addr_union in6addr;
199 int r;
200
201 assert(filename);
202 assert(section);
203 assert(lvalue);
204 assert(rvalue);
205 assert(data);
206
207 r = prefix_new_static(network, filename, section_line, &p);
208 if (r < 0)
209 return log_oom();
210
211 r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
212 if (r < 0) {
213 log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
214 return 0;
215 }
216
217 r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen);
218 if (r < 0) {
219 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue);
220 return 0;
221 }
222
223 p = NULL;
224
225 return 0;
226 }
227
228 int config_parse_prefix_flags(
229 const char *unit,
230 const char *filename,
231 unsigned line,
232 const char *section,
233 unsigned section_line,
234 const char *lvalue,
235 int ltype,
236 const char *rvalue,
237 void *data,
238 void *userdata) {
239
240 Network *network = userdata;
241 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
242 int r;
243
244 assert(filename);
245 assert(section);
246 assert(lvalue);
247 assert(rvalue);
248 assert(data);
249
250 r = prefix_new_static(network, filename, section_line, &p);
251 if (r < 0)
252 return log_oom();
253
254 r = parse_boolean(rvalue);
255 if (r < 0) {
256 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
257 return 0;
258 }
259
260 if (streq(lvalue, "OnLink"))
261 r = sd_radv_prefix_set_onlink(p->radv_prefix, r);
262 else if (streq(lvalue, "AddressAutoconfiguration"))
263 r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r);
264 if (r < 0) {
265 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
266 return 0;
267 }
268
269 p = NULL;
270
271 return 0;
272 }
273
274 int config_parse_prefix_lifetime(
275 const char *unit,
276 const char *filename,
277 unsigned line,
278 const char *section,
279 unsigned section_line,
280 const char *lvalue,
281 int ltype,
282 const char *rvalue,
283 void *data,
284 void *userdata) {
285
286 Network *network = userdata;
287 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
288 usec_t usec;
289 int r;
290
291 assert(filename);
292 assert(section);
293 assert(lvalue);
294 assert(rvalue);
295 assert(data);
296
297 r = prefix_new_static(network, filename, section_line, &p);
298 if (r < 0)
299 return log_oom();
300
301 r = parse_sec(rvalue, &usec);
302 if (r < 0) {
303 log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
304 return 0;
305 }
306
307 /* a value of 0xffffffff represents infinity */
308 if (streq(lvalue, "PreferredLifetimeSec"))
309 r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
310 DIV_ROUND_UP(usec, USEC_PER_SEC));
311 else if (streq(lvalue, "ValidLifetimeSec"))
312 r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
313 DIV_ROUND_UP(usec, USEC_PER_SEC));
314 if (r < 0) {
315 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
316 return 0;
317 }
318
319 p = NULL;
320
321 return 0;
322 }
323
324 int config_parse_prefix_assign(
325 const char *unit,
326 const char *filename,
327 unsigned line,
328 const char *section,
329 unsigned section_line,
330 const char *lvalue,
331 int ltype,
332 const char *rvalue,
333 void *data,
334 void *userdata) {
335
336 Network *network = userdata;
337 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
338 int r;
339
340 assert(filename);
341 assert(section);
342 assert(lvalue);
343 assert(rvalue);
344 assert(data);
345
346 r = prefix_new_static(network, filename, section_line, &p);
347 if (r < 0)
348 return log_oom();
349
350 r = parse_boolean(rvalue);
351 if (r < 0) {
352 log_syntax(unit, LOG_WARNING, filename, line, r,
353 "Failed to parse %s=, ignoring assignment: %s",
354 lvalue, rvalue);
355 return 0;
356 }
357
358 p->assign = r;
359 p = NULL;
360
361 return 0;
362 }
363
364 int config_parse_route_prefix(
365 const char *unit,
366 const char *filename,
367 unsigned line,
368 const char *section,
369 unsigned section_line,
370 const char *lvalue,
371 int ltype,
372 const char *rvalue,
373 void *data,
374 void *userdata) {
375
376 Network *network = userdata;
377 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
378 uint8_t prefixlen = 64;
379 union in_addr_union in6addr;
380 int r;
381
382 assert(filename);
383 assert(section);
384 assert(lvalue);
385 assert(rvalue);
386 assert(data);
387
388 r = route_prefix_new_static(network, filename, section_line, &p);
389 if (r < 0)
390 return log_oom();
391
392 r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
393 if (r < 0) {
394 log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
395 return 0;
396 }
397
398 r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen);
399 if (r < 0) {
400 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m");
401 return 0;
402 }
403
404 p = NULL;
405
406 return 0;
407 }
408
409 int config_parse_route_prefix_lifetime(
410 const char *unit,
411 const char *filename,
412 unsigned line,
413 const char *section,
414 unsigned section_line,
415 const char *lvalue,
416 int ltype,
417 const char *rvalue,
418 void *data,
419 void *userdata) {
420
421 Network *network = userdata;
422 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
423 usec_t usec;
424 int r;
425
426 assert(filename);
427 assert(section);
428 assert(lvalue);
429 assert(rvalue);
430 assert(data);
431
432 r = route_prefix_new_static(network, filename, section_line, &p);
433 if (r < 0)
434 return log_oom();
435
436 r = parse_sec(rvalue, &usec);
437 if (r < 0) {
438 log_syntax(unit, LOG_WARNING, filename, line, r,
439 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
440 return 0;
441 }
442
443 /* a value of 0xffffffff represents infinity */
444 r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC));
445 if (r < 0) {
446 log_syntax(unit, LOG_WARNING, filename, line, r,
447 "Failed to set route lifetime, ignoring assignment: %m");
448 return 0;
449 }
450
451 p = NULL;
452
453 return 0;
454 }
455
456 static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
457 _cleanup_free_ struct in6_addr *addresses = NULL;
458 size_t n_addresses = 0, n_allocated = 0;
459
460 assert(network);
461 assert(ret_addresses);
462 assert(ret_size);
463
464 for (size_t i = 0; i < network->n_dns; i++) {
465 union in_addr_union *addr;
466
467 if (network->dns[i]->family != AF_INET6)
468 continue;
469
470 addr = &network->dns[i]->address;
471
472 if (in_addr_is_null(AF_INET6, addr) ||
473 in_addr_is_link_local(AF_INET6, addr) ||
474 in_addr_is_localhost(AF_INET6, addr))
475 continue;
476
477 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
478 return -ENOMEM;
479
480 addresses[n_addresses++] = addr->in6;
481 }
482
483 *ret_addresses = TAKE_PTR(addresses);
484 *ret_size = n_addresses;
485
486 return n_addresses;
487 }
488
489 static int radv_set_dns(Link *link, Link *uplink) {
490 _cleanup_free_ struct in6_addr *dns = NULL;
491 usec_t lifetime_usec;
492 size_t n_dns;
493 int r;
494
495 if (!link->network->router_emit_dns)
496 return 0;
497
498 if (link->network->router_dns) {
499 struct in6_addr *p;
500
501 dns = new(struct in6_addr, link->network->n_router_dns);
502 if (!dns)
503 return -ENOMEM;
504
505 p = dns;
506 for (size_t i = 0; i < link->network->n_router_dns; i++)
507 if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) {
508 if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address))
509 *(p++) = link->ipv6ll_address;
510 } else
511 *(p++) = link->network->router_dns[i];
512
513 n_dns = p - dns;
514 lifetime_usec = link->network->router_dns_lifetime_usec;
515
516 goto set_dns;
517 }
518
519 lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
520
521 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
522 if (r > 0)
523 goto set_dns;
524
525 if (uplink) {
526 if (!uplink->network) {
527 log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
528 return 0;
529 }
530
531 r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
532 if (r > 0)
533 goto set_dns;
534 }
535
536 return 0;
537
538 set_dns:
539 return sd_radv_set_rdnss(link->radv,
540 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
541 dns, n_dns);
542 }
543
544 static int radv_set_domains(Link *link, Link *uplink) {
545 OrderedSet *search_domains;
546 usec_t lifetime_usec;
547 _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
548
549 if (!link->network->router_emit_domains)
550 return 0;
551
552 search_domains = link->network->router_search_domains;
553 lifetime_usec = link->network->router_dns_lifetime_usec;
554
555 if (search_domains)
556 goto set_domains;
557
558 lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
559
560 search_domains = link->network->search_domains;
561 if (search_domains)
562 goto set_domains;
563
564 if (uplink) {
565 if (!uplink->network) {
566 log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
567 return 0;
568 }
569
570 search_domains = uplink->network->search_domains;
571 if (search_domains)
572 goto set_domains;
573 }
574
575 return 0;
576
577 set_domains:
578 s = ordered_set_get_strv(search_domains);
579 if (!s)
580 return log_oom();
581
582 return sd_radv_set_dnssl(link->radv,
583 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
584 s);
585
586 }
587
588 int radv_emit_dns(Link *link) {
589 Link *uplink;
590 int r;
591
592 uplink = manager_find_uplink(link->manager, link);
593
594 r = radv_set_dns(link, uplink);
595 if (r < 0)
596 log_link_warning_errno(link, r, "Could not set RA DNS: %m");
597
598 r = radv_set_domains(link, uplink);
599 if (r < 0)
600 log_link_warning_errno(link, r, "Could not set RA Domains: %m");
601
602 return 0;
603 }
604
605 int radv_configure(Link *link) {
606 int r;
607
608 assert(link);
609 assert(link->network);
610
611 r = sd_radv_new(&link->radv);
612 if (r < 0)
613 return r;
614
615 r = sd_radv_attach_event(link->radv, NULL, 0);
616 if (r < 0)
617 return r;
618
619 r = sd_radv_set_mac(link->radv, &link->mac);
620 if (r < 0)
621 return r;
622
623 r = sd_radv_set_ifindex(link->radv, link->ifindex);
624 if (r < 0)
625 return r;
626
627 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
628 if (r < 0)
629 return r;
630
631 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
632 if (r < 0)
633 return r;
634
635 /* a value of 0xffffffff represents infinity, 0x0 means this host is
636 not a router */
637 r = sd_radv_set_router_lifetime(link->radv,
638 DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC));
639 if (r < 0)
640 return r;
641
642 if (link->network->router_lifetime_usec > 0) {
643 r = sd_radv_set_preference(link->radv,
644 link->network->router_preference);
645 if (r < 0)
646 return r;
647 }
648
649 if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) {
650 RoutePrefix *q;
651 Prefix *p;
652
653 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
654 r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
655 if (r == -EEXIST)
656 continue;
657 if (r == -ENOEXEC) {
658 log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section.");
659 continue;
660 }
661 if (r < 0)
662 return r;
663 }
664
665 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
666 r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false);
667 if (r == -EEXIST)
668 continue;
669 if (r < 0)
670 return r;
671 }
672 }
673
674 return 0;
675 }
676
677 int radv_add_prefix(
678 Link *link,
679 const struct in6_addr *prefix,
680 uint8_t prefix_len,
681 uint32_t lifetime_preferred,
682 uint32_t lifetime_valid) {
683
684 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
685 int r;
686
687 assert(link);
688
689 if (!link->radv)
690 return 0;
691
692 r = sd_radv_prefix_new(&p);
693 if (r < 0)
694 return r;
695
696 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
697 if (r < 0)
698 return r;
699
700 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
701 if (r < 0)
702 return r;
703
704 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
705 if (r < 0)
706 return r;
707
708 r = sd_radv_add_prefix(link->radv, p, true);
709 if (r < 0 && r != -EEXIST)
710 return r;
711
712 return 0;
713 }
714
715 int config_parse_radv_dns(
716 const char *unit,
717 const char *filename,
718 unsigned line,
719 const char *section,
720 unsigned section_line,
721 const char *lvalue,
722 int ltype,
723 const char *rvalue,
724 void *data,
725 void *userdata) {
726
727 Network *n = data;
728 int r;
729
730 assert(filename);
731 assert(lvalue);
732 assert(rvalue);
733
734 for (const char *p = rvalue;;) {
735 _cleanup_free_ char *w = NULL;
736 union in_addr_union a;
737
738 r = extract_first_word(&p, &w, NULL, 0);
739 if (r == -ENOMEM)
740 return log_oom();
741 if (r < 0) {
742 log_syntax(unit, LOG_WARNING, filename, line, r,
743 "Failed to extract word, ignoring: %s", rvalue);
744 return 0;
745 }
746 if (r == 0)
747 return 0;
748
749 if (streq(w, "_link_local"))
750 a = IN_ADDR_NULL;
751 else {
752 r = in_addr_from_string(AF_INET6, w, &a);
753 if (r < 0) {
754 log_syntax(unit, LOG_WARNING, filename, line, r,
755 "Failed to parse DNS server address, ignoring: %s", w);
756 continue;
757 }
758
759 if (in_addr_is_null(AF_INET6, &a)) {
760 log_syntax(unit, LOG_WARNING, filename, line, 0,
761 "DNS server address is null, ignoring: %s", w);
762 continue;
763 }
764 }
765
766 struct in6_addr *m;
767 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
768 if (!m)
769 return log_oom();
770
771 m[n->n_router_dns++] = a.in6;
772 n->router_dns = m;
773 }
774 }
775
776 int config_parse_radv_search_domains(
777 const char *unit,
778 const char *filename,
779 unsigned line,
780 const char *section,
781 unsigned section_line,
782 const char *lvalue,
783 int ltype,
784 const char *rvalue,
785 void *data,
786 void *userdata) {
787
788 Network *n = data;
789 int r;
790
791 assert(filename);
792 assert(lvalue);
793 assert(rvalue);
794
795 for (const char *p = rvalue;;) {
796 _cleanup_free_ char *w = NULL, *idna = NULL;
797
798 r = extract_first_word(&p, &w, NULL, 0);
799 if (r == -ENOMEM)
800 return log_oom();
801 if (r < 0) {
802 log_syntax(unit, LOG_WARNING, filename, line, r,
803 "Failed to extract word, ignoring: %s", rvalue);
804 return 0;
805 }
806 if (r == 0)
807 return 0;
808
809 r = dns_name_apply_idna(w, &idna);
810 if (r < 0) {
811 log_syntax(unit, LOG_WARNING, filename, line, r,
812 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
813 continue;
814 } else if (r == 0)
815 /* transfer ownership to simplify subsequent operations */
816 idna = TAKE_PTR(w);
817
818 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops);
819 if (r < 0)
820 return log_oom();
821
822 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
823 if (r < 0)
824 return log_oom();
825 }
826 }
827
828 static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
829 [RADV_PREFIX_DELEGATION_NONE] = "no",
830 [RADV_PREFIX_DELEGATION_STATIC] = "static",
831 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
832 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
833 };
834
835 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
836 radv_prefix_delegation,
837 RADVPrefixDelegation,
838 RADV_PREFIX_DELEGATION_BOTH);
839
840 DEFINE_CONFIG_PARSE_ENUM(
841 config_parse_router_prefix_delegation,
842 radv_prefix_delegation,
843 RADVPrefixDelegation,
844 "Invalid router prefix delegation");
845
846 int config_parse_router_preference(
847 const char *unit,
848 const char *filename,
849 unsigned line,
850 const char *section,
851 unsigned section_line,
852 const char *lvalue,
853 int ltype,
854 const char *rvalue,
855 void *data,
856 void *userdata) {
857
858 Network *network = userdata;
859
860 assert(filename);
861 assert(section);
862 assert(lvalue);
863 assert(rvalue);
864 assert(data);
865
866 if (streq(rvalue, "high"))
867 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
868 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
869 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
870 else if (streq(rvalue, "low"))
871 network->router_preference = SD_NDISC_PREFERENCE_LOW;
872 else
873 log_syntax(unit, LOG_WARNING, filename, line, 0,
874 "Invalid router preference, ignoring assignment: %s", rvalue);
875
876 return 0;
877 }