1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014 Intel Corporation. All rights reserved.
6 #include <netinet/icmp6.h>
7 #include <netinet/in.h>
12 #include "icmp6-util.h"
13 #include "in-addr-util.h"
14 #include "network-common.h"
15 #include "socket-util.h"
17 int icmp6_bind(int ifindex
, bool is_router
) {
18 struct icmp6_filter filter
= {};
19 struct ipv6_mreq mreq
;
20 _cleanup_close_
int s
= -EBADF
;
25 ICMP6_FILTER_SETBLOCKALL(&filter
);
27 mreq
= (struct ipv6_mreq
) {
28 .ipv6mr_multiaddr
= IN6_ADDR_ALL_ROUTERS_MULTICAST
,
29 .ipv6mr_ifindex
= ifindex
,
31 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
33 mreq
= (struct ipv6_mreq
) {
34 .ipv6mr_multiaddr
= IN6_ADDR_ALL_NODES_MULTICAST
,
35 .ipv6mr_ifindex
= ifindex
,
37 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT
, &filter
);
38 ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT
, &filter
);
39 ICMP6_FILTER_SETPASS(ND_REDIRECT
, &filter
);
42 s
= socket(AF_INET6
, SOCK_RAW
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, IPPROTO_ICMPV6
);
46 if (setsockopt(s
, IPPROTO_ICMPV6
, ICMP6_FILTER
, &filter
, sizeof(filter
)) < 0)
49 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq
, sizeof(mreq
)) < 0)
52 /* RFC 3315, section 6.7, bullet point 2 may indicate that an IPV6_PKTINFO socket option also applies
53 * for ICMPv6 multicast. Empirical experiments indicates otherwise and therefore an IPV6_MULTICAST_IF
54 * socket option is used here instead. */
55 r
= setsockopt_int(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, ifindex
);
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
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, true);
75 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
79 r
= socket_bind_to_ifindex(s
, ifindex
);
86 int icmp6_send(int fd
, const struct in6_addr
*dst
, const struct iovec
*iov
, size_t n_iov
) {
87 struct sockaddr_in6 sa
= {
88 .sin6_family
= AF_INET6
,
89 .sin6_addr
= *ASSERT_PTR(dst
),
93 .msg_namelen
= sizeof(struct sockaddr_in6
),
94 .msg_iov
= (struct iovec
*) iov
,
100 if (sendmsg(fd
, &msg
, 0) < 0)
110 struct in6_addr
*ret_sender
,
111 triple_timestamp
*ret_timestamp
) {
113 /* This needs to be initialized with zero. See #20741.
114 * The issue is fixed on glibc-2.35 (8fba672472ae0055387e9315fc2eddfa6775ca79). */
115 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
116 CMSG_SPACE_TIMEVAL
) control
= {};
117 struct iovec iov
= { buffer
, size
};
118 union sockaddr_union sa
= {};
119 struct msghdr msg
= {
121 .msg_namelen
= sizeof(sa
),
124 .msg_control
= &control
,
125 .msg_controllen
= sizeof(control
),
129 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
133 if ((size_t) len
!= size
)
136 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
)
137 return -EPFNOSUPPORT
;
139 if (!in6_addr_is_link_local(&sa
.in6
.sin6_addr
) && !in6_addr_is_null(&sa
.in6
.sin6_addr
))
140 return -EADDRNOTAVAIL
;
142 assert(!(msg
.msg_flags
& MSG_TRUNC
));
144 int *hops
= CMSG_FIND_DATA(&msg
, IPPROTO_IPV6
, IPV6_HOPLIMIT
, int);
145 if (hops
&& *hops
!= 255)
149 triple_timestamp_from_cmsg(ret_timestamp
, &msg
);
151 *ret_sender
= sa
.in6
.sin6_addr
;