Squid Web Cache master
Loading...
Searching...
No Matches
IcmpSquid.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 37 ICMP Routines */
10
11#include "squid.h"
12#include "base/Assure.h"
13#include "comm.h"
14#include "comm/Loops.h"
15#include "compat/socket.h"
16#include "defines.h"
17#include "fd.h"
18#include "icmp/IcmpConfig.h"
19#include "icmp/IcmpSquid.h"
20#include "icmp/net_db.h"
21#include "ip/tools.h"
22#include "SquidConfig.h"
23#include "SquidIpc.h"
24
25#include <cerrno>
26
27// Instance global to be available in main() and elsewhere.
29
30#if USE_ICMP
31
32#define S_ICMP_ECHO 1
33#define S_ICMP_DOM 3
34
35static void * hIpc;
36static pid_t pid;
37
38#endif /* USE_ICMP */
39
41{
42 ; // nothing new.
43}
44
49
50#if USE_ICMP
51
52void
53IcmpSquid::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
54{
55 static pingerEchoData pecho;
56 int x, slen;
57
59 if (icmp_sock < 0) {
60 debugs(37, 2, " Socket Closed. Aborted send to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize);
61 return;
62 }
63
65 if (!payload)
66 len = 0;
67
72 else if (payload && len == 0)
73 len = strlen(payload);
74
75 // All our callers supply a DNS name. PINGER_PAYLOAD_SZ must accommodate the
76 // longest DNS name Squid supports. TODO: Simplify and improve the rest of
77 // this code accordingly.
79
80 pecho.to = to;
81
82 pecho.opcode = (unsigned char) opcode;
83
84 pecho.psize = len;
85
86 if (len > 0)
87 memcpy(pecho.payload, payload, len);
88
89 slen = sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + pecho.psize;
90
91 debugs(37, 2, "to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize);
92
93 x = comm_udp_send(icmp_sock, (char *)&pecho, slen, 0);
94
95 if (x < 0) {
96 int xerrno = errno;
97 debugs(37, DBG_IMPORTANT, MYNAME << "send: " << xstrerr(xerrno));
98
100 // TODO: try restarting the helper a few times before giving up?
101 if (xerrno == ECONNREFUSED || xerrno == EPIPE) {
102 Close();
103 return;
104 }
106 } else if (x != slen) {
107 debugs(37, DBG_IMPORTANT, "Wrote " << x << " of " << slen << " bytes");
108 }
109}
110
111// static Callback to wrap the squid-side ICMP handler.
112// the IcmpSquid::Recv cannot be declared both static and virtual.
113static void
114icmpSquidRecv(int, void *)
115{
117}
118
119void
121{
122 int n;
123 static int fail_count = 0;
124 pingerReplyData preply;
125 static Ip::Address F;
126
129 (char *) &preply,
130 sizeof(pingerReplyData),
131 0);
132
133 if (n < 0 && EAGAIN != errno) {
134 int xerrno = errno;
135 debugs(37, DBG_IMPORTANT, MYNAME << "recv: " << xstrerr(xerrno));
136
137 if (xerrno == ECONNREFUSED)
138 Close();
139
140 if (xerrno == ECONNRESET)
141 Close();
142
143 if (++fail_count == 10)
144 Close();
145
146 return;
147 }
148
149 fail_count = 0;
150
152 if (n == 0) {
153 return;
154 }
155
156 const auto base = static_cast<int>(sizeof(preply) - sizeof(preply.payload));
157 if (n < base) {
158 debugs(37, 2, "short reply header (" << n << " < " << base << "); dropping");
159 return;
160 }
161 const auto avail = n - base;
162 if (avail > static_cast<int>(sizeof(preply.payload))) {
163 debugs(37, 2, "oversized reply payload (" << avail << "); dropping");
164 return;
165 }
166 if (preply.psize < 0) {
167 debugs(37, 2, "negative psize (" << preply.psize << "); dropping");
168 return;
169 }
170 if (preply.psize > avail) {
171 debugs(37, 2, "truncated reply (psize=" << preply.psize << ", avail=" << avail << "); dropping");
172 return;
173 }
174 // Accept variable-length replies: base header + psize bytes.
175 // We already validated 'n >= base' and 'preply.psize <= avail'.
176 // If the datagram was truncated in transit, drop it.
177 if (n < (base + preply.psize)) {
178 debugs(37, 2, "truncated reply datagram; dropping");
179 return;
180 }
181
182 F = preply.from;
183
184 F.port(0);
185
186 switch (preply.opcode) {
187
188 case S_ICMP_ECHO:
189 debugs(37,4, " ICMP_ECHO of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt);
190 break;
191
192 case S_ICMP_DOM:
193 debugs(37,4, " DomainPing of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt);
194 netdbHandlePingReply(F, preply.hops, preply.rtt);
195 break;
196
197 default:
198 debugs(37, DBG_IMPORTANT, "ERROR: Bad opcode: " << preply.opcode << " from " << F);
199 break;
200 }
201}
202
203#endif /* USE_ICMP */
204
205void
206IcmpSquid::DomainPing(Ip::Address &to, const char *domain)
207{
208#if USE_ICMP
209 debugs(37, 4, "'" << domain << "' (" << to << ")");
210 SendEcho(to, S_ICMP_DOM, domain, 0);
211#else
212 (void)to;
213 (void)domain;
214#endif
215}
216
217int
219{
220#if USE_ICMP
221 const char *args[2];
222 int rfd;
223 int wfd;
224 Ip::Address localhost;
225
226 /* User configured disabled. */
227 if (!IcmpCfg.enable) {
228 Close();
229 return -1;
230 }
231
232 args[0] = "(pinger)";
233 args[1] = nullptr;
234 localhost.setLocalhost();
235
236 /*
237 * Do NOT use IPC_DGRAM (=IPC_UNIX_DGRAM) here because you can't
238 * send() more than 4096 bytes on a socketpair() socket (at
239 * least on FreeBSD).
240 */
243 args,
244 "Pinger Socket",
245 localhost,
246 &rfd,
247 &wfd,
248 &hIpc);
249
250 if (pid < 0)
251 return -1;
252
253 assert(rfd == wfd);
254
255 icmp_sock = rfd;
256
257 fd_note(icmp_sock, "pinger");
258
260
262
263 debugs(37, DBG_IMPORTANT, "Pinger socket opened on FD " << icmp_sock);
264
265 /* Tests the pinger immediately using localhost */
266 if (Ip::EnableIpv6)
267 SendEcho(localhost, S_ICMP_ECHO, "ip6-localhost");
268 if (localhost.setIPv4())
269 SendEcho(localhost, S_ICMP_ECHO, "localhost");
270
271#if _SQUID_WINDOWS_
272
273 debugs(37, 4, "Pinger handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid);
274
275#endif /* _SQUID_WINDOWS_ */
276 return icmp_sock;
277#else /* USE_ICMP */
278 return -1;
279#endif /* USE_ICMP */
280}
281
282void
284{
285#if USE_ICMP
286
287 if (icmp_sock < 0)
288 return;
289
290 debugs(37, DBG_IMPORTANT, "Closing Pinger socket on FD " << icmp_sock);
291
292#if _SQUID_WINDOWS_
293
294 xsend(icmp_sock, (const void *) "$shutdown\n", 10, 0);
295
296#endif
297
299
300#if _SQUID_WINDOWS_
301
302 if (hIpc) {
303 if (WaitForSingleObject(hIpc, 12000) != WAIT_OBJECT_0) {
305 debugs(37, DBG_CRITICAL, "WARNING: (pinger," << pid << ") didn't exit in 12 seconds");
306 }
307
308 CloseHandle(hIpc);
309 }
310
311#endif
312 icmp_sock = -1;
313
314#endif
315}
316
#define Assure(condition)
Definition Assure.h:35
IcmpConfig IcmpCfg
Definition IcmpConfig.cc:17
#define S_ICMP_ECHO
Definition IcmpSquid.cc:32
static void * hIpc
Definition IcmpSquid.cc:35
#define S_ICMP_DOM
Definition IcmpSquid.cc:33
IcmpSquid icmpEngine
Definition IcmpSquid.cc:28
static void icmpSquidRecv(int, void *)
Definition IcmpSquid.cc:114
static pid_t pid
Definition IcmpSquid.cc:36
#define PINGER_PAYLOAD_SZ
Definition Icmp.h:16
#define assert(EX)
Definition assert.h:17
SBuf program
Definition IcmpConfig.h:32
void DomainPing(Ip::Address &to, const char *domain)
Definition IcmpSquid.cc:206
void Close() override
Shutdown pinger helper and control channel.
Definition IcmpSquid.cc:283
~IcmpSquid() override
Definition IcmpSquid.cc:45
void Recv(void) override
Handle ICMP responses.
Definition IcmpSquid.cc:120
int Open() override
Start pinger helper and initiate control channel.
Definition IcmpSquid.cc:218
void SendEcho(Ip::Address &to, int opcode, const char *payload=nullptr, int len=0) override
Definition IcmpSquid.cc:53
Definition Icmp.h:68
int icmp_sock
Definition Icmp.h:121
bool setIPv4()
Definition Address.cc:244
void setLocalhost()
Definition Address.cc:275
unsigned short port() const
Definition Address.cc:790
const char * c_str()
Definition SBuf.cc:516
void commUnsetFdTimeout(int fd)
clear a timeout handler by FD number
Definition comm.cc:581
int comm_udp_recv(int fd, void *buf, size_t len, int flags)
Definition comm.cc:141
ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags)
Definition comm.cc:148
#define comm_close(x)
Definition comm.h:36
#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
#define COMM_SELECT_READ
Definition defines.h:24
#define IPC_UDP_SOCKET
Definition defines.h:90
void fd_note(int fd, const char *s)
Definition fd.cc:211
pid_t ipcCreate(int type, const char *prog, const char *const args[], const char *name, Ip::Address &local_addr, int *rfd, int *wfd, void **hIpc)
Definition ipc.cc:63
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
void netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
Definition net_db.cc:828
ssize_t xsend(int socketFd, const void *buf, size_t bufLength, int flags)
POSIX send(2) equivalent.
Definition socket.h:110
int psize
Definition Icmp.h:30
Ip::Address to
Definition Icmp.h:28
unsigned char opcode
Definition Icmp.h:29
char payload[PINGER_PAYLOAD_SZ]
Definition Icmp.h:31
unsigned char opcode
Definition Icmp.h:38
char payload[PINGER_PAYLOAD_SZ]
Definition Icmp.h:42
Ip::Address from
Definition Icmp.h:37
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
const char * xstrerr(int error)
Definition xstrerror.cc:83