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 index
= mreq
->ipv6mr_interface
;
35 _cleanup_close_
int s
= -1;
36 char ifname
[IF_NAMESIZE
] = "";
39 s
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, IPPROTO_ICMPV6
);
43 r
= setsockopt(s
, IPPROTO_ICMPV6
, ICMP6_FILTER
, filter
, sizeof(*filter
));
47 r
= setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, mreq
, sizeof(*mreq
));
51 /* RFC 3315, section 6.7, bullet point 2 may indicate that an
52 IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
53 Empirical experiments indicates otherwise and therefore an
54 IPV6_MULTICAST_IF socket option is used here instead */
55 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, index
);
59 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, false);
63 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, 255);
67 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, 255);
71 r
= setsockopt_int(s
, SOL_IPV6
, IPV6_RECVHOPLIMIT
, true);
75 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
79 if (if_indextoname(index
, ifname
) == 0)
82 r
= setsockopt(s
, SOL_SOCKET
, SO_BINDTODEVICE
, ifname
, strlen(ifname
));
89 int icmp6_bind_router_solicitation(int index
) {
90 struct icmp6_filter filter
= {};
91 struct ipv6_mreq mreq
= {
92 .ipv6mr_multiaddr
= IN6ADDR_ALL_NODES_MULTICAST_INIT
,
93 .ipv6mr_interface
= index
,
96 ICMP6_FILTER_SETBLOCKALL(&filter
);
97 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT
, &filter
);
99 return icmp6_bind_router_message(&filter
, &mreq
);
102 int icmp6_bind_router_advertisement(int index
) {
103 struct icmp6_filter filter
= {};
104 struct ipv6_mreq mreq
= {
105 .ipv6mr_multiaddr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
106 .ipv6mr_interface
= index
,
109 ICMP6_FILTER_SETBLOCKALL(&filter
);
110 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
112 return icmp6_bind_router_message(&filter
, &mreq
);
115 int icmp6_send_router_solicitation(int s
, const struct ether_addr
*ether_addr
) {
116 struct sockaddr_in6 dst
= {
117 .sin6_family
= AF_INET6
,
118 .sin6_addr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
121 struct nd_router_solicit rs
;
122 struct nd_opt_hdr rs_opt
;
123 struct ether_addr rs_opt_mac
;
125 .rs
.nd_rs_type
= ND_ROUTER_SOLICIT
,
126 .rs_opt
.nd_opt_type
= ND_OPT_SOURCE_LINKADDR
,
127 .rs_opt
.nd_opt_len
= 1,
131 .iov_len
= sizeof(rs
),
133 struct msghdr msg
= {
135 .msg_namelen
= sizeof(dst
),
144 rs
.rs_opt_mac
= *ether_addr
;
146 r
= sendmsg(s
, &msg
, 0);
153 int icmp6_receive(int fd
, void *buffer
, size_t size
, struct in6_addr
*dst
,
154 triple_timestamp
*timestamp
) {
156 struct cmsghdr cmsghdr
;
157 uint8_t buf
[CMSG_SPACE(sizeof(int)) + /* ttl */
158 CMSG_SPACE(sizeof(struct timeval
))];
160 struct iovec iov
= {};
161 union sockaddr_union sa
= {};
162 struct msghdr msg
= {
164 .msg_namelen
= sizeof(sa
),
167 .msg_control
= &control
,
168 .msg_controllen
= sizeof(control
),
170 struct cmsghdr
*cmsg
;
173 iov
= IOVEC_MAKE(buffer
, size
);
175 len
= recvmsg(fd
, &msg
, MSG_DONTWAIT
);
179 if ((size_t) len
!= size
)
182 if (msg
.msg_namelen
== sizeof(struct sockaddr_in6
) &&
183 sa
.in6
.sin6_family
== AF_INET6
) {
185 *dst
= sa
.in6
.sin6_addr
;
186 if (in_addr_is_link_local(AF_INET6
, (union in_addr_union
*) dst
) <= 0)
187 return -EADDRNOTAVAIL
;
189 } else if (msg
.msg_namelen
> 0)
190 return -EPFNOSUPPORT
;
192 /* namelen == 0 only happens when running the test-suite over a socketpair */
194 assert(!(msg
.msg_flags
& MSG_CTRUNC
));
195 assert(!(msg
.msg_flags
& MSG_TRUNC
));
197 CMSG_FOREACH(cmsg
, &msg
) {
198 if (cmsg
->cmsg_level
== SOL_IPV6
&&
199 cmsg
->cmsg_type
== IPV6_HOPLIMIT
&&
200 cmsg
->cmsg_len
== CMSG_LEN(sizeof(int))) {
201 int hops
= *(int*) CMSG_DATA(cmsg
);
207 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
208 cmsg
->cmsg_type
== SO_TIMESTAMP
&&
209 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct timeval
)))
210 triple_timestamp_from_realtime(timestamp
, timeval_load((struct timeval
*) CMSG_DATA(cmsg
)));
213 if (!triple_timestamp_is_set(timestamp
))
214 triple_timestamp_get(timestamp
);