1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright (C) 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 _public_ sd_ndisc_router
* sd_ndisc_router_ref(sd_ndisc_router
*rt
) {
22 assert(rt
->n_ref
> 0);
28 _public_ sd_ndisc_router
* sd_ndisc_router_unref(sd_ndisc_router
*rt
) {
32 assert(rt
->n_ref
> 0);
41 sd_ndisc_router
*ndisc_router_new(size_t raw_size
) {
44 rt
= malloc0(ALIGN(sizeof(sd_ndisc_router
)) + raw_size
);
48 rt
->raw_size
= raw_size
;
54 _public_
int sd_ndisc_router_from_raw(sd_ndisc_router
**ret
, const void *raw
, size_t raw_size
) {
55 _cleanup_(sd_ndisc_router_unrefp
) sd_ndisc_router
*rt
= NULL
;
58 assert_return(ret
, -EINVAL
);
59 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
61 rt
= ndisc_router_new(raw_size
);
65 memcpy(NDISC_ROUTER_RAW(rt
), raw
, raw_size
);
66 r
= ndisc_router_parse(rt
);
75 _public_
int sd_ndisc_router_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
76 assert_return(rt
, -EINVAL
);
77 assert_return(ret_addr
, -EINVAL
);
79 if (IN6_IS_ADDR_UNSPECIFIED(&rt
->address
))
82 *ret_addr
= rt
->address
;
86 _public_
int sd_ndisc_router_get_timestamp(sd_ndisc_router
*rt
, clockid_t clock
, uint64_t *ret
) {
87 assert_return(rt
, -EINVAL
);
88 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
89 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
90 assert_return(ret
, -EINVAL
);
92 if (!triple_timestamp_is_set(&rt
->timestamp
))
95 *ret
= triple_timestamp_by_clock(&rt
->timestamp
, clock
);
99 _public_
int sd_ndisc_router_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *size
) {
100 assert_return(rt
, -EINVAL
);
101 assert_return(ret
, -EINVAL
);
102 assert_return(size
, -EINVAL
);
104 *ret
= NDISC_ROUTER_RAW(rt
);
105 *size
= rt
->raw_size
;
110 int ndisc_router_parse(sd_ndisc_router
*rt
) {
111 struct nd_router_advert
*a
;
113 bool has_mtu
= false, has_flag_extension
= false;
118 if (rt
->raw_size
< sizeof(struct nd_router_advert
)) {
119 log_ndisc("Too small to be a router advertisement, ignoring.");
123 /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
124 a
= NDISC_ROUTER_RAW(rt
);
126 if (a
->nd_ra_type
!= ND_ROUTER_ADVERT
) {
127 log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
131 if (a
->nd_ra_code
!= 0) {
132 log_ndisc("Received ND packet with wrong RA code, ignoring.");
136 rt
->hop_limit
= a
->nd_ra_curhoplimit
;
137 rt
->flags
= a
->nd_ra_flags_reserved
; /* the first 8bit */
138 rt
->lifetime
= be16toh(a
->nd_ra_router_lifetime
);
140 rt
->preference
= (rt
->flags
>> 3) & 3;
141 if (!IN_SET(rt
->preference
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
142 rt
->preference
= SD_NDISC_PREFERENCE_MEDIUM
;
144 p
= (const uint8_t*) NDISC_ROUTER_RAW(rt
) + sizeof(struct nd_router_advert
);
145 left
= rt
->raw_size
- sizeof(struct nd_router_advert
);
155 log_ndisc("Option lacks header, ignoring datagram.");
163 log_ndisc("Zero-length option, ignoring datagram.");
167 log_ndisc("Option truncated, ignoring datagram.");
173 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
176 log_ndisc("Prefix option of invalid size, ignoring datagram.");
181 log_ndisc("Bad prefix length, ignoring datagram.");
187 case SD_NDISC_OPTION_MTU
: {
191 log_ndisc("MTU option specified twice, ignoring.");
196 log_ndisc("MTU option of invalid size, ignoring datagram.");
200 m
= be32toh(*(uint32_t*) (p
+ 4));
201 if (m
>= IPV6_MIN_MTU
) /* ignore invalidly small MTUs */
208 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
209 if (length
< 1*8 || length
> 3*8) {
210 log_ndisc("Route information option of invalid size, ignoring datagram.");
215 log_ndisc("Bad route prefix length, ignoring datagram.");
221 case SD_NDISC_OPTION_RDNSS
:
222 if (length
< 3*8 || (length
% (2*8)) != 1*8) {
223 log_ndisc("RDNSS option has invalid size.");
229 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
231 if (has_flag_extension
) {
232 log_ndisc("Flags extension option specified twice, ignoring.");
237 log_ndisc("Flags extension option has invalid size.");
241 /* Add in the additional flags bits */
243 ((uint64_t) p
[2] << 8) |
244 ((uint64_t) p
[3] << 16) |
245 ((uint64_t) p
[4] << 24) |
246 ((uint64_t) p
[5] << 32) |
247 ((uint64_t) p
[6] << 40) |
248 ((uint64_t) p
[7] << 48);
250 has_flag_extension
= true;
253 case SD_NDISC_OPTION_DNSSL
:
255 log_ndisc("DNSSL option has invalid size.");
262 p
+= length
, left
-= length
;
265 rt
->rindex
= sizeof(struct nd_router_advert
);
269 _public_
int sd_ndisc_router_get_hop_limit(sd_ndisc_router
*rt
, uint8_t *ret
) {
270 assert_return(rt
, -EINVAL
);
271 assert_return(ret
, -EINVAL
);
273 *ret
= rt
->hop_limit
;
277 _public_
int sd_ndisc_router_get_flags(sd_ndisc_router
*rt
, uint64_t *ret_flags
) {
278 assert_return(rt
, -EINVAL
);
279 assert_return(ret_flags
, -EINVAL
);
281 *ret_flags
= rt
->flags
;
285 _public_
int sd_ndisc_router_get_lifetime(sd_ndisc_router
*rt
, uint16_t *ret_lifetime
) {
286 assert_return(rt
, -EINVAL
);
287 assert_return(ret_lifetime
, -EINVAL
);
289 *ret_lifetime
= rt
->lifetime
;
293 _public_
int sd_ndisc_router_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
294 assert_return(rt
, -EINVAL
);
295 assert_return(ret
, -EINVAL
);
297 *ret
= rt
->preference
;
301 _public_
int sd_ndisc_router_get_mtu(sd_ndisc_router
*rt
, uint32_t *ret
) {
302 assert_return(rt
, -EINVAL
);
303 assert_return(ret
, -EINVAL
);
312 _public_
int sd_ndisc_router_option_rewind(sd_ndisc_router
*rt
) {
313 assert_return(rt
, -EINVAL
);
315 assert(rt
->raw_size
>= sizeof(struct nd_router_advert
));
316 rt
->rindex
= sizeof(struct nd_router_advert
);
318 return rt
->rindex
< rt
->raw_size
;
321 _public_
int sd_ndisc_router_option_next(sd_ndisc_router
*rt
) {
324 assert_return(rt
, -EINVAL
);
326 if (rt
->rindex
== rt
->raw_size
) /* EOF */
329 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
332 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
333 if (rt
->rindex
+ length
> rt
->raw_size
)
336 rt
->rindex
+= length
;
337 return rt
->rindex
< rt
->raw_size
;
340 _public_
int sd_ndisc_router_option_get_type(sd_ndisc_router
*rt
, uint8_t *ret
) {
341 assert_return(rt
, -EINVAL
);
342 assert_return(ret
, -EINVAL
);
344 if (rt
->rindex
== rt
->raw_size
) /* EOF */
347 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
350 *ret
= NDISC_ROUTER_OPTION_TYPE(rt
);
354 _public_
int sd_ndisc_router_option_is_type(sd_ndisc_router
*rt
, uint8_t type
) {
358 assert_return(rt
, -EINVAL
);
360 r
= sd_ndisc_router_option_get_type(rt
, &k
);
367 _public_
int sd_ndisc_router_option_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *size
) {
370 assert_return(rt
, -EINVAL
);
371 assert_return(ret
, -EINVAL
);
372 assert_return(size
, -EINVAL
);
374 /* Note that this returns the full option, including the option header */
376 if (rt
->rindex
+ 2 > rt
->raw_size
)
379 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
380 if (rt
->rindex
+ length
> rt
->raw_size
)
383 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
389 static int get_prefix_info(sd_ndisc_router
*rt
, struct nd_opt_prefix_info
**ret
) {
390 struct nd_opt_prefix_info
*ri
;
397 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_PREFIX_INFORMATION
);
403 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
404 if (length
!= sizeof(struct nd_opt_prefix_info
))
407 ri
= (struct nd_opt_prefix_info
*) ((uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
);
408 if (ri
->nd_opt_pi_prefix_len
> 128)
415 _public_
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
416 struct nd_opt_prefix_info
*ri
;
419 assert_return(rt
, -EINVAL
);
420 assert_return(ret
, -EINVAL
);
422 r
= get_prefix_info(rt
, &ri
);
426 *ret
= be32toh(ri
->nd_opt_pi_valid_time
);
430 _public_
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
431 struct nd_opt_prefix_info
*pi
;
434 assert_return(rt
, -EINVAL
);
435 assert_return(ret
, -EINVAL
);
437 r
= get_prefix_info(rt
, &pi
);
441 *ret
= be32toh(pi
->nd_opt_pi_preferred_time
);
445 _public_
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router
*rt
, uint8_t *ret
) {
446 struct nd_opt_prefix_info
*pi
;
450 assert_return(rt
, -EINVAL
);
451 assert_return(ret
, -EINVAL
);
453 r
= get_prefix_info(rt
, &pi
);
457 flags
= pi
->nd_opt_pi_flags_reserved
;
459 if ((flags
& ND_OPT_PI_FLAG_AUTO
) && (pi
->nd_opt_pi_prefix_len
!= 64)) {
460 log_ndisc("Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
461 flags
&= ~ND_OPT_PI_FLAG_AUTO
;
468 _public_
int sd_ndisc_router_prefix_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
469 struct nd_opt_prefix_info
*pi
;
472 assert_return(rt
, -EINVAL
);
473 assert_return(ret_addr
, -EINVAL
);
475 r
= get_prefix_info(rt
, &pi
);
479 *ret_addr
= pi
->nd_opt_pi_prefix
;
483 _public_
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
484 struct nd_opt_prefix_info
*pi
;
487 assert_return(rt
, -EINVAL
);
488 assert_return(ret
, -EINVAL
);
490 r
= get_prefix_info(rt
, &pi
);
494 if (pi
->nd_opt_pi_prefix_len
> 128)
497 *ret
= pi
->nd_opt_pi_prefix_len
;
501 static int get_route_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
509 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_ROUTE_INFORMATION
);
515 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
516 if (length
< 1*8 || length
> 3*8)
519 ri
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
528 _public_
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
532 assert_return(rt
, -EINVAL
);
533 assert_return(ret
, -EINVAL
);
535 r
= get_route_info(rt
, &ri
);
539 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
543 _public_
int sd_ndisc_router_route_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
547 assert_return(rt
, -EINVAL
);
548 assert_return(ret_addr
, -EINVAL
);
550 r
= get_route_info(rt
, &ri
);
555 memcpy(ret_addr
, ri
+ 8, NDISC_ROUTER_OPTION_LENGTH(rt
) - 8);
560 _public_
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
564 assert_return(rt
, -EINVAL
);
565 assert_return(ret
, -EINVAL
);
567 r
= get_route_info(rt
, &ri
);
575 _public_
int sd_ndisc_router_route_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
579 assert_return(rt
, -EINVAL
);
580 assert_return(ret
, -EINVAL
);
582 r
= get_route_info(rt
, &ri
);
586 *ret
= (ri
[3] >> 3) & 3;
587 if (!IN_SET(*ret
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
588 *ret
= SD_NDISC_PREFERENCE_MEDIUM
;
593 static int get_rdnss_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
600 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_RDNSS
);
606 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
607 if (length
< 3*8 || (length
% (2*8)) != 1*8)
610 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
614 _public_
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router
*rt
, const struct in6_addr
**ret
) {
618 assert_return(rt
, -EINVAL
);
619 assert_return(ret
, -EINVAL
);
621 r
= get_rdnss_info(rt
, &ri
);
625 *ret
= (const struct in6_addr
*) (ri
+ 8);
626 return (NDISC_ROUTER_OPTION_LENGTH(rt
) - 8) / 16;
629 _public_
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
633 assert_return(rt
, -EINVAL
);
634 assert_return(ret
, -EINVAL
);
636 r
= get_rdnss_info(rt
, &ri
);
640 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
644 static int get_dnssl_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
651 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_DNSSL
);
657 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
661 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
665 _public_
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router
*rt
, char ***ret
) {
666 _cleanup_strv_free_
char **l
= NULL
;
667 _cleanup_free_
char *e
= NULL
;
668 size_t allocated
= 0, n
= 0, left
;
674 assert_return(rt
, -EINVAL
);
675 assert_return(ret
, -EINVAL
);
677 r
= get_dnssl_info(rt
, &ri
);
682 left
= NDISC_ROUTER_OPTION_LENGTH(rt
) - 8;
687 if (n
> 0) /* Not properly NUL terminated */
694 /* Found NUL termination */
697 _cleanup_free_
char *normalized
= NULL
;
700 r
= dns_name_normalize(e
, &normalized
);
704 /* Ignore the root domain name or "localhost" and friends */
705 if (!is_localhost(normalized
) &&
706 !dns_name_is_root(normalized
)) {
708 if (strv_push(&l
, normalized
) < 0)
722 /* Check for compression (which is not allowed) */
726 if (1U + *p
+ 1U > left
)
729 if (!GREEDY_REALLOC(e
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
+ 1U))
737 r
= dns_label_escape((char*) p
+1, *p
, e
+ n
, DNS_LABEL_ESCAPED_MAX
);
747 if (strv_isempty(l
)) {
757 _public_
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret_sec
) {
761 assert_return(rt
, -EINVAL
);
762 assert_return(ret_sec
, -EINVAL
);
764 r
= get_dnssl_info(rt
, &ri
);
768 *ret_sec
= be32toh(*(uint32_t*) (ri
+ 4));