1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014 Intel Corporation. All rights reserved.
6 #include <netinet/icmp6.h>
10 #include "alloc-util.h"
11 #include "dns-domain.h"
12 #include "hostname-util.h"
13 #include "memory-util.h"
14 #include "missing_network.h"
15 #include "ndisc-internal.h"
16 #include "ndisc-protocol.h"
17 #include "ndisc-router.h"
20 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router
, sd_ndisc_router
, mfree
);
22 sd_ndisc_router
*ndisc_router_new(size_t raw_size
) {
25 if (raw_size
> SIZE_MAX
- ALIGN(sizeof(sd_ndisc_router
)))
28 rt
= malloc0(ALIGN(sizeof(sd_ndisc_router
)) + raw_size
);
32 rt
->raw_size
= raw_size
;
38 int sd_ndisc_router_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret
) {
39 assert_return(rt
, -EINVAL
);
40 assert_return(ret
, -EINVAL
);
42 if (in6_addr_is_null(&rt
->address
))
49 int sd_ndisc_router_get_timestamp(sd_ndisc_router
*rt
, clockid_t clock
, uint64_t *ret
) {
50 assert_return(rt
, -EINVAL
);
51 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
52 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
53 assert_return(ret
, -EINVAL
);
55 if (!triple_timestamp_is_set(&rt
->timestamp
))
58 *ret
= triple_timestamp_by_clock(&rt
->timestamp
, clock
);
62 #define DEFINE_GET_TIMESTAMP(name) \
63 int sd_ndisc_router_##name##_timestamp( \
64 sd_ndisc_router *rt, \
71 assert_return(rt, -EINVAL); \
72 assert_return(ret, -EINVAL); \
74 r = sd_ndisc_router_##name(rt, &s); \
78 r = sd_ndisc_router_get_timestamp(rt, clock, &t); \
82 *ret = time_span_to_stamp(s, t); \
86 DEFINE_GET_TIMESTAMP(get_lifetime
);
87 DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime
);
88 DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime
);
89 DEFINE_GET_TIMESTAMP(route_get_lifetime
);
90 DEFINE_GET_TIMESTAMP(rdnss_get_lifetime
);
91 DEFINE_GET_TIMESTAMP(dnssl_get_lifetime
);
92 DEFINE_GET_TIMESTAMP(prefix64_get_lifetime
);
94 int sd_ndisc_router_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *ret_size
) {
95 assert_return(rt
, -EINVAL
);
96 assert_return(ret
, -EINVAL
);
97 assert_return(ret_size
, -EINVAL
);
99 *ret
= NDISC_ROUTER_RAW(rt
);
100 *ret_size
= rt
->raw_size
;
105 static bool pref64_option_verify(const struct nd_opt_prefix64_info
*p
, size_t length
) {
106 uint16_t lifetime_and_plc
;
110 if (length
!= sizeof(struct nd_opt_prefix64_info
))
113 lifetime_and_plc
= be16toh(p
->lifetime_and_plc
);
114 if (pref64_plc_to_prefix_length(lifetime_and_plc
, NULL
) < 0)
120 int ndisc_router_parse(sd_ndisc
*nd
, sd_ndisc_router
*rt
) {
121 struct nd_router_advert
*a
;
123 bool has_mtu
= false, has_flag_extension
= false;
128 if (rt
->raw_size
< sizeof(struct nd_router_advert
))
129 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
130 "Too small to be a router advertisement, ignoring.");
132 /* Router advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
133 a
= NDISC_ROUTER_RAW(rt
);
135 if (a
->nd_ra_type
!= ND_ROUTER_ADVERT
)
136 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
137 "Received ND packet that is not a router advertisement, ignoring.");
139 if (a
->nd_ra_code
!= 0)
140 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
141 "Received ND packet with wrong RA code, ignoring.");
143 rt
->hop_limit
= a
->nd_ra_curhoplimit
;
144 rt
->flags
= a
->nd_ra_flags_reserved
; /* the first 8 bits */
145 rt
->lifetime_usec
= be16_sec_to_usec(a
->nd_ra_router_lifetime
, /* max_as_infinity = */ false);
146 rt
->icmp6_ratelimit_usec
= be32_msec_to_usec(a
->nd_ra_retransmit
, /* max_as_infinity = */ false);
147 rt
->retransmission_time_usec
= be32_msec_to_usec(a
->nd_ra_retransmit
, /* max_as_infinity = */ false);
149 rt
->preference
= (rt
->flags
>> 3) & 3;
150 if (!IN_SET(rt
->preference
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
151 rt
->preference
= SD_NDISC_PREFERENCE_MEDIUM
;
153 p
= (const uint8_t*) NDISC_ROUTER_RAW(rt
) + sizeof(struct nd_router_advert
);
154 left
= rt
->raw_size
- sizeof(struct nd_router_advert
);
164 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
165 "Option lacks header, ignoring datagram.");
171 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
172 "Zero-length option, ignoring datagram.");
174 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
175 "Option truncated, ignoring datagram.");
179 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
182 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
183 "Prefix option of invalid size, ignoring datagram.");
186 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
187 "Bad prefix length, ignoring datagram.");
191 case SD_NDISC_OPTION_MTU
: {
195 log_ndisc(nd
, "MTU option specified twice, ignoring.");
200 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
201 "MTU option of invalid size, ignoring datagram.");
203 m
= be32toh(*(uint32_t*) (p
+ 4));
204 if (m
>= IPV6_MIN_MTU
) /* ignore invalidly small MTUs */
211 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
212 if (length
< 1*8 || length
> 3*8)
213 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
214 "Route information option of invalid size, ignoring datagram.");
217 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
218 "Bad route prefix length, ignoring datagram.");
222 case SD_NDISC_OPTION_RDNSS
:
223 if (length
< 3*8 || (length
% (2*8)) != 1*8)
224 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
), "RDNSS option has invalid size.");
228 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
230 if (has_flag_extension
) {
231 log_ndisc(nd
, "Flags extension option specified twice, ignoring.");
236 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
237 "Flags extension option has invalid size.");
239 /* Add in the additional flags bits */
241 ((uint64_t) p
[2] << 8) |
242 ((uint64_t) p
[3] << 16) |
243 ((uint64_t) p
[4] << 24) |
244 ((uint64_t) p
[5] << 32) |
245 ((uint64_t) p
[6] << 40) |
246 ((uint64_t) p
[7] << 48);
248 has_flag_extension
= true;
251 case SD_NDISC_OPTION_DNSSL
:
253 return log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
254 "DNSSL option has invalid size.");
257 case SD_NDISC_OPTION_PREF64
: {
258 if (!pref64_option_verify((struct nd_opt_prefix64_info
*) p
, length
))
259 log_ndisc_errno(nd
, SYNTHETIC_ERRNO(EBADMSG
),
260 "PREF64 prefix has invalid prefix length.");
264 p
+= length
, left
-= length
;
267 rt
->rindex
= sizeof(struct nd_router_advert
);
271 int sd_ndisc_router_get_hop_limit(sd_ndisc_router
*rt
, uint8_t *ret
) {
272 assert_return(rt
, -EINVAL
);
273 assert_return(ret
, -EINVAL
);
275 *ret
= rt
->hop_limit
;
279 int sd_ndisc_router_get_retransmission_time(sd_ndisc_router
*rt
, uint64_t *ret
) {
280 assert_return(rt
, -EINVAL
);
281 assert_return(ret
, -EINVAL
);
283 *ret
= rt
->retransmission_time_usec
;
287 int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router
*rt
, uint64_t *ret
) {
288 assert_return(rt
, -EINVAL
);
289 assert_return(ret
, -EINVAL
);
291 *ret
= rt
->icmp6_ratelimit_usec
;
295 int sd_ndisc_router_get_flags(sd_ndisc_router
*rt
, uint64_t *ret
) {
296 assert_return(rt
, -EINVAL
);
297 assert_return(ret
, -EINVAL
);
303 int sd_ndisc_router_get_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
304 assert_return(rt
, -EINVAL
);
305 assert_return(ret
, -EINVAL
);
307 *ret
= rt
->lifetime_usec
;
311 int sd_ndisc_router_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
312 assert_return(rt
, -EINVAL
);
313 assert_return(ret
, -EINVAL
);
315 *ret
= rt
->preference
;
319 int sd_ndisc_router_get_mtu(sd_ndisc_router
*rt
, uint32_t *ret
) {
320 assert_return(rt
, -EINVAL
);
321 assert_return(ret
, -EINVAL
);
330 int sd_ndisc_router_option_rewind(sd_ndisc_router
*rt
) {
331 assert_return(rt
, -EINVAL
);
333 assert(rt
->raw_size
>= sizeof(struct nd_router_advert
));
334 rt
->rindex
= sizeof(struct nd_router_advert
);
336 return rt
->rindex
< rt
->raw_size
;
339 int sd_ndisc_router_option_next(sd_ndisc_router
*rt
) {
342 assert_return(rt
, -EINVAL
);
344 if (rt
->rindex
== rt
->raw_size
) /* EOF */
347 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
350 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
351 if (rt
->rindex
+ length
> rt
->raw_size
)
354 rt
->rindex
+= length
;
355 return rt
->rindex
< rt
->raw_size
;
358 int sd_ndisc_router_option_get_type(sd_ndisc_router
*rt
, uint8_t *ret
) {
359 assert_return(rt
, -EINVAL
);
360 assert_return(ret
, -EINVAL
);
362 if (rt
->rindex
== rt
->raw_size
) /* EOF */
365 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
368 *ret
= NDISC_ROUTER_OPTION_TYPE(rt
);
372 int sd_ndisc_router_option_is_type(sd_ndisc_router
*rt
, uint8_t type
) {
376 assert_return(rt
, -EINVAL
);
378 r
= sd_ndisc_router_option_get_type(rt
, &k
);
385 int sd_ndisc_router_option_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *ret_size
) {
388 assert_return(rt
, -EINVAL
);
389 assert_return(ret
, -EINVAL
);
390 assert_return(ret_size
, -EINVAL
);
392 /* Note that this returns the full option, including the option header */
394 if (rt
->rindex
+ 2 > rt
->raw_size
)
397 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
398 if (rt
->rindex
+ length
> rt
->raw_size
)
401 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
407 static int get_prefix_info(sd_ndisc_router
*rt
, struct nd_opt_prefix_info
**ret
) {
408 struct nd_opt_prefix_info
*ri
;
415 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_PREFIX_INFORMATION
);
421 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
422 if (length
!= sizeof(struct nd_opt_prefix_info
))
425 ri
= (struct nd_opt_prefix_info
*) ((uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
);
426 if (ri
->nd_opt_pi_prefix_len
> 128)
433 int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
434 struct nd_opt_prefix_info
*ri
;
437 assert_return(rt
, -EINVAL
);
438 assert_return(ret
, -EINVAL
);
440 r
= get_prefix_info(rt
, &ri
);
444 *ret
= be32_sec_to_usec(ri
->nd_opt_pi_valid_time
, /* max_as_infinity = */ true);
448 int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
449 struct nd_opt_prefix_info
*pi
;
452 assert_return(rt
, -EINVAL
);
453 assert_return(ret
, -EINVAL
);
455 r
= get_prefix_info(rt
, &pi
);
459 *ret
= be32_sec_to_usec(pi
->nd_opt_pi_preferred_time
, /* max_as_infinity = */ true);
463 int sd_ndisc_router_prefix_get_flags(sd_ndisc_router
*rt
, uint8_t *ret
) {
464 struct nd_opt_prefix_info
*pi
;
468 assert_return(rt
, -EINVAL
);
469 assert_return(ret
, -EINVAL
);
471 r
= get_prefix_info(rt
, &pi
);
475 flags
= pi
->nd_opt_pi_flags_reserved
;
477 if ((flags
& ND_OPT_PI_FLAG_AUTO
) && (pi
->nd_opt_pi_prefix_len
!= 64)) {
478 log_ndisc(NULL
, "Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
479 flags
&= ~ND_OPT_PI_FLAG_AUTO
;
486 int sd_ndisc_router_prefix_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret
) {
487 struct nd_opt_prefix_info
*pi
;
490 assert_return(rt
, -EINVAL
);
491 assert_return(ret
, -EINVAL
);
493 r
= get_prefix_info(rt
, &pi
);
497 *ret
= pi
->nd_opt_pi_prefix
;
501 int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
502 struct nd_opt_prefix_info
*pi
;
505 assert_return(rt
, -EINVAL
);
506 assert_return(ret
, -EINVAL
);
508 r
= get_prefix_info(rt
, &pi
);
512 if (pi
->nd_opt_pi_prefix_len
> 128)
515 *ret
= pi
->nd_opt_pi_prefix_len
;
519 static int get_route_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
527 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_ROUTE_INFORMATION
);
533 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
534 if (length
< 1*8 || length
> 3*8)
537 ri
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
546 int sd_ndisc_router_route_get_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
550 assert_return(rt
, -EINVAL
);
551 assert_return(ret
, -EINVAL
);
553 r
= get_route_info(rt
, &ri
);
557 *ret
= unaligned_be32_sec_to_usec(ri
+ 4, /* max_as_infinity = */ true);
561 int sd_ndisc_router_route_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret
) {
565 assert_return(rt
, -EINVAL
);
566 assert_return(ret
, -EINVAL
);
568 r
= get_route_info(rt
, &ri
);
573 memcpy(ret
, ri
+ 8, NDISC_ROUTER_OPTION_LENGTH(rt
) - 8);
578 int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
582 assert_return(rt
, -EINVAL
);
583 assert_return(ret
, -EINVAL
);
585 r
= get_route_info(rt
, &ri
);
593 int sd_ndisc_router_route_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
597 assert_return(rt
, -EINVAL
);
598 assert_return(ret
, -EINVAL
);
600 r
= get_route_info(rt
, &ri
);
604 if (!IN_SET((ri
[3] >> 3) & 3, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_MEDIUM
, SD_NDISC_PREFERENCE_HIGH
))
607 *ret
= (ri
[3] >> 3) & 3;
611 static int get_rdnss_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
618 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_RDNSS
);
624 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
625 if (length
< 3*8 || (length
% (2*8)) != 1*8)
628 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
632 int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router
*rt
, const struct in6_addr
**ret
) {
636 assert_return(rt
, -EINVAL
);
637 assert_return(ret
, -EINVAL
);
639 r
= get_rdnss_info(rt
, &ri
);
643 *ret
= (const struct in6_addr
*) (ri
+ 8);
644 return (NDISC_ROUTER_OPTION_LENGTH(rt
) - 8) / 16;
647 int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
651 assert_return(rt
, -EINVAL
);
652 assert_return(ret
, -EINVAL
);
654 r
= get_rdnss_info(rt
, &ri
);
658 *ret
= unaligned_be32_sec_to_usec(ri
+ 4, /* max_as_infinity = */ true);
662 static int get_dnssl_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
669 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_DNSSL
);
675 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
679 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
683 int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router
*rt
, char ***ret
) {
684 _cleanup_strv_free_
char **l
= NULL
;
685 _cleanup_free_
char *e
= NULL
;
692 assert_return(rt
, -EINVAL
);
693 assert_return(ret
, -EINVAL
);
695 r
= get_dnssl_info(rt
, &ri
);
700 left
= NDISC_ROUTER_OPTION_LENGTH(rt
) - 8;
705 if (n
> 0) /* Not properly NUL terminated */
712 /* Found NUL termination */
715 _cleanup_free_
char *normalized
= NULL
;
718 r
= dns_name_normalize(e
, 0, &normalized
);
722 /* Ignore the root domain name or "localhost" and friends */
723 if (!is_localhost(normalized
) &&
724 !dns_name_is_root(normalized
)) {
726 if (strv_push(&l
, normalized
) < 0)
740 /* Check for compression (which is not allowed) */
744 if (1U + *p
+ 1U > left
)
747 if (!GREEDY_REALLOC(e
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
+ 1U))
755 r
= dns_label_escape((char*) p
+1, *p
, e
+ n
, DNS_LABEL_ESCAPED_MAX
);
765 if (strv_isempty(l
)) {
775 int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
779 assert_return(rt
, -EINVAL
);
780 assert_return(ret
, -EINVAL
);
782 r
= get_dnssl_info(rt
, &ri
);
786 *ret
= unaligned_be32_sec_to_usec(ri
+ 4, /* max_as_infinity = */ true);
790 int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router
*rt
, const char **ret
, size_t *ret_size
) {
792 const char *nd_opt_captive_portal
;
795 assert_return(rt
, -EINVAL
);
796 assert_return(ret
, -EINVAL
);
797 assert_return(ret_size
, -EINVAL
);
799 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_CAPTIVE_PORTAL
);
805 r
= sd_ndisc_router_option_get_raw(rt
, (void *)&nd_opt_captive_portal
, &length
);
809 /* The length field has units of 8 octets */
810 assert(length
% 8 == 0);
814 /* Check that the message is not truncated by an embedded NUL.
815 * NUL padding to a multiple of 8 is expected. */
816 size_t size
= strnlen(nd_opt_captive_portal
+ 2, length
- 2);
817 if (DIV_ROUND_UP(size
+ 2, 8) != length
/ 8)
820 /* Let's not return an empty buffer */
827 *ret
= nd_opt_captive_portal
+ 2;
833 static int get_pref64_prefix_info(sd_ndisc_router
*rt
, struct nd_opt_prefix64_info
**ret
) {
834 struct nd_opt_prefix64_info
*ri
;
841 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_PREF64
);
847 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
848 if (length
!= sizeof(struct nd_opt_prefix64_info
))
851 ri
= (struct nd_opt_prefix64_info
*) ((uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
);
852 if (!pref64_option_verify(ri
, length
))
859 int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router
*rt
, struct in6_addr
*ret
) {
860 struct nd_opt_prefix64_info
*pi
;
861 struct in6_addr a
= {};
865 assert_return(rt
, -EINVAL
);
866 assert_return(ret
, -EINVAL
);
868 r
= get_pref64_prefix_info(rt
, &pi
);
872 r
= sd_ndisc_router_prefix64_get_prefixlen(rt
, &prefixlen
);
876 memcpy(&a
, pi
->prefix
, sizeof(pi
->prefix
));
877 in6_addr_mask(&a
, prefixlen
);
878 /* extra safety check for refusing malformed prefix. */
879 if (memcmp(&a
, pi
->prefix
, sizeof(pi
->prefix
)) != 0)
886 int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
887 struct nd_opt_prefix64_info
*pi
;
888 uint16_t lifetime_prefix_len
;
892 assert_return(rt
, -EINVAL
);
893 assert_return(ret
, -EINVAL
);
895 r
= get_pref64_prefix_info(rt
, &pi
);
899 lifetime_prefix_len
= be16toh(pi
->lifetime_and_plc
);
900 pref64_plc_to_prefix_length(lifetime_prefix_len
, &prefix_len
);
906 int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router
*rt
, uint64_t *ret
) {
907 struct nd_opt_prefix64_info
*pi
;
908 uint16_t lifetime_prefix_len
;
911 assert_return(rt
, -EINVAL
);
912 assert_return(ret
, -EINVAL
);
914 r
= get_pref64_prefix_info(rt
, &pi
);
918 lifetime_prefix_len
= be16toh(pi
->lifetime_and_plc
);
920 *ret
= (lifetime_prefix_len
& PREF64_SCALED_LIFETIME_MASK
) * USEC_PER_SEC
;