2 This file is part of systemd.
4 Copyright (C) 2014 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <netinet/icmp6.h>
24 #include "alloc-util.h"
25 #include "dns-domain.h"
26 #include "hostname-util.h"
28 #include "ndisc-internal.h"
29 #include "ndisc-router.h"
32 _public_ sd_ndisc_router
* sd_ndisc_router_ref(sd_ndisc_router
*rt
) {
36 assert(rt
->n_ref
> 0);
42 _public_ sd_ndisc_router
* sd_ndisc_router_unref(sd_ndisc_router
*rt
) {
46 assert(rt
->n_ref
> 0);
55 sd_ndisc_router
*ndisc_router_new(size_t raw_size
) {
58 rt
= malloc0(ALIGN(sizeof(sd_ndisc_router
)) + raw_size
);
62 rt
->raw_size
= raw_size
;
68 _public_
int sd_ndisc_router_from_raw(sd_ndisc_router
**ret
, const void *raw
, size_t raw_size
) {
69 _cleanup_(sd_ndisc_router_unrefp
) sd_ndisc_router
*rt
= NULL
;
72 assert_return(ret
, -EINVAL
);
73 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
75 rt
= ndisc_router_new(raw_size
);
79 memcpy(NDISC_ROUTER_RAW(rt
), raw
, raw_size
);
80 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
;
464 assert_return(rt
, -EINVAL
);
465 assert_return(ret
, -EINVAL
);
467 r
= get_prefix_info(rt
, &pi
);
471 *ret
= pi
->nd_opt_pi_flags_reserved
;
475 _public_
int sd_ndisc_router_prefix_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
476 struct nd_opt_prefix_info
*pi
;
479 assert_return(rt
, -EINVAL
);
480 assert_return(ret_addr
, -EINVAL
);
482 r
= get_prefix_info(rt
, &pi
);
486 *ret_addr
= pi
->nd_opt_pi_prefix
;
490 _public_
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
491 struct nd_opt_prefix_info
*pi
;
494 assert_return(rt
, -EINVAL
);
495 assert_return(ret
, -EINVAL
);
497 r
= get_prefix_info(rt
, &pi
);
501 if (pi
->nd_opt_pi_prefix_len
> 128)
504 *ret
= pi
->nd_opt_pi_prefix_len
;
508 static int get_route_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
516 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_ROUTE_INFORMATION
);
522 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
523 if (length
< 1*8 || length
> 3*8)
526 ri
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
535 _public_
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
539 assert_return(rt
, -EINVAL
);
540 assert_return(ret
, -EINVAL
);
542 r
= get_route_info(rt
, &ri
);
546 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
550 _public_
int sd_ndisc_router_route_get_address(sd_ndisc_router
*rt
, struct in6_addr
*ret_addr
) {
554 assert_return(rt
, -EINVAL
);
555 assert_return(ret_addr
, -EINVAL
);
557 r
= get_route_info(rt
, &ri
);
562 memcpy(ret_addr
, ri
+ 8, NDISC_ROUTER_OPTION_LENGTH(rt
) - 8);
567 _public_
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router
*rt
, unsigned *ret
) {
571 assert_return(rt
, -EINVAL
);
572 assert_return(ret
, -EINVAL
);
574 r
= get_route_info(rt
, &ri
);
582 _public_
int sd_ndisc_router_route_get_preference(sd_ndisc_router
*rt
, unsigned *ret
) {
586 assert_return(rt
, -EINVAL
);
587 assert_return(ret
, -EINVAL
);
589 r
= get_route_info(rt
, &ri
);
593 *ret
= (ri
[3] >> 3) & 3;
594 if (!IN_SET(*ret
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_HIGH
))
595 *ret
= SD_NDISC_PREFERENCE_MEDIUM
;
600 static int get_rdnss_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
607 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_RDNSS
);
613 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
614 if (length
< 3*8 || (length
% (2*8)) != 1*8)
617 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
621 _public_
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router
*rt
, const struct in6_addr
**ret
) {
625 assert_return(rt
, -EINVAL
);
626 assert_return(ret
, -EINVAL
);
628 r
= get_rdnss_info(rt
, &ri
);
632 *ret
= (const struct in6_addr
*) (ri
+ 8);
633 return (NDISC_ROUTER_OPTION_LENGTH(rt
) - 8) / 16;
636 _public_
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret
) {
640 assert_return(rt
, -EINVAL
);
641 assert_return(ret
, -EINVAL
);
643 r
= get_rdnss_info(rt
, &ri
);
647 *ret
= be32toh(*(uint32_t*) (ri
+ 4));
651 static int get_dnssl_info(sd_ndisc_router
*rt
, uint8_t **ret
) {
658 r
= sd_ndisc_router_option_is_type(rt
, SD_NDISC_OPTION_DNSSL
);
664 length
= NDISC_ROUTER_OPTION_LENGTH(rt
);
668 *ret
= (uint8_t*) NDISC_ROUTER_RAW(rt
) + rt
->rindex
;
672 _public_
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router
*rt
, char ***ret
) {
673 _cleanup_strv_free_
char **l
= NULL
;
674 _cleanup_free_
char *e
= NULL
;
675 size_t allocated
= 0, n
= 0, left
;
681 assert_return(rt
, -EINVAL
);
682 assert_return(ret
, -EINVAL
);
684 r
= get_dnssl_info(rt
, &ri
);
689 left
= NDISC_ROUTER_OPTION_LENGTH(rt
) - 8;
694 if (n
> 0) /* Not properly NUL terminated */
701 /* Found NUL termination */
704 _cleanup_free_
char *normalized
= NULL
;
707 r
= dns_name_normalize(e
, &normalized
);
711 /* Ignore the root domain name or "localhost" and friends */
712 if (!is_localhost(normalized
) &&
713 !dns_name_is_root(normalized
)) {
715 if (strv_push(&l
, normalized
) < 0)
729 /* Check for compression (which is not allowed) */
733 if (1U + *p
+ 1U > left
)
736 if (!GREEDY_REALLOC(e
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
+ 1U))
744 r
= dns_label_escape((char*) p
+1, *p
, e
+ n
, DNS_LABEL_ESCAPED_MAX
);
754 if (strv_isempty(l
)) {
765 _public_
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router
*rt
, uint32_t *ret_sec
) {
769 assert_return(rt
, -EINVAL
);
770 assert_return(ret_sec
, -EINVAL
);
772 r
= get_dnssl_info(rt
, &ri
);
776 *ret_sec
= be32toh(*(uint32_t*) (ri
+ 4));