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