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
= IN6_ADDR_ALL_ROUTERS_MULTICAST
,
37 .ipv6mr_interface
= ifindex
,
39 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
41 mreq
= (struct ipv6_mreq
) {
42 .ipv6mr_multiaddr
= IN6_ADDR_ALL_NODES_MULTICAST
,
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 in6_addr
*dst
, const struct iovec
*iov
, size_t n_iov
) {
95 struct sockaddr_in6 sa
= {
96 .sin6_family
= AF_INET6
,
97 .sin6_addr
= *ASSERT_PTR(dst
),
101 .msg_namelen
= sizeof(struct sockaddr_in6
),
102 .msg_iov
= (struct iovec
*) iov
,
108 if (sendmsg(fd
, &msg
, 0) < 0)
118 struct in6_addr
*ret_sender
,
119 triple_timestamp
*ret_timestamp
) {
121 /* This needs to be initialized with zero. See #20741. */
122 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
123 CMSG_SPACE_TIMEVAL
) control
= {};
124 struct iovec iov
= { buffer
, size
};
125 union sockaddr_union sa
= {};
126 struct msghdr msg
= {
128 .msg_namelen
= sizeof(sa
),
131 .msg_control
= &control
,
132 .msg_controllen
= sizeof(control
),
136 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
140 if ((size_t) len
!= size
)
143 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
)
144 return -EPFNOSUPPORT
;
146 if (!in6_addr_is_link_local(&sa
.in6
.sin6_addr
) && !in6_addr_is_null(&sa
.in6
.sin6_addr
))
147 return -EADDRNOTAVAIL
;
149 assert(!(msg
.msg_flags
& MSG_TRUNC
));
151 int *hops
= CMSG_FIND_DATA(&msg
, SOL_IPV6
, IPV6_HOPLIMIT
, int);
152 if (hops
&& *hops
!= 255)
156 triple_timestamp_from_cmsg(ret_timestamp
, &msg
);
158 *ret_sender
= sa
.in6
.sin6_addr
;