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/socket.h>
13 #include <sys/types.h>
16 #include <linux/if_packet.h>
19 #include "icmp6-util.h"
20 #include "in-addr-util.h"
22 #include "socket-util.h"
24 #define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
25 { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
28 #define IN6ADDR_ALL_NODES_MULTICAST_INIT \
29 { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
32 static int icmp6_bind_router_message(const struct icmp6_filter
*filter
,
33 const struct ipv6_mreq
*mreq
) {
34 int ifindex
= mreq
->ipv6mr_interface
;
35 _cleanup_close_
int s
= -1;
38 s
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, IPPROTO_ICMPV6
);
42 r
= setsockopt(s
, IPPROTO_ICMPV6
, ICMP6_FILTER
, filter
, sizeof(*filter
));
46 r
= setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, mreq
, sizeof(*mreq
));
50 /* RFC 3315, section 6.7, bullet point 2 may indicate that an
51 IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
52 Empirical experiments indicates otherwise and therefore an
53 IPV6_MULTICAST_IF socket option is used here instead */
54 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, ifindex
);
58 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, false);
62 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, 255);
66 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, 255);
70 r
= setsockopt_int(s
, SOL_IPV6
, IPV6_RECVHOPLIMIT
, true);
74 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
78 r
= socket_bind_to_ifindex(s
, ifindex
);
85 int icmp6_bind_router_solicitation(int index
) {
86 struct icmp6_filter filter
= {};
87 struct ipv6_mreq mreq
= {
88 .ipv6mr_multiaddr
= IN6ADDR_ALL_NODES_MULTICAST_INIT
,
89 .ipv6mr_interface
= index
,
92 ICMP6_FILTER_SETBLOCKALL(&filter
);
93 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT
, &filter
);
95 return icmp6_bind_router_message(&filter
, &mreq
);
98 int icmp6_bind_router_advertisement(int index
) {
99 struct icmp6_filter filter
= {};
100 struct ipv6_mreq mreq
= {
101 .ipv6mr_multiaddr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
102 .ipv6mr_interface
= index
,
105 ICMP6_FILTER_SETBLOCKALL(&filter
);
106 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
108 return icmp6_bind_router_message(&filter
, &mreq
);
111 int icmp6_send_router_solicitation(int s
, const struct ether_addr
*ether_addr
) {
112 struct sockaddr_in6 dst
= {
113 .sin6_family
= AF_INET6
,
114 .sin6_addr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
117 struct nd_router_solicit rs
;
118 struct nd_opt_hdr rs_opt
;
119 struct ether_addr rs_opt_mac
;
121 .rs
.nd_rs_type
= ND_ROUTER_SOLICIT
,
122 .rs_opt
.nd_opt_type
= ND_OPT_SOURCE_LINKADDR
,
123 .rs_opt
.nd_opt_len
= 1,
127 .iov_len
= sizeof(rs
),
129 struct msghdr msg
= {
131 .msg_namelen
= sizeof(dst
),
140 rs
.rs_opt_mac
= *ether_addr
;
142 r
= sendmsg(s
, &msg
, 0);
149 int icmp6_receive(int fd
, void *buffer
, size_t size
, struct in6_addr
*dst
,
150 triple_timestamp
*timestamp
) {
152 struct cmsghdr cmsghdr
;
153 uint8_t buf
[CMSG_SPACE(sizeof(int)) + /* ttl */
154 CMSG_SPACE(sizeof(struct timeval
))];
156 struct iovec iov
= {};
157 union sockaddr_union sa
= {};
158 struct msghdr msg
= {
160 .msg_namelen
= sizeof(sa
),
163 .msg_control
= &control
,
164 .msg_controllen
= sizeof(control
),
166 struct cmsghdr
*cmsg
;
169 iov
= IOVEC_MAKE(buffer
, size
);
171 len
= recvmsg(fd
, &msg
, MSG_DONTWAIT
);
175 if ((size_t) len
!= size
)
178 if (msg
.msg_namelen
== sizeof(struct sockaddr_in6
) &&
179 sa
.in6
.sin6_family
== AF_INET6
) {
181 *dst
= sa
.in6
.sin6_addr
;
182 if (in_addr_is_link_local(AF_INET6
, (union in_addr_union
*) dst
) <= 0)
183 return -EADDRNOTAVAIL
;
185 } else if (msg
.msg_namelen
> 0)
186 return -EPFNOSUPPORT
;
188 /* namelen == 0 only happens when running the test-suite over a socketpair */
190 assert(!(msg
.msg_flags
& MSG_CTRUNC
));
191 assert(!(msg
.msg_flags
& MSG_TRUNC
));
193 CMSG_FOREACH(cmsg
, &msg
) {
194 if (cmsg
->cmsg_level
== SOL_IPV6
&&
195 cmsg
->cmsg_type
== IPV6_HOPLIMIT
&&
196 cmsg
->cmsg_len
== CMSG_LEN(sizeof(int))) {
197 int hops
= *(int*) CMSG_DATA(cmsg
);
203 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
204 cmsg
->cmsg_type
== SO_TIMESTAMP
&&
205 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct timeval
)))
206 triple_timestamp_from_realtime(timestamp
, timeval_load((struct timeval
*) CMSG_DATA(cmsg
)));
209 if (!triple_timestamp_is_set(timestamp
))
210 triple_timestamp_get(timestamp
);