1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/icmp6.h>
5 #include "dns-domain.h"
6 #include "ether-addr-util.h"
7 #include "hostname-util.h"
8 #include "icmp6-util.h"
9 #include "in-addr-util.h"
10 #include "iovec-util.h"
11 #include "missing_network.h"
12 #include "ndisc-option.h"
13 #include "network-common.h"
15 #include "unaligned.h"
17 /* RFC does not say anything about the maximum number of options, but let's limit the number of options for
18 * safety. Typically, the number of options in an ICMPv6 message should be only a few. */
19 #define MAX_OPTIONS 128
21 int ndisc_option_parse(
26 const uint8_t **ret_opt
) {
30 if (offset
== p
->raw_size
)
31 return -ESPIPE
; /* end of the packet */
33 if (offset
> p
->raw_size
)
36 if (p
->raw_size
- offset
< sizeof(struct nd_opt_hdr
))
39 assert_cc(alignof(struct nd_opt_hdr
) == 1);
40 const struct nd_opt_hdr
*hdr
= (const struct nd_opt_hdr
*) (p
->raw_packet
+ offset
);
41 if (hdr
->nd_opt_len
== 0)
44 size_t len
= hdr
->nd_opt_len
* 8;
45 if (p
->raw_size
- offset
< len
)
49 *ret_type
= hdr
->nd_opt_type
;
53 *ret_opt
= p
->raw_packet
+ offset
;
58 static sd_ndisc_option
* ndisc_option_new(uint8_t type
, size_t offset
) {
59 sd_ndisc_option
*p
= new0(sd_ndisc_option
, 1); /* use new0() here to make the fuzzers silent. */
63 /* As the same reason in the above, do not use the structured initializer here. */
70 static void ndisc_raw_done(sd_ndisc_raw
*raw
) {
77 static void ndisc_rdnss_done(sd_ndisc_rdnss
*rdnss
) {
81 free(rdnss
->addresses
);
84 static void ndisc_dnssl_done(sd_ndisc_dnssl
*dnssl
) {
88 strv_free(dnssl
->domains
);
91 sd_ndisc_option
* ndisc_option_free(sd_ndisc_option
*option
) {
95 switch (option
->type
) {
97 ndisc_raw_done(&option
->raw
);
100 case SD_NDISC_OPTION_RDNSS
:
101 ndisc_rdnss_done(&option
->rdnss
);
104 case SD_NDISC_OPTION_DNSSL
:
105 ndisc_dnssl_done(&option
->dnssl
);
108 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
109 free(option
->captive_portal
);
113 return mfree(option
);
116 static int ndisc_option_compare_func(const sd_ndisc_option
*x
, const sd_ndisc_option
*y
) {
122 r
= CMP(x
->type
, y
->type
);
128 return memcmp_nn(x
->raw
.bytes
, x
->raw
.length
, y
->raw
.bytes
, y
->raw
.length
);
130 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS
:
131 case SD_NDISC_OPTION_TARGET_LL_ADDRESS
:
132 case SD_NDISC_OPTION_REDIRECTED_HEADER
:
133 case SD_NDISC_OPTION_MTU
:
134 case SD_NDISC_OPTION_HOME_AGENT
:
135 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
136 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
137 /* These options cannot be specified multiple times. */
140 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
141 /* Should not specify the same prefix multiple times. */
142 r
= CMP(x
->prefix
.prefixlen
, y
->prefix
.prefixlen
);
146 return memcmp(&x
->prefix
.address
, &y
->prefix
.address
, sizeof(struct in6_addr
));
148 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
149 r
= CMP(x
->route
.prefixlen
, y
->route
.prefixlen
);
153 return memcmp(&x
->route
.address
, &y
->route
.address
, sizeof(struct in6_addr
));
155 case SD_NDISC_OPTION_PREF64
:
156 r
= CMP(x
->prefix64
.prefixlen
, y
->prefix64
.prefixlen
);
160 return memcmp(&x
->prefix64
.prefix
, &y
->prefix64
.prefix
, sizeof(struct in6_addr
));
163 /* DNSSL, RDNSS, and other unsupported options can be specified multiple times. */
164 return trivial_compare_func(x
, y
);
168 static void ndisc_option_hash_func(const sd_ndisc_option
*option
, struct siphash
*state
) {
172 siphash24_compress_typesafe(option
->type
, state
);
174 switch (option
->type
) {
176 siphash24_compress(option
->raw
.bytes
, option
->raw
.length
, state
);
179 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS
:
180 case SD_NDISC_OPTION_TARGET_LL_ADDRESS
:
181 case SD_NDISC_OPTION_REDIRECTED_HEADER
:
182 case SD_NDISC_OPTION_MTU
:
183 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
184 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
187 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
188 siphash24_compress_typesafe(option
->prefix
.prefixlen
, state
);
189 siphash24_compress_typesafe(option
->prefix
.address
, state
);
192 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
193 siphash24_compress_typesafe(option
->route
.prefixlen
, state
);
194 siphash24_compress_typesafe(option
->route
.address
, state
);
197 case SD_NDISC_OPTION_PREF64
:
198 siphash24_compress_typesafe(option
->prefix64
.prefixlen
, state
);
199 siphash24_compress_typesafe(option
->prefix64
.prefix
, state
);
203 trivial_hash_func(option
, state
);
207 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
208 ndisc_option_hash_ops
,
210 ndisc_option_hash_func
,
211 ndisc_option_compare_func
,
214 static int ndisc_option_consume(Set
**options
, sd_ndisc_option
*p
) {
218 if (set_size(*options
) >= MAX_OPTIONS
) {
219 ndisc_option_free(p
);
220 return -ETOOMANYREFS
; /* recognizable error code */
223 return set_ensure_consume(options
, &ndisc_option_hash_ops
, p
);
226 int ndisc_option_set_raw(Set
**options
, size_t length
, const uint8_t *bytes
) {
227 _cleanup_free_
uint8_t *copy
= NULL
;
235 copy
= newdup(uint8_t, bytes
, length
);
239 sd_ndisc_option
*p
= ndisc_option_new(/* type = */ 0, /* offset = */ 0);
243 p
->raw
= (sd_ndisc_raw
) {
244 .bytes
= TAKE_PTR(copy
),
248 return ndisc_option_consume(options
, p
);
251 static int ndisc_option_build_raw(const sd_ndisc_option
*option
, uint8_t **ret
) {
253 assert(option
->type
== 0);
256 _cleanup_free_
uint8_t *buf
= newdup(uint8_t, option
->raw
.bytes
, option
->raw
.length
);
260 *ret
= TAKE_PTR(buf
);
264 int ndisc_option_add_link_layer_address(Set
**options
, uint8_t type
, size_t offset
, const struct ether_addr
*mac
) {
266 assert(IN_SET(type
, SD_NDISC_OPTION_SOURCE_LL_ADDRESS
, SD_NDISC_OPTION_TARGET_LL_ADDRESS
));
268 if (!mac
|| ether_addr_is_null(mac
)) {
269 ndisc_option_remove_by_type(*options
, type
);
273 sd_ndisc_option
*p
= ndisc_option_get_by_type(*options
, type
);
275 /* offset == 0 means that we are now building a packet to be sent, and in that case we allow
276 * to override the option we previously set.
277 * offset != 0 means that we are now parsing a received packet, and we refuse to override
278 * conflicting options. */
286 p
= ndisc_option_new(type
, offset
);
292 return set_ensure_consume(options
, &ndisc_option_hash_ops
, p
);
295 static int ndisc_option_parse_link_layer_address(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
299 if (len
!= sizeof(struct ether_addr
) + 2)
302 if (!IN_SET(opt
[0], SD_NDISC_OPTION_SOURCE_LL_ADDRESS
, SD_NDISC_OPTION_TARGET_LL_ADDRESS
))
305 struct ether_addr mac
;
306 memcpy(&mac
, opt
+ 2, sizeof(struct ether_addr
));
308 if (ether_addr_is_null(&mac
))
311 return ndisc_option_add_link_layer_address(options
, opt
[0], offset
, &mac
);
314 static int ndisc_option_build_link_layer_address(const sd_ndisc_option
*option
, uint8_t **ret
) {
316 assert(IN_SET(option
->type
, SD_NDISC_OPTION_SOURCE_LL_ADDRESS
, SD_NDISC_OPTION_TARGET_LL_ADDRESS
));
319 assert_cc(2 + sizeof(struct ether_addr
) == 8);
321 _cleanup_free_
uint8_t *buf
= new(uint8_t, 2 + sizeof(struct ether_addr
));
325 buf
[0] = option
->type
;
327 memcpy(buf
+ 2, &option
->mac
, sizeof(struct ether_addr
));
329 *ret
= TAKE_PTR(buf
);
333 int ndisc_option_add_prefix_internal(
338 const struct in6_addr
*address
,
339 usec_t valid_lifetime
,
340 usec_t preferred_lifetime
,
342 usec_t preferred_until
) {
350 struct in6_addr addr
= *address
;
351 in6_addr_mask(&addr
, prefixlen
);
353 /* RFC 4861 and 4862 only state that link-local prefix should be ignored.
354 * But here we also ignore null and multicast addresses. */
355 if (in6_addr_is_link_local(&addr
) || in6_addr_is_null(&addr
) || in6_addr_is_multicast(&addr
))
358 if (preferred_lifetime
> valid_lifetime
)
361 if (preferred_until
> valid_until
)
364 sd_ndisc_option
*p
= ndisc_option_get(
366 &(const sd_ndisc_option
) {
367 .type
= SD_NDISC_OPTION_PREFIX_INFORMATION
,
368 .prefix
.prefixlen
= prefixlen
,
369 .prefix
.address
= addr
,
375 p
->prefix
.flags
= flags
;
376 p
->prefix
.valid_lifetime
= valid_lifetime
;
377 p
->prefix
.preferred_lifetime
= preferred_lifetime
;
378 p
->prefix
.valid_until
= valid_until
;
379 p
->prefix
.preferred_until
= preferred_until
;
383 p
= ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION
, offset
);
387 p
->prefix
= (sd_ndisc_prefix
) {
389 .prefixlen
= prefixlen
,
391 .valid_lifetime
= valid_lifetime
,
392 .preferred_lifetime
= preferred_lifetime
,
393 .valid_until
= valid_until
,
394 .preferred_until
= preferred_until
,
397 return ndisc_option_consume(options
, p
);
400 static int ndisc_option_parse_prefix(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
401 const struct nd_opt_prefix_info
*pi
= (const struct nd_opt_prefix_info
*) ASSERT_PTR(opt
);
405 if (len
!= sizeof(struct nd_opt_prefix_info
))
408 if (pi
->nd_opt_pi_type
!= SD_NDISC_OPTION_PREFIX_INFORMATION
)
411 usec_t valid
= be32_sec_to_usec(pi
->nd_opt_pi_valid_time
, /* max_as_infinity = */ true);
412 usec_t pref
= be32_sec_to_usec(pi
->nd_opt_pi_preferred_time
, /* max_as_infinity = */ true);
414 /* We only support 64 bits interface identifier for addrconf. */
415 uint8_t flags
= pi
->nd_opt_pi_flags_reserved
;
416 if (FLAGS_SET(flags
, ND_OPT_PI_FLAG_AUTO
) && pi
->nd_opt_pi_prefix_len
!= 64)
417 flags
&= ~ND_OPT_PI_FLAG_AUTO
;
419 return ndisc_option_add_prefix(options
, offset
, flags
,
420 pi
->nd_opt_pi_prefix_len
, &pi
->nd_opt_pi_prefix
,
424 static int ndisc_option_build_prefix(const sd_ndisc_option
*option
, usec_t timestamp
, uint8_t **ret
) {
426 assert(option
->type
== SD_NDISC_OPTION_PREFIX_INFORMATION
);
429 assert_cc(sizeof(struct nd_opt_prefix_info
) % 8 == 0);
431 _cleanup_free_
struct nd_opt_prefix_info
*buf
= new(struct nd_opt_prefix_info
, 1);
435 usec_t valid
= MIN(option
->prefix
.valid_lifetime
,
436 usec_sub_unsigned(option
->prefix
.valid_until
, timestamp
));
437 usec_t pref
= MIN3(valid
,
438 option
->prefix
.preferred_lifetime
,
439 usec_sub_unsigned(option
->prefix
.preferred_until
, timestamp
));
441 *buf
= (struct nd_opt_prefix_info
) {
442 .nd_opt_pi_type
= SD_NDISC_OPTION_PREFIX_INFORMATION
,
443 .nd_opt_pi_len
= sizeof(struct nd_opt_prefix_info
) / 8,
444 .nd_opt_pi_prefix_len
= option
->prefix
.prefixlen
,
445 .nd_opt_pi_flags_reserved
= option
->prefix
.flags
,
446 .nd_opt_pi_valid_time
= usec_to_be32_sec(valid
),
447 .nd_opt_pi_preferred_time
= usec_to_be32_sec(pref
),
448 .nd_opt_pi_prefix
= option
->prefix
.address
,
451 *ret
= (uint8_t*) TAKE_PTR(buf
);
455 int ndisc_option_add_redirected_header(Set
**options
, size_t offset
, const struct ip6_hdr
*hdr
) {
459 ndisc_option_remove_by_type(*options
, SD_NDISC_OPTION_REDIRECTED_HEADER
);
463 sd_ndisc_option
*p
= ndisc_option_get_by_type(*options
, SD_NDISC_OPTION_REDIRECTED_HEADER
);
468 memcpy(&p
->hdr
, hdr
, sizeof(struct ip6_hdr
));
472 p
= ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER
, offset
);
476 /* For safety, here we copy only IPv6 header. */
477 memcpy(&p
->hdr
, hdr
, sizeof(struct ip6_hdr
));
479 return ndisc_option_consume(options
, p
);
482 static int ndisc_option_parse_redirected_header(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
486 if (len
< sizeof(struct nd_opt_rd_hdr
) + sizeof(struct ip6_hdr
))
489 if (opt
[0] != SD_NDISC_OPTION_REDIRECTED_HEADER
)
492 return ndisc_option_add_redirected_header(options
, offset
, (const struct ip6_hdr
*) (opt
+ sizeof(struct nd_opt_rd_hdr
)));
495 static int ndisc_option_build_redirected_header(const sd_ndisc_option
*option
, uint8_t **ret
) {
497 assert(option
->type
== SD_NDISC_OPTION_REDIRECTED_HEADER
);
500 assert_cc((sizeof(struct nd_opt_rd_hdr
) + sizeof(struct ip6_hdr
)) % 8 == 0);
502 size_t len
= DIV_ROUND_UP(sizeof(struct nd_opt_rd_hdr
) + sizeof(struct ip6_hdr
), 8);
504 _cleanup_free_
uint8_t *buf
= new(uint8_t, len
* 8);
510 &(const struct nd_opt_rd_hdr
) {
511 .nd_opt_rh_type
= SD_NDISC_OPTION_REDIRECTED_HEADER
,
512 .nd_opt_rh_len
= len
,
514 sizeof(struct nd_opt_rd_hdr
));
515 memcpy(p
, &option
->hdr
, sizeof(struct ip6_hdr
));
517 *ret
= TAKE_PTR(buf
);
521 int ndisc_option_add_mtu(Set
**options
, size_t offset
, uint32_t mtu
) {
524 if (mtu
< IPV6_MIN_MTU
)
527 sd_ndisc_option
*p
= ndisc_option_get_by_type(*options
, SD_NDISC_OPTION_MTU
);
536 p
= ndisc_option_new(SD_NDISC_OPTION_MTU
, offset
);
542 return ndisc_option_consume(options
, p
);
545 static int ndisc_option_parse_mtu(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
546 const struct nd_opt_mtu
*pm
= (const struct nd_opt_mtu
*) ASSERT_PTR(opt
);
550 if (len
!= sizeof(struct nd_opt_mtu
))
553 if (pm
->nd_opt_mtu_type
!= SD_NDISC_OPTION_MTU
)
556 return ndisc_option_add_mtu(options
, offset
, be32toh(pm
->nd_opt_mtu_mtu
));
559 static int ndisc_option_build_mtu(const sd_ndisc_option
*option
, uint8_t **ret
) {
561 assert(option
->type
== SD_NDISC_OPTION_MTU
);
564 assert_cc(sizeof(struct nd_opt_mtu
) % 8 == 0);
566 _cleanup_free_
struct nd_opt_mtu
*buf
= new(struct nd_opt_mtu
, 1);
570 *buf
= (struct nd_opt_mtu
) {
571 .nd_opt_mtu_type
= SD_NDISC_OPTION_MTU
,
572 .nd_opt_mtu_len
= sizeof(struct nd_opt_mtu
) / 8,
573 .nd_opt_mtu_mtu
= htobe32(option
->mtu
),
576 *ret
= (uint8_t*) TAKE_PTR(buf
);
580 int ndisc_option_add_home_agent_internal(
585 usec_t valid_until
) {
589 if (lifetime
> UINT16_MAX
* USEC_PER_SEC
)
592 sd_ndisc_option
*p
= ndisc_option_get_by_type(*options
, SD_NDISC_OPTION_HOME_AGENT
);
597 p
->home_agent
= (sd_ndisc_home_agent
) {
598 .preference
= preference
,
599 .lifetime
= lifetime
,
600 .valid_until
= valid_until
,
605 p
= ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT
, offset
);
609 p
->home_agent
= (sd_ndisc_home_agent
) {
610 .preference
= preference
,
611 .lifetime
= lifetime
,
612 .valid_until
= valid_until
,
615 return ndisc_option_consume(options
, p
);
618 static int ndisc_option_parse_home_agent(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
619 const struct nd_opt_home_agent_info
*p
= (const struct nd_opt_home_agent_info
*) ASSERT_PTR(opt
);
623 if (len
!= sizeof(struct nd_opt_home_agent_info
))
626 if (p
->nd_opt_home_agent_info_type
!= SD_NDISC_OPTION_HOME_AGENT
)
629 return ndisc_option_add_home_agent(
631 be16toh(p
->nd_opt_home_agent_info_preference
),
632 be16_sec_to_usec(p
->nd_opt_home_agent_info_lifetime
, /* max_as_infinity = */ false));
635 static int ndisc_option_build_home_agent(const sd_ndisc_option
*option
, usec_t timestamp
, uint8_t **ret
) {
637 assert(option
->type
== SD_NDISC_OPTION_HOME_AGENT
);
640 assert_cc(sizeof(struct nd_opt_home_agent_info
) % 8 == 0);
642 usec_t lifetime
= MIN(option
->home_agent
.lifetime
,
643 usec_sub_unsigned(option
->home_agent
.valid_until
, timestamp
));
645 _cleanup_free_
struct nd_opt_home_agent_info
*buf
= new(struct nd_opt_home_agent_info
, 1);
649 *buf
= (struct nd_opt_home_agent_info
) {
650 .nd_opt_home_agent_info_type
= SD_NDISC_OPTION_HOME_AGENT
,
651 .nd_opt_home_agent_info_len
= sizeof(struct nd_opt_home_agent_info
) / 8,
652 .nd_opt_home_agent_info_preference
= htobe16(option
->home_agent
.preference
),
653 .nd_opt_home_agent_info_lifetime
= usec_to_be16_sec(lifetime
),
656 *ret
= (uint8_t*) TAKE_PTR(buf
);
660 int ndisc_option_add_route_internal(
665 const struct in6_addr
*prefix
,
667 usec_t valid_until
) {
675 /* RFC 4191 section 2.3
676 * Prf (Route Preference)
677 * 2-bit signed integer. The Route Preference indicates whether to prefer the router associated with
678 * this prefix over others, when multiple identical prefixes (for different routers) have been
679 * received. If the Reserved (10) value is received, the Route Information Option MUST be ignored. */
680 if (!IN_SET(preference
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_MEDIUM
, SD_NDISC_PREFERENCE_HIGH
))
683 struct in6_addr addr
= *prefix
;
684 in6_addr_mask(&addr
, prefixlen
);
686 sd_ndisc_option
*p
= ndisc_option_get(
688 &(const sd_ndisc_option
) {
689 .type
= SD_NDISC_OPTION_ROUTE_INFORMATION
,
690 .route
.prefixlen
= prefixlen
,
691 .route
.address
= addr
,
697 p
->route
.preference
= preference
;
698 p
->route
.lifetime
= lifetime
;
699 p
->route
.valid_until
= valid_until
;
703 p
= ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION
, offset
);
707 p
->route
= (sd_ndisc_route
) {
708 .preference
= preference
,
709 .prefixlen
= prefixlen
,
711 .lifetime
= lifetime
,
712 .valid_until
= valid_until
,
715 return ndisc_option_consume(options
, p
);
718 static int ndisc_option_parse_route(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
722 if (!IN_SET(len
, 1*8, 2*8, 3*8))
725 if (opt
[0] != SD_NDISC_OPTION_ROUTE_INFORMATION
)
728 uint8_t prefixlen
= opt
[2];
732 if (len
< (size_t) (DIV_ROUND_UP(prefixlen
, 64) + 1) * 8)
735 uint8_t preference
= (opt
[3] >> 3) & 0x03;
736 usec_t lifetime
= unaligned_be32_sec_to_usec(opt
+ 4, /* max_as_infinity = */ true);
738 struct in6_addr prefix
;
739 memcpy(&prefix
, opt
+ 8, len
- 8);
740 in6_addr_mask(&prefix
, prefixlen
);
742 return ndisc_option_add_route(options
, offset
, preference
, prefixlen
, &prefix
, lifetime
);
745 static int ndisc_option_build_route(const sd_ndisc_option
*option
, usec_t timestamp
, uint8_t **ret
) {
747 assert(option
->type
== SD_NDISC_OPTION_ROUTE_INFORMATION
);
748 assert(option
->route
.prefixlen
<= 128);
751 size_t len
= 1 + DIV_ROUND_UP(option
->route
.prefixlen
, 64);
752 be32_t lifetime
= usec_to_be32_sec(MIN(option
->route
.lifetime
,
753 usec_sub_unsigned(option
->route
.valid_until
, timestamp
)));
755 _cleanup_free_
uint8_t *buf
= new(uint8_t, len
* 8);
759 buf
[0] = SD_NDISC_OPTION_ROUTE_INFORMATION
;
761 buf
[2] = option
->route
.prefixlen
;
762 buf
[3] = option
->route
.preference
<< 3;
763 memcpy(buf
+ 4, &lifetime
, sizeof(be32_t
));
764 memcpy_safe(buf
+ 8, &option
->route
.address
, (len
- 1) * 8);
766 *ret
= TAKE_PTR(buf
);
770 int ndisc_option_add_rdnss_internal(
774 const struct in6_addr
*addresses
,
776 usec_t valid_until
) {
781 if (n_addresses
== 0)
784 _cleanup_free_
struct in6_addr
*addrs
= newdup(struct in6_addr
, addresses
, n_addresses
);
788 sd_ndisc_option
*p
= ndisc_option_new(SD_NDISC_OPTION_RDNSS
, offset
);
792 p
->rdnss
= (sd_ndisc_rdnss
) {
793 .n_addresses
= n_addresses
,
794 .addresses
= TAKE_PTR(addrs
),
795 .lifetime
= lifetime
,
796 .valid_until
= valid_until
,
799 return ndisc_option_consume(options
, p
);
802 static int ndisc_option_parse_rdnss(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
806 if (len
< 8 + sizeof(struct in6_addr
) || (len
% sizeof(struct in6_addr
)) != 8)
809 if (opt
[0] != SD_NDISC_OPTION_RDNSS
)
812 usec_t lifetime
= unaligned_be32_sec_to_usec(opt
+ 4, /* max_as_infinity = */ true);
813 size_t n_addrs
= len
/ sizeof(struct in6_addr
);
815 return ndisc_option_add_rdnss(options
, offset
, n_addrs
, (const struct in6_addr
*) (opt
+ 8), lifetime
);
818 static int ndisc_option_build_rdnss(const sd_ndisc_option
*option
, usec_t timestamp
, uint8_t **ret
) {
820 assert(option
->type
== SD_NDISC_OPTION_RDNSS
);
823 size_t len
= option
->rdnss
.n_addresses
* 2 + 1;
824 be32_t lifetime
= usec_to_be32_sec(MIN(option
->rdnss
.lifetime
,
825 usec_sub_unsigned(option
->rdnss
.valid_until
, timestamp
)));
827 _cleanup_free_
uint8_t *buf
= new(uint8_t, len
* 8);
831 buf
[0] = SD_NDISC_OPTION_RDNSS
;
835 memcpy(buf
+ 4, &lifetime
, sizeof(be32_t
));
836 memcpy(buf
+ 8, option
->rdnss
.addresses
, sizeof(struct in6_addr
) * option
->rdnss
.n_addresses
);
838 *ret
= TAKE_PTR(buf
);
842 int ndisc_option_add_flags_extension(Set
**options
, size_t offset
, uint64_t flags
) {
845 if ((flags
& UINT64_C(0x00ffffffffffff00)) != flags
)
848 sd_ndisc_option
*p
= ndisc_option_get_by_type(*options
, SD_NDISC_OPTION_FLAGS_EXTENSION
);
853 p
->extended_flags
= flags
;
857 p
= ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION
, offset
);
861 p
->extended_flags
= flags
;
863 return ndisc_option_consume(options
, p
);
866 static int ndisc_option_parse_flags_extension(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
873 if (opt
[0] != SD_NDISC_OPTION_FLAGS_EXTENSION
)
876 uint64_t flags
= (unaligned_read_be64(opt
) & UINT64_C(0xffffffffffff0000)) >> 8;
877 return ndisc_option_add_flags_extension(options
, offset
, flags
);
880 static int ndisc_option_build_flags_extension(const sd_ndisc_option
*option
, uint8_t **ret
) {
882 assert(option
->type
== SD_NDISC_OPTION_FLAGS_EXTENSION
);
885 _cleanup_free_
uint8_t *buf
= new(uint8_t, 8);
889 unaligned_write_be64(buf
, (option
->extended_flags
& UINT64_C(0x00ffffffffffff00)) << 8);
890 buf
[0] = SD_NDISC_OPTION_FLAGS_EXTENSION
;
893 *ret
= TAKE_PTR(buf
);
897 int ndisc_option_add_dnssl_internal(
899 size_t offset
, char *
902 usec_t valid_until
) {
908 if (strv_isempty(domains
))
911 STRV_FOREACH(s
, domains
) {
912 r
= dns_name_is_valid(*s
);
916 if (is_localhost(*s
) || dns_name_is_root(*s
))
920 _cleanup_strv_free_
char **copy
= strv_copy(domains
);
924 sd_ndisc_option
*p
= ndisc_option_new(SD_NDISC_OPTION_DNSSL
, offset
);
928 p
->dnssl
= (sd_ndisc_dnssl
) {
929 .domains
= TAKE_PTR(copy
),
930 .lifetime
= lifetime
,
931 .valid_until
= valid_until
,
934 return ndisc_option_consume(options
, p
);
937 static int ndisc_option_parse_dnssl(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
946 if (opt
[0] != SD_NDISC_OPTION_DNSSL
)
949 usec_t lifetime
= unaligned_be32_sec_to_usec(opt
+ 4, /* max_as_infinity = */ true);
951 _cleanup_strv_free_
char **l
= NULL
;
952 _cleanup_free_
char *e
= NULL
;
954 for (size_t c
, pos
= 8; pos
< len
; pos
+= c
) {
960 /* Found NUL termination */
963 _cleanup_free_
char *normalized
= NULL
;
966 r
= dns_name_normalize(e
, 0, &normalized
);
970 /* Ignore the root domain name or "localhost" and friends */
971 if (!is_localhost(normalized
) && !dns_name_is_root(normalized
)) {
972 r
= strv_consume(&l
, TAKE_PTR(normalized
));
982 /* Check for compression (which is not allowed) */
989 if (!GREEDY_REALLOC(e
, n
+ (n
!= 0) + DNS_LABEL_ESCAPED_MAX
+ 1U))
995 r
= dns_label_escape((const char*) (opt
+ pos
), c
, e
+ n
, DNS_LABEL_ESCAPED_MAX
);
1002 if (n
> 0) /* Not properly NUL terminated */
1005 return ndisc_option_add_dnssl(options
, offset
, l
, lifetime
);
1008 static int ndisc_option_build_dnssl(const sd_ndisc_option
*option
, usec_t timestamp
, uint8_t **ret
) {
1012 assert(option
->type
== SD_NDISC_OPTION_DNSSL
);
1016 STRV_FOREACH(s
, option
->dnssl
.domains
)
1017 len
+= strlen(*s
) + 2;
1018 len
= DIV_ROUND_UP(len
, 8);
1020 be32_t lifetime
= usec_to_be32_sec(MIN(option
->dnssl
.lifetime
,
1021 usec_sub_unsigned(option
->dnssl
.valid_until
, timestamp
)));
1023 _cleanup_free_
uint8_t *buf
= new(uint8_t, len
* 8);
1027 buf
[0] = SD_NDISC_OPTION_DNSSL
;
1031 memcpy(buf
+ 4, &lifetime
, sizeof(be32_t
));
1033 size_t remaining
= len
* 8 - 8;
1034 uint8_t *p
= buf
+ 8;
1036 STRV_FOREACH(s
, option
->dnssl
.domains
) {
1037 r
= dns_name_to_wire_format(*s
, p
, remaining
, /* canonical = */ false);
1041 assert(remaining
>= (size_t) r
);
1046 memzero(p
, remaining
);
1048 *ret
= TAKE_PTR(buf
);
1052 int ndisc_option_add_captive_portal(Set
**options
, size_t offset
, const char *portal
) {
1055 if (isempty(portal
))
1058 if (!in_charset(portal
, URI_VALID
))
1061 sd_ndisc_option
*p
= ndisc_option_get_by_type(*options
, SD_NDISC_OPTION_CAPTIVE_PORTAL
);
1066 return free_and_strdup(&p
->captive_portal
, portal
);
1069 _cleanup_free_
char *copy
= strdup(portal
);
1073 p
= ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL
, offset
);
1077 p
->captive_portal
= TAKE_PTR(copy
);
1079 return ndisc_option_consume(options
, p
);
1082 static int ndisc_option_parse_captive_portal(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
1089 if (opt
[0] != SD_NDISC_OPTION_CAPTIVE_PORTAL
)
1092 _cleanup_free_
char *portal
= memdup_suffix0(opt
+ 2, len
- 2);
1096 size_t size
= strlen(portal
);
1100 /* Check that the message is not truncated by an embedded NUL.
1101 * NUL padding to a multiple of 8 is expected. */
1102 if (DIV_ROUND_UP(size
+ 2, 8) * 8 != len
&& DIV_ROUND_UP(size
+ 3, 8) * 8 != len
)
1105 return ndisc_option_add_captive_portal(options
, offset
, portal
);
1108 static int ndisc_option_build_captive_portal(const sd_ndisc_option
*option
, uint8_t **ret
) {
1110 assert(option
->type
== SD_NDISC_OPTION_CAPTIVE_PORTAL
);
1113 size_t len_portal
= strlen(option
->captive_portal
);
1114 size_t len
= DIV_ROUND_UP(len_portal
+ 1 + 2, 8);
1116 _cleanup_free_
uint8_t *buf
= new(uint8_t, len
* 8);
1120 buf
[0] = SD_NDISC_OPTION_CAPTIVE_PORTAL
;
1123 uint8_t *p
= mempcpy(buf
+ 2, option
->captive_portal
, len_portal
);
1124 size_t remaining
= len
* 8 - 2 - len_portal
;
1126 memzero(p
, remaining
);
1128 *ret
= TAKE_PTR(buf
);
1132 static const uint8_t prefix_length_code_to_prefix_length
[_PREFIX_LENGTH_CODE_MAX
] = {
1133 [PREFIX_LENGTH_CODE_96
] = 96,
1134 [PREFIX_LENGTH_CODE_64
] = 64,
1135 [PREFIX_LENGTH_CODE_56
] = 56,
1136 [PREFIX_LENGTH_CODE_48
] = 48,
1137 [PREFIX_LENGTH_CODE_40
] = 40,
1138 [PREFIX_LENGTH_CODE_32
] = 32,
1141 int pref64_prefix_length_to_plc(uint8_t prefixlen
, uint8_t *ret
) {
1142 for (size_t i
= 0; i
< ELEMENTSOF(prefix_length_code_to_prefix_length
); i
++)
1143 if (prefix_length_code_to_prefix_length
[i
] == prefixlen
) {
1152 static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc
, uint8_t *ret_prefixlen
, usec_t
*ret_lifetime
) {
1153 uint16_t plc
= lifetime_and_plc
& PREF64_PLC_MASK
;
1154 if (plc
>= _PREFIX_LENGTH_CODE_MAX
)
1158 *ret_prefixlen
= prefix_length_code_to_prefix_length
[plc
];
1160 *ret_lifetime
= (lifetime_and_plc
& PREF64_SCALED_LIFETIME_MASK
) * USEC_PER_SEC
;
1164 int ndisc_option_add_prefix64_internal(
1168 const struct in6_addr
*prefix
,
1170 usec_t valid_until
) {
1177 r
= pref64_prefix_length_to_plc(prefixlen
, NULL
);
1181 if (lifetime
> PREF64_MAX_LIFETIME_USEC
)
1184 struct in6_addr addr
= *prefix
;
1185 in6_addr_mask(&addr
, prefixlen
);
1187 sd_ndisc_option
*p
= ndisc_option_get(
1189 &(const sd_ndisc_option
) {
1190 .type
= SD_NDISC_OPTION_PREF64
,
1191 .prefix64
.prefixlen
= prefixlen
,
1192 .prefix64
.prefix
= addr
,
1198 p
->prefix64
.lifetime
= lifetime
;
1199 p
->prefix64
.valid_until
= valid_until
;
1203 p
= ndisc_option_new(SD_NDISC_OPTION_PREF64
, offset
);
1207 p
->prefix64
= (sd_ndisc_prefix64
) {
1208 .prefixlen
= prefixlen
,
1210 .lifetime
= lifetime
,
1211 .valid_until
= valid_until
,
1214 return ndisc_option_consume(options
, p
);
1217 static int ndisc_option_parse_prefix64(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
1226 if (opt
[0] != SD_NDISC_OPTION_PREF64
)
1231 r
= pref64_lifetime_and_plc_parse(unaligned_read_be16(opt
+ 2), &prefixlen
, &lifetime
);
1235 struct in6_addr prefix
;
1236 memcpy(&prefix
, opt
+ 4, len
- 4);
1237 in6_addr_mask(&prefix
, prefixlen
);
1239 return ndisc_option_add_prefix64(options
, offset
, prefixlen
, &prefix
, lifetime
);
1242 static int ndisc_option_build_prefix64(const sd_ndisc_option
*option
, usec_t timestamp
, uint8_t **ret
) {
1246 assert(option
->type
== SD_NDISC_OPTION_PREF64
);
1250 r
= pref64_prefix_length_to_plc(option
->prefix64
.prefixlen
, &code
);
1254 uint16_t lifetime
= (uint16_t) DIV_ROUND_UP(MIN(option
->prefix64
.lifetime
,
1255 usec_sub_unsigned(option
->prefix64
.valid_until
, timestamp
)),
1256 USEC_PER_SEC
) & PREF64_SCALED_LIFETIME_MASK
;
1258 _cleanup_free_
uint8_t *buf
= new(uint8_t, 2 * 8);
1262 buf
[0] = SD_NDISC_OPTION_PREF64
;
1264 unaligned_write_be16(buf
+ 2, lifetime
| code
);
1265 memcpy(buf
+ 4, &option
->prefix64
.prefix
, 12);
1267 *ret
= TAKE_PTR(buf
);
1271 static int ndisc_option_parse_default(Set
**options
, size_t offset
, size_t len
, const uint8_t *opt
) {
1276 sd_ndisc_option
*p
= ndisc_option_new(opt
[0], offset
);
1280 return ndisc_option_consume(options
, p
);
1283 static int ndisc_header_size(uint8_t icmp6_type
) {
1284 switch (icmp6_type
) {
1285 case ND_ROUTER_SOLICIT
:
1286 return sizeof(struct nd_router_solicit
);
1287 case ND_ROUTER_ADVERT
:
1288 return sizeof(struct nd_router_advert
);
1289 case ND_NEIGHBOR_SOLICIT
:
1290 return sizeof(struct nd_neighbor_solicit
);
1291 case ND_NEIGHBOR_ADVERT
:
1292 return sizeof(struct nd_neighbor_advert
);
1294 return sizeof(struct nd_redirect
);
1300 int ndisc_parse_options(ICMP6Packet
*packet
, Set
**ret_options
) {
1301 _cleanup_set_free_ Set
*options
= NULL
;
1305 assert(ret_options
);
1307 r
= icmp6_packet_get_type(packet
);
1311 r
= ndisc_header_size(r
);
1314 size_t header_size
= r
;
1316 if (packet
->raw_size
< header_size
)
1319 for (size_t length
, offset
= header_size
; offset
< packet
->raw_size
; offset
+= length
) {
1323 r
= ndisc_option_parse(packet
, offset
, &type
, &length
, &opt
);
1325 return log_debug_errno(r
, "Failed to parse NDisc option header: %m");
1332 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS
:
1333 case SD_NDISC_OPTION_TARGET_LL_ADDRESS
:
1334 r
= ndisc_option_parse_link_layer_address(&options
, offset
, length
, opt
);
1337 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
1338 r
= ndisc_option_parse_prefix(&options
, offset
, length
, opt
);
1341 case SD_NDISC_OPTION_REDIRECTED_HEADER
:
1342 r
= ndisc_option_parse_redirected_header(&options
, offset
, length
, opt
);
1345 case SD_NDISC_OPTION_MTU
:
1346 r
= ndisc_option_parse_mtu(&options
, offset
, length
, opt
);
1349 case SD_NDISC_OPTION_HOME_AGENT
:
1350 r
= ndisc_option_parse_home_agent(&options
, offset
, length
, opt
);
1353 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
1354 r
= ndisc_option_parse_route(&options
, offset
, length
, opt
);
1357 case SD_NDISC_OPTION_RDNSS
:
1358 r
= ndisc_option_parse_rdnss(&options
, offset
, length
, opt
);
1361 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
1362 r
= ndisc_option_parse_flags_extension(&options
, offset
, length
, opt
);
1365 case SD_NDISC_OPTION_DNSSL
:
1366 r
= ndisc_option_parse_dnssl(&options
, offset
, length
, opt
);
1369 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
1370 r
= ndisc_option_parse_captive_portal(&options
, offset
, length
, opt
);
1373 case SD_NDISC_OPTION_PREF64
:
1374 r
= ndisc_option_parse_prefix64(&options
, offset
, length
, opt
);
1378 r
= ndisc_option_parse_default(&options
, offset
, length
, opt
);
1381 return log_oom_debug();
1383 log_debug_errno(r
, "Failed to parse NDisc option %u, ignoring: %m", type
);
1386 *ret_options
= TAKE_PTR(options
);
1390 int ndisc_option_get_mac(Set
*options
, uint8_t type
, struct ether_addr
*ret
) {
1391 assert(IN_SET(type
, SD_NDISC_OPTION_SOURCE_LL_ADDRESS
, SD_NDISC_OPTION_TARGET_LL_ADDRESS
));
1393 sd_ndisc_option
*p
= ndisc_option_get_by_type(options
, type
);
1402 int ndisc_send(int fd
, const struct in6_addr
*dst
, const struct icmp6_hdr
*hdr
, Set
*options
, usec_t timestamp
) {
1410 _cleanup_free_ sd_ndisc_option
**list
= NULL
;
1411 r
= set_dump_sorted(options
, (void***) &list
, &n
);
1415 struct iovec
*iov
= NULL
;
1417 CLEANUP_ARRAY(iov
, n_iov
, iovec_array_free
);
1419 iov
= new(struct iovec
, 1 + n
);
1423 r
= ndisc_header_size(hdr
->icmp6_type
);
1426 size_t hdr_size
= r
;
1428 _cleanup_free_
uint8_t *copy
= newdup(uint8_t, hdr
, hdr_size
);
1432 iov
[n_iov
++] = IOVEC_MAKE(TAKE_PTR(copy
), hdr_size
);
1434 FOREACH_ARRAY(p
, list
, n
) {
1435 _cleanup_free_
uint8_t *buf
= NULL
;
1436 sd_ndisc_option
*option
= *p
;
1438 switch (option
->type
) {
1440 r
= ndisc_option_build_raw(option
, &buf
);
1443 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS
:
1444 case SD_NDISC_OPTION_TARGET_LL_ADDRESS
:
1445 r
= ndisc_option_build_link_layer_address(option
, &buf
);
1448 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
1449 r
= ndisc_option_build_prefix(option
, timestamp
, &buf
);
1452 case SD_NDISC_OPTION_REDIRECTED_HEADER
:
1453 r
= ndisc_option_build_redirected_header(option
, &buf
);
1456 case SD_NDISC_OPTION_MTU
:
1457 r
= ndisc_option_build_mtu(option
, &buf
);
1460 case SD_NDISC_OPTION_HOME_AGENT
:
1461 r
= ndisc_option_build_home_agent(option
, timestamp
, &buf
);
1464 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
1465 r
= ndisc_option_build_route(option
, timestamp
, &buf
);
1468 case SD_NDISC_OPTION_RDNSS
:
1469 r
= ndisc_option_build_rdnss(option
, timestamp
, &buf
);
1472 case SD_NDISC_OPTION_FLAGS_EXTENSION
:
1473 r
= ndisc_option_build_flags_extension(option
, &buf
);
1476 case SD_NDISC_OPTION_DNSSL
:
1477 r
= ndisc_option_build_dnssl(option
, timestamp
, &buf
);
1480 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
1481 r
= ndisc_option_build_captive_portal(option
, &buf
);
1484 case SD_NDISC_OPTION_PREF64
:
1485 r
= ndisc_option_build_prefix64(option
, timestamp
, &buf
);
1492 return log_oom_debug();
1494 log_debug_errno(r
, "Failed to build NDisc option %u, ignoring: %m", option
->type
);
1496 iov
[n_iov
++] = IOVEC_MAKE(buf
, buf
[1] * 8);
1500 return icmp6_send(fd
, dst
, iov
, n_iov
);