]>
Commit | Line | Data |
---|---|---|
cc192b50 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
cc192b50 | 3 | * |
bbc27441 AJ |
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. | |
cc192b50 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 37 ICMP Routines */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
d841c88d AJ |
12 | #include "comm.h" |
13 | #include "comm/Loops.h" | |
f1d156fa | 14 | #include "defines.h" |
c4ad1349 | 15 | #include "fd.h" |
7a9d36e3 | 16 | #include "icmp/IcmpConfig.h" |
9b5c4a9a AJ |
17 | #include "icmp/IcmpSquid.h" |
18 | #include "icmp/net_db.h" | |
055421ee | 19 | #include "ip/tools.h" |
4d5904f7 | 20 | #include "SquidConfig.h" |
96097880 | 21 | #include "SquidIpc.h" |
cc192b50 | 22 | |
1a30fdf5 | 23 | #include <cerrno> |
21d845b1 | 24 | |
cc192b50 | 25 | // Instance global to be available in main() and elsewhere. |
b826ffb5 | 26 | IcmpSquid icmpEngine; |
cc192b50 | 27 | |
28 | #if USE_ICMP | |
29 | ||
30 | #define S_ICMP_ECHO 1 | |
cc192b50 | 31 | #define S_ICMP_DOM 3 |
32 | ||
33 | static void * hIpc; | |
34 | static pid_t pid; | |
35 | ||
36 | #endif /* USE_ICMP */ | |
37 | ||
b826ffb5 | 38 | IcmpSquid::IcmpSquid() : Icmp() |
cc192b50 | 39 | { |
40 | ; // nothing new. | |
41 | } | |
42 | ||
b826ffb5 | 43 | IcmpSquid::~IcmpSquid() |
cc192b50 | 44 | { |
45 | Close(); | |
46 | } | |
47 | ||
cc192b50 | 48 | #if USE_ICMP |
49 | ||
50 | void | |
b7ac5457 | 51 | IcmpSquid::SendEcho(Ip::Address &to, int opcode, const char *payload, int len) |
cc192b50 | 52 | { |
53 | static pingerEchoData pecho; | |
54 | int x, slen; | |
55 | ||
56 | /** \li Does nothing if the pinger socket is not available. */ | |
26ac0430 | 57 | if (icmp_sock < 0) { |
bf95c10a | 58 | debugs(37, 2, " Socket Closed. Aborted send to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize); |
cc192b50 | 59 | return; |
60 | } | |
61 | ||
62 | /** \li If no payload is given or is set as NULL it will ignore payload and len */ | |
26ac0430 | 63 | if (!payload) |
cc192b50 | 64 | len = 0; |
26ac0430 | 65 | |
cc192b50 | 66 | /** \li Otherwise if len is 0, uses strlen() to detect length of payload. |
e0d875fa AJ |
67 | * XXX: This will result in part of the payload being truncated if it contains a NUL character. |
68 | * Or it may result in a buffer over-read if the payload is not NUL-terminated properly. | |
cc192b50 | 69 | */ |
70 | else if (payload && len == 0) | |
71 | len = strlen(payload); | |
72 | ||
9837567d AJ |
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. | |
cc192b50 | 75 | assert(len <= PINGER_PAYLOAD_SZ); |
76 | ||
77 | pecho.to = to; | |
78 | ||
79 | pecho.opcode = (unsigned char) opcode; | |
80 | ||
81 | pecho.psize = len; | |
82 | ||
26ac0430 | 83 | if (len > 0) |
41d00cd3 | 84 | memcpy(pecho.payload, payload, len); |
cc192b50 | 85 | |
86 | slen = sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + pecho.psize; | |
87 | ||
bf95c10a | 88 | debugs(37, 2, "to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize); |
cc192b50 | 89 | |
90 | x = comm_udp_send(icmp_sock, (char *)&pecho, slen, 0); | |
91 | ||
92 | if (x < 0) { | |
b69e9ffa AJ |
93 | int xerrno = errno; |
94 | debugs(37, DBG_IMPORTANT, MYNAME << "send: " << xstrerr(xerrno)); | |
cc192b50 | 95 | |
96 | /** \li If the send results in ECONNREFUSED or EPIPE errors from helper, will cleanly shutdown the module. */ | |
9837567d | 97 | // TODO: try restarting the helper a few times before giving up? |
b69e9ffa | 98 | if (xerrno == ECONNREFUSED || xerrno == EPIPE) { |
cc192b50 | 99 | Close(); |
100 | return; | |
101 | } | |
102 | /** All other send errors are ignored. */ | |
9166e142 | 103 | } else if (x != slen) { |
bf95c10a | 104 | debugs(37, DBG_IMPORTANT, "Wrote " << x << " of " << slen << " bytes"); |
cc192b50 | 105 | } |
106 | } | |
107 | ||
108 | // static Callback to wrap the squid-side ICMP handler. | |
b826ffb5 | 109 | // the IcmpSquid::Recv cannot be declared both static and virtual. |
cc192b50 | 110 | static void |
8b082ed9 | 111 | icmpSquidRecv(int, void *) |
cc192b50 | 112 | { |
113 | icmpEngine.Recv(); | |
114 | } | |
115 | ||
116 | void | |
b826ffb5 | 117 | IcmpSquid::Recv() |
cc192b50 | 118 | { |
119 | int n; | |
120 | static int fail_count = 0; | |
121 | pingerReplyData preply; | |
b7ac5457 | 122 | static Ip::Address F; |
cc192b50 | 123 | |
aee3523a | 124 | Comm::SetSelect(icmp_sock, COMM_SELECT_READ, icmpSquidRecv, nullptr, 0); |
cc192b50 | 125 | n = comm_udp_recv(icmp_sock, |
126 | (char *) &preply, | |
127 | sizeof(pingerReplyData), | |
128 | 0); | |
129 | ||
130 | if (n < 0 && EAGAIN != errno) { | |
b69e9ffa AJ |
131 | int xerrno = errno; |
132 | debugs(37, DBG_IMPORTANT, MYNAME << "recv: " << xstrerr(xerrno)); | |
cc192b50 | 133 | |
b69e9ffa | 134 | if (xerrno == ECONNREFUSED) |
cc192b50 | 135 | Close(); |
136 | ||
b69e9ffa | 137 | if (xerrno == ECONNRESET) |
cc192b50 | 138 | Close(); |
139 | ||
140 | if (++fail_count == 10) | |
141 | Close(); | |
142 | ||
143 | return; | |
144 | } | |
145 | ||
146 | fail_count = 0; | |
147 | ||
148 | /** If its a test probe from the pinger. Do nothing. */ | |
149 | if (n == 0) { | |
150 | return; | |
151 | } | |
152 | ||
153 | F = preply.from; | |
154 | ||
4dd643d5 | 155 | F.port(0); |
cc192b50 | 156 | |
157 | switch (preply.opcode) { | |
158 | ||
159 | case S_ICMP_ECHO: | |
bf95c10a | 160 | debugs(37,4, " ICMP_ECHO of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt); |
cc192b50 | 161 | break; |
162 | ||
163 | case S_ICMP_DOM: | |
bf95c10a | 164 | debugs(37,4, " DomainPing of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt); |
cc192b50 | 165 | netdbHandlePingReply(F, preply.hops, preply.rtt); |
166 | break; | |
167 | ||
168 | default: | |
d816f28d | 169 | debugs(37, DBG_IMPORTANT, "ERROR: Bad opcode: " << preply.opcode << " from " << F); |
cc192b50 | 170 | break; |
171 | } | |
172 | } | |
173 | ||
174 | #endif /* USE_ICMP */ | |
175 | ||
176 | void | |
b7ac5457 | 177 | IcmpSquid::DomainPing(Ip::Address &to, const char *domain) |
cc192b50 | 178 | { |
179 | #if USE_ICMP | |
bf95c10a | 180 | debugs(37, 4, "'" << domain << "' (" << to << ")"); |
cc192b50 | 181 | SendEcho(to, S_ICMP_DOM, domain, 0); |
8b082ed9 FC |
182 | #else |
183 | (void)to; | |
184 | (void)domain; | |
cc192b50 | 185 | #endif |
186 | } | |
187 | ||
188 | int | |
b826ffb5 | 189 | IcmpSquid::Open(void) |
cc192b50 | 190 | { |
191 | #if USE_ICMP | |
192 | const char *args[2]; | |
193 | int rfd; | |
194 | int wfd; | |
b7ac5457 | 195 | Ip::Address localhost; |
cc192b50 | 196 | |
197 | /* User configured disabled. */ | |
7a9d36e3 | 198 | if (!IcmpCfg.enable) { |
cc192b50 | 199 | Close(); |
200 | return -1; | |
201 | } | |
202 | ||
203 | args[0] = "(pinger)"; | |
aee3523a | 204 | args[1] = nullptr; |
4dd643d5 | 205 | localhost.setLocalhost(); |
cc192b50 | 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 | */ | |
212 | pid = ipcCreate(IPC_UDP_SOCKET, | |
7a9d36e3 | 213 | IcmpCfg.program.c_str(), |
cc192b50 | 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 | ||
aee3523a | 230 | Comm::SetSelect(icmp_sock, COMM_SELECT_READ, icmpSquidRecv, nullptr, 0); |
cc192b50 | 231 | |
933dd095 | 232 | commUnsetFdTimeout(icmp_sock); |
cc192b50 | 233 | |
bf95c10a | 234 | debugs(37, DBG_IMPORTANT, "Pinger socket opened on FD " << icmp_sock); |
cc192b50 | 235 | |
236 | /* Tests the pinger immediately using localhost */ | |
055421ee AJ |
237 | if (Ip::EnableIpv6) |
238 | SendEcho(localhost, S_ICMP_ECHO, "ip6-localhost"); | |
4dd643d5 | 239 | if (localhost.setIPv4()) |
cc192b50 | 240 | SendEcho(localhost, S_ICMP_ECHO, "localhost"); |
241 | ||
7aa9bb3e | 242 | #if _SQUID_WINDOWS_ |
cc192b50 | 243 | |
bf95c10a | 244 | debugs(37, 4, "Pinger handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid); |
cc192b50 | 245 | |
7aa9bb3e | 246 | #endif /* _SQUID_WINDOWS_ */ |
cc192b50 | 247 | return icmp_sock; |
b8e19d32 HN |
248 | #else /* USE_ICMP */ |
249 | return -1; | |
250 | #endif /* USE_ICMP */ | |
cc192b50 | 251 | } |
252 | ||
253 | void | |
b826ffb5 | 254 | IcmpSquid::Close(void) |
cc192b50 | 255 | { |
256 | #if USE_ICMP | |
257 | ||
258 | if (icmp_sock < 0) | |
259 | return; | |
260 | ||
bf95c10a | 261 | debugs(37, DBG_IMPORTANT, "Closing Pinger socket on FD " << icmp_sock); |
cc192b50 | 262 | |
7aa9bb3e | 263 | #if _SQUID_WINDOWS_ |
cc192b50 | 264 | |
265 | send(icmp_sock, (const void *) "$shutdown\n", 10, 0); | |
266 | ||
267 | #endif | |
268 | ||
269 | comm_close(icmp_sock); | |
270 | ||
7aa9bb3e | 271 | #if _SQUID_WINDOWS_ |
cc192b50 | 272 | |
273 | if (hIpc) { | |
274 | if (WaitForSingleObject(hIpc, 12000) != WAIT_OBJECT_0) { | |
275 | getCurrentTime(); | |
bf95c10a | 276 | debugs(37, DBG_CRITICAL, "WARNING: (pinger," << pid << ") didn't exit in 12 seconds"); |
cc192b50 | 277 | } |
278 | ||
279 | CloseHandle(hIpc); | |
280 | } | |
281 | ||
282 | #endif | |
283 | icmp_sock = -1; | |
284 | ||
285 | #endif | |
286 | } | |
f53969cc | 287 |