]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-radv.c
8949d91e2fcd9575fd74112237a53381e7132d65
[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_drop_invalid_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_drop_invalid_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 static bool link_radv_enabled(Link *link) {
606 assert(link);
607
608 if (!link_ipv6ll_enabled(link))
609 return false;
610
611 return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE;
612 }
613
614 int radv_configure(Link *link) {
615 int r;
616
617 assert(link);
618 assert(link->network);
619
620 if (!link_radv_enabled(link))
621 return 0;
622
623 r = sd_radv_new(&link->radv);
624 if (r < 0)
625 return r;
626
627 r = sd_radv_attach_event(link->radv, link->manager->event, 0);
628 if (r < 0)
629 return r;
630
631 r = sd_radv_set_mac(link->radv, &link->mac);
632 if (r < 0)
633 return r;
634
635 r = sd_radv_set_ifindex(link->radv, link->ifindex);
636 if (r < 0)
637 return r;
638
639 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
640 if (r < 0)
641 return r;
642
643 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
644 if (r < 0)
645 return r;
646
647 /* a value of 0xffffffff represents infinity, 0x0 means this host is
648 not a router */
649 r = sd_radv_set_router_lifetime(link->radv,
650 DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC));
651 if (r < 0)
652 return r;
653
654 if (link->network->router_lifetime_usec > 0) {
655 r = sd_radv_set_preference(link->radv,
656 link->network->router_preference);
657 if (r < 0)
658 return r;
659 }
660
661 if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) {
662 RoutePrefix *q;
663 Prefix *p;
664
665 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
666 r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
667 if (r == -EEXIST)
668 continue;
669 if (r == -ENOEXEC) {
670 log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section.");
671 continue;
672 }
673 if (r < 0)
674 return r;
675 }
676
677 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
678 r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false);
679 if (r == -EEXIST)
680 continue;
681 if (r < 0)
682 return r;
683 }
684 }
685
686 return 0;
687 }
688
689 int radv_update_mac(Link *link) {
690 bool restart;
691 int r;
692
693 assert(link);
694
695 if (!link->radv)
696 return 0;
697
698 restart = sd_radv_is_running(link->radv);
699
700 if (restart) {
701 r = sd_radv_stop(link->radv);
702 if (r < 0)
703 return r;
704 }
705
706 r = sd_radv_set_mac(link->radv, &link->mac);
707 if (r < 0)
708 return r;
709
710 if (restart) {
711 r = sd_radv_start(link->radv);
712 if (r < 0)
713 return r;
714 }
715
716 return 0;
717 }
718
719 int radv_add_prefix(
720 Link *link,
721 const struct in6_addr *prefix,
722 uint8_t prefix_len,
723 uint32_t lifetime_preferred,
724 uint32_t lifetime_valid) {
725
726 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
727 int r;
728
729 assert(link);
730
731 if (!link->radv)
732 return 0;
733
734 r = sd_radv_prefix_new(&p);
735 if (r < 0)
736 return r;
737
738 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
739 if (r < 0)
740 return r;
741
742 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
743 if (r < 0)
744 return r;
745
746 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
747 if (r < 0)
748 return r;
749
750 r = sd_radv_add_prefix(link->radv, p, true);
751 if (r < 0 && r != -EEXIST)
752 return r;
753
754 return 0;
755 }
756
757 int config_parse_radv_dns(
758 const char *unit,
759 const char *filename,
760 unsigned line,
761 const char *section,
762 unsigned section_line,
763 const char *lvalue,
764 int ltype,
765 const char *rvalue,
766 void *data,
767 void *userdata) {
768
769 Network *n = data;
770 int r;
771
772 assert(filename);
773 assert(lvalue);
774 assert(rvalue);
775
776 for (const char *p = rvalue;;) {
777 _cleanup_free_ char *w = NULL;
778 union in_addr_union a;
779
780 r = extract_first_word(&p, &w, NULL, 0);
781 if (r == -ENOMEM)
782 return log_oom();
783 if (r < 0) {
784 log_syntax(unit, LOG_WARNING, filename, line, r,
785 "Failed to extract word, ignoring: %s", rvalue);
786 return 0;
787 }
788 if (r == 0)
789 return 0;
790
791 if (streq(w, "_link_local"))
792 a = IN_ADDR_NULL;
793 else {
794 r = in_addr_from_string(AF_INET6, w, &a);
795 if (r < 0) {
796 log_syntax(unit, LOG_WARNING, filename, line, r,
797 "Failed to parse DNS server address, ignoring: %s", w);
798 continue;
799 }
800
801 if (in_addr_is_null(AF_INET6, &a)) {
802 log_syntax(unit, LOG_WARNING, filename, line, 0,
803 "DNS server address is null, ignoring: %s", w);
804 continue;
805 }
806 }
807
808 struct in6_addr *m;
809 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
810 if (!m)
811 return log_oom();
812
813 m[n->n_router_dns++] = a.in6;
814 n->router_dns = m;
815 }
816 }
817
818 int config_parse_radv_search_domains(
819 const char *unit,
820 const char *filename,
821 unsigned line,
822 const char *section,
823 unsigned section_line,
824 const char *lvalue,
825 int ltype,
826 const char *rvalue,
827 void *data,
828 void *userdata) {
829
830 Network *n = data;
831 int r;
832
833 assert(filename);
834 assert(lvalue);
835 assert(rvalue);
836
837 for (const char *p = rvalue;;) {
838 _cleanup_free_ char *w = NULL, *idna = NULL;
839
840 r = extract_first_word(&p, &w, NULL, 0);
841 if (r == -ENOMEM)
842 return log_oom();
843 if (r < 0) {
844 log_syntax(unit, LOG_WARNING, filename, line, r,
845 "Failed to extract word, ignoring: %s", rvalue);
846 return 0;
847 }
848 if (r == 0)
849 return 0;
850
851 r = dns_name_apply_idna(w, &idna);
852 if (r < 0) {
853 log_syntax(unit, LOG_WARNING, filename, line, r,
854 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
855 continue;
856 } else if (r == 0)
857 /* transfer ownership to simplify subsequent operations */
858 idna = TAKE_PTR(w);
859
860 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
861 if (r < 0)
862 return log_oom();
863
864 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
865 if (r < 0)
866 return log_oom();
867 }
868 }
869
870 static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
871 [RADV_PREFIX_DELEGATION_NONE] = "no",
872 [RADV_PREFIX_DELEGATION_STATIC] = "static",
873 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
874 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
875 };
876
877 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
878 radv_prefix_delegation,
879 RADVPrefixDelegation,
880 RADV_PREFIX_DELEGATION_BOTH);
881
882 DEFINE_CONFIG_PARSE_ENUM(
883 config_parse_router_prefix_delegation,
884 radv_prefix_delegation,
885 RADVPrefixDelegation,
886 "Invalid router prefix delegation");
887
888 int config_parse_router_preference(
889 const char *unit,
890 const char *filename,
891 unsigned line,
892 const char *section,
893 unsigned section_line,
894 const char *lvalue,
895 int ltype,
896 const char *rvalue,
897 void *data,
898 void *userdata) {
899
900 Network *network = userdata;
901
902 assert(filename);
903 assert(section);
904 assert(lvalue);
905 assert(rvalue);
906 assert(data);
907
908 if (streq(rvalue, "high"))
909 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
910 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
911 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
912 else if (streq(rvalue, "low"))
913 network->router_preference = SD_NDISC_PREFERENCE_LOW;
914 else
915 log_syntax(unit, LOG_WARNING, filename, line, 0,
916 "Invalid router preference, ignoring assignment: %s", rvalue);
917
918 return 0;
919 }