Squid Web Cache master
Loading...
Searching...
No Matches
Icmp6.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 42 ICMP Pinger program */
10
11//#define SQUID_HELPER 1
12
13#include "squid.h"
14
15#if USE_ICMP
16
17#include "base/Assure.h"
18#include "compat/socket.h"
19#include "debug/Stream.h"
20#include "Icmp6.h"
21#include "IcmpPinger.h"
22#include "time/gadgets.h"
23
24// Some system headers are only needed internally here.
25// They should not be included via the header.
26
27#if HAVE_NETINET_IP6_H
28#include <netinet/ip6.h>
29#endif
30
31// Icmp6 OP-Codes
32// see http://www.iana.org/assignments/icmpv6-parameters
33static const char *
35{
36 // NP: LowPktStr is for codes 0-127
37 static const char *icmp6LowPktStr[] = {
38 "ICMPv6 0", // 0
39 "Destination Unreachable", // 1 - RFC2463
40 "Packet Too Big", // 2 - RFC2463
41 "Time Exceeded", // 3 - RFC2463
42 "Parameter Problem", // 4 - RFC2463
43 };
44
45 // low codes 1-4 registered
46 if (0 < v && v < 5)
47 return icmp6LowPktStr[(int)(v&0x7f)];
48
49 // NP: HighPktStr is for codes 128-255
50 static const char *icmp6HighPktStr[] = {
51 "Echo Request", // 128 - RFC2463
52 "Echo Reply", // 129 - RFC2463
53 "Multicast Listener Query", // 130 - RFC2710
54 "Multicast Listener Report", // 131 - RFC2710
55 "Multicast Listener Done", // 132 - RFC2710
56 "Router Solicitation", // 133 - RFC4861
57 "Router Advertisement", // 134 - RFC4861
58 "Neighbor Solicitation", // 135 - RFC4861
59 "Neighbor Advertisement", // 136 - RFC4861
60 "Redirect Message", // 137 - RFC4861
61 "Router Renumbering", // 138 - Crawford
62 "ICMP Node Information Query", // 139 - RFC4620
63 "ICMP Node Information Response", // 140 - RFC4620
64 "Inverse Neighbor Discovery Solicitation", // 141 - RFC3122
65 "Inverse Neighbor Discovery Advertisement", // 142 - RFC3122
66 "Version 2 Multicast Listener Report", // 143 - RFC3810
67 "Home Agent Address Discovery Request", // 144 - RFC3775
68 "Home Agent Address Discovery Reply", // 145 - RFC3775
69 "Mobile Prefix Solicitation", // 146 - RFC3775
70 "Mobile Prefix Advertisement", // 147 - RFC3775
71 "Certification Path Solicitation", // 148 - RFC3971
72 "Certification Path Advertisement", // 149 - RFC3971
73 "ICMP Experimental (150)", // 150 - RFC4065
74 "Multicast Router Advertisement", // 151 - RFC4286
75 "Multicast Router Solicitation", // 152 - RFC4286
76 "Multicast Router Termination", // 153 - [RFC4286]
77 };
78
79 // high codes 127-153 registered
80 if (127 < v && v < 154)
81 return icmp6HighPktStr[(int)(v&0x7f)];
82
83 // give all others a generic display
84 static char buf[50];
85 snprintf(buf, sizeof(buf), "ICMPv6 %u", v);
86 return buf;
87}
88
90{
91 ; // nothing new.
92}
93
95{
96 Close();
97}
98
99int
101{
102 icmp_sock = xsocket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
103
104 if (icmp_sock < 0) {
105 int xerrno = errno;
106 debugs(50, DBG_CRITICAL, MYNAME << " icmp_sock: " << xstrerr(xerrno));
107 return -1;
108 }
109
110 icmp_ident = getpid() & 0xffff;
111 debugs(42, DBG_IMPORTANT, "ICMPv6 socket opened");
112
113 return icmp_sock;
114}
115
119void
120Icmp6::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
121{
122 int x;
123 LOCAL_ARRAY(char, pkt, MAX_PKT6_SZ);
124 struct icmp6_hdr *icmp = nullptr;
125 icmpEchoData *echo = nullptr;
126 struct addrinfo *S = nullptr;
127 size_t icmp6_pktsize = 0;
128
129 static_assert(sizeof(*icmp) + sizeof(*echo) <= sizeof(pkt), "our custom ICMPv6 Echo payload fits the packet buffer");
130
131 memset(pkt, '\0', MAX_PKT6_SZ);
132 icmp = (struct icmp6_hdr *)pkt;
133
134 /*
135 * cevans - beware signed/unsigned issues in untrusted data from
136 * the network!!
137 */
138 if (len < 0) {
139 len = 0;
140 }
141
142 // Construct Icmp6 ECHO header
143 icmp->icmp6_type = ICMP6_ECHO_REQUEST;
144 icmp->icmp6_code = 0;
145 icmp->icmp6_cksum = 0;
146 icmp->icmp6_id = icmp_ident;
147 icmp->icmp6_seq = (unsigned short) icmp_pkts_sent;
149
150 icmp6_pktsize = sizeof(struct icmp6_hdr);
151
152 // Fill Icmp6 ECHO data content
153 echo = reinterpret_cast<icmpEchoData *>(reinterpret_cast<char *>(pkt) + sizeof(*icmp));
154 echo->opcode = (unsigned char) opcode;
155 memcpy(&echo->tv, &current_time, sizeof(struct timeval));
156
157 icmp6_pktsize += sizeof(struct timeval) + sizeof(char);
158
159 if (payload) {
160 if (len > MAX_PAYLOAD)
161 len = MAX_PAYLOAD;
162
163 memcpy(echo->payload, payload, len);
164
165 icmp6_pktsize += len;
166 }
167
168 icmp->icmp6_cksum = CheckSum((unsigned short *) icmp, icmp6_pktsize);
169
170 to.getAddrInfo(S, AF_INET6);
171 Assure(S);
172
173 ((sockaddr_in6*)S->ai_addr)->sin6_port = 0;
174
175 assert(icmp6_pktsize <= MAX_PKT6_SZ);
176
177 debugs(42, 5, "Send Icmp6 packet to " << to << ".");
178
179 x = xsendto(icmp_sock,
180 pkt,
181 icmp6_pktsize,
182 0,
183 S->ai_addr,
184 S->ai_addrlen);
185
186 if (x < 0) {
187 int xerrno = errno;
188 debugs(42, DBG_IMPORTANT, "ERROR: sending ICMPv6 packet to " << to << ": " << xstrerr(xerrno));
189 }
190 debugs(42,9, "x=" << x);
191
192 Log(to, 0, "", 0, 0);
194}
195
199void
201{
202 int n;
203 struct addrinfo *from = nullptr;
204// struct ip6_hdr *ip = nullptr;
205 static char *pkt = nullptr;
206 struct icmp6_hdr *icmp6header = nullptr;
207 icmpEchoData *echo = nullptr;
208 struct timeval now;
209 static pingerReplyData preply;
210
211 if (icmp_sock < 0) {
212 debugs(42, DBG_CRITICAL, "dropping ICMPv6 read. No socket!?");
213 return;
214 }
215
216 if (pkt == nullptr) {
217 pkt = (char *)xmalloc(MAX_PKT6_SZ);
218 }
219
221
223 pkt,
225 0,
226 from->ai_addr,
227 &from->ai_addrlen);
228
229 if (n <= 0) {
230 debugs(42, DBG_CRITICAL, "ERROR: when calling recvfrom() on ICMPv6 socket.");
232 return;
233 }
234 if (n < static_cast<int>(sizeof(struct icmp6_hdr))) {
236 return;
237 }
238
239 preply.from = *from;
240
241#if GETTIMEOFDAY_NO_TZP
242
243 gettimeofday(&now);
244
245#else
246
247 gettimeofday(&now, nullptr);
248
249#endif
250
251 debugs(42, 8, n << " bytes from " << preply.from);
252
253// XXX: The IPv6 Header (ip6_hdr) is not available directly
254//
255// TTL still has to come from the IP header somewhere.
256// still need to strip and process it properly.
257// probably have to rely on RTT as given by timestamp in data sent and current.
258 /* IPv6 Header Structures (linux)
259 struct ip6_hdr
260
261 // fields (via simple define)
262 #define ip6_vfc // N.A
263 #define ip6_flow // N/A
264 #define ip6_plen // payload length.
265 #define ip6_nxt // expect to be type 0x3a - ICMPv6
266 #define ip6_hlim // MAX hops (always 64, but no guarantee)
267 #define ip6_hops // HOPS!!! (can it be true??)
268
269 ip = (struct ip6_hdr *) pkt;
270 // += sizeof(ip6_hdr);
271
272 debugs(42, DBG_CRITICAL, "ip6_nxt=" << ip->ip6_nxt <<
273 ", ip6_plen=" << ip->ip6_plen <<
274 ", ip6_hlim=" << ip->ip6_hlim <<
275 ", ip6_hops=" << ip->ip6_hops <<
276 " ::: 40 == sizef(ip6_hdr) == " << sizeof(ip6_hdr)
277 );
278 */
279
280 icmp6header = (struct icmp6_hdr *) pkt;
281
282 if (icmp6header->icmp6_type != ICMP6_ECHO_REPLY) {
283
284 switch (icmp6header->icmp6_type) {
285 case 134:
286 case 135:
287 case 136:
288 /* ignore Router/Neighbour Advertisements */
289 break;
290
291 default:
292 debugs(42, 8, preply.from << " said: " << icmp6header->icmp6_type << "/" << (int)icmp6header->icmp6_code << " " <<
293 IcmpPacketType(icmp6header->icmp6_type));
294 }
296 return;
297 }
298
299 if (ntohs(icmp6header->icmp6_id) != static_cast<uint16_t>(icmp_ident)) {
300 debugs(42, 8, "dropping Icmp6 read. IDENT check failed. ident=='" << icmp_ident << "'=='" << icmp6header->icmp6_id << "'");
302 return;
303 }
304
305 const auto meta = static_cast<int>(sizeof(struct icmp6_hdr) + sizeof(struct timeval) + sizeof(unsigned char));
306 if (n < meta) {
308 return;
309 }
310 echo = (icmpEchoData *)(pkt + sizeof(struct icmp6_hdr));
311
312 preply.opcode = echo->opcode;
313
314 struct timeval tv;
315 memcpy(&tv, &echo->tv, sizeof(struct timeval));
316 preply.rtt = tvSubMsec(tv, now);
317
318 /*
319 * Without access to the IPv6-Hops header we must rely on the total RTT
320 * and could calculate the hops from that, but it produces some weird value mappings using ipHops
321 * for now everything is 1 v6 hop away with variant RTT
322 * WANT: preply.hops = ip->ip6_hops; // ipHops(ip->ip_hops);
323 */
324 preply.hops = 1;
325
326 auto payload_len = n - meta;
327 assert(payload_len >= 0);
328 if (payload_len > MAX_PAYLOAD)
329 payload_len = MAX_PAYLOAD;
330
331 preply.psize = payload_len;
332 if (preply.psize > 0) {
333 memcpy(preply.payload, echo->payload, preply.psize);
334 }
335
336 Log(preply.from,
337 icmp6header->icmp6_type,
338 IcmpPacketType(icmp6header->icmp6_type),
339 preply.rtt,
340 preply.hops);
341
342 /* send results of the lookup back to squid.*/
343 control.SendResult(preply, (sizeof(pingerReplyData) - PINGER_PAYLOAD_SZ + preply.psize) );
345}
346
347#endif /* USE_ICMP */
348
#define Assure(condition)
Definition Assure.h:35
static const char * IcmpPacketType(uint8_t v)
Definition Icmp4.cc:25
static const char * IcmpPacketType(uint8_t v)
Definition Icmp6.cc:34
#define IPPROTO_ICMPV6
Definition Icmp6.h:38
IcmpPinger control
pinger helper contains one of these as a global object.
Definition pinger.cc:93
#define PINGER_PAYLOAD_SZ
Definition Icmp.h:16
int icmp_pkts_sent
Definition pinger.cc:97
#define MAX_PKT6_SZ
Definition Icmp.h:20
#define MAX_PAYLOAD
Definition Icmp.h:18
#define assert(EX)
Definition assert.h:17
~Icmp6() override
Definition Icmp6.cc:94
Icmp6()
Definition Icmp6.cc:89
int Open() override
Start pinger helper and initiate control channel.
Definition Icmp6.cc:100
void SendEcho(Ip::Address &, int, const char *, int) override
Definition Icmp6.cc:120
void Recv(void) override
Definition Icmp6.cc:200
void SendResult(pingerReplyData &preply, int len)
Send ICMP results back to squid.
Definition Icmp.h:68
virtual void Close()
Shutdown pinger helper and control channel.
Definition Icmp.cc:26
int CheckSum(unsigned short *ptr, int size)
Calculate a packet checksum.
Definition Icmp.cc:39
int icmp_ident
Definition Icmp.h:122
int icmp_sock
Definition Icmp.h:121
static void InitAddr(struct addrinfo *&ai)
Definition Address.cc:680
static void FreeAddr(struct addrinfo *&ai)
Definition Address.cc:698
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition Address.cc:619
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
Definition Config.h:18
#define xmalloc
int xsocket(int domain, int type, int protocol)
POSIX socket(2) equivalent.
Definition socket.h:128
ssize_t xsendto(int socketFd, const void *buf, size_t bufLength, int flags, const struct sockaddr *to, socklen_t toLength)
POSIX sendto(2) equivalent.
Definition socket.h:116
ssize_t xrecvfrom(int socketFd, void *buf, size_t bufLength, int flags, struct sockaddr *from, socklen_t *fromLength)
POSIX recvfrom(2) equivalent.
Definition socket.h:104
#define LOCAL_ARRAY(type, name, size)
Definition squid.h:62
char payload[MAX_PAYLOAD]
Definition Icmp.h:48
unsigned char opcode
Definition Icmp.h:47
struct timeval tv
Definition Icmp.h:46
int unsigned int
Definition stub_fd.cc:19
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition gadgets.cc:18
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:51
const char * xstrerr(int error)
Definition xstrerror.cc:83