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