Squid Web Cache master
Loading...
Searching...
No Matches
pinger.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 42 ICMP Pinger program */
10
11#define SQUID_HELPER 1
12
42#include "squid.h"
43#include "debug/Stream.h"
44
45#if USE_ICMP
46
47#include "base/Stopwatch.h"
48#include "base/TextException.h"
49#include "compat/select.h"
50#include "compat/socket.h"
51#include "Icmp4.h"
52#include "Icmp6.h"
53#include "IcmpPinger.h"
54#include "ip/tools.h"
55#include "time/gadgets.h"
56
57#if HAVE_SYS_CAPABILITY_H
58#include <sys/capability.h>
59#endif
60
61#if _SQUID_WINDOWS_
62
63#include <process.h>
64
65#include "fde.h"
66
67/* windows uses the control socket for feedback to squid */
68#define LINK_TO_SQUID squid_link
69
70// windows still requires WSAFD but there are too many dependency problems
71// to just link to win32.cc where it is normally defined.
72
73int
74Win32__WSAFDIsSet(int fd, fd_set FAR * set)
75{
76 fde *F = &fd_table[fd];
77 SOCKET s = F->win32.handle;
78
79 return __WSAFDIsSet(s, set);
80}
81
82#else
83
84/* non-windows use STDOUT for feedback to squid */
85#define LINK_TO_SQUID 1
86
87#endif /* _SQUID_WINDOWS_ */
88
89using namespace std::literals::chrono_literals;
90static const auto PingerTimeout = 10s;
91
92// ICMP Engines are declared global here so they can call each other easily.
96
98
100static std::ostream &
101TerminationReason(std::ostream &os)
102{
103 if (std::current_exception())
104 os << CurrentException;
105 else
106 os << "An undetermined failure";
107 return os;
108}
109
110static void
112{
113 // ignore recursive calls to avoid termination loops
114 static bool terminating = false;
115 if (terminating)
116 return;
117 terminating = true;
118
119 debugs(1, DBG_CRITICAL, "FATAL: " << TerminationReason);
120
121 control.Close(); // TODO: Here and elsewhere, rely on IcmpPinger class destructor instead.
123 abort();
124}
125
130int
131main(int, char **)
132{
133 // TODO: Apply this try/catch-less approach to address SquidMainSafe() XXX.
134 (void)std::set_terminate(&OnTerminate);
135
136 fd_set R;
137 int max_fd = 0;
138
139 /*
140 * cevans - do this first. It grabs a raw socket. After this we can
141 * drop privs
142 */
143 int icmp4_worker = -1;
144 int icmp6_worker = -1;
145 int squid_link = -1;
146
147 Debug::NameThisHelper("pinger");
148
150
151 // determine IPv4 or IPv6 capabilities before using sockets.
153
154 debugs(42, DBG_CRITICAL, "Initialising ICMP pinger ...");
155
156 icmp4_worker = icmp4.Open();
157 if (icmp4_worker < 0) {
158 debugs(42, DBG_CRITICAL, "ERROR: Unable to start ICMP pinger.");
159 }
160 max_fd = max(max_fd, icmp4_worker);
161
162#if USE_IPV6
163 icmp6_worker = icmp6.Open();
164 if (icmp6_worker <0 ) {
165 debugs(42, DBG_CRITICAL, "ERROR: Unable to start ICMPv6 pinger.");
166 }
167 max_fd = max(max_fd, icmp6_worker);
168#endif
169
171 if (icmp4_worker < 0 && icmp6_worker < 0) {
172 debugs(42, DBG_CRITICAL, "FATAL: Unable to open any ICMP sockets.");
173 exit(EXIT_FAILURE);
174 }
175
176 if ( (squid_link = control.Open()) < 0) {
177 debugs(42, DBG_CRITICAL, "FATAL: Unable to setup Pinger control sockets.");
178 icmp4.Close();
179 icmp6.Close();
180 exit(EXIT_FAILURE); // fatal error if the control channel fails.
181 }
182 max_fd = max(max_fd, squid_link);
183
184 if (setgid(getgid()) < 0) {
185 int xerrno = errno;
186 debugs(42, DBG_CRITICAL, "FATAL: setgid(" << getgid() << ") failed: " << xstrerr(xerrno));
187 icmp4.Close();
188 icmp6.Close();
189 exit(EXIT_FAILURE);
190 }
191 if (setuid(getuid()) < 0) {
192 int xerrno = errno;
193 debugs(42, DBG_CRITICAL, "FATAL: setuid(" << getuid() << ") failed: " << xstrerr(xerrno));
194 icmp4.Close();
195 icmp6.Close();
196 exit(EXIT_FAILURE);
197 }
198
199#if HAVE_LIBCAP
200 // Drop remaining capabilities (if installed as non-setuid setcap cap_net_raw=ep).
201 // If pinger binary was installed setuid root, setuid() above already dropped all
202 // capabilities, and this is no-op.
203 cap_t caps;
204 caps = cap_init();
205 if (!caps) {
206 int xerrno = errno;
207 debugs(42, DBG_CRITICAL, "FATAL: cap_init() failed: " << xstrerr(xerrno));
208 icmp4.Close();
209 icmp6.Close();
210 exit(EXIT_FAILURE);
211 } else {
212 if (cap_set_proc(caps) != 0) {
213 int xerrno = errno;
214 // cap_set_proc(cap_init()) is expected to never fail
215 debugs(42, DBG_CRITICAL, "FATAL: cap_set_proc(none) failed: " << xstrerr(xerrno));
216 cap_free(caps);
217 icmp4.Close();
218 icmp6.Close();
219 exit(EXIT_FAILURE);
220 }
221 cap_free(caps);
222 }
223#endif
224
225 for (;;) {
226 struct timeval tv;
227 tv.tv_sec = std::chrono::seconds(PingerTimeout).count();
228 tv.tv_usec = 0;
229 FD_ZERO(&R);
230 if (icmp4_worker >= 0) {
231 FD_SET(icmp4_worker, &R);
232 }
233 if (icmp6_worker >= 0) {
234 FD_SET(icmp6_worker, &R);
235 }
236
237 FD_SET(squid_link, &R);
238 Stopwatch timer;
239 timer.resume();
240 const auto x = xselect(max_fd+1, &R, nullptr, nullptr, &tv);
242
243 if (x < 0) {
244 int xerrno = errno;
245 debugs(42, DBG_CRITICAL, "FATAL: select()==" << x << ", ERR: " << xstrerr(xerrno));
246 control.Close();
247 exit(EXIT_FAILURE);
248 }
249
250 if (FD_ISSET(squid_link, &R)) {
251 control.Recv();
252 }
253
254 if (icmp6_worker >= 0 && FD_ISSET(icmp6_worker, &R)) {
255 icmp6.Recv();
256 }
257 if (icmp4_worker >= 0 && FD_ISSET(icmp4_worker, &R)) {
258 icmp4.Recv();
259 }
260
261 const auto delay = std::chrono::duration_cast<std::chrono::seconds>(timer.total());
262 if (delay >= PingerTimeout) {
263 if (xsend(LINK_TO_SQUID, &tv, 0, 0) < 0) {
264 debugs(42, DBG_CRITICAL, "Closing. No requests in last " << delay.count() << " seconds.");
265 control.Close();
266 exit(EXIT_FAILURE);
267 }
268 }
269 }
270
271 /* NOTREACHED */
272 return EXIT_SUCCESS;
273}
274
275#else /* !USE_ICMP */
276
277#include <ostream>
278int
279main(int, char *argv[])
280{
281 std::cerr << argv[0] << ": ICMP support not compiled in." << std::endl;
282 return EXIT_FAILURE;
283}
284
285#endif /* USE_ICMP */
286
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
static void PrepareToDie()
Definition debug.cc:563
static void NameThisHelper(const char *name)
Definition debug.cc:384
Definition Icmp4.h:127
void Recv(void) override
Handle ICMP responses.
Definition Icmp4.cc:160
int Open() override
Start pinger helper and initiate control channel.
Definition Icmp4.cc:68
Definition Icmp6.h:46
int Open() override
Start pinger helper and initiate control channel.
Definition Icmp6.cc:100
void Recv(void) override
Definition Icmp6.cc:200
void Recv(void) override
Handle ICMP requests from squid, passing to helpers.
void Close() override
Shutdown pinger helper and control channel.
int Open() override
Start and initiate control channel to squid.
Definition IcmpPinger.cc:48
virtual void Close()
Shutdown pinger helper and control channel.
Definition Icmp.cc:26
Clock::duration total() const
Definition Stopwatch.cc:22
void resume()
Definition Stopwatch.cc:31
Definition fde.h:52
A const & max(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define fd_table
Definition fde.h:189
int main()
void ProbeTransport(void)
Probe to discover IPv6 capabilities.
IcmpPinger control
pinger helper contains one of these as a global object.
Definition pinger.cc:93
int icmp_pkts_sent
Definition pinger.cc:97
#define LINK_TO_SQUID
Definition pinger.cc:85
static void OnTerminate()
Definition pinger.cc:111
static const auto PingerTimeout
Definition pinger.cc:90
Icmp6 icmp6
pinger helper contains one of these as a global object.
Definition pinger.cc:95
static std::ostream & TerminationReason(std::ostream &os)
reports std::terminate() cause (e.g., an uncaught or prohibited exception)
Definition pinger.cc:101
Icmp4 icmp4
pinger helper contains one of these as a global object.
Definition pinger.cc:94
int xselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
POSIX select(2) equivalent.
Definition select.h:22
ssize_t xsend(int socketFd, const void *buf, size_t bufLength, int flags)
POSIX send(2) equivalent.
Definition socket.h:110
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
const char * xstrerr(int error)
Definition xstrerror.cc:83