1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <netinet/icmp6.h>
25 #include "alloc-util.h"
26 #include "dns-domain.h"
27 #include "hostname-util.h"
29 #include "ndisc-internal.h"
30 #include "ndisc-router.h"
33 _public_ sd_ndisc_router
* sd_ndisc_router_ref(sd_ndisc_router
*rt
) {
37 assert(rt
->n_ref
> 0);
43 _public_ sd_ndisc_router
* sd_ndisc_router_unref(sd_ndisc_router
*rt
) {
47 assert(rt
->n_ref
> 0);
56 sd_ndisc_router
*ndisc_router_new(size_t raw_size
) {
59 rt
= malloc0(ALIGN(sizeof(sd_ndisc_router
)) + raw_size
);
63 rt
->raw_size
= raw_size
;
69 _public_
int sd_ndisc_router_from_raw(sd_ndisc_router
**ret
, const void *raw
, size_t raw_size
) {
70 _cleanup_(sd_ndisc_router_unrefp
) sd_ndisc_router
*rt
= NULL
;
73 assert_return(ret
, -EINVAL
);
74 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
76 rt
= ndisc_router_new(raw_size
);
80 memcpy(NDISC_ROUTER_RAW(rt
), raw
, raw_size
);
81 r
= ndisc_router_parse(rt
);
90 _public_
int sd_ndisc_router_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
91 assert_return(rt
, -EINVAL
);
92 assert_return(ret_addr
, -EINVAL
);
94 if (IN6_IS_ADDR_UNSPECIFIED(&rt
->address
))
97 *ret_addr
= rt
->address
;
101 _public_
int sd_ndisc_router_get_timestamp(sd_ndisc_router
*rt
, clockid_t clock
, uint64_t *ret
) {
102 assert_return(rt
, -EINVAL
);
103 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
104 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
105 assert_return(ret
, -EINVAL
);
107 if (!triple_timestamp_is_set(&rt
->timestamp
))
110 *ret
= triple_timestamp_by_clock(&rt
->timestamp
, clock
);
114 _public_
int sd_ndisc_router_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *size
) {
115 assert_return(rt
, -EINVAL
);
116 assert_return(ret
, -EINVAL
);
117 assert_return(size
, -EINVAL
);
119 *ret
= NDISC_ROUTER_RAW(rt
);
120 *size
= rt
->raw_size
;
125 int ndisc_router_parse(sd_ndisc_router
*rt
) {
126 struct nd_router_advert
*a
;
128 bool has_mtu
= false, has_flag_extension
= false;
133 if (rt
->raw_size
< sizeof(struct nd_router_advert
)) {
134 log_ndisc("Too small to be a router advertisement, ignoring.");
138 /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
139 a
= NDISC_ROUTER_RAW(rt
);
141 if (a
->nd_ra_type
!= ND_ROUTER_ADVERT
) {
142 log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
146 if (a
->nd_ra_code
!= 0) {
147 log_ndisc("Received ND packet with wrong RA code, ignoring.");
151 rt
->hop_limit
= a
->nd_ra_curhoplimit
;
152 rt
->flags
= a
->nd_ra_flags_reserved
; /* the first 8bit */
153 rt
->lifetime
= be16toh(a
->nd_ra_router_lifetime
);
155 rt
->preference
= (rt
->flags
>> 3) & 3;
156 if (!IN_SET(rt
->preference
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
157 rt
->preference
= SD_NDISC_PREFERENCE_MEDIUM
;
159 p
= (const uint8_t*) NDISC_ROUTER_RAW(rt
) + sizeof(struct nd_router_advert
);
160 left
= rt
->raw_size
- sizeof(struct nd_router_advert
);
170 log_ndisc("Option lacks header, ignoring datagram.");
178 log_ndisc("Zero-length option, ignoring datagram.");
182 log_ndisc("Option truncated, ignoring datagram.");
188 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
191 log_ndisc("Prefix option of invalid size, ignoring datagram.");
196 log_ndisc("Bad prefix length, ignoring datagram.");
202 case SD_NDISC_OPTION_MTU
: {
206 log_ndisc("MTU option specified twice, ignoring.");
211 log_ndisc("MTU option of invalid size, ignoring datagram.");
215 m
= be32toh(*(uint32_t*) (p
+ 4));
216 if (m
>= IPV6_MIN_MTU
) /* ignore invalidly small MTUs */
223 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
224 if (length
< 1*8 || length
> 3*8) {
225 log_ndisc("Route information option of invalid size, ignoring datagram.");
230 log_ndisc("Bad route prefix length, ignoring datagram.");
236 case SD_NDISC_OPTION_RDNSS
:
237 if (length
< 3*8 || (length
% (2*8)) != 1*8) {
238 log_ndisc("RDNSS option has invalid size.");
244 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
246 if (has_flag_extension
) {
247 log_ndisc("Flags extension option specified twice, ignoring.");
252 log_ndisc("Flags extension option has invalid size.");
256 /* Add in the additional flags bits */
258 ((uint64_t) p
[2] << 8) |
259 ((uint64_t) p
[3] << 16) |
260 ((uint64_t) p
[4] << 24) |
261 ((uint64_t) p
[5] << 32) |
262 ((uint64_t) p
[6] << 40) |
263 ((uint64_t) p
[7] << 48);
265 has_flag_extension
= true;
268 case SD_NDISC_OPTION_DNSSL
:
270 log_ndisc("DNSSL option has invalid size.");
277 p
+= length
, left
-= length
;
280 rt
->rindex
= sizeof(struct nd_router_advert
);
284 _public_
int sd_ndisc_router_get_hop_limit(sd_ndisc_router
*rt
, uint8_t *ret
) {
285 assert_return(rt
, -EINVAL
);
286 assert_return(ret
, -EINVAL
);
288 *ret
= rt
->hop_limit
;
292 _public_
int sd_ndisc_router_get_flags(sd_ndisc_router
*rt
, uint64_t *ret_flags
) {
293 assert_return(rt
, -EINVAL
);
294 assert_return(ret_flags
, -EINVAL
);
296 *ret_flags
= rt
->flags
;
300 _public_
int sd_ndisc_router_get_lifetime(sd_ndisc_router
*rt
, uint16_t *ret_lifetime
) {
301 assert_return(rt
, -EINVAL
);
302 assert_return(ret_lifetime
, -EINVAL
);
304 *ret_lifetime
= rt
->lifetime
;
308 _public_
int sd_ndisc_router_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
309 assert_return(rt
, -EINVAL
);
310 assert_return(ret
, -EINVAL
);
312 *ret
= rt
->preference
;
316 _public_
int sd_ndisc_router_get_mtu(sd_ndisc_router
*rt
, uint32_t *ret
) {
317 assert_return(rt
, -EINVAL
);
318 assert_return(ret
, -EINVAL
);
327 _public_
int sd_ndisc_router_option_rewind(sd_ndisc_router
*rt
) {
328 assert_return(rt
, -EINVAL
);
330 assert(rt
->raw_size
>= sizeof(struct nd_router_advert
));
331 rt
->rindex
= sizeof(struct nd_router_advert
);
333 return rt
->rindex
< rt
->raw_size
;
336 _public_
int sd_ndisc_router_option_next(sd_ndisc_router
*rt
) {
339 assert_return(rt
, -EINVAL
);
341 if (rt
->rindex
== rt
->raw_size
) /* EOF */
344 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
347 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
348 if (rt
->rindex
+ length
> rt
->raw_size
)
351 rt
->rindex
+= length
;
352 return rt
->rindex
< rt
->raw_size
;
355 _public_
int sd_ndisc_router_option_get_type(sd_ndisc_router
*rt
, uint8_t *ret
) {
356 assert_return(rt
, -EINVAL
);
357 assert_return(ret
, -EINVAL
);
359 if (rt
->rindex
== rt
->raw_size
) /* EOF */
362 if (rt
->rindex
+ 2 > rt
->raw_size
) /* Truncated message */
365 *ret
= NDISC_ROUTER_OPTION_TYPE(rt
);
369 _public_
int sd_ndisc_router_option_is_type(sd_ndisc_router
*rt
, uint8_t type
) {
373 assert_return(rt
, -EINVAL
);
375 r
= sd_ndisc_router_option_get_type(rt
, &k
);
382 _public_
int sd_ndisc_router_option_get_raw(sd_ndisc_router
*rt
, const void **ret
, size_t *size
) {
385 assert_return(rt
, -EINVAL
);
386 assert_return(ret
, -EINVAL
);
387 assert_return(size
, -EINVAL
);
389 /* Note that this returns the full option, including the option header */
391 if (rt
->rindex
+ 2 > rt
->raw_size
)
394 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
395 if (rt
->rindex
+ length
> rt
->raw_size
)
398 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
404 static int get_prefix_info(sd_ndisc_router
*rt
, struct nd_opt_prefix_info
**ret
) {
405 struct nd_opt_prefix_info
*ri
;
412 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_PREFIX_INFORMATION
);
418 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
419 if (length
!= sizeof(struct nd_opt_prefix_info
))
422 ri
= (struct nd_opt_prefix_info
*) ((uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
);
423 if (ri
->nd_opt_pi_prefix_len
> 128)
430 _public_
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
431 struct nd_opt_prefix_info
*ri
;
434 assert_return(rt
, -EINVAL
);
435 assert_return(ret
, -EINVAL
);
437 r
= get_prefix_info(rt
, &ri
);
441 *ret
= be32toh(ri
->nd_opt_pi_valid_time
);
445 _public_
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
446 struct nd_opt_prefix_info
*pi
;
449 assert_return(rt
, -EINVAL
);
450 assert_return(ret
, -EINVAL
);
452 r
= get_prefix_info(rt
, &pi
);
456 *ret
= be32toh(pi
->nd_opt_pi_preferred_time
);
460 _public_
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router
*rt
, uint8_t *ret
) {
461 struct nd_opt_prefix_info
*pi
;
465 assert_return(rt
, -EINVAL
);
466 assert_return(ret
, -EINVAL
);
468 r
= get_prefix_info(rt
, &pi
);
472 flags
= pi
->nd_opt_pi_flags_reserved
;
474 if ((flags
& ND_OPT_PI_FLAG_AUTO
) && (pi
->nd_opt_pi_prefix_len
!= 64)) {
475 log_ndisc("Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
476 flags
&= ~ND_OPT_PI_FLAG_AUTO
;
483 _public_
int sd_ndisc_router_prefix_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
484 struct nd_opt_prefix_info
*pi
;
487 assert_return(rt
, -EINVAL
);
488 assert_return(ret_addr
, -EINVAL
);
490 r
= get_prefix_info(rt
, &pi
);
494 *ret_addr
= pi
->nd_opt_pi_prefix
;
498 _public_
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
499 struct nd_opt_prefix_info
*pi
;
502 assert_return(rt
, -EINVAL
);
503 assert_return(ret
, -EINVAL
);
505 r
= get_prefix_info(rt
, &pi
);
509 if (pi
->nd_opt_pi_prefix_len
> 128)
512 *ret
= pi
->nd_opt_pi_prefix_len
;
516 static int get_route_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
524 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_ROUTE_INFORMATION
);
530 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
531 if (length
< 1*8 || length
> 3*8)
534 ri
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
543 _public_
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
547 assert_return(rt
, -EINVAL
);
548 assert_return(ret
, -EINVAL
);
550 r
= get_route_info(rt
, &ri
);
554 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
558 _public_
int sd_ndisc_router_route_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
562 assert_return(rt
, -EINVAL
);
563 assert_return(ret_addr
, -EINVAL
);
565 r
= get_route_info(rt
, &ri
);
570 memcpy(ret_addr
, ri
+ 8, NDISC_ROUTER_OPTION_LENGTH(rt
) - 8);
575 _public_
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
579 assert_return(rt
, -EINVAL
);
580 assert_return(ret
, -EINVAL
);
582 r
= get_route_info(rt
, &ri
);
590 _public_
int sd_ndisc_router_route_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
594 assert_return(rt
, -EINVAL
);
595 assert_return(ret
, -EINVAL
);
597 r
= get_route_info(rt
, &ri
);
601 *ret
= (ri
[3] >> 3) & 3;
602 if (!IN_SET(*ret
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
603 *ret
= SD_NDISC_PREFERENCE_MEDIUM
;
608 static int get_rdnss_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
615 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_RDNSS
);
621 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
622 if (length
< 3*8 || (length
% (2*8)) != 1*8)
625 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
629 _public_
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router
*rt
, const struct in6_addr
**ret
) {
633 assert_return(rt
, -EINVAL
);
634 assert_return(ret
, -EINVAL
);
636 r
= get_rdnss_info(rt
, &ri
);
640 *ret
= (const struct in6_addr
*) (ri
+ 8);
641 return (NDISC_ROUTER_OPTION_LENGTH(rt
) - 8) / 16;
644 _public_
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
648 assert_return(rt
, -EINVAL
);
649 assert_return(ret
, -EINVAL
);
651 r
= get_rdnss_info(rt
, &ri
);
655 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
659 static int get_dnssl_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
666 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_DNSSL
);
672 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
676 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
680 _public_
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router
*rt
, char ***ret
) {
681 _cleanup_strv_free_
char **l
= NULL
;
682 _cleanup_free_
char *e
= NULL
;
683 size_t allocated
= 0, n
= 0, left
;
689 assert_return(rt
, -EINVAL
);
690 assert_return(ret
, -EINVAL
);
692 r
= get_dnssl_info(rt
, &ri
);
697 left
= NDISC_ROUTER_OPTION_LENGTH(rt
) - 8;
702 if (n
> 0) /* Not properly NUL terminated */
709 /* Found NUL termination */
712 _cleanup_free_
char *normalized
= NULL
;
715 r
= dns_name_normalize(e
, &normalized
);
719 /* Ignore the root domain name or "localhost" and friends */
720 if (!is_localhost(normalized
) &&
721 !dns_name_is_root(normalized
)) {
723 if (strv_push(&l
, normalized
) < 0)
737 /* Check for compression (which is not allowed) */
741 if (1U + *p
+ 1U > left
)
744 if (!GREEDY_REALLOC(e
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
+ 1U))
752 r
= dns_label_escape((char*) p
+1, *p
, e
+ n
, DNS_LABEL_ESCAPED_MAX
);
762 if (strv_isempty(l
)) {
772 _public_
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret_sec
) {
776 assert_return(rt
, -EINVAL
);
777 assert_return(ret_sec
, -EINVAL
);
779 r
= get_dnssl_info(rt
, &ri
);
783 *ret_sec
= be32toh(*(uint32_t*) (ri
+ 4));