]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/icmp6-util.c
Merge pull request #33062 from DaanDeMeyer/virtio-scsi
[thirdparty/systemd.git] / src / libsystemd-network / icmp6-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 /* Make sure the net/if.h header is included before any linux/ one */
7 #include <net/if.h>
8 #include <errno.h>
9 #include <linux/if_packet.h>
10 #include <netinet/icmp6.h>
11 #include <netinet/in.h>
12 #include <netinet/ip6.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "fd-util.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"
24
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;
29 int r;
30
31 assert(ifindex > 0);
32
33 ICMP6_FILTER_SETBLOCKALL(&filter);
34 if (is_router) {
35 mreq = (struct ipv6_mreq) {
36 .ipv6mr_multiaddr = IN6_ADDR_ALL_ROUTERS_MULTICAST,
37 .ipv6mr_interface = ifindex,
38 };
39 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
40 } else {
41 mreq = (struct ipv6_mreq) {
42 .ipv6mr_multiaddr = IN6_ADDR_ALL_NODES_MULTICAST,
43 .ipv6mr_interface = ifindex,
44 };
45 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
46 ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
47 ICMP6_FILTER_SETPASS(ND_REDIRECT, &filter);
48 }
49
50 s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
51 if (s < 0)
52 return -errno;
53
54 if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) < 0)
55 return -errno;
56
57 if (setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
58 return -errno;
59
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);
64 if (r < 0)
65 return r;
66
67 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
68 if (r < 0)
69 return r;
70
71 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
72 if (r < 0)
73 return r;
74
75 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
76 if (r < 0)
77 return r;
78
79 r = setsockopt_int(s, SOL_IPV6, IPV6_RECVHOPLIMIT, true);
80 if (r < 0)
81 return r;
82
83 r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
84 if (r < 0)
85 return r;
86
87 r = socket_bind_to_ifindex(s, ifindex);
88 if (r < 0)
89 return r;
90
91 return TAKE_FD(s);
92 }
93
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),
98 };
99 struct msghdr msg = {
100 .msg_name = &sa,
101 .msg_namelen = sizeof(struct sockaddr_in6),
102 .msg_iov = (struct iovec*) iov,
103 .msg_iovlen = n_iov,
104 };
105
106 assert(fd >= 0);
107
108 if (sendmsg(fd, &msg, 0) < 0)
109 return -errno;
110
111 return 0;
112 }
113
114 int icmp6_receive(
115 int fd,
116 void *buffer,
117 size_t size,
118 struct in6_addr *ret_sender,
119 triple_timestamp *ret_timestamp) {
120
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 = {
127 .msg_name = &sa.sa,
128 .msg_namelen = sizeof(sa),
129 .msg_iov = &iov,
130 .msg_iovlen = 1,
131 .msg_control = &control,
132 .msg_controllen = sizeof(control),
133 };
134 ssize_t len;
135
136 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
137 if (len < 0)
138 return (int) len;
139
140 if ((size_t) len != size)
141 return -EINVAL;
142
143 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6)
144 return -EPFNOSUPPORT;
145
146 if (!in6_addr_is_link_local(&sa.in6.sin6_addr) && !in6_addr_is_null(&sa.in6.sin6_addr))
147 return -EADDRNOTAVAIL;
148
149 assert(!(msg.msg_flags & MSG_TRUNC));
150
151 int *hops = CMSG_FIND_DATA(&msg, SOL_IPV6, IPV6_HOPLIMIT, int);
152 if (hops && *hops != 255)
153 return -EMULTIHOP;
154
155 if (ret_timestamp)
156 triple_timestamp_from_cmsg(ret_timestamp, &msg);
157 if (ret_sender)
158 *ret_sender = sa.in6.sin6_addr;
159 return 0;
160 }