]>
Commit | Line | Data |
---|---|---|
9d90e665 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
e25c139f | 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. | |
9d90e665 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 42 ICMP Pinger program */ |
10 | ||
128fe1c6 | 11 | #define SQUID_HELPER 1 |
12 | ||
63be0a78 | 13 | /** |
14 | \defgroup pinger pinger | |
15 | \ingroup ExternalPrograms | |
16 | \par | |
17 | * Although it would be possible for Squid to send and receive | |
18 | * ICMP messages directly, we use an external process for | |
19 | * two important reasons: | |
20 | * | |
21 | \li Because squid handles many filedescriptors simultaneously, | |
22 | * we get much more accurate RTT measurements when ICMP is | |
23 | * handled by a separate process. | |
24 | * | |
25 | \li Superuser privileges are required to send and receive ICMP. | |
26 | * Rather than require Squid to be started as root, we prefer | |
27 | * to have the smaller and simpler pinger program installed | |
28 | * with setuid permissions. | |
29 | * | |
30 | \par | |
31 | * If you want to use Squid's ICMP features (highly recommended!) | |
32 | * When USE_ICMP is defined, Squid will send ICMP pings | |
33 | * to origin server sites. | |
34 | * This information is used in numerous ways: | |
35 | \li - Sent in ICP replies so neighbor caches know how close | |
36 | * you are to the source. | |
37 | \li - For finding the closest instance of a URN. | |
38 | \li - With the 'test_reachability' option. Squid will return | |
39 | * ICP_OP_MISS_NOFETCH for sites which it cannot ping. | |
40 | */ | |
41 | ||
582c2af2 | 42 | #include "squid.h" |
675b8408 | 43 | #include "debug/Stream.h" |
9d90e665 | 44 | |
43426991 | 45 | #if USE_ICMP |
46 | ||
0d51e000 | 47 | #include "base/Stopwatch.h" |
663ff9aa AJ |
48 | #include "Icmp4.h" |
49 | #include "Icmp6.h" | |
50 | #include "IcmpPinger.h" | |
059d13ed | 51 | #include "ip/tools.h" |
98cacedb | 52 | #include "time/gadgets.h" |
0ab98d6b | 53 | |
59eac907 AJ |
54 | #if HAVE_SYS_CAPABILITY_H |
55 | #include <sys/capability.h> | |
56 | #endif | |
57 | ||
7aa9bb3e | 58 | #if _SQUID_WINDOWS_ |
bdb741f4 | 59 | |
bfe8dedf | 60 | #if HAVE_WINSOCK2_H |
bdb741f4 | 61 | #include <winsock2.h> |
bfe8dedf | 62 | #endif |
bdb741f4 | 63 | #include <process.h> |
59eac907 | 64 | |
cc192b50 | 65 | #include "fde.h" |
bdb741f4 | 66 | |
cc192b50 | 67 | /* windows uses the control socket for feedback to squid */ |
68 | #define LINK_TO_SQUID squid_link | |
bdb741f4 | 69 | |
2f8abb64 | 70 | // windows still requires WSAFD but there are too many dependency problems |
cc192b50 | 71 | // to just link to win32.cc where it is normally defined. |
bdb741f4 | 72 | |
cc192b50 | 73 | int |
74 | Win32__WSAFDIsSet(int fd, fd_set FAR * set) | |
bdb741f4 | 75 | { |
cc192b50 | 76 | fde *F = &fd_table[fd]; |
77 | SOCKET s = F->win32.handle; | |
bdb741f4 | 78 | |
cc192b50 | 79 | return __WSAFDIsSet(s, set); |
bdb741f4 | 80 | } |
81 | ||
cc192b50 | 82 | #else |
bdb741f4 | 83 | |
cc192b50 | 84 | /* non-windows use STDOUT for feedback to squid */ |
f53969cc | 85 | #define LINK_TO_SQUID 1 |
bdb741f4 | 86 | |
f53969cc | 87 | #endif /* _SQUID_WINDOWS_ */ |
bdb741f4 | 88 | |
0d51e000 AJ |
89 | using namespace std::literals::chrono_literals; |
90 | static const auto PingerTimeout = 10s; | |
91 | ||
cc192b50 | 92 | // ICMP Engines are declared global here so they can call each other easily. |
663ff9aa AJ |
93 | IcmpPinger control; |
94 | Icmp4 icmp4; | |
663ff9aa | 95 | Icmp6 icmp6; |
62e76326 | 96 | |
5be53f2d | 97 | int icmp_pkts_sent = 0; |
9d90e665 | 98 | |
63be0a78 | 99 | /** |
100 | \ingroup pinger | |
101 | \par This is the pinger external process. | |
63be0a78 | 102 | */ |
cc192b50 | 103 | int |
8b082ed9 | 104 | main(int, char **) |
9d90e665 | 105 | { |
cc192b50 | 106 | fd_set R; |
bdb741f4 | 107 | int x; |
cc192b50 | 108 | int max_fd = 0; |
bdb741f4 | 109 | |
cc192b50 | 110 | /* |
111 | * cevans - do this first. It grabs a raw socket. After this we can | |
112 | * drop privs | |
113 | */ | |
114 | int icmp4_worker = -1; | |
cc192b50 | 115 | int icmp6_worker = -1; |
cc192b50 | 116 | int squid_link = -1; |
62e76326 | 117 | |
879c39f7 | 118 | Debug::NameThisHelper("pinger"); |
bdb741f4 | 119 | |
120 | getCurrentTime(); | |
62e76326 | 121 | |
059d13ed AJ |
122 | // determine IPv4 or IPv6 capabilities before using sockets. |
123 | Ip::ProbeTransport(); | |
124 | ||
879c39f7 | 125 | debugs(42, DBG_CRITICAL, "Initialising ICMP pinger ..."); |
62e76326 | 126 | |
cc192b50 | 127 | icmp4_worker = icmp4.Open(); |
26ac0430 | 128 | if (icmp4_worker < 0) { |
879c39f7 | 129 | debugs(42, DBG_CRITICAL, "ERROR: Unable to start ICMP pinger."); |
b6a2f15e | 130 | } |
cc192b50 | 131 | max_fd = max(max_fd, icmp4_worker); |
62e76326 | 132 | |
cc192b50 | 133 | #if USE_IPV6 |
134 | icmp6_worker = icmp6.Open(); | |
26ac0430 | 135 | if (icmp6_worker <0 ) { |
879c39f7 | 136 | debugs(42, DBG_CRITICAL, "ERROR: Unable to start ICMPv6 pinger."); |
9d90e665 | 137 | } |
cc192b50 | 138 | max_fd = max(max_fd, icmp6_worker); |
d20b1cd0 | 139 | #endif |
62e76326 | 140 | |
63be0a78 | 141 | /** abort if neither worker could open a socket. */ |
055421ee | 142 | if (icmp4_worker < 0 && icmp6_worker < 0) { |
879c39f7 | 143 | debugs(42, DBG_CRITICAL, "FATAL: Unable to open any ICMP sockets."); |
24885773 | 144 | exit(EXIT_FAILURE); |
b6a2f15e | 145 | } |
62e76326 | 146 | |
26ac0430 | 147 | if ( (squid_link = control.Open()) < 0) { |
879c39f7 | 148 | debugs(42, DBG_CRITICAL, "FATAL: Unable to setup Pinger control sockets."); |
cc192b50 | 149 | icmp4.Close(); |
cc192b50 | 150 | icmp6.Close(); |
24885773 | 151 | exit(EXIT_FAILURE); // fatal error if the control channel fails. |
365a4bce | 152 | } |
cc192b50 | 153 | max_fd = max(max_fd, squid_link); |
62e76326 | 154 | |
77468ee5 | 155 | if (setgid(getgid()) < 0) { |
23c38c73 | 156 | int xerrno = errno; |
879c39f7 | 157 | debugs(42, DBG_CRITICAL, "FATAL: setgid(" << getgid() << ") failed: " << xstrerr(xerrno)); |
77468ee5 AJ |
158 | icmp4.Close(); |
159 | icmp6.Close(); | |
24885773 | 160 | exit(EXIT_FAILURE); |
77468ee5 AJ |
161 | } |
162 | if (setuid(getuid()) < 0) { | |
23c38c73 | 163 | int xerrno = errno; |
879c39f7 | 164 | debugs(42, DBG_CRITICAL, "FATAL: setuid(" << getuid() << ") failed: " << xstrerr(xerrno)); |
77468ee5 AJ |
165 | icmp4.Close(); |
166 | icmp6.Close(); | |
24885773 | 167 | exit(EXIT_FAILURE); |
77468ee5 | 168 | } |
b6a2f15e | 169 | |
23c38c73 YK |
170 | #if USE_LIBCAP |
171 | // Drop remaining capabilities (if installed as non-setuid setcap cap_net_raw=ep). | |
172 | // If pinger binary was installed setuid root, setuid() above already dropped all | |
173 | // capabilities, and this is no-op. | |
174 | cap_t caps; | |
175 | caps = cap_init(); | |
176 | if (!caps) { | |
177 | int xerrno = errno; | |
879c39f7 | 178 | debugs(42, DBG_CRITICAL, "FATAL: cap_init() failed: " << xstrerr(xerrno)); |
23c38c73 YK |
179 | icmp4.Close(); |
180 | icmp6.Close(); | |
24885773 | 181 | exit(EXIT_FAILURE); |
23c38c73 YK |
182 | } else { |
183 | if (cap_set_proc(caps) != 0) { | |
184 | int xerrno = errno; | |
185 | // cap_set_proc(cap_init()) is expected to never fail | |
879c39f7 | 186 | debugs(42, DBG_CRITICAL, "FATAL: cap_set_proc(none) failed: " << xstrerr(xerrno)); |
23c38c73 YK |
187 | cap_free(caps); |
188 | icmp4.Close(); | |
189 | icmp6.Close(); | |
24885773 | 190 | exit(EXIT_FAILURE); |
23c38c73 YK |
191 | } |
192 | cap_free(caps); | |
193 | } | |
194 | #endif | |
195 | ||
9d90e665 | 196 | for (;;) { |
0d51e000 AJ |
197 | struct timeval tv; |
198 | tv.tv_sec = std::chrono::seconds(PingerTimeout).count(); | |
62e76326 | 199 | tv.tv_usec = 0; |
200 | FD_ZERO(&R); | |
26ac0430 | 201 | if (icmp4_worker >= 0) { |
cc192b50 | 202 | FD_SET(icmp4_worker, &R); |
203 | } | |
26ac0430 | 204 | if (icmp6_worker >= 0) { |
cc192b50 | 205 | FD_SET(icmp6_worker, &R); |
206 | } | |
055421ee | 207 | |
cc192b50 | 208 | FD_SET(squid_link, &R); |
0d51e000 AJ |
209 | Stopwatch timer; |
210 | timer.resume(); | |
aee3523a | 211 | x = select(max_fd+1, &R, nullptr, nullptr, &tv); |
62e76326 | 212 | getCurrentTime(); |
213 | ||
bdb741f4 | 214 | if (x < 0) { |
23c38c73 | 215 | int xerrno = errno; |
d816f28d | 216 | debugs(42, DBG_CRITICAL, "FATAL: select()==" << x << ", ERR: " << xstrerr(xerrno)); |
cc192b50 | 217 | control.Close(); |
24885773 | 218 | exit(EXIT_FAILURE); |
bdb741f4 | 219 | } |
62e76326 | 220 | |
cc192b50 | 221 | if (FD_ISSET(squid_link, &R)) { |
222 | control.Recv(); | |
223 | } | |
62e76326 | 224 | |
cc192b50 | 225 | if (icmp6_worker >= 0 && FD_ISSET(icmp6_worker, &R)) { |
226 | icmp6.Recv(); | |
227 | } | |
cc192b50 | 228 | if (icmp4_worker >= 0 && FD_ISSET(icmp4_worker, &R)) { |
229 | icmp4.Recv(); | |
230 | } | |
62e76326 | 231 | |
0d51e000 AJ |
232 | const auto delay = std::chrono::duration_cast<std::chrono::seconds>(timer.total()); |
233 | if (delay >= PingerTimeout) { | |
cc192b50 | 234 | if (send(LINK_TO_SQUID, &tv, 0, 0) < 0) { |
0d51e000 | 235 | debugs(42, DBG_CRITICAL, "Closing. No requests in last " << delay.count() << " seconds."); |
cc192b50 | 236 | control.Close(); |
24885773 | 237 | exit(EXIT_FAILURE); |
bdb741f4 | 238 | } |
62e76326 | 239 | } |
9d90e665 | 240 | } |
62e76326 | 241 | |
145766ef | 242 | /* NOTREACHED */ |
24885773 | 243 | return EXIT_SUCCESS; |
9d90e665 | 244 | } |
365a4bce | 245 | |
5e66b1a6 | 246 | #else /* !USE_ICMP */ |
074d6a40 AJ |
247 | |
248 | #include <ostream> | |
9d90e665 | 249 | int |
250 | main(int argc, char *argv[]) | |
251 | { | |
074d6a40 | 252 | std::cerr << argv[0] << ": ICMP support not compiled in." << std::endl; |
24885773 | 253 | return EXIT_FAILURE; |
9d90e665 | 254 | } |
62e76326 | 255 | |
9d90e665 | 256 | #endif /* USE_ICMP */ |
f53969cc | 257 |