1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014 Intel Corporation. All rights reserved.
7 #include <netinet/icmp6.h>
8 #include <netinet/in.h>
9 #include <netinet/ip6.h>
12 #include <sys/types.h>
15 #include <linux/if_packet.h>
18 #include "icmp6-util.h"
19 #include "in-addr-util.h"
21 #include "socket-util.h"
23 #define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
24 { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
27 #define IN6ADDR_ALL_NODES_MULTICAST_INIT \
28 { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
31 static int icmp6_bind_router_message(const struct icmp6_filter
*filter
,
32 const struct ipv6_mreq
*mreq
) {
33 int ifindex
= mreq
->ipv6mr_interface
;
34 _cleanup_close_
int s
= -1;
37 s
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, IPPROTO_ICMPV6
);
41 r
= setsockopt(s
, IPPROTO_ICMPV6
, ICMP6_FILTER
, filter
, sizeof(*filter
));
45 r
= setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, mreq
, sizeof(*mreq
));
49 /* RFC 3315, section 6.7, bullet point 2 may indicate that an
50 IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
51 Empirical experiments indicates otherwise and therefore an
52 IPV6_MULTICAST_IF socket option is used here instead */
53 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, ifindex
);
57 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, false);
61 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, 255);
65 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, 255);
69 r
= setsockopt_int(s
, SOL_IPV6
, IPV6_RECVHOPLIMIT
, true);
73 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
77 r
= socket_bind_to_ifindex(s
, ifindex
);
84 int icmp6_bind_router_solicitation(int index
) {
85 struct icmp6_filter filter
= {};
86 struct ipv6_mreq mreq
= {
87 .ipv6mr_multiaddr
= IN6ADDR_ALL_NODES_MULTICAST_INIT
,
88 .ipv6mr_interface
= index
,
91 ICMP6_FILTER_SETBLOCKALL(&filter
);
92 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT
, &filter
);
94 return icmp6_bind_router_message(&filter
, &mreq
);
97 int icmp6_bind_router_advertisement(int index
) {
98 struct icmp6_filter filter
= {};
99 struct ipv6_mreq mreq
= {
100 .ipv6mr_multiaddr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
101 .ipv6mr_interface
= index
,
104 ICMP6_FILTER_SETBLOCKALL(&filter
);
105 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
107 return icmp6_bind_router_message(&filter
, &mreq
);
110 int icmp6_send_router_solicitation(int s
, const struct ether_addr
*ether_addr
) {
111 struct sockaddr_in6 dst
= {
112 .sin6_family
= AF_INET6
,
113 .sin6_addr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
116 struct nd_router_solicit rs
;
117 struct nd_opt_hdr rs_opt
;
118 struct ether_addr rs_opt_mac
;
120 .rs
.nd_rs_type
= ND_ROUTER_SOLICIT
,
121 .rs_opt
.nd_opt_type
= ND_OPT_SOURCE_LINKADDR
,
122 .rs_opt
.nd_opt_len
= 1,
126 .iov_len
= sizeof(rs
),
128 struct msghdr msg
= {
130 .msg_namelen
= sizeof(dst
),
139 rs
.rs_opt_mac
= *ether_addr
;
141 r
= sendmsg(s
, &msg
, 0);
148 int icmp6_receive(int fd
, void *buffer
, size_t size
, struct in6_addr
*dst
,
149 triple_timestamp
*timestamp
) {
151 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
152 CMSG_SPACE(sizeof(struct timeval
))) control
;
153 struct iovec iov
= {};
154 union sockaddr_union sa
= {};
155 struct msghdr msg
= {
157 .msg_namelen
= sizeof(sa
),
160 .msg_control
= &control
,
161 .msg_controllen
= sizeof(control
),
163 struct cmsghdr
*cmsg
;
166 iov
= IOVEC_MAKE(buffer
, size
);
168 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
172 if ((size_t) len
!= size
)
175 if (msg
.msg_namelen
== sizeof(struct sockaddr_in6
) &&
176 sa
.in6
.sin6_family
== AF_INET6
) {
178 *dst
= sa
.in6
.sin6_addr
;
179 if (in_addr_is_link_local(AF_INET6
, (union in_addr_union
*) dst
) <= 0)
180 return -EADDRNOTAVAIL
;
182 } else if (msg
.msg_namelen
> 0)
183 return -EPFNOSUPPORT
;
185 /* namelen == 0 only happens when running the test-suite over a socketpair */
187 assert(!(msg
.msg_flags
& MSG_CTRUNC
));
188 assert(!(msg
.msg_flags
& MSG_TRUNC
));
190 CMSG_FOREACH(cmsg
, &msg
) {
191 if (cmsg
->cmsg_level
== SOL_IPV6
&&
192 cmsg
->cmsg_type
== IPV6_HOPLIMIT
&&
193 cmsg
->cmsg_len
== CMSG_LEN(sizeof(int))) {
194 int hops
= *(int*) CMSG_DATA(cmsg
);
200 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
201 cmsg
->cmsg_type
== SO_TIMESTAMP
&&
202 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct timeval
)))
203 triple_timestamp_from_realtime(timestamp
, timeval_load((struct timeval
*) CMSG_DATA(cmsg
)));
206 if (!triple_timestamp_is_set(timestamp
))
207 triple_timestamp_get(timestamp
);