1 /* SPDX-License-Identifier: LGPL-2.1+ */
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"
14 #include "ndisc-internal.h"
15 #include "ndisc-router.h"
18 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router
, sd_ndisc_router
, mfree
);
20 sd_ndisc_router
*ndisc_router_new(size_t raw_size
) {
23 rt
= malloc0(ALIGN(sizeof(sd_ndisc_router
)) + raw_size
);
27 rt
->raw_size
= raw_size
;
33 _public_
int sd_ndisc_router_from_raw(sd_ndisc_router
**ret
, const void *raw
, size_t raw_size
) {
34 _cleanup_(sd_ndisc_router_unrefp
) sd_ndisc_router
*rt
= NULL
;
37 assert_return(ret
, -EINVAL
);
38 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
40 rt
= ndisc_router_new(raw_size
);
44 memcpy(NDISC_ROUTER_RAW(rt
), raw
, raw_size
);
45 r
= ndisc_router_parse(rt
);
54 _public_
int sd_ndisc_router_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
55 assert_return(rt
, -EINVAL
);
56 assert_return(ret_addr
, -EINVAL
);
58 if (IN6_IS_ADDR_UNSPECIFIED(&rt
->address
))
61 *ret_addr
= rt
->address
;
65 _public_
int sd_ndisc_router_get_timestamp(sd_ndisc_router
*rt
, clockid_t clock
, uint64_t *ret
) {
66 assert_return(rt
, -EINVAL
);
67 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
68 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
69 assert_return(ret
, -EINVAL
);
71 if (!triple_timestamp_is_set(&rt
->timestamp
))
74 *ret
= triple_timestamp_by_clock(&rt
->timestamp
, clock
);
78 _public_
int sd_ndisc_router_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *size
) {
79 assert_return(rt
, -EINVAL
);
80 assert_return(ret
, -EINVAL
);
81 assert_return(size
, -EINVAL
);
83 *ret
= NDISC_ROUTER_RAW(rt
);
89 int ndisc_router_parse(sd_ndisc_router
*rt
) {
90 struct nd_router_advert
*a
;
92 bool has_mtu
= false, has_flag_extension
= false;
97 if (rt
->raw_size
< sizeof(struct nd_router_advert
)) {
98 log_ndisc("Too small to be a router advertisement, ignoring.");
102 /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
103 a
= NDISC_ROUTER_RAW(rt
);
105 if (a
->nd_ra_type
!= ND_ROUTER_ADVERT
) {
106 log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
110 if (a
->nd_ra_code
!= 0) {
111 log_ndisc("Received ND packet with wrong RA code, ignoring.");
115 rt
->hop_limit
= a
->nd_ra_curhoplimit
;
116 rt
->flags
= a
->nd_ra_flags_reserved
; /* the first 8bit */
117 rt
->lifetime
= be16toh(a
->nd_ra_router_lifetime
);
119 rt
->preference
= (rt
->flags
>> 3) & 3;
120 if (!IN_SET(rt
->preference
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
121 rt
->preference
= SD_NDISC_PREFERENCE_MEDIUM
;
123 p
= (const uint8_t*) NDISC_ROUTER_RAW(rt
) + sizeof(struct nd_router_advert
);
124 left
= rt
->raw_size
- sizeof(struct nd_router_advert
);
134 log_ndisc("Option lacks header, ignoring datagram.");
142 log_ndisc("Zero-length option, ignoring datagram.");
146 log_ndisc("Option truncated, ignoring datagram.");
152 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
155 log_ndisc("Prefix option of invalid size, ignoring datagram.");
160 log_ndisc("Bad prefix length, ignoring datagram.");
166 case SD_NDISC_OPTION_MTU
: {
170 log_ndisc("MTU option specified twice, ignoring.");
175 log_ndisc("MTU option of invalid size, ignoring datagram.");
179 m
= be32toh(*(uint32_t*) (p
+ 4));
180 if (m
>= IPV6_MIN_MTU
) /* ignore invalidly small MTUs */
187 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
188 if (length
< 1*8 || length
> 3*8) {
189 log_ndisc("Route information option of invalid size, ignoring datagram.");
194 log_ndisc("Bad route prefix length, ignoring datagram.");
200 case SD_NDISC_OPTION_RDNSS
:
201 if (length
< 3*8 || (length
% (2*8)) != 1*8) {
202 log_ndisc("RDNSS option has invalid size.");
208 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
210 if (has_flag_extension
) {
211 log_ndisc("Flags extension option specified twice, ignoring.");
216 log_ndisc("Flags extension option has invalid size.");
220 /* Add in the additional flags bits */
222 ((uint64_t) p
[2] << 8) |
223 ((uint64_t) p
[3] << 16) |
224 ((uint64_t) p
[4] << 24) |
225 ((uint64_t) p
[5] << 32) |
226 ((uint64_t) p
[6] << 40) |
227 ((uint64_t) p
[7] << 48);
229 has_flag_extension
= true;
232 case SD_NDISC_OPTION_DNSSL
:
234 log_ndisc("DNSSL option has invalid size.");
241 p
+= length
, left
-= length
;
244 rt
->rindex
= sizeof(struct nd_router_advert
);
248 _public_
int sd_ndisc_router_get_hop_limit(sd_ndisc_router
*rt
, uint8_t *ret
) {
249 assert_return(rt
, -EINVAL
);
250 assert_return(ret
, -EINVAL
);
252 *ret
= rt
->hop_limit
;
256 _public_
int sd_ndisc_router_get_flags(sd_ndisc_router
*rt
, uint64_t *ret_flags
) {
257 assert_return(rt
, -EINVAL
);
258 assert_return(ret_flags
, -EINVAL
);
260 *ret_flags
= rt
->flags
;
264 _public_
int sd_ndisc_router_get_lifetime(sd_ndisc_router
*rt
, uint16_t *ret_lifetime
) {
265 assert_return(rt
, -EINVAL
);
266 assert_return(ret_lifetime
, -EINVAL
);
268 *ret_lifetime
= rt
->lifetime
;
272 _public_
int sd_ndisc_router_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
273 assert_return(rt
, -EINVAL
);
274 assert_return(ret
, -EINVAL
);
276 *ret
= rt
->preference
;
280 _public_
int sd_ndisc_router_get_mtu(sd_ndisc_router
*rt
, uint32_t *ret
) {
281 assert_return(rt
, -EINVAL
);
282 assert_return(ret
, -EINVAL
);
291 _public_
int sd_ndisc_router_option_rewind(sd_ndisc_router
*rt
) {
292 assert_return(rt
, -EINVAL
);
294 assert(rt
->raw_size
>= sizeof(struct nd_router_advert
));
295 rt
->rindex
= sizeof(struct nd_router_advert
);
297 return rt
->rindex
< rt
->raw_size
;
300 _public_
int sd_ndisc_router_option_next(sd_ndisc_router
*rt
) {
303 assert_return(rt
, -EINVAL
);
305 if (rt
->rindex
== rt
->raw_size
) /* EOF */
308 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
311 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
312 if (rt
->rindex
+ length
> rt
->raw_size
)
315 rt
->rindex
+= length
;
316 return rt
->rindex
< rt
->raw_size
;
319 _public_
int sd_ndisc_router_option_get_type(sd_ndisc_router
*rt
, uint8_t *ret
) {
320 assert_return(rt
, -EINVAL
);
321 assert_return(ret
, -EINVAL
);
323 if (rt
->rindex
== rt
->raw_size
) /* EOF */
326 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
329 *ret
= NDISC_ROUTER_OPTION_TYPE(rt
);
333 _public_
int sd_ndisc_router_option_is_type(sd_ndisc_router
*rt
, uint8_t type
) {
337 assert_return(rt
, -EINVAL
);
339 r
= sd_ndisc_router_option_get_type(rt
, &k
);
346 _public_
int sd_ndisc_router_option_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *size
) {
349 assert_return(rt
, -EINVAL
);
350 assert_return(ret
, -EINVAL
);
351 assert_return(size
, -EINVAL
);
353 /* Note that this returns the full option, including the option header */
355 if (rt
->rindex
+ 2 > rt
->raw_size
)
358 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
359 if (rt
->rindex
+ length
> rt
->raw_size
)
362 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
368 static int get_prefix_info(sd_ndisc_router
*rt
, struct nd_opt_prefix_info
**ret
) {
369 struct nd_opt_prefix_info
*ri
;
376 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_PREFIX_INFORMATION
);
382 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
383 if (length
!= sizeof(struct nd_opt_prefix_info
))
386 ri
= (struct nd_opt_prefix_info
*) ((uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
);
387 if (ri
->nd_opt_pi_prefix_len
> 128)
394 _public_
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
395 struct nd_opt_prefix_info
*ri
;
398 assert_return(rt
, -EINVAL
);
399 assert_return(ret
, -EINVAL
);
401 r
= get_prefix_info(rt
, &ri
);
405 *ret
= be32toh(ri
->nd_opt_pi_valid_time
);
409 _public_
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
410 struct nd_opt_prefix_info
*pi
;
413 assert_return(rt
, -EINVAL
);
414 assert_return(ret
, -EINVAL
);
416 r
= get_prefix_info(rt
, &pi
);
420 *ret
= be32toh(pi
->nd_opt_pi_preferred_time
);
424 _public_
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router
*rt
, uint8_t *ret
) {
425 struct nd_opt_prefix_info
*pi
;
429 assert_return(rt
, -EINVAL
);
430 assert_return(ret
, -EINVAL
);
432 r
= get_prefix_info(rt
, &pi
);
436 flags
= pi
->nd_opt_pi_flags_reserved
;
438 if ((flags
& ND_OPT_PI_FLAG_AUTO
) && (pi
->nd_opt_pi_prefix_len
!= 64)) {
439 log_ndisc("Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
440 flags
&= ~ND_OPT_PI_FLAG_AUTO
;
447 _public_
int sd_ndisc_router_prefix_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
448 struct nd_opt_prefix_info
*pi
;
451 assert_return(rt
, -EINVAL
);
452 assert_return(ret_addr
, -EINVAL
);
454 r
= get_prefix_info(rt
, &pi
);
458 *ret_addr
= pi
->nd_opt_pi_prefix
;
462 _public_
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
463 struct nd_opt_prefix_info
*pi
;
466 assert_return(rt
, -EINVAL
);
467 assert_return(ret
, -EINVAL
);
469 r
= get_prefix_info(rt
, &pi
);
473 if (pi
->nd_opt_pi_prefix_len
> 128)
476 *ret
= pi
->nd_opt_pi_prefix_len
;
480 static int get_route_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
488 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_ROUTE_INFORMATION
);
494 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
495 if (length
< 1*8 || length
> 3*8)
498 ri
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
507 _public_
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
511 assert_return(rt
, -EINVAL
);
512 assert_return(ret
, -EINVAL
);
514 r
= get_route_info(rt
, &ri
);
518 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
522 _public_
int sd_ndisc_router_route_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
526 assert_return(rt
, -EINVAL
);
527 assert_return(ret_addr
, -EINVAL
);
529 r
= get_route_info(rt
, &ri
);
534 memcpy(ret_addr
, ri
+ 8, NDISC_ROUTER_OPTION_LENGTH(rt
) - 8);
539 _public_
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
543 assert_return(rt
, -EINVAL
);
544 assert_return(ret
, -EINVAL
);
546 r
= get_route_info(rt
, &ri
);
554 _public_
int sd_ndisc_router_route_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
558 assert_return(rt
, -EINVAL
);
559 assert_return(ret
, -EINVAL
);
561 r
= get_route_info(rt
, &ri
);
565 *ret
= (ri
[3] >> 3) & 3;
566 if (!IN_SET(*ret
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
567 *ret
= SD_NDISC_PREFERENCE_MEDIUM
;
572 static int get_rdnss_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
579 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_RDNSS
);
585 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
586 if (length
< 3*8 || (length
% (2*8)) != 1*8)
589 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
593 _public_
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router
*rt
, const struct in6_addr
**ret
) {
597 assert_return(rt
, -EINVAL
);
598 assert_return(ret
, -EINVAL
);
600 r
= get_rdnss_info(rt
, &ri
);
604 *ret
= (const struct in6_addr
*) (ri
+ 8);
605 return (NDISC_ROUTER_OPTION_LENGTH(rt
) - 8) / 16;
608 _public_
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
612 assert_return(rt
, -EINVAL
);
613 assert_return(ret
, -EINVAL
);
615 r
= get_rdnss_info(rt
, &ri
);
619 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
623 static int get_dnssl_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
630 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_DNSSL
);
636 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
640 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
644 _public_
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router
*rt
, char ***ret
) {
645 _cleanup_strv_free_
char **l
= NULL
;
646 _cleanup_free_
char *e
= NULL
;
647 size_t allocated
= 0, n
= 0, left
;
653 assert_return(rt
, -EINVAL
);
654 assert_return(ret
, -EINVAL
);
656 r
= get_dnssl_info(rt
, &ri
);
661 left
= NDISC_ROUTER_OPTION_LENGTH(rt
) - 8;
666 if (n
> 0) /* Not properly NUL terminated */
673 /* Found NUL termination */
676 _cleanup_free_
char *normalized
= NULL
;
679 r
= dns_name_normalize(e
, 0, &normalized
);
683 /* Ignore the root domain name or "localhost" and friends */
684 if (!is_localhost(normalized
) &&
685 !dns_name_is_root(normalized
)) {
687 if (strv_push(&l
, normalized
) < 0)
701 /* Check for compression (which is not allowed) */
705 if (1U + *p
+ 1U > left
)
708 if (!GREEDY_REALLOC(e
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
+ 1U))
716 r
= dns_label_escape((char*) p
+1, *p
, e
+ n
, DNS_LABEL_ESCAPED_MAX
);
726 if (strv_isempty(l
)) {
736 _public_
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret_sec
) {
740 assert_return(rt
, -EINVAL
);
741 assert_return(ret_sec
, -EINVAL
);
743 r
= get_dnssl_info(rt
, &ri
);
747 *ret_sec
= be32toh(*(uint32_t*) (ri
+ 4));