1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2017 Intel Corporation. All rights reserved.
6 #include <netinet/icmp6.h>
7 #include <netinet/in.h>
12 #include "alloc-util.h"
13 #include "dns-domain.h"
14 #include "ether-addr-util.h"
15 #include "event-util.h"
17 #include "icmp6-util.h"
18 #include "in-addr-util.h"
21 #include "memory-util.h"
22 #include "radv-internal.h"
23 #include "random-util.h"
24 #include "socket-util.h"
25 #include "string-util.h"
28 _public_
int sd_radv_new(sd_radv
**ret
) {
29 _cleanup_(sd_radv_unrefp
) sd_radv
*ra
= NULL
;
31 assert_return(ret
, -EINVAL
);
47 _public_
int sd_radv_attach_event(sd_radv
*ra
, sd_event
*event
, int64_t priority
) {
50 assert_return(ra
, -EINVAL
);
51 assert_return(!ra
->event
, -EBUSY
);
54 ra
->event
= sd_event_ref(event
);
56 r
= sd_event_default(&ra
->event
);
61 ra
->event_priority
= priority
;
66 _public_
int sd_radv_detach_event(sd_radv
*ra
) {
68 assert_return(ra
, -EINVAL
);
70 ra
->event
= sd_event_unref(ra
->event
);
74 _public_ sd_event
*sd_radv_get_event(sd_radv
*ra
) {
75 assert_return(ra
, NULL
);
80 static void radv_reset(sd_radv
*ra
) {
83 (void) event_source_disable(ra
->timeout_event_source
);
85 ra
->recv_event_source
=
86 sd_event_source_unref(ra
->recv_event_source
);
91 static sd_radv
*radv_free(sd_radv
*ra
) {
95 while (ra
->prefixes
) {
96 sd_radv_prefix
*p
= ra
->prefixes
;
98 LIST_REMOVE(prefix
, ra
->prefixes
, p
);
99 sd_radv_prefix_unref(p
);
105 ra
->timeout_event_source
= sd_event_source_unref(ra
->timeout_event_source
);
109 sd_radv_detach_event(ra
);
111 ra
->fd
= safe_close(ra
->fd
);
116 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv
, sd_radv
, radv_free
);
118 static int radv_send(sd_radv
*ra
, const struct in6_addr
*dst
, uint32_t router_lifetime
) {
119 sd_radv_route_prefix
*rt
;
121 struct sockaddr_in6 dst_addr
= {
122 .sin6_family
= AF_INET6
,
123 .sin6_addr
= IN6ADDR_ALL_NODES_MULTICAST_INIT
,
125 struct nd_router_advert adv
= {};
127 struct nd_opt_hdr opthdr
;
128 struct ether_addr slladdr
;
129 } _packed_ opt_mac
= {
131 .nd_opt_type
= ND_OPT_SOURCE_LINKADDR
,
132 .nd_opt_len
= (sizeof(struct nd_opt_hdr
) +
133 sizeof(struct ether_addr
) - 1) /8 + 1,
136 struct nd_opt_mtu opt_mtu
= {
137 .nd_opt_mtu_type
= ND_OPT_MTU
,
140 /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, RDNSS
142 struct iovec iov
[5 + ra
->n_prefixes
+ ra
->n_route_prefixes
];
143 struct msghdr msg
= {
144 .msg_name
= &dst_addr
,
145 .msg_namelen
= sizeof(dst_addr
),
153 r
= sd_event_now(ra
->event
, clock_boottime_or_monotonic(), &time_now
);
157 if (dst
&& !IN6_IS_ADDR_UNSPECIFIED(dst
))
158 dst_addr
.sin6_addr
= *dst
;
160 adv
.nd_ra_type
= ND_ROUTER_ADVERT
;
161 adv
.nd_ra_curhoplimit
= ra
->hop_limit
;
162 adv
.nd_ra_flags_reserved
= ra
->flags
;
163 adv
.nd_ra_router_lifetime
= htobe16(router_lifetime
);
164 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(&adv
, sizeof(adv
));
166 /* MAC address is optional, either because the link does not use L2
167 addresses or load sharing is desired. See RFC 4861, Section 4.2 */
168 if (!ether_addr_is_null(&ra
->mac_addr
)) {
169 opt_mac
.slladdr
= ra
->mac_addr
;
170 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(&opt_mac
, sizeof(opt_mac
));
174 opt_mtu
.nd_opt_mtu_mtu
= htobe32(ra
->mtu
);
175 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(&opt_mtu
, sizeof(opt_mtu
));
178 LIST_FOREACH(prefix
, p
, ra
->prefixes
) {
179 if (p
->valid_until
) {
181 if (time_now
> p
->valid_until
)
182 p
->opt
.valid_lifetime
= 0;
184 p
->opt
.valid_lifetime
= htobe32((p
->valid_until
- time_now
) / USEC_PER_SEC
);
186 if (time_now
> p
->preferred_until
)
187 p
->opt
.preferred_lifetime
= 0;
189 p
->opt
.preferred_lifetime
= htobe32((p
->preferred_until
- time_now
) / USEC_PER_SEC
);
191 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(&p
->opt
, sizeof(p
->opt
));
194 LIST_FOREACH(prefix
, rt
, ra
->route_prefixes
)
195 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(&rt
->opt
, sizeof(rt
->opt
));
198 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(ra
->rdnss
, ra
->rdnss
->length
* 8);
201 iov
[msg
.msg_iovlen
++] = IOVEC_MAKE(ra
->dnssl
, ra
->dnssl
->length
* 8);
203 if (sendmsg(ra
->fd
, &msg
, 0) < 0)
209 static int radv_recv(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
210 sd_radv
*ra
= userdata
;
211 _cleanup_free_
char *addr
= NULL
;
213 triple_timestamp timestamp
;
216 _cleanup_free_
char *buf
= NULL
;
222 buflen
= next_datagram_size_fd(fd
);
226 buf
= new0(char, buflen
);
230 r
= icmp6_receive(fd
, buf
, buflen
, &src
, ×tamp
);
234 (void) in_addr_to_string(AF_INET6
, (union in_addr_union
*) &src
, &addr
);
235 log_radv("Received RS from non-link-local address %s. Ignoring", addr
);
239 log_radv("Received RS with invalid hop limit. Ignoring.");
243 log_radv("Received invalid source address from ICMPv6 socket. Ignoring.");
246 case -EAGAIN
: /* ignore spurious wakeups */
250 log_radv_errno(r
, "Unexpected error receiving from ICMPv6 socket: %m");
257 if ((size_t) buflen
< sizeof(struct nd_router_solicit
)) {
258 log_radv("Too short packet received");
262 (void) in_addr_to_string(AF_INET6
, (union in_addr_union
*) &src
, &addr
);
264 r
= radv_send(ra
, &src
, ra
->lifetime
);
266 log_radv_errno(r
, "Unable to send solicited Router Advertisement to %s: %m", strnull(addr
));
268 log_radv("Sent solicited Router Advertisement to %s", strnull(addr
));
273 static usec_t
radv_compute_timeout(usec_t min
, usec_t max
) {
274 assert_return(min
<= max
, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC
);
276 return min
+ (random_u32() % (max
- min
));
279 static int radv_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
281 sd_radv
*ra
= userdata
;
282 usec_t min_timeout
= SD_RADV_DEFAULT_MIN_TIMEOUT_USEC
;
283 usec_t max_timeout
= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC
;
284 usec_t time_now
, timeout
;
285 char time_string
[FORMAT_TIMESPAN_MAX
];
291 r
= sd_event_now(ra
->event
, clock_boottime_or_monotonic(), &time_now
);
295 r
= radv_send(ra
, NULL
, ra
->lifetime
);
297 log_radv_errno(r
, "Unable to send Router Advertisement: %m");
299 /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
300 if (ra
->ra_sent
< SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS
) {
301 max_timeout
= SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC
;
302 min_timeout
= SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC
/ 3;
305 timeout
= radv_compute_timeout(min_timeout
, max_timeout
);
307 log_radv("Next Router Advertisement in %s",
308 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
,
309 timeout
, USEC_PER_SEC
));
311 r
= event_reset_time(ra
->event
, &ra
->timeout_event_source
,
312 clock_boottime_or_monotonic(),
313 time_now
+ timeout
, MSEC_PER_SEC
,
315 ra
->event_priority
, "radv-timeout", true);
329 _public_
int sd_radv_stop(sd_radv
*ra
) {
332 assert_return(ra
, -EINVAL
);
334 if (ra
->state
== SD_RADV_STATE_IDLE
)
337 log_radv("Stopping IPv6 Router Advertisement daemon");
339 /* RFC 4861, Section 6.2.5, send at least one Router Advertisement
340 with zero lifetime */
341 r
= radv_send(ra
, NULL
, 0);
343 log_radv_errno(r
, "Unable to send last Router Advertisement with router lifetime set to zero: %m");
346 ra
->fd
= safe_close(ra
->fd
);
347 ra
->state
= SD_RADV_STATE_IDLE
;
352 _public_
int sd_radv_start(sd_radv
*ra
) {
355 assert_return(ra
, -EINVAL
);
356 assert_return(ra
->event
, -EINVAL
);
357 assert_return(ra
->ifindex
> 0, -EINVAL
);
359 if (ra
->state
!= SD_RADV_STATE_IDLE
)
362 r
= event_reset_time(ra
->event
, &ra
->timeout_event_source
,
363 clock_boottime_or_monotonic(),
366 ra
->event_priority
, "radv-timeout", true);
370 r
= icmp6_bind_router_advertisement(ra
->ifindex
);
376 r
= sd_event_add_io(ra
->event
, &ra
->recv_event_source
, ra
->fd
, EPOLLIN
, radv_recv
, ra
);
380 r
= sd_event_source_set_priority(ra
->recv_event_source
, ra
->event_priority
);
384 (void) sd_event_source_set_description(ra
->recv_event_source
, "radv-receive-message");
386 ra
->state
= SD_RADV_STATE_ADVERTISING
;
388 log_radv("Started IPv6 Router Advertisement daemon");
398 _public_
int sd_radv_set_ifindex(sd_radv
*ra
, int ifindex
) {
399 assert_return(ra
, -EINVAL
);
400 assert_return(ifindex
>= -1, -EINVAL
);
402 if (ra
->state
!= SD_RADV_STATE_IDLE
)
405 ra
->ifindex
= ifindex
;
410 _public_
int sd_radv_set_mac(sd_radv
*ra
, const struct ether_addr
*mac_addr
) {
411 assert_return(ra
, -EINVAL
);
413 if (ra
->state
!= SD_RADV_STATE_IDLE
)
417 ra
->mac_addr
= *mac_addr
;
424 _public_
int sd_radv_set_mtu(sd_radv
*ra
, uint32_t mtu
) {
425 assert_return(ra
, -EINVAL
);
426 assert_return(mtu
>= 1280, -EINVAL
);
433 _public_
int sd_radv_set_hop_limit(sd_radv
*ra
, uint8_t hop_limit
) {
434 assert_return(ra
, -EINVAL
);
436 if (ra
->state
!= SD_RADV_STATE_IDLE
)
439 ra
->hop_limit
= hop_limit
;
444 _public_
int sd_radv_set_router_lifetime(sd_radv
*ra
, uint32_t router_lifetime
) {
445 assert_return(ra
, -EINVAL
);
447 if (ra
->state
!= SD_RADV_STATE_IDLE
)
450 /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the
451 preference value MUST be set to (00) by the sender..." */
452 if (router_lifetime
== 0 &&
453 (ra
->flags
& (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM
<< 3))
456 ra
->lifetime
= router_lifetime
;
461 _public_
int sd_radv_set_managed_information(sd_radv
*ra
, int managed
) {
462 assert_return(ra
, -EINVAL
);
464 if (ra
->state
!= SD_RADV_STATE_IDLE
)
467 SET_FLAG(ra
->flags
, ND_RA_FLAG_MANAGED
, managed
);
472 _public_
int sd_radv_set_other_information(sd_radv
*ra
, int other
) {
473 assert_return(ra
, -EINVAL
);
475 if (ra
->state
!= SD_RADV_STATE_IDLE
)
478 SET_FLAG(ra
->flags
, ND_RA_FLAG_OTHER
, other
);
483 _public_
int sd_radv_set_preference(sd_radv
*ra
, unsigned preference
) {
486 assert_return(ra
, -EINVAL
);
487 assert_return(IN_SET(preference
,
488 SD_NDISC_PREFERENCE_LOW
,
489 SD_NDISC_PREFERENCE_MEDIUM
,
490 SD_NDISC_PREFERENCE_HIGH
), -EINVAL
);
492 ra
->flags
= (ra
->flags
& ~(0x3 << 3)) | (preference
<< 3);
497 _public_
int sd_radv_add_prefix(sd_radv
*ra
, sd_radv_prefix
*p
, int dynamic
) {
500 _cleanup_free_
char *addr_p
= NULL
;
501 char time_string_preferred
[FORMAT_TIMESPAN_MAX
];
502 char time_string_valid
[FORMAT_TIMESPAN_MAX
];
503 usec_t time_now
, valid
, preferred
, valid_until
, preferred_until
;
505 assert_return(ra
, -EINVAL
);
510 /* Refuse prefixes that don't have a prefix set */
511 if (IN6_IS_ADDR_UNSPECIFIED(&p
->opt
.in6_addr
))
514 LIST_FOREACH(prefix
, cur
, ra
->prefixes
) {
516 r
= in_addr_prefix_intersect(AF_INET6
,
517 (union in_addr_union
*) &cur
->opt
.in6_addr
,
519 (union in_addr_union
*) &p
->opt
.in6_addr
,
522 _cleanup_free_
char *addr_cur
= NULL
;
524 (void) in_addr_to_string(AF_INET6
,
525 (union in_addr_union
*) &p
->opt
.in6_addr
,
528 if (dynamic
&& cur
->opt
.prefixlen
== p
->opt
.prefixlen
)
531 (void) in_addr_to_string(AF_INET6
,
532 (union in_addr_union
*) &cur
->opt
.in6_addr
,
534 log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u",
535 addr_cur
, cur
->opt
.prefixlen
,
536 addr_p
, p
->opt
.prefixlen
);
542 p
= sd_radv_prefix_ref(p
);
544 LIST_APPEND(prefix
, ra
->prefixes
, p
);
548 (void) in_addr_to_string(AF_INET6
, (union in_addr_union
*) &p
->opt
.in6_addr
, &addr_p
);
551 log_radv("Added prefix %s/%d", addr_p
, p
->opt
.prefixlen
);
558 r
= sd_event_now(ra
->event
, clock_boottime_or_monotonic(), &time_now
);
562 valid
= be32toh(p
->opt
.valid_lifetime
) * USEC_PER_SEC
;
563 valid_until
= usec_add(valid
, time_now
);
564 if (valid_until
== USEC_INFINITY
)
567 preferred
= be32toh(p
->opt
.preferred_lifetime
) * USEC_PER_SEC
;
568 preferred_until
= usec_add(preferred
, time_now
);
569 if (preferred_until
== USEC_INFINITY
)
572 cur
->valid_until
= valid_until
;
573 cur
->preferred_until
= preferred_until
;
575 log_radv("%s prefix %s/%u preferred %s valid %s",
576 cur
? "Updated": "Added",
577 addr_p
, p
->opt
.prefixlen
,
578 format_timespan(time_string_preferred
, FORMAT_TIMESPAN_MAX
,
579 preferred
, USEC_PER_SEC
),
580 format_timespan(time_string_valid
, FORMAT_TIMESPAN_MAX
,
581 valid
, USEC_PER_SEC
));
586 _public_ sd_radv_prefix
*sd_radv_remove_prefix(sd_radv
*ra
,
587 const struct in6_addr
*prefix
,
588 unsigned char prefixlen
) {
589 sd_radv_prefix
*cur
, *next
;
591 assert_return(ra
, NULL
);
592 assert_return(prefix
, NULL
);
594 LIST_FOREACH_SAFE(prefix
, cur
, next
, ra
->prefixes
) {
595 if (prefixlen
!= cur
->opt
.prefixlen
)
598 if (!in_addr_equal(AF_INET6
,
599 (union in_addr_union
*)prefix
,
600 (union in_addr_union
*)&cur
->opt
.in6_addr
))
603 LIST_REMOVE(prefix
, ra
->prefixes
, cur
);
605 sd_radv_prefix_unref(cur
);
613 _public_
int sd_radv_add_route_prefix(sd_radv
*ra
, sd_radv_route_prefix
*p
, int dynamic
) {
614 char time_string_valid
[FORMAT_TIMESPAN_MAX
];
615 usec_t time_now
, valid
, valid_until
;
616 _cleanup_free_
char *pretty
= NULL
;
617 sd_radv_route_prefix
*cur
;
620 assert_return(ra
, -EINVAL
);
625 (void) in_addr_to_string(AF_INET6
,
626 (union in_addr_union
*) &p
->opt
.in6_addr
,
629 LIST_FOREACH(prefix
, cur
, ra
->route_prefixes
) {
630 _cleanup_free_
char *addr
= NULL
;
632 r
= in_addr_prefix_intersect(AF_INET6
,
633 (union in_addr_union
*) &cur
->opt
.in6_addr
,
635 (union in_addr_union
*) &p
->opt
.in6_addr
,
642 if (dynamic
&& cur
->opt
.prefixlen
== p
->opt
.prefixlen
)
645 (void) in_addr_to_string(AF_INET6
,
646 (union in_addr_union
*) &cur
->opt
.in6_addr
,
648 log_radv("IPv6 route prefix %s/%u already configured, ignoring %s/%u",
649 strempty(addr
), cur
->opt
.prefixlen
,
650 strempty(pretty
), p
->opt
.prefixlen
);
655 p
= sd_radv_route_prefix_ref(p
);
657 LIST_APPEND(prefix
, ra
->route_prefixes
, p
);
658 ra
->n_route_prefixes
++;
662 log_radv("Added prefix %s/%u", strempty(pretty
), p
->opt
.prefixlen
);
667 r
= sd_event_now(ra
->event
, clock_boottime_or_monotonic(), &time_now
);
671 valid
= be32toh(p
->opt
.lifetime
) * USEC_PER_SEC
;
672 valid_until
= usec_add(valid
, time_now
);
673 if (valid_until
== USEC_INFINITY
)
676 log_radv("%s route prefix %s/%u valid %s",
677 cur
? "Updated": "Added",
678 strempty(pretty
), p
->opt
.prefixlen
,
679 format_timespan(time_string_valid
, FORMAT_TIMESPAN_MAX
, valid
, USEC_PER_SEC
));
684 _public_
int sd_radv_set_rdnss(sd_radv
*ra
, uint32_t lifetime
,
685 const struct in6_addr
*dns
, size_t n_dns
) {
686 _cleanup_free_
struct sd_radv_opt_dns
*opt_rdnss
= NULL
;
689 assert_return(ra
, -EINVAL
);
690 assert_return(n_dns
< 128, -EINVAL
);
692 if (!dns
|| n_dns
== 0) {
693 ra
->rdnss
= mfree(ra
->rdnss
);
699 len
= sizeof(struct sd_radv_opt_dns
) + sizeof(struct in6_addr
) * n_dns
;
701 opt_rdnss
= malloc0(len
);
705 opt_rdnss
->type
= SD_RADV_OPT_RDNSS
;
706 opt_rdnss
->length
= len
/ 8;
707 opt_rdnss
->lifetime
= htobe32(lifetime
);
709 memcpy(opt_rdnss
+ 1, dns
, n_dns
* sizeof(struct in6_addr
));
711 free_and_replace(ra
->rdnss
, opt_rdnss
);
718 _public_
int sd_radv_set_dnssl(sd_radv
*ra
, uint32_t lifetime
,
719 char **search_list
) {
720 _cleanup_free_
struct sd_radv_opt_dns
*opt_dnssl
= NULL
;
725 assert_return(ra
, -EINVAL
);
727 if (strv_isempty(search_list
)) {
728 ra
->dnssl
= mfree(ra
->dnssl
);
732 STRV_FOREACH(s
, search_list
)
733 len
+= strlen(*s
) + 2;
735 len
= (sizeof(struct sd_radv_opt_dns
) + len
+ 7) & ~0x7;
737 opt_dnssl
= malloc0(len
);
741 opt_dnssl
->type
= SD_RADV_OPT_DNSSL
;
742 opt_dnssl
->length
= len
/ 8;
743 opt_dnssl
->lifetime
= htobe32(lifetime
);
745 p
= (uint8_t *)(opt_dnssl
+ 1);
746 len
-= sizeof(struct sd_radv_opt_dns
);
748 STRV_FOREACH(s
, search_list
) {
751 r
= dns_name_to_wire_format(*s
, p
, len
, false);
762 free_and_replace(ra
->dnssl
, opt_dnssl
);
767 _public_
int sd_radv_prefix_new(sd_radv_prefix
**ret
) {
770 assert_return(ret
, -EINVAL
);
772 p
= new(sd_radv_prefix
, 1);
776 *p
= (sd_radv_prefix
) {
779 .opt
.type
= ND_OPT_PREFIX_INFORMATION
,
780 .opt
.length
= (sizeof(p
->opt
) - 1)/8 + 1,
783 /* RFC 4861, Section 6.2.1 */
784 .opt
.flags
= ND_OPT_PI_FLAG_ONLINK
|ND_OPT_PI_FLAG_AUTO
,
786 .opt
.preferred_lifetime
= htobe32(604800),
787 .opt
.valid_lifetime
= htobe32(2592000),
794 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix
, sd_radv_prefix
, mfree
);
796 _public_
int sd_radv_prefix_set_prefix(sd_radv_prefix
*p
, const struct in6_addr
*in6_addr
,
797 unsigned char prefixlen
) {
798 assert_return(p
, -EINVAL
);
799 assert_return(in6_addr
, -EINVAL
);
801 if (prefixlen
< 3 || prefixlen
> 128)
805 /* unusual but allowed, log it */
806 log_radv("Unusual prefix length %d greater than 64", prefixlen
);
808 p
->opt
.in6_addr
= *in6_addr
;
809 p
->opt
.prefixlen
= prefixlen
;
814 _public_
int sd_radv_prefix_set_onlink(sd_radv_prefix
*p
, int onlink
) {
815 assert_return(p
, -EINVAL
);
817 SET_FLAG(p
->opt
.flags
, ND_OPT_PI_FLAG_ONLINK
, onlink
);
822 _public_
int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix
*p
,
823 int address_autoconfiguration
) {
824 assert_return(p
, -EINVAL
);
826 SET_FLAG(p
->opt
.flags
, ND_OPT_PI_FLAG_AUTO
, address_autoconfiguration
);
831 _public_
int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix
*p
,
832 uint32_t valid_lifetime
) {
833 assert_return(p
, -EINVAL
);
835 p
->opt
.valid_lifetime
= htobe32(valid_lifetime
);
840 _public_
int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix
*p
,
841 uint32_t preferred_lifetime
) {
842 assert_return(p
, -EINVAL
);
844 p
->opt
.preferred_lifetime
= htobe32(preferred_lifetime
);
849 _public_
int sd_radv_route_prefix_new(sd_radv_route_prefix
**ret
) {
850 sd_radv_route_prefix
*p
;
852 assert_return(ret
, -EINVAL
);
854 p
= new(sd_radv_route_prefix
, 1);
858 *p
= (sd_radv_route_prefix
) {
861 .opt
.type
= SD_RADV_OPT_ROUTE_INFORMATION
,
862 .opt
.length
= DIV_ROUND_UP(sizeof(p
->opt
), 8),
865 .opt
.lifetime
= htobe32(604800),
872 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix
, sd_radv_route_prefix
, mfree
);
874 _public_
int sd_radv_prefix_set_route_prefix(sd_radv_route_prefix
*p
, const struct in6_addr
*in6_addr
,
875 unsigned char prefixlen
) {
876 assert_return(p
, -EINVAL
);
877 assert_return(in6_addr
, -EINVAL
);
883 /* unusual but allowed, log it */
884 log_radv("Unusual prefix length %u greater than 64", prefixlen
);
886 p
->opt
.in6_addr
= *in6_addr
;
887 p
->opt
.prefixlen
= prefixlen
;
892 _public_
int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix
*p
, uint32_t valid_lifetime
) {
893 assert_return(p
, -EINVAL
);
895 p
->opt
.lifetime
= htobe32(valid_lifetime
);