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 "comm.h"
13 #include "comm/Loops.h"
14 #include "defines.h"
15 #include "fd.h"
16 #include "icmp/IcmpConfig.h"
17 #include "icmp/IcmpSquid.h"
18 #include "icmp/net_db.h"
19 #include "ip/tools.h"
20 #include "SquidConfig.h"
21 #include "SquidIpc.h"
22 
23 #include <cerrno>
24 
25 // Instance global to be available in main() and elsewhere.
27 
28 #if USE_ICMP
29 
30 #define S_ICMP_ECHO 1
31 #define S_ICMP_DOM 3
32 
33 static void * hIpc;
34 static pid_t pid;
35 
36 #endif /* USE_ICMP */
37 
39 {
40  ; // nothing new.
41 }
42 
44 {
45  Close();
46 }
47 
48 #if USE_ICMP
49 
50 void
51 IcmpSquid::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
52 {
53  static pingerEchoData pecho;
54  int x, slen;
55 
57  if (icmp_sock < 0) {
58  debugs(37, 2, " Socket Closed. Aborted send to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize);
59  return;
60  }
61 
63  if (!payload)
64  len = 0;
65 
70  else if (payload && len == 0)
71  len = strlen(payload);
72 
73  // XXX: If length specified or auto-detected is greater than the possible payload squid will die with an assert.
74  // TODO: This should perhapse be reduced to a truncated payload? or no payload. A WARNING is due anyway.
75  assert(len <= PINGER_PAYLOAD_SZ);
76 
77  pecho.to = to;
78 
79  pecho.opcode = (unsigned char) opcode;
80 
81  pecho.psize = len;
82 
83  if (len > 0)
84  memcpy(pecho.payload, payload, len);
85 
86  slen = sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + pecho.psize;
87 
88  debugs(37, 2, "to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize);
89 
90  x = comm_udp_send(icmp_sock, (char *)&pecho, slen, 0);
91 
92  if (x < 0) {
93  int xerrno = errno;
94  debugs(37, DBG_IMPORTANT, MYNAME << "send: " << xstrerr(xerrno));
95 
97  // TODO: try restarting the helper a few times before giving up?
98  if (xerrno == ECONNREFUSED || xerrno == EPIPE) {
99  Close();
100  return;
101  }
103  } else if (x != slen) {
104  debugs(37, DBG_IMPORTANT, "Wrote " << x << " of " << slen << " bytes");
105  }
106 }
107 
108 // static Callback to wrap the squid-side ICMP handler.
109 // the IcmpSquid::Recv cannot be declared both static and virtual.
110 static void
111 icmpSquidRecv(int, void *)
112 {
113  icmpEngine.Recv();
114 }
115 
116 void
118 {
119  int n;
120  static int fail_count = 0;
121  pingerReplyData preply;
122  static Ip::Address F;
123 
126  (char *) &preply,
127  sizeof(pingerReplyData),
128  0);
129 
130  if (n < 0 && EAGAIN != errno) {
131  int xerrno = errno;
132  debugs(37, DBG_IMPORTANT, MYNAME << "recv: " << xstrerr(xerrno));
133 
134  if (xerrno == ECONNREFUSED)
135  Close();
136 
137  if (xerrno == ECONNRESET)
138  Close();
139 
140  if (++fail_count == 10)
141  Close();
142 
143  return;
144  }
145 
146  fail_count = 0;
147 
149  if (n == 0) {
150  return;
151  }
152 
153  F = preply.from;
154 
155  F.port(0);
156 
157  switch (preply.opcode) {
158 
159  case S_ICMP_ECHO:
160  debugs(37,4, " ICMP_ECHO of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt);
161  break;
162 
163  case S_ICMP_DOM:
164  debugs(37,4, " DomainPing of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt);
165  netdbHandlePingReply(F, preply.hops, preply.rtt);
166  break;
167 
168  default:
169  debugs(37, DBG_IMPORTANT, "ERROR: Bad opcode: " << preply.opcode << " from " << F);
170  break;
171  }
172 }
173 
174 #endif /* USE_ICMP */
175 
176 void
177 IcmpSquid::DomainPing(Ip::Address &to, const char *domain)
178 {
179 #if USE_ICMP
180  debugs(37, 4, "'" << domain << "' (" << to << ")");
181  SendEcho(to, S_ICMP_DOM, domain, 0);
182 #else
183  (void)to;
184  (void)domain;
185 #endif
186 }
187 
188 int
190 {
191 #if USE_ICMP
192  const char *args[2];
193  int rfd;
194  int wfd;
195  Ip::Address localhost;
196 
197  /* User configured disabled. */
198  if (!IcmpCfg.enable) {
199  Close();
200  return -1;
201  }
202 
203  args[0] = "(pinger)";
204  args[1] = nullptr;
205  localhost.setLocalhost();
206 
207  /*
208  * Do NOT use IPC_DGRAM (=IPC_UNIX_DGRAM) here because you can't
209  * send() more than 4096 bytes on a socketpair() socket (at
210  * least on FreeBSD).
211  */
214  args,
215  "Pinger Socket",
216  localhost,
217  &rfd,
218  &wfd,
219  &hIpc);
220 
221  if (pid < 0)
222  return -1;
223 
224  assert(rfd == wfd);
225 
226  icmp_sock = rfd;
227 
228  fd_note(icmp_sock, "pinger");
229 
231 
233 
234  debugs(37, DBG_IMPORTANT, "Pinger socket opened on FD " << icmp_sock);
235 
236  /* Tests the pinger immediately using localhost */
237  if (Ip::EnableIpv6)
238  SendEcho(localhost, S_ICMP_ECHO, "ip6-localhost");
239  if (localhost.setIPv4())
240  SendEcho(localhost, S_ICMP_ECHO, "localhost");
241 
242 #if _SQUID_WINDOWS_
243 
244  debugs(37, 4, "Pinger handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid);
245 
246 #endif /* _SQUID_WINDOWS_ */
247  return icmp_sock;
248 #else /* USE_ICMP */
249  return -1;
250 #endif /* USE_ICMP */
251 }
252 
253 void
255 {
256 #if USE_ICMP
257 
258  if (icmp_sock < 0)
259  return;
260 
261  debugs(37, DBG_IMPORTANT, "Closing Pinger socket on FD " << icmp_sock);
262 
263 #if _SQUID_WINDOWS_
264 
265  send(icmp_sock, (const void *) "$shutdown\n", 10, 0);
266 
267 #endif
268 
270 
271 #if _SQUID_WINDOWS_
272 
273  if (hIpc) {
274  if (WaitForSingleObject(hIpc, 12000) != WAIT_OBJECT_0) {
275  getCurrentTime();
276  debugs(37, DBG_CRITICAL, "WARNING: (pinger," << pid << ") didn't exit in 12 seconds");
277  }
278 
279  CloseHandle(hIpc);
280  }
281 
282 #endif
283  icmp_sock = -1;
284 
285 #endif
286 }
287 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void Close() override
Shutdown pinger helper and control channel.
Definition: IcmpSquid.cc:254
#define DBG_CRITICAL
Definition: Stream.h:37
void setLocalhost()
Definition: Address.cc:275
#define IPC_UDP_SOCKET
Definition: defines.h:90
void fd_note(int fd, const char *s)
Definition: fd.cc:216
#define PINGER_PAYLOAD_SZ
Definition: Icmp.h:16
IcmpSquid icmpEngine
Definition: IcmpSquid.cc:26
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags)
Definition: comm.cc:146
int comm_udp_recv(int fd, void *buf, size_t len, int flags)
Definition: comm.cc:139
#define comm_close(x)
Definition: comm.h:36
#define S_ICMP_DOM
Definition: IcmpSquid.cc:31
int icmp_sock
Definition: Icmp.h:121
char payload[PINGER_PAYLOAD_SZ]
Definition: Icmp.h:31
static void * hIpc
Definition: IcmpSquid.cc:33
static pid_t pid
Definition: IcmpSquid.cc:34
void commUnsetFdTimeout(int fd)
clear a timeout handler by FD number
Definition: comm.cc:579
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
void Recv(void) override
Handle ICMP responses.
Definition: IcmpSquid.cc:117
~IcmpSquid() override
Definition: IcmpSquid.cc:43
int enable
Definition: IcmpConfig.h:35
Ip::Address to
Definition: Icmp.h:28
unsigned char opcode
Definition: Icmp.h:29
#define assert(EX)
Definition: assert.h:17
bool setIPv4()
Definition: Address.cc:244
#define COMM_SELECT_READ
Definition: defines.h:24
const char * c_str()
Definition: SBuf.cc:516
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:71
int psize
Definition: Icmp.h:30
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:220
void DomainPing(Ip::Address &to, const char *domain)
Definition: IcmpSquid.cc:177
SBuf program
Definition: IcmpConfig.h:32
#define DBG_IMPORTANT
Definition: Stream.h:38
#define MYNAME
Definition: Stream.h:219
void SendEcho(Ip::Address &to, int opcode, const char *payload=nullptr, int len=0) override
Definition: IcmpSquid.cc:51
#define S_ICMP_ECHO
Definition: IcmpSquid.cc:30
IcmpConfig IcmpCfg
Definition: IcmpConfig.cc:17
int Open() override
Start pinger helper and initiate control channel.
Definition: IcmpSquid.cc:189
void netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
Definition: net_db.cc:828
static void icmpSquidRecv(int, void *)
Definition: IcmpSquid.cc:111
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
Definition: Icmp.h:67

 

Introduction

Documentation

Support

Miscellaneous