Squid Web Cache master
Loading...
Searching...
No Matches
Icmp4.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 "Icmp4.h"
21#include "IcmpPinger.h"
22#include "time/gadgets.h"
23
24static const char *
26{
27 static const char *icmpPktStr[] = {
28 "Echo Reply",
29 "ICMP 1",
30 "ICMP 2",
31 "Destination Unreachable",
32 "Source Quench",
33 "Redirect",
34 "ICMP 6",
35 "ICMP 7",
36 "Echo",
37 "ICMP 9",
38 "ICMP 10",
39 "Time Exceeded",
40 "Parameter Problem",
41 "Timestamp",
42 "Timestamp Reply",
43 "Info Request",
44 "Info Reply",
45 "Out of Range Type"
46 };
47
48 if (v > 17) {
49 static char buf[50];
50 snprintf(buf, sizeof(buf), "ICMP %u (invalid)", v);
51 return buf;
52 }
53
54 return icmpPktStr[v];
55}
56
58{
59 ;
60}
61
63{
64 Close();
65}
66
67int
69{
70 icmp_sock = xsocket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
71
72 if (icmp_sock < 0) {
73 int xerrno = errno;
74 debugs(50, DBG_CRITICAL, MYNAME << " icmp_sock: " << xstrerr(xerrno));
75 return -1;
76 }
77
78 icmp_ident = getpid() & 0xffff;
79 debugs(42, DBG_IMPORTANT, "ICMP socket opened.");
80
81 return icmp_sock;
82}
83
84void
85Icmp4::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
86{
87 int x;
88 LOCAL_ARRAY(char, pkt, MAX_PKT4_SZ);
89
90 struct icmphdr *icmp = nullptr;
91 icmpEchoData *echo;
92 size_t icmp_pktsize = sizeof(struct icmphdr);
93 struct addrinfo *S = nullptr;
94
95 static_assert(sizeof(*icmp) + sizeof(*echo) <= sizeof(pkt), "our custom ICMPv4 Echo payload fits the packet buffer");
96
97 memset(pkt, '\0', MAX_PKT4_SZ);
98
99 icmp = (struct icmphdr *) (void *) pkt;
100
101 /*
102 * cevans - beware signed/unsigned issues in untrusted data from
103 * the network!!
104 */
105 if (len < 0) {
106 len = 0;
107 }
108
109 // Construct ICMP packet header
110 icmp->icmp_type = ICMP_ECHO;
111 icmp->icmp_code = 0;
112 icmp->icmp_cksum = 0;
113 icmp->icmp_id = icmp_ident;
114 icmp->icmp_seq = (unsigned short) icmp_pkts_sent;
116
117 // Construct ICMP packet data content
118 echo = reinterpret_cast<icmpEchoData *>(reinterpret_cast<char *>(pkt) + sizeof(*icmp));
119 echo->opcode = (unsigned char) opcode;
120 memcpy(&echo->tv, &current_time, sizeof(struct timeval));
121
122 icmp_pktsize += sizeof(icmpEchoData) - MAX_PAYLOAD;
123
124 if (payload) {
125 if (len > MAX_PAYLOAD)
126 len = MAX_PAYLOAD;
127
128 memcpy(echo->payload, payload, len);
129
130 icmp_pktsize += len;
131 }
132
133 icmp->icmp_cksum = CheckSum((unsigned short *) icmp, icmp_pktsize);
134
135 to.getAddrInfo(S, AF_INET);
136 Assure(S);
137
138 ((sockaddr_in*)S->ai_addr)->sin_port = 0;
139 assert(icmp_pktsize <= MAX_PKT4_SZ);
140
141 debugs(42, 5, "Send ICMP packet to " << to << ".");
142
143 x = xsendto(icmp_sock,
144 pkt,
145 icmp_pktsize,
146 0,
147 S->ai_addr,
148 S->ai_addrlen);
149
150 if (x < 0) {
151 int xerrno = errno;
152 debugs(42, DBG_IMPORTANT, "ERROR: sending ICMP packet to " << to << ": " << xstrerr(xerrno));
153 }
154
155 Log(to, ' ', "", 0, 0);
157}
158
159void
161{
162 int n;
163 struct addrinfo *from = nullptr;
164 int iphdrlen = sizeof(iphdr);
165 struct iphdr *ip = nullptr;
166 struct icmphdr *icmp = nullptr;
167 static char *pkt = nullptr;
168 struct timeval now;
169 icmpEchoData *echo;
170 static pingerReplyData preply;
171
172 if (icmp_sock < 0) {
173 debugs(42, DBG_CRITICAL, "No socket! Recv() should not be called.");
174 return;
175 }
176
177 if (pkt == nullptr)
178 pkt = (char *)xmalloc(MAX_PKT4_SZ);
179
182 pkt,
184 0,
185 from->ai_addr,
186 &from->ai_addrlen);
187
188 if (n <= 0) {
189 debugs(42, DBG_CRITICAL, "ERROR: when calling recvfrom() on ICMP socket.");
191 return;
192 }
193
194 preply.from = *from;
195
196#if GETTIMEOFDAY_NO_TZP
197
198 gettimeofday(&now);
199
200#else
201
202 gettimeofday(&now, nullptr);
203
204#endif
205
206 debugs(42, 8, n << " bytes from " << preply.from);
207
208 ip = (struct iphdr *) (void *) pkt;
209 if (n < static_cast<int>(sizeof(*ip))) {
210 debugs(42, 2, "short packet: only " << n << " bytes; expecting at least " << sizeof(*ip) << "-byte IP header");
212 return;
213 }
214
215#if HAVE_STRUCT_IPHDR_IP_HL
216
217 iphdrlen = ip->ip_hl << 2;
218
219#else /* HAVE_STRUCT_IPHDR_IP_HL */
220#if WORDS_BIGENDIAN
221
222 iphdrlen = (ip->ip_vhl >> 4) << 2;
223
224#else
225
226 iphdrlen = (ip->ip_vhl & 0xF) << 2;
227
228#endif
229#endif /* HAVE_STRUCT_IPHDR_IP_HL */
230
231 if (iphdrlen < 20 || n < iphdrlen) {
232 debugs(42, 2, "bogus IP header length " << iphdrlen << " in " << n << "-byte packet");
234 return;
235 }
236 icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
237 const int icmpAvail = n - iphdrlen;
238 if (icmpAvail < static_cast<int>(sizeof(*icmp))) {
239 debugs(42, 2, "short ICMP header: only " << icmpAvail << " bytes available; expecting at least " << sizeof(*icmp));
241 return;
242 }
243
244 if (icmp->icmp_type != ICMP_ECHOREPLY) {
246 return;
247 }
248
249 if (icmp->icmp_id != icmp_ident) {
251 return;
252 }
253
254 echo = (icmpEchoData *) (void *) (icmp + 1);
255
256 const auto echoHdr = static_cast<int>(sizeof(icmpEchoData) - MAX_PAYLOAD);
257 const auto icmpDataLen = icmpAvail - sizeof(*icmp);
258 if (icmpDataLen < echoHdr) { // do not read past end of the packet
259 debugs(42, 2, "short ICMP echo data: " << icmpDataLen << " bytes; expecting " << echoHdr);
261 return;
262 }
263
264 preply.opcode = echo->opcode;
265
266 preply.hops = ipHops(ip->ip_ttl);
267
268 struct timeval tv;
269 memcpy(&tv, &echo->tv, sizeof(struct timeval));
270 preply.rtt = tvSubMsec(tv, now);
271
272 // Payload length = (ICMP total data) - (opcode + timeval)
273 preply.psize = icmpDataLen - echoHdr;
274 if (preply.psize > MAX_PAYLOAD)
275 preply.psize = MAX_PAYLOAD;
276
277 if (preply.psize < 0) {
278 debugs(42, DBG_CRITICAL, "ERROR: Malformed ICMP packet.");
280 return;
281 }
282
283 control.SendResult(preply, (sizeof(pingerReplyData) - PINGER_PAYLOAD_SZ + preply.psize));
284
285 Log(preply.from, icmp->icmp_type, IcmpPacketType(icmp->icmp_type), preply.rtt, preply.hops);
287}
288
289#endif /* USE_ICMP */
290
#define Assure(condition)
Definition Assure.h:35
static const char * IcmpPacketType(uint8_t v)
Definition Icmp4.cc:25
#define ICMP_ECHOREPLY
Definition Icmp4.h:105
#define IPPROTO_ICMP
Definition Icmp4.h:109
#define iphdr
Definition Icmp4.h:28
#define ICMP_ECHO
Definition Icmp4.h:101
#define icmphdr
Definition Icmp4.h:27
IcmpPinger control
pinger helper contains one of these as a global object.
Definition pinger.cc:93
#define MAX_PKT4_SZ
Definition Icmp.h:19
#define PINGER_PAYLOAD_SZ
Definition Icmp.h:16
int icmp_pkts_sent
Definition pinger.cc:97
#define MAX_PAYLOAD
Definition Icmp.h:18
#define assert(EX)
Definition assert.h:17
Icmp4()
Definition Icmp4.cc:57
void SendEcho(Ip::Address &, int, const char *, int) override
Definition Icmp4.cc:85
void Recv(void) override
Handle ICMP responses.
Definition Icmp4.cc:160
~Icmp4() override
Definition Icmp4.cc:62
int Open() override
Start pinger helper and initiate control channel.
Definition Icmp4.cc:68
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
int ipHops(int ttl)
Definition Icmp.cc:68
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
unsigned char opcode
Definition Icmp.h:38
Ip::Address from
Definition Icmp.h:37
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