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 
73 int
74 Win32__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 
89 using namespace std::literals::chrono_literals;
90 static const auto PingerTimeout = 10s;
91 
92 // ICMP Engines are declared global here so they can call each other easily.
96 
98 
100 static std::ostream &
101 TerminationReason(std::ostream &os)
102 {
103  if (std::current_exception())
104  os << CurrentException;
105  else
106  os << "An undetermined failure";
107  return os;
108 }
109 
110 static 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 
130 int
131 main(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 
149  getCurrentTime();
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);
241  getCurrentTime();
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>
278 int
279 main(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 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
int icmp_pkts_sent
Definition: pinger.cc:97
#define DBG_CRITICAL
Definition: Stream.h:37
const A & max(A const &lhs, A const &rhs)
int Open() override
Start pinger helper and initiate control channel.
Definition: Icmp6.cc:100
virtual void Close()
Shutdown pinger helper and control channel.
Definition: Icmp.cc:26
Definition: fde.h:51
int main(int, char **)
Definition: pinger.cc:131
ssize_t xsend(int socketFd, const void *buf, size_t bufLength, int flags)
POSIX send(2) equivalent.
Definition: socket.h:110
void Recv(void) override
Definition: Icmp6.cc:200
static std::ostream & TerminationReason(std::ostream &os)
reports std::terminate() cause (e.g., an uncaught or prohibited exception)
Definition: pinger.cc:101
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
int Open() override
Start pinger helper and initiate control channel.
Definition: Icmp4.cc:68
static void NameThisHelper(const char *name)
Definition: debug.cc:384
static void PrepareToDie()
Definition: debug.cc:563
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
Icmp6 icmp6
pinger helper contains one of these as a global object.
Definition: pinger.cc:95
IcmpPinger control
pinger helper contains one of these as a global object.
Definition: pinger.cc:93
static const auto PingerTimeout
Definition: pinger.cc:90
void resume()
Definition: Stopwatch.cc:31
#define fd_table
Definition: fde.h:189
void Recv(void) override
Handle ICMP responses.
Definition: Icmp4.cc:160
int xselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
POSIX select(2) equivalent.
Definition: select.h:22
int Open() override
Start and initiate control channel to squid.
Definition: IcmpPinger.cc:48
#define LINK_TO_SQUID
Definition: pinger.cc:85
Definition: Icmp6.h:45
void Close() override
Shutdown pinger helper and control channel.
Definition: IcmpPinger.cc:154
Icmp4 icmp4
pinger helper contains one of these as a global object.
Definition: pinger.cc:94
void Recv(void) override
Handle ICMP requests from squid, passing to helpers.
Definition: IcmpPinger.cc:170
Definition: Icmp4.h:126
Clock::duration total() const
Definition: Stopwatch.cc:22
void ProbeTransport(void)
Probe to discover IPv6 capabilities.
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static void OnTerminate()
Definition: pinger.cc:111

 

Introduction

Documentation

Support

Miscellaneous