]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/icmp/pinger.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / icmp / pinger.cc
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2020 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
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
42#include "squid.h"
43#include "Debug.h"
44#include "SquidTime.h"
45
46#if USE_ICMP
47
48#include "Icmp4.h"
49#include "Icmp6.h"
50#include "IcmpPinger.h"
51#include "ip/tools.h"
52
53#if _SQUID_WINDOWS_
54
55#if HAVE_WINSOCK2_H
56#include <winsock2.h>
57#elif HAVE_WINSOCK_H
58#include <winsock.h>
59#endif
60#include <process.h>
61#include "fde.h"
62
63#define PINGER_TIMEOUT 5
64
65/* windows uses the control socket for feedback to squid */
66#define LINK_TO_SQUID squid_link
67
68// windows still requires WSAFD but there are too many dependancy problems
69// to just link to win32.cc where it is normally defined.
70
71int
72Win32__WSAFDIsSet(int fd, fd_set FAR * set)
73{
74 fde *F = &fd_table[fd];
75 SOCKET s = F->win32.handle;
76
77 return __WSAFDIsSet(s, set);
78}
79
80#else
81
82#define PINGER_TIMEOUT 10
83
84/* non-windows use STDOUT for feedback to squid */
85#define LINK_TO_SQUID 1
86
87#endif /* _SQUID_WINDOWS_ */
88
89// ICMP Engines are declared global here so they can call each other easily.
90IcmpPinger control;
91Icmp4 icmp4;
92Icmp6 icmp6;
93
94int icmp_pkts_sent = 0;
95
96/**
97 \ingroup pinger
98 \par This is the pinger external process.
99 *
100 \param argc Ignored.
101 \param argv Ignored.
102 */
103int
104main(int argc, char *argv[])
105{
106 fd_set R;
107 int x;
108 int max_fd = 0;
109
110 struct timeval tv;
111 const char *debug_args = "ALL,10";
112 char *t;
113 time_t last_check_time = 0;
114
115 /*
116 * cevans - do this first. It grabs a raw socket. After this we can
117 * drop privs
118 */
119 int icmp4_worker = -1;
120 int icmp6_worker = -1;
121 int squid_link = -1;
122
123 /** start by initializing the pinger debug cache.log-pinger. */
124 if ((t = getenv("SQUID_DEBUG")))
125 debug_args = xstrdup(t);
126
127 getCurrentTime();
128
129 // determine IPv4 or IPv6 capabilities before using sockets.
130 Ip::ProbeTransport();
131
132 _db_init(NULL, debug_args);
133
134 debugs(42, DBG_CRITICAL, "pinger: Initialising ICMP pinger ...");
135
136 icmp4_worker = icmp4.Open();
137 if (icmp4_worker < 0) {
138 debugs(42, DBG_CRITICAL, "pinger: Unable to start ICMP pinger.");
139 }
140 max_fd = max(max_fd, icmp4_worker);
141
142#if USE_IPV6
143 icmp6_worker = icmp6.Open();
144 if (icmp6_worker <0 ) {
145 debugs(42, DBG_CRITICAL, "pinger: Unable to start ICMPv6 pinger.");
146 }
147 max_fd = max(max_fd, icmp6_worker);
148#endif
149
150 /** abort if neither worker could open a socket. */
151 if (icmp4_worker < 0 && icmp6_worker < 0) {
152 debugs(42, DBG_CRITICAL, "FATAL: pinger: Unable to open any ICMP sockets.");
153 exit(EXIT_FAILURE);
154 }
155
156 if ( (squid_link = control.Open()) < 0) {
157 debugs(42, DBG_CRITICAL, "FATAL: pinger: Unable to setup Pinger control sockets.");
158 icmp4.Close();
159 icmp6.Close();
160 exit(EXIT_FAILURE); // fatal error if the control channel fails.
161 }
162 max_fd = max(max_fd, squid_link);
163
164 if (setgid(getgid()) < 0) {
165 int xerrno = errno;
166 debugs(42, DBG_CRITICAL, "FATAL: pinger: setgid(" << getgid() << ") failed: " << xstrerr(xerrno));
167 icmp4.Close();
168 icmp6.Close();
169 exit(EXIT_FAILURE);
170 }
171 if (setuid(getuid()) < 0) {
172 int xerrno = errno;
173 debugs(42, DBG_CRITICAL, "FATAL: pinger: setuid(" << getuid() << ") failed: " << xstrerr(xerrno));
174 icmp4.Close();
175 icmp6.Close();
176 exit(EXIT_FAILURE);
177 }
178
179#if USE_LIBCAP
180 // Drop remaining capabilities (if installed as non-setuid setcap cap_net_raw=ep).
181 // If pinger binary was installed setuid root, setuid() above already dropped all
182 // capabilities, and this is no-op.
183 cap_t caps;
184 caps = cap_init();
185 if (!caps) {
186 int xerrno = errno;
187 debugs(42, DBG_CRITICAL, "FATAL: pinger: cap_init() failed: " << xstrerr(xerrno));
188 icmp4.Close();
189 icmp6.Close();
190 exit(EXIT_FAILURE);
191 } else {
192 if (cap_set_proc(caps) != 0) {
193 int xerrno = errno;
194 // cap_set_proc(cap_init()) is expected to never fail
195 debugs(42, DBG_CRITICAL, "FATAL: pinger: cap_set_proc(none) failed: " << xstrerr(xerrno));
196 cap_free(caps);
197 icmp4.Close();
198 icmp6.Close();
199 exit(EXIT_FAILURE);
200 }
201 cap_free(caps);
202 }
203#endif
204
205 last_check_time = squid_curtime;
206
207 for (;;) {
208 tv.tv_sec = PINGER_TIMEOUT;
209 tv.tv_usec = 0;
210 FD_ZERO(&R);
211 if (icmp4_worker >= 0) {
212 FD_SET(icmp4_worker, &R);
213 }
214 if (icmp6_worker >= 0) {
215 FD_SET(icmp6_worker, &R);
216 }
217
218 FD_SET(squid_link, &R);
219 x = select(max_fd+1, &R, NULL, NULL, &tv);
220 getCurrentTime();
221
222 if (x < 0) {
223 int xerrno = errno;
224 debugs(42, DBG_CRITICAL, HERE << " FATAL Shutdown. select()==" << x << ", ERR: " << xstrerr(xerrno));
225 control.Close();
226 exit(EXIT_FAILURE);
227 }
228
229 if (FD_ISSET(squid_link, &R)) {
230 control.Recv();
231 }
232
233 if (icmp6_worker >= 0 && FD_ISSET(icmp6_worker, &R)) {
234 icmp6.Recv();
235 }
236 if (icmp4_worker >= 0 && FD_ISSET(icmp4_worker, &R)) {
237 icmp4.Recv();
238 }
239
240 if (PINGER_TIMEOUT + last_check_time < squid_curtime) {
241 if (send(LINK_TO_SQUID, &tv, 0, 0) < 0) {
242 debugs(42, DBG_CRITICAL, "pinger: Closing. No requests in last " << PINGER_TIMEOUT << " seconds.");
243 control.Close();
244 exit(EXIT_FAILURE);
245 }
246
247 last_check_time = squid_curtime;
248 }
249 }
250
251 /* NOTREACHED */
252 return EXIT_SUCCESS;
253}
254
255#else /* !USE_ICMP */
256
257#include <ostream>
258int
259main(int argc, char *argv[])
260{
261 std::cerr << argv[0] << ": ICMP support not compiled in." << std::endl;
262 return EXIT_FAILURE;
263}
264
265#endif /* USE_ICMP */
266