]>
Commit | Line | Data |
---|---|---|
cc192b50 | 1 | /* |
bbc27441 | 2 | * Copyright (C) 1996-2014 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" |
9b5c4a9a AJ |
16 | #include "icmp/IcmpSquid.h" |
17 | #include "icmp/net_db.h" | |
055421ee | 18 | #include "ip/tools.h" |
4d5904f7 | 19 | #include "SquidConfig.h" |
96097880 | 20 | #include "SquidIpc.h" |
cc192b50 | 21 | #include "SquidTime.h" |
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) { |
cc192b50 | 58 | debugs(37, 2, HERE << " Socket Closed. Aborted send to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize); |
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. |
67 | \bug This will result in part of the payload being truncated if it contains a NULL character. | |
68 | \bug Or it may result in a buffer over-run if the payload is not nul-terminated properly. | |
69 | */ | |
70 | else if (payload && len == 0) | |
71 | len = strlen(payload); | |
72 | ||
73 | /** \li | |
74 | \bug If length specified or auto-detected is greater than the possible payload squid will die with an assert. | |
75 | \todo This should perhapse be reduced to a truncated payload? or no payload. A WARNING is due anyway. | |
76 | */ | |
77 | assert(len <= PINGER_PAYLOAD_SZ); | |
78 | ||
79 | pecho.to = to; | |
80 | ||
81 | pecho.opcode = (unsigned char) opcode; | |
82 | ||
83 | pecho.psize = len; | |
84 | ||
26ac0430 | 85 | if (len > 0) |
41d00cd3 | 86 | memcpy(pecho.payload, payload, len); |
cc192b50 | 87 | |
88 | slen = sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + pecho.psize; | |
89 | ||
90 | debugs(37, 2, HERE << "to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize); | |
91 | ||
92 | x = comm_udp_send(icmp_sock, (char *)&pecho, slen, 0); | |
93 | ||
94 | if (x < 0) { | |
e0236918 | 95 | debugs(37, DBG_IMPORTANT, HERE << "send: " << xstrerror()); |
cc192b50 | 96 | |
97 | /** \li If the send results in ECONNREFUSED or EPIPE errors from helper, will cleanly shutdown the module. */ | |
98 | /** \todo This should try restarting the helper a few times?? before giving up? */ | |
99 | if (errno == ECONNREFUSED || errno == EPIPE) { | |
100 | Close(); | |
101 | return; | |
102 | } | |
103 | /** All other send errors are ignored. */ | |
9166e142 | 104 | } else if (x != slen) { |
e0236918 | 105 | debugs(37, DBG_IMPORTANT, HERE << "Wrote " << x << " of " << slen << " bytes"); |
cc192b50 | 106 | } |
107 | } | |
108 | ||
109 | // static Callback to wrap the squid-side ICMP handler. | |
b826ffb5 | 110 | // the IcmpSquid::Recv cannot be declared both static and virtual. |
cc192b50 | 111 | static void |
112 | icmpSquidRecv(int unused1, void *unused2) | |
113 | { | |
114 | icmpEngine.Recv(); | |
115 | } | |
116 | ||
117 | void | |
b826ffb5 | 118 | IcmpSquid::Recv() |
cc192b50 | 119 | { |
120 | int n; | |
121 | static int fail_count = 0; | |
122 | pingerReplyData preply; | |
b7ac5457 | 123 | static Ip::Address F; |
cc192b50 | 124 | |
d841c88d | 125 | Comm::SetSelect(icmp_sock, COMM_SELECT_READ, icmpSquidRecv, NULL, 0); |
cc192b50 | 126 | memset(&preply, '\0', sizeof(pingerReplyData)); |
127 | n = comm_udp_recv(icmp_sock, | |
128 | (char *) &preply, | |
129 | sizeof(pingerReplyData), | |
130 | 0); | |
131 | ||
132 | if (n < 0 && EAGAIN != errno) { | |
e0236918 | 133 | debugs(37, DBG_IMPORTANT, HERE << "recv: " << xstrerror()); |
cc192b50 | 134 | |
135 | if (errno == ECONNREFUSED) | |
136 | Close(); | |
137 | ||
138 | if (errno == ECONNRESET) | |
139 | Close(); | |
140 | ||
141 | if (++fail_count == 10) | |
142 | Close(); | |
143 | ||
144 | return; | |
145 | } | |
146 | ||
147 | fail_count = 0; | |
148 | ||
149 | /** If its a test probe from the pinger. Do nothing. */ | |
150 | if (n == 0) { | |
151 | return; | |
152 | } | |
153 | ||
154 | F = preply.from; | |
155 | ||
4dd643d5 | 156 | F.port(0); |
cc192b50 | 157 | |
158 | switch (preply.opcode) { | |
159 | ||
160 | case S_ICMP_ECHO: | |
161 | debugs(37,4, HERE << " ICMP_ECHO of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt); | |
162 | break; | |
163 | ||
164 | case S_ICMP_DOM: | |
165 | debugs(37,4, HERE << " DomainPing of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt); | |
166 | netdbHandlePingReply(F, preply.hops, preply.rtt); | |
167 | break; | |
168 | ||
169 | default: | |
e0236918 | 170 | debugs(37, DBG_IMPORTANT, HERE << "Bad opcode: " << preply.opcode << " from " << F); |
cc192b50 | 171 | break; |
172 | } | |
173 | } | |
174 | ||
175 | #endif /* USE_ICMP */ | |
176 | ||
177 | void | |
b7ac5457 | 178 | IcmpSquid::DomainPing(Ip::Address &to, const char *domain) |
cc192b50 | 179 | { |
180 | #if USE_ICMP | |
181 | debugs(37, 4, HERE << "'" << domain << "' (" << to << ")"); | |
182 | SendEcho(to, S_ICMP_DOM, domain, 0); | |
183 | #endif | |
184 | } | |
185 | ||
186 | int | |
b826ffb5 | 187 | IcmpSquid::Open(void) |
cc192b50 | 188 | { |
189 | #if USE_ICMP | |
190 | const char *args[2]; | |
191 | int rfd; | |
192 | int wfd; | |
b7ac5457 | 193 | Ip::Address localhost; |
cc192b50 | 194 | |
195 | /* User configured disabled. */ | |
26ac0430 | 196 | if (!Config.pinger.enable) { |
cc192b50 | 197 | Close(); |
198 | return -1; | |
199 | } | |
200 | ||
201 | args[0] = "(pinger)"; | |
202 | args[1] = NULL; | |
4dd643d5 | 203 | localhost.setLocalhost(); |
cc192b50 | 204 | |
205 | /* | |
206 | * Do NOT use IPC_DGRAM (=IPC_UNIX_DGRAM) here because you can't | |
207 | * send() more than 4096 bytes on a socketpair() socket (at | |
208 | * least on FreeBSD). | |
209 | */ | |
210 | pid = ipcCreate(IPC_UDP_SOCKET, | |
211 | Config.pinger.program, | |
212 | args, | |
213 | "Pinger Socket", | |
214 | localhost, | |
215 | &rfd, | |
216 | &wfd, | |
217 | &hIpc); | |
218 | ||
219 | if (pid < 0) | |
220 | return -1; | |
221 | ||
222 | assert(rfd == wfd); | |
223 | ||
224 | icmp_sock = rfd; | |
225 | ||
226 | fd_note(icmp_sock, "pinger"); | |
227 | ||
d841c88d | 228 | Comm::SetSelect(icmp_sock, COMM_SELECT_READ, icmpSquidRecv, NULL, 0); |
cc192b50 | 229 | |
933dd095 | 230 | commUnsetFdTimeout(icmp_sock); |
cc192b50 | 231 | |
e0236918 | 232 | debugs(37, DBG_IMPORTANT, HERE << "Pinger socket opened on FD " << icmp_sock); |
cc192b50 | 233 | |
234 | /* Tests the pinger immediately using localhost */ | |
055421ee AJ |
235 | if (Ip::EnableIpv6) |
236 | SendEcho(localhost, S_ICMP_ECHO, "ip6-localhost"); | |
4dd643d5 | 237 | if (localhost.setIPv4()) |
cc192b50 | 238 | SendEcho(localhost, S_ICMP_ECHO, "localhost"); |
239 | ||
7aa9bb3e | 240 | #if _SQUID_WINDOWS_ |
cc192b50 | 241 | |
242 | debugs(37, 4, HERE << "Pinger handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid); | |
243 | ||
7aa9bb3e | 244 | #endif /* _SQUID_WINDOWS_ */ |
cc192b50 | 245 | return icmp_sock; |
b8e19d32 HN |
246 | #else /* USE_ICMP */ |
247 | return -1; | |
248 | #endif /* USE_ICMP */ | |
cc192b50 | 249 | } |
250 | ||
251 | void | |
b826ffb5 | 252 | IcmpSquid::Close(void) |
cc192b50 | 253 | { |
254 | #if USE_ICMP | |
255 | ||
256 | if (icmp_sock < 0) | |
257 | return; | |
258 | ||
e0236918 | 259 | debugs(37, DBG_IMPORTANT, HERE << "Closing Pinger socket on FD " << icmp_sock); |
cc192b50 | 260 | |
7aa9bb3e | 261 | #if _SQUID_WINDOWS_ |
cc192b50 | 262 | |
263 | send(icmp_sock, (const void *) "$shutdown\n", 10, 0); | |
264 | ||
265 | #endif | |
266 | ||
267 | comm_close(icmp_sock); | |
268 | ||
7aa9bb3e | 269 | #if _SQUID_WINDOWS_ |
cc192b50 | 270 | |
271 | if (hIpc) { | |
272 | if (WaitForSingleObject(hIpc, 12000) != WAIT_OBJECT_0) { | |
273 | getCurrentTime(); | |
fa84c01d | 274 | debugs(37, DBG_CRITICAL, HERE << "WARNING: (pinger," << pid << ") didn't exit in 12 seconds"); |
cc192b50 | 275 | } |
276 | ||
277 | CloseHandle(hIpc); | |
278 | } | |
279 | ||
280 | #endif | |
281 | icmp_sock = -1; | |
282 | ||
283 | #endif | |
284 | } |