1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014 Intel Corporation. All rights reserved.
6 /* Make sure the net/if.h header is included before any linux/ one */
9 #include <linux/if_packet.h>
10 #include <netinet/icmp6.h>
11 #include <netinet/in.h>
12 #include <netinet/ip6.h>
15 #include <sys/types.h>
19 #include "icmp6-util.h"
20 #include "in-addr-util.h"
21 #include "iovec-util.h"
22 #include "network-common.h"
23 #include "socket-util.h"
25 int icmp6_bind(int ifindex
, bool is_router
) {
26 struct icmp6_filter filter
= {};
27 struct ipv6_mreq mreq
;
28 _cleanup_close_
int s
= -EBADF
;
33 ICMP6_FILTER_SETBLOCKALL(&filter
);
35 mreq
= (struct ipv6_mreq
) {
36 .ipv6mr_multiaddr
= IN6ADDR_ALL_ROUTERS_MULTICAST_INIT
,
37 .ipv6mr_interface
= ifindex
,
39 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
41 mreq
= (struct ipv6_mreq
) {
42 .ipv6mr_multiaddr
= IN6ADDR_ALL_NODES_MULTICAST_INIT
,
43 .ipv6mr_interface
= ifindex
,
45 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT
, &filter
);
46 ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT
, &filter
);
47 ICMP6_FILTER_SETPASS(ND_REDIRECT
, &filter
);
50 s
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, IPPROTO_ICMPV6
);
54 if (setsockopt(s
, IPPROTO_ICMPV6
, ICMP6_FILTER
, &filter
, sizeof(filter
)) < 0)
57 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
)) < 0)
60 /* RFC 3315, section 6.7, bullet point 2 may indicate that an IPV6_PKTINFO socket option also applies
61 * for ICMPv6 multicast. Empirical experiments indicates otherwise and therefore an IPV6_MULTICAST_IF
62 * socket option is used here instead. */
63 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, ifindex
);
67 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, false);
71 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, 255);
75 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, 255);
79 r
= setsockopt_int(s
, SOL_IPV6
, IPV6_RECVHOPLIMIT
, true);
83 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
87 r
= socket_bind_to_ifindex(s
, ifindex
);
94 int icmp6_send(int fd
, const struct sockaddr_in6
*dst
, const struct iovec
*iov
, size_t n_iov
) {
96 .msg_name
= (struct sockaddr_in6
*) dst
,
97 .msg_namelen
= sizeof(struct sockaddr_in6
),
98 .msg_iov
= (struct iovec
*) iov
,
102 if (sendmsg(fd
, &msg
, 0) < 0)
112 struct in6_addr
*ret_sender
,
113 triple_timestamp
*ret_timestamp
) {
115 /* This needs to be initialized with zero. See #20741. */
116 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
117 CMSG_SPACE_TIMEVAL
) control
= {};
118 struct iovec iov
= {};
119 union sockaddr_union sa
= {};
120 struct msghdr msg
= {
122 .msg_namelen
= sizeof(sa
),
125 .msg_control
= &control
,
126 .msg_controllen
= sizeof(control
),
130 iov
= IOVEC_MAKE(buffer
, size
);
132 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
136 if ((size_t) len
!= size
)
139 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
)
140 return -EPFNOSUPPORT
;
142 if (!in6_addr_is_link_local(&sa
.in6
.sin6_addr
) && !in6_addr_is_null(&sa
.in6
.sin6_addr
))
143 return -EADDRNOTAVAIL
;
145 assert(!(msg
.msg_flags
& MSG_TRUNC
));
147 int *hops
= CMSG_FIND_DATA(&msg
, SOL_IPV6
, IPV6_HOPLIMIT
, int);
148 if (hops
&& *hops
!= 255)
152 triple_timestamp_from_cmsg(ret_timestamp
, &msg
);
154 *ret_sender
= sa
.in6
.sin6_addr
;