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 "Icmp4.h"
49 #include "Icmp6.h"
50 #include "IcmpPinger.h"
51 #include "ip/tools.h"
52 #include "time/gadgets.h"
53 
54 #if HAVE_SYS_CAPABILITY_H
55 #include <sys/capability.h>
56 #endif
57 
58 #if _SQUID_WINDOWS_
59 
60 #include <process.h>
61 
62 #include "fde.h"
63 
64 /* windows uses the control socket for feedback to squid */
65 #define LINK_TO_SQUID squid_link
66 
67 // windows still requires WSAFD but there are too many dependency problems
68 // to just link to win32.cc where it is normally defined.
69 
70 int
71 Win32__WSAFDIsSet(int fd, fd_set FAR * set)
72 {
73  fde *F = &fd_table[fd];
74  SOCKET s = F->win32.handle;
75 
76  return __WSAFDIsSet(s, set);
77 }
78 
79 #else
80 
81 /* non-windows use STDOUT for feedback to squid */
82 #define LINK_TO_SQUID 1
83 
84 #endif /* _SQUID_WINDOWS_ */
85 
86 using namespace std::literals::chrono_literals;
87 static const auto PingerTimeout = 10s;
88 
89 // ICMP Engines are declared global here so they can call each other easily.
93 
95 
100 int
101 main(int, char **)
102 {
103  fd_set R;
104  int x;
105  int max_fd = 0;
106 
107  /*
108  * cevans - do this first. It grabs a raw socket. After this we can
109  * drop privs
110  */
111  int icmp4_worker = -1;
112  int icmp6_worker = -1;
113  int squid_link = -1;
114 
115  Debug::NameThisHelper("pinger");
116 
117  getCurrentTime();
118 
119  // determine IPv4 or IPv6 capabilities before using sockets.
121 
122  debugs(42, DBG_CRITICAL, "Initialising ICMP pinger ...");
123 
124  icmp4_worker = icmp4.Open();
125  if (icmp4_worker < 0) {
126  debugs(42, DBG_CRITICAL, "ERROR: Unable to start ICMP pinger.");
127  }
128  max_fd = max(max_fd, icmp4_worker);
129 
130 #if USE_IPV6
131  icmp6_worker = icmp6.Open();
132  if (icmp6_worker <0 ) {
133  debugs(42, DBG_CRITICAL, "ERROR: Unable to start ICMPv6 pinger.");
134  }
135  max_fd = max(max_fd, icmp6_worker);
136 #endif
137 
139  if (icmp4_worker < 0 && icmp6_worker < 0) {
140  debugs(42, DBG_CRITICAL, "FATAL: Unable to open any ICMP sockets.");
141  exit(EXIT_FAILURE);
142  }
143 
144  if ( (squid_link = control.Open()) < 0) {
145  debugs(42, DBG_CRITICAL, "FATAL: Unable to setup Pinger control sockets.");
146  icmp4.Close();
147  icmp6.Close();
148  exit(EXIT_FAILURE); // fatal error if the control channel fails.
149  }
150  max_fd = max(max_fd, squid_link);
151 
152  if (setgid(getgid()) < 0) {
153  int xerrno = errno;
154  debugs(42, DBG_CRITICAL, "FATAL: setgid(" << getgid() << ") failed: " << xstrerr(xerrno));
155  icmp4.Close();
156  icmp6.Close();
157  exit(EXIT_FAILURE);
158  }
159  if (setuid(getuid()) < 0) {
160  int xerrno = errno;
161  debugs(42, DBG_CRITICAL, "FATAL: setuid(" << getuid() << ") failed: " << xstrerr(xerrno));
162  icmp4.Close();
163  icmp6.Close();
164  exit(EXIT_FAILURE);
165  }
166 
167 #if HAVE_LIBCAP
168  // Drop remaining capabilities (if installed as non-setuid setcap cap_net_raw=ep).
169  // If pinger binary was installed setuid root, setuid() above already dropped all
170  // capabilities, and this is no-op.
171  cap_t caps;
172  caps = cap_init();
173  if (!caps) {
174  int xerrno = errno;
175  debugs(42, DBG_CRITICAL, "FATAL: cap_init() failed: " << xstrerr(xerrno));
176  icmp4.Close();
177  icmp6.Close();
178  exit(EXIT_FAILURE);
179  } else {
180  if (cap_set_proc(caps) != 0) {
181  int xerrno = errno;
182  // cap_set_proc(cap_init()) is expected to never fail
183  debugs(42, DBG_CRITICAL, "FATAL: cap_set_proc(none) failed: " << xstrerr(xerrno));
184  cap_free(caps);
185  icmp4.Close();
186  icmp6.Close();
187  exit(EXIT_FAILURE);
188  }
189  cap_free(caps);
190  }
191 #endif
192 
193  for (;;) {
194  struct timeval tv;
195  tv.tv_sec = std::chrono::seconds(PingerTimeout).count();
196  tv.tv_usec = 0;
197  FD_ZERO(&R);
198  if (icmp4_worker >= 0) {
199  FD_SET(icmp4_worker, &R);
200  }
201  if (icmp6_worker >= 0) {
202  FD_SET(icmp6_worker, &R);
203  }
204 
205  FD_SET(squid_link, &R);
206  Stopwatch timer;
207  timer.resume();
208  x = select(max_fd+1, &R, nullptr, nullptr, &tv);
209  getCurrentTime();
210 
211  if (x < 0) {
212  int xerrno = errno;
213  debugs(42, DBG_CRITICAL, "FATAL: select()==" << x << ", ERR: " << xstrerr(xerrno));
214  control.Close();
215  exit(EXIT_FAILURE);
216  }
217 
218  if (FD_ISSET(squid_link, &R)) {
219  control.Recv();
220  }
221 
222  if (icmp6_worker >= 0 && FD_ISSET(icmp6_worker, &R)) {
223  icmp6.Recv();
224  }
225  if (icmp4_worker >= 0 && FD_ISSET(icmp4_worker, &R)) {
226  icmp4.Recv();
227  }
228 
229  const auto delay = std::chrono::duration_cast<std::chrono::seconds>(timer.total());
230  if (delay >= PingerTimeout) {
231  if (send(LINK_TO_SQUID, &tv, 0, 0) < 0) {
232  debugs(42, DBG_CRITICAL, "Closing. No requests in last " << delay.count() << " seconds.");
233  control.Close();
234  exit(EXIT_FAILURE);
235  }
236  }
237  }
238 
239  /* NOTREACHED */
240  return EXIT_SUCCESS;
241 }
242 
243 #else /* !USE_ICMP */
244 
245 #include <ostream>
246 int
247 main(int argc, char *argv[])
248 {
249  std::cerr << argv[0] << ": ICMP support not compiled in." << std::endl;
250  return EXIT_FAILURE;
251 }
252 
253 #endif /* USE_ICMP */
254 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
int icmp_pkts_sent
Definition: pinger.cc:94
#define DBG_CRITICAL
Definition: Stream.h:37
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
const A & max(A const &lhs, A const &rhs)
int Open() override
Start pinger helper and initiate control channel.
Definition: Icmp6.cc:98
virtual void Close()
Shutdown pinger helper and control channel.
Definition: Icmp.cc:25
Definition: fde.h:51
int main(int, char **)
Definition: pinger.cc:101
void Recv(void) override
Definition: Icmp6.cc:196
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
int Open() override
Start pinger helper and initiate control channel.
Definition: Icmp4.cc:66
static void NameThisHelper(const char *name)
Definition: debug.cc:384
Icmp6 icmp6
pinger helper contains one of these as a global object.
Definition: pinger.cc:92
IcmpPinger control
pinger helper contains one of these as a global object.
Definition: pinger.cc:90
static const auto PingerTimeout
Definition: pinger.cc:87
void resume()
Definition: Stopwatch.cc:31
#define fd_table
Definition: fde.h:189
void Recv(void) override
Handle ICMP responses.
Definition: Icmp4.cc:156
int Open() override
Start and initiate control channel to squid.
Definition: IcmpPinger.cc:46
#define LINK_TO_SQUID
Definition: pinger.cc:82
Definition: Icmp6.h:45
void Close() override
Shutdown pinger helper and control channel.
Definition: IcmpPinger.cc:150
Icmp4 icmp4
pinger helper contains one of these as a global object.
Definition: pinger.cc:91
void Recv(void) override
Handle ICMP requests from squid, passing to helpers.
Definition: IcmpPinger.cc:165
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

 

Introduction

Documentation

Support

Miscellaneous