]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/icmp6-util.c
hwdb: Add support for Elgato Stream Deck Plus
[thirdparty/systemd.git] / src / libsystemd-network / icmp6-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
940367a0 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
940367a0
TG
4***/
5
6#include <errno.h>
940367a0
TG
7#include <netinet/icmp6.h>
8#include <netinet/in.h>
3ffd4af2
LP
9#include <netinet/ip6.h>
10#include <stdio.h>
11#include <string.h>
3ffd4af2
LP
12#include <sys/types.h>
13#include <unistd.h>
15fec93b 14#include <net/if.h>
3ffd4af2 15#include <linux/if_packet.h>
940367a0 16
3ffd4af2 17#include "fd-util.h"
940367a0 18#include "icmp6-util.h"
88d5a3db 19#include "in-addr-util.h"
bd1ae178 20#include "iovec-util.h"
dd59d5e5 21#include "network-common.h"
5cfa2c3d 22#include "socket-util.h"
940367a0
TG
23
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 } } }
27
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 } } }
31
6142bb37
PF
32static int icmp6_bind_router_message(const struct icmp6_filter *filter,
33 const struct ipv6_mreq *mreq) {
953a02d1 34 int ifindex = mreq->ipv6mr_interface;
5bb1d7fb 35 _cleanup_close_ int s = -EBADF;
1e7a0e21 36 int r;
940367a0 37
113e124f
YW
38 assert(filter);
39 assert(mreq);
40
cddf4d81 41 s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
940367a0
TG
42 if (s < 0)
43 return -errno;
44
113e124f 45 if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, filter, sizeof(*filter)) < 0)
6142bb37
PF
46 return -errno;
47
113e124f 48 if (setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq, sizeof(*mreq)) < 0)
940367a0
TG
49 return -errno;
50
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 */
953a02d1 55 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex);
940367a0 56 if (r < 0)
9e5b6496 57 return r;
940367a0 58
2ff48e98 59 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
940367a0 60 if (r < 0)
2ff48e98 61 return r;
940367a0 62
9e5b6496 63 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
940367a0 64 if (r < 0)
9e5b6496 65 return r;
940367a0 66
9e5b6496 67 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
cddf4d81 68 if (r < 0)
9e5b6496 69 return r;
cddf4d81 70
2ff48e98 71 r = setsockopt_int(s, SOL_IPV6, IPV6_RECVHOPLIMIT, true);
940367a0 72 if (r < 0)
2ff48e98 73 return r;
940367a0 74
2ff48e98 75 r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
1e7a0e21 76 if (r < 0)
2ff48e98 77 return r;
1e7a0e21 78
953a02d1 79 r = socket_bind_to_ifindex(s, ifindex);
15fec93b 80 if (r < 0)
953a02d1 81 return r;
15fec93b 82
c10d6bdb 83 return TAKE_FD(s);
940367a0
TG
84}
85
1a6c9136 86int icmp6_bind_router_solicitation(int ifindex) {
6142bb37
PF
87 struct icmp6_filter filter = {};
88 struct ipv6_mreq mreq = {
89 .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
1a6c9136 90 .ipv6mr_interface = ifindex,
6142bb37
PF
91 };
92
93 ICMP6_FILTER_SETBLOCKALL(&filter);
94 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
95
96 return icmp6_bind_router_message(&filter, &mreq);
97}
98
1a6c9136 99int icmp6_bind_router_advertisement(int ifindex) {
6142bb37
PF
100 struct icmp6_filter filter = {};
101 struct ipv6_mreq mreq = {
102 .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
1a6c9136 103 .ipv6mr_interface = ifindex,
6142bb37
PF
104 };
105
106 ICMP6_FILTER_SETBLOCKALL(&filter);
107 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
108
109 return icmp6_bind_router_message(&filter, &mreq);
110}
111
940367a0
TG
112int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
113 struct sockaddr_in6 dst = {
114 .sin6_family = AF_INET6,
115 .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
116 };
117 struct {
118 struct nd_router_solicit rs;
119 struct nd_opt_hdr rs_opt;
120 struct ether_addr rs_opt_mac;
121 } _packed_ rs = {
122 .rs.nd_rs_type = ND_ROUTER_SOLICIT,
6d06ac1f
TG
123 .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
124 .rs_opt.nd_opt_len = 1,
940367a0 125 };
6d06ac1f
TG
126 struct iovec iov = {
127 .iov_base = &rs,
128 .iov_len = sizeof(rs),
940367a0
TG
129 };
130 struct msghdr msg = {
131 .msg_name = &dst,
132 .msg_namelen = sizeof(dst),
6d06ac1f 133 .msg_iov = &iov,
940367a0
TG
134 .msg_iovlen = 1,
135 };
940367a0 136
6d06ac1f
TG
137 assert(s >= 0);
138 assert(ether_addr);
139
140 rs.rs_opt_mac = *ether_addr;
940367a0 141
113e124f 142 if (sendmsg(s, &msg, 0) < 0)
940367a0
TG
143 return -errno;
144
145 return 0;
146}
88d5a3db 147
51211638
YW
148int icmp6_receive(
149 int fd,
150 void *buffer,
151 size_t size,
152 struct in6_addr *ret_sender,
153 triple_timestamp *ret_timestamp) {
fb29cdbe 154
f782eee6 155 /* This needs to be initialized with zero. See #20741. */
fb29cdbe 156 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
f782eee6 157 CMSG_SPACE_TIMEVAL) control = {};
88d5a3db
PF
158 struct iovec iov = {};
159 union sockaddr_union sa = {};
160 struct msghdr msg = {
161 .msg_name = &sa.sa,
162 .msg_namelen = sizeof(sa),
163 .msg_iov = &iov,
164 .msg_iovlen = 1,
165 .msg_control = &control,
166 .msg_controllen = sizeof(control),
167 };
94876904 168 struct in6_addr addr = {};
88d5a3db
PF
169 ssize_t len;
170
5cfa2c3d 171 iov = IOVEC_MAKE(buffer, size);
88d5a3db 172
2adfd1bd 173 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
14f37112 174 if (len < 0)
2adfd1bd 175 return (int) len;
88d5a3db
PF
176
177 if ((size_t) len != size)
178 return -EINVAL;
179
180 if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
181 sa.in6.sin6_family == AF_INET6) {
182
94876904 183 addr = sa.in6.sin6_addr;
4961f566 184 if (!in6_addr_is_link_local(&addr) && !in6_addr_is_null(&addr))
88d5a3db
PF
185 return -EADDRNOTAVAIL;
186
187 } else if (msg.msg_namelen > 0)
188 return -EPFNOSUPPORT;
189
190 /* namelen == 0 only happens when running the test-suite over a socketpair */
191
88d5a3db
PF
192 assert(!(msg.msg_flags & MSG_TRUNC));
193
dd59d5e5
YW
194 int *hops = CMSG_FIND_DATA(&msg, SOL_IPV6, IPV6_HOPLIMIT, int);
195 if (hops && *hops != 255)
196 return -EMULTIHOP;
88d5a3db 197
dd59d5e5
YW
198 if (ret_timestamp)
199 triple_timestamp_from_cmsg(ret_timestamp, &msg);
51211638
YW
200 if (ret_sender)
201 *ret_sender = addr;
88d5a3db
PF
202 return 0;
203}