]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/icmp6-util.c
man: Remove OSConfig project mentioning for systemd-confext
[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
edda10f2
FS
6/* Make sure the net/if.h header is included before any linux/ one */
7#include <net/if.h>
940367a0 8#include <errno.h>
edda10f2 9#include <linux/if_packet.h>
940367a0
TG
10#include <netinet/icmp6.h>
11#include <netinet/in.h>
3ffd4af2
LP
12#include <netinet/ip6.h>
13#include <stdio.h>
14#include <string.h>
3ffd4af2
LP
15#include <sys/types.h>
16#include <unistd.h>
940367a0 17
3ffd4af2 18#include "fd-util.h"
940367a0 19#include "icmp6-util.h"
88d5a3db 20#include "in-addr-util.h"
bd1ae178 21#include "iovec-util.h"
dd59d5e5 22#include "network-common.h"
5cfa2c3d 23#include "socket-util.h"
940367a0 24
37c011e7
YW
25int icmp6_bind(int ifindex, bool is_router) {
26 struct icmp6_filter filter = {};
27 struct ipv6_mreq mreq;
5bb1d7fb 28 _cleanup_close_ int s = -EBADF;
1e7a0e21 29 int r;
940367a0 30
37c011e7
YW
31 assert(ifindex > 0);
32
33 ICMP6_FILTER_SETBLOCKALL(&filter);
34 if (is_router) {
35 mreq = (struct ipv6_mreq) {
2c28eb02 36 .ipv6mr_multiaddr = IN6_ADDR_ALL_ROUTERS_MULTICAST,
37c011e7
YW
37 .ipv6mr_interface = ifindex,
38 };
39 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
40 } else {
41 mreq = (struct ipv6_mreq) {
2c28eb02 42 .ipv6mr_multiaddr = IN6_ADDR_ALL_NODES_MULTICAST,
37c011e7
YW
43 .ipv6mr_interface = ifindex,
44 };
45 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
696eb2b8 46 ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
44e8cf30 47 ICMP6_FILTER_SETPASS(ND_REDIRECT, &filter);
37c011e7 48 }
113e124f 49
cddf4d81 50 s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
940367a0
TG
51 if (s < 0)
52 return -errno;
53
37c011e7 54 if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) < 0)
6142bb37
PF
55 return -errno;
56
37c011e7 57 if (setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
940367a0
TG
58 return -errno;
59
37c011e7
YW
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. */
953a02d1 63 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex);
940367a0 64 if (r < 0)
9e5b6496 65 return r;
940367a0 66
2ff48e98 67 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
940367a0 68 if (r < 0)
2ff48e98 69 return r;
940367a0 70
9e5b6496 71 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
940367a0 72 if (r < 0)
9e5b6496 73 return r;
940367a0 74
9e5b6496 75 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
cddf4d81 76 if (r < 0)
9e5b6496 77 return r;
cddf4d81 78
2ff48e98 79 r = setsockopt_int(s, SOL_IPV6, IPV6_RECVHOPLIMIT, true);
940367a0 80 if (r < 0)
2ff48e98 81 return r;
940367a0 82
2ff48e98 83 r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
1e7a0e21 84 if (r < 0)
2ff48e98 85 return r;
1e7a0e21 86
953a02d1 87 r = socket_bind_to_ifindex(s, ifindex);
15fec93b 88 if (r < 0)
953a02d1 89 return r;
15fec93b 90
c10d6bdb 91 return TAKE_FD(s);
940367a0
TG
92}
93
ac336e75
YW
94int 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 };
ca114462 99 struct msghdr msg = {
ac336e75 100 .msg_name = &sa,
ca114462
YW
101 .msg_namelen = sizeof(struct sockaddr_in6),
102 .msg_iov = (struct iovec*) iov,
103 .msg_iovlen = n_iov,
104 };
105
ac336e75
YW
106 assert(fd >= 0);
107
ca114462
YW
108 if (sendmsg(fd, &msg, 0) < 0)
109 return -errno;
110
111 return 0;
112}
113
51211638
YW
114int icmp6_receive(
115 int fd,
116 void *buffer,
117 size_t size,
118 struct in6_addr *ret_sender,
119 triple_timestamp *ret_timestamp) {
fb29cdbe 120
f782eee6 121 /* This needs to be initialized with zero. See #20741. */
fb29cdbe 122 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
f782eee6 123 CMSG_SPACE_TIMEVAL) control = {};
88d5a3db
PF
124 struct iovec iov = {};
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 };
88d5a3db
PF
134 ssize_t len;
135
5cfa2c3d 136 iov = IOVEC_MAKE(buffer, size);
88d5a3db 137
2adfd1bd 138 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
14f37112 139 if (len < 0)
2adfd1bd 140 return (int) len;
88d5a3db
PF
141
142 if ((size_t) len != size)
143 return -EINVAL;
144
9f0430b7 145 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6)
88d5a3db
PF
146 return -EPFNOSUPPORT;
147
9f0430b7
YW
148 if (!in6_addr_is_link_local(&sa.in6.sin6_addr) && !in6_addr_is_null(&sa.in6.sin6_addr))
149 return -EADDRNOTAVAIL;
88d5a3db 150
88d5a3db
PF
151 assert(!(msg.msg_flags & MSG_TRUNC));
152
dd59d5e5
YW
153 int *hops = CMSG_FIND_DATA(&msg, SOL_IPV6, IPV6_HOPLIMIT, int);
154 if (hops && *hops != 255)
155 return -EMULTIHOP;
88d5a3db 156
dd59d5e5
YW
157 if (ret_timestamp)
158 triple_timestamp_from_cmsg(ret_timestamp, &msg);
51211638 159 if (ret_sender)
9f0430b7 160 *ret_sender = sa.in6.sin6_addr;
88d5a3db
PF
161 return 0;
162}