]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ip/Intercept.cc
netfilter_ipv4.h requires including limits.h first. Added.
[thirdparty/squid.git] / src / ip / Intercept.cc
CommitLineData
c8be6d7b 1/*
26ac0430 2 * DEBUG: section 89 NAT / IP Interception
c8be6d7b 3 * AUTHOR: Robert Collins
04f87469 4 * AUTHOR: Amos Jeffries
c8be6d7b 5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
26ac0430 22 *
c8be6d7b 23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
26ac0430 27 *
c8be6d7b 28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
f7f3304a 33#include "squid.h"
40d34a62 34#include "comm/Connection.h"
96d89ea0 35#include "ip/Intercept.h"
b3166404 36#include "fde.h"
fc27cd70 37
c8be6d7b 38#if IPF_TRANSPARENT
34ec5c62 39
c8be6d7b 40#if HAVE_SYS_IOCTL_H
41#include <sys/ioctl.h>
42#endif
e9e7c285 43#if HAVE_NETINET_TCP_H
c8be6d7b 44#include <netinet/tcp.h>
e9e7c285 45#endif
46#if HAVE_NET_IF_H
c8be6d7b 47#include <net/if.h>
e9e7c285 48#endif
b0dfd10b 49#if HAVE_IPL_H
dbc5782a 50#include <ipl.h>
51#elif HAVE_NETINET_IPL_H
52#include <netinet/ipl.h>
53#endif
c8be6d7b 54#if HAVE_IP_FIL_COMPAT_H
55#include <ip_fil_compat.h>
56#elif HAVE_NETINET_IP_FIL_COMPAT_H
57#include <netinet/ip_fil_compat.h>
58#elif HAVE_IP_COMPAT_H
59#include <ip_compat.h>
60#elif HAVE_NETINET_IP_COMPAT_H
61#include <netinet/ip_compat.h>
62#endif
63#if HAVE_IP_FIL_H
64#include <ip_fil.h>
65#elif HAVE_NETINET_IP_FIL_H
66#include <netinet/ip_fil.h>
67#endif
68#if HAVE_IP_NAT_H
69#include <ip_nat.h>
70#elif HAVE_NETINET_IP_NAT_H
71#include <netinet/ip_nat.h>
72#endif
21d845b1
FC
73#if HAVE_ERRNO_H
74#include <errno.h>
75#endif
34ec5c62
AJ
76
77#endif /* IPF_TRANSPARENT required headers */
c8be6d7b 78
79#if PF_TRANSPARENT
c8be6d7b 80#include <sys/socket.h>
81#include <sys/ioctl.h>
82#include <sys/fcntl.h>
83#include <net/if.h>
84#include <netinet/in.h>
b0dfd10b 85#if HAVE_NET_PF_PFVAR_H
ec9909b0
AJ
86#include <net/pf/pfvar.h>
87#endif /* HAVE_NET_PF_PFVAR_H */
b0dfd10b 88#if HAVE_NET_PFVAR_H
c8be6d7b 89#include <net/pfvar.h>
ec9909b0 90#endif /* HAVE_NET_PFVAR_H */
34ec5c62 91#endif /* PF_TRANSPARENT required headers */
62c972a6
FC
92/* must be before including netfilter_ipv4.h */
93#if HAVE_LIMITS_H
94#include <limits.h>
95#endif
c8be6d7b 96#if LINUX_NETFILTER
97#include <linux/netfilter_ipv4.h>
98#endif
99
0fc2952e 100// single global instance for access by other components.
b7ac5457 101Ip::Intercept Ip::Interceptor;
0fc2952e 102
04f87469 103void
b7ac5457 104Ip::Intercept::StopTransparency(const char *str)
26ac0430 105{
40d34a62 106 if (transparentActive_) {
788625af 107 debugs(89, DBG_IMPORTANT, "Stopping full transparency: " << str);
40d34a62 108 transparentActive_ = 0;
788625af 109 }
04f87469
AJ
110}
111
112void
b7ac5457 113Ip::Intercept::StopInterception(const char *str)
26ac0430 114{
40d34a62 115 if (interceptActive_) {
788625af 116 debugs(89, DBG_IMPORTANT, "Stopping IP interception: " << str);
40d34a62 117 interceptActive_ = 0;
788625af 118 }
04f87469 119}
0fc2952e 120
40d34a62
AJ
121bool
122Ip::Intercept::NetfilterInterception(const Comm::ConnectionPointer &newConn, int silent)
7b0a0d1f
AJ
123{
124#if LINUX_NETFILTER
40d34a62
AJ
125 struct sockaddr_in lookup;
126 socklen_t len = sizeof(struct sockaddr_in);
127 newConn->local.GetSockAddr(lookup);
7b0a0d1f
AJ
128
129 /** \par
130 * Try NAT lookup for REDIRECT or DNAT targets. */
40d34a62 131 if ( getsockopt(newConn->fd, IPPROTO_IP, SO_ORIGINAL_DST, &lookup, &len) != 0) {
26ac0430 132 if (!silent) {
40d34a62
AJ
133 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed on " << newConn << ": " << xstrerror());
134 lastReported_ = squid_curtime;
7b0a0d1f 135 }
40d34a62
AJ
136 debugs(89, 9, HERE << "address: " << newConn);
137 return false;
26ac0430 138 } else {
40d34a62
AJ
139 newConn->local = lookup;
140 debugs(89, 5, HERE << "address NAT: " << newConn);
141 return true;
7b0a0d1f 142 }
7b0a0d1f 143#endif
40d34a62 144 return false;
7b0a0d1f
AJ
145}
146
40d34a62
AJ
147bool
148Ip::Intercept::NetfilterTransparent(const Comm::ConnectionPointer &newConn, int silent)
7b0a0d1f
AJ
149{
150#if LINUX_NETFILTER
acaa7194
AJ
151 /* Trust the user configured properly. If not no harm done.
152 * We will simply attempt a bind outgoing on our own IP.
acaa7194 153 */
40d34a62
AJ
154 newConn->remote.SetPort(0); // allow random outgoing port to prevent address clashes
155 debugs(89, 5, HERE << "address TPROXY: " << newConn);
156 return true;
157#else
158 return false;
acaa7194 159#endif
7b0a0d1f
AJ
160}
161
40d34a62
AJ
162bool
163Ip::Intercept::IpfwInterception(const Comm::ConnectionPointer &newConn, int silent)
d8b5bcbc
AJ
164{
165#if IPFW_TRANSPARENT
1b76e6c1
AJ
166 struct sockaddr_storage lookup;
167 socklen_t len = sizeof(struct sockaddr_storage);
168 newConn->local.GetSockAddr(lookup, AF_INET);
d8b5bcbc
AJ
169
170 /** \par
171 * Try lookup for IPFW interception. */
1b76e6c1 172 if ( getsockname(newConn->fd, (struct sockaddr*)&lookup, &len) != 0 ) {
26ac0430 173 if ( !silent ) {
d8b5bcbc 174 debugs(89, DBG_IMPORTANT, HERE << " IPFW getsockname(...) failed: " << xstrerror());
40d34a62 175 lastReported_ = squid_curtime;
d8b5bcbc 176 }
40d34a62
AJ
177 debugs(89, 9, HERE << "address: " << newConn);
178 return false;
26ac0430 179 } else {
40d34a62
AJ
180 newConn->local = lookup;
181 debugs(89, 5, HERE << "address NAT: " << newConn);
182 return true;
d8b5bcbc 183 }
d8b5bcbc 184#endif
40d34a62 185 return false;
d8b5bcbc 186}
0fc2952e 187
40d34a62
AJ
188bool
189Ip::Intercept::IpfInterception(const Comm::ConnectionPointer &newConn, int silent)
3f38a55e 190{
f1e0717c 191#if IPF_TRANSPARENT /* --enable-ipf-transparent */
62e76326 192
c8be6d7b 193 struct natlookup natLookup;
194 static int natfd = -1;
c8be6d7b 195 int x;
3f38a55e 196
c74be4ce 197 // all fields must be set to 0
dcfb239f 198 memset(&natLookup, 0, sizeof(natLookup));
c74be4ce 199 // for NAT lookup set local and remote IP:port's
40d34a62
AJ
200 natLookup.nl_inport = htons(newConn->local.GetPort());
201 newConn->local.GetInAddr(natLookup.nl_inip);
9a8b1816 202 natLookup.nl_outport = htons(newConn->remote.GetPort());
40d34a62 203 newConn->remote.GetInAddr(natLookup.nl_outip);
c74be4ce 204 // ... and the TCP flag
c8be6d7b 205 natLookup.nl_flags = IPN_TCP;
62e76326 206
26ac0430 207 if (natfd < 0) {
62e76326 208 int save_errno;
209 enter_suid();
dbc5782a 210#ifdef IPNAT_NAME
dbc5782a 211 natfd = open(IPNAT_NAME, O_RDONLY, 0);
98a3bc99 212#else
62e76326 213 natfd = open(IPL_NAT, O_RDONLY, 0);
98a3bc99 214#endif
62e76326 215 save_errno = errno;
216 leave_suid();
217 errno = save_errno;
c8be6d7b 218 }
62e76326 219
26ac0430 220 if (natfd < 0) {
219f8edb 221 if (!silent) {
c74be4ce 222 debugs(89, DBG_IMPORTANT, "IPF (IPFilter) NAT open failed: " << xstrerror());
40d34a62
AJ
223 lastReported_ = squid_curtime;
224 return false;
d6026916 225 }
c8be6d7b 226 }
62e76326 227
dbc5782a 228#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
c74be4ce
AJ
229 struct ipfobj obj;
230 memset(&obj, 0, sizeof(obj));
231 obj.ipfo_rev = IPFILTER_VERSION;
232 obj.ipfo_size = sizeof(natLookup);
233 obj.ipfo_ptr = &natLookup;
234 obj.ipfo_type = IPFOBJ_NATLOOKUP;
235
dbc5782a 236 x = ioctl(natfd, SIOCGNATL, &obj);
dbc5782a 237#else
3f38a55e 238 /*
dbc5782a 239 * IP-Filter changed the type for SIOCGNATL between
240 * 3.3 and 3.4. It also changed the cmd value for
241 * SIOCGNATL, so at least we can detect it. We could
242 * put something in configure and use ifdefs here, but
243 * this seems simpler.
244 */
c74be4ce 245 static int siocgnatl_cmd = SIOCGNATL & 0xff;
26ac0430 246 if (63 == siocgnatl_cmd) {
62e76326 247 struct natlookup *nlp = &natLookup;
248 x = ioctl(natfd, SIOCGNATL, &nlp);
26ac0430 249 } else {
62e76326 250 x = ioctl(natfd, SIOCGNATL, &natLookup);
251 }
252
dbc5782a 253#endif
26ac0430 254 if (x < 0) {
62e76326 255 if (errno != ESRCH) {
219f8edb 256 if (!silent) {
c74be4ce 257 debugs(89, DBG_IMPORTANT, "IPF (IPFilter) NAT lookup failed: ioctl(SIOCGNATL) (v=" << IPFILTER_VERSION << "): " << xstrerror());
40d34a62 258 lastReported_ = squid_curtime;
d6026916 259 }
260
62e76326 261 close(natfd);
262 natfd = -1;
263 }
264
40d34a62
AJ
265 debugs(89, 9, HERE << "address: " << newConn);
266 return false;
26ac0430 267 } else {
40d34a62
AJ
268 newConn->local = natLookup.nl_realip;
269 newConn->local.SetPort(ntohs(natLookup.nl_realport));
270 debugs(89, 5, HERE << "address NAT: " << newConn);
271 return true;
c8be6d7b 272 }
62e76326 273
219f8edb 274#endif /* --enable-ipf-transparent */
40d34a62 275 return false;
219f8edb 276}
f1e0717c 277
40d34a62
AJ
278bool
279Ip::Intercept::PfInterception(const Comm::ConnectionPointer &newConn, int silent)
219f8edb 280{
51f4d36b 281#if PF_TRANSPARENT /* --enable-pf-transparent */
62e76326 282
3f38a55e 283 struct pfioc_natlook nl;
284 static int pffd = -1;
62e76326 285
286 if (pffd < 0)
d14c6ef2 287 pffd = open("/dev/pf", O_RDONLY);
62e76326 288
26ac0430 289 if (pffd < 0) {
51f4d36b 290 if (!silent) {
e9172f79 291 debugs(89, DBG_IMPORTANT, HERE << "PF open failed: " << xstrerror());
40d34a62 292 lastReported_ = squid_curtime;
d6026916 293 }
40d34a62 294 return false;
c8be6d7b 295 }
62e76326 296
c8be6d7b 297 memset(&nl, 0, sizeof(struct pfioc_natlook));
40d34a62
AJ
298 newConn->remote.GetInAddr(nl.saddr.v4);
299 nl.sport = htons(newConn->remote.GetPort());
cc192b50 300
40d34a62
AJ
301 newConn->local.GetInAddr(nl.daddr.v4);
302 nl.dport = htons(newConn->local.GetPort());
cc192b50 303
c8be6d7b 304 nl.af = AF_INET;
305 nl.proto = IPPROTO_TCP;
306 nl.direction = PF_OUT;
62e76326 307
26ac0430 308 if (ioctl(pffd, DIOCNATLOOK, &nl)) {
62e76326 309 if (errno != ENOENT) {
51f4d36b 310 if (!silent) {
e9172f79 311 debugs(89, DBG_IMPORTANT, HERE << "PF lookup failed: ioctl(DIOCNATLOOK)");
40d34a62 312 lastReported_ = squid_curtime;
d6026916 313 }
62e76326 314 close(pffd);
315 pffd = -1;
316 }
40d34a62
AJ
317 debugs(89, 9, HERE << "address: " << newConn);
318 return false;
26ac0430 319 } else {
40d34a62
AJ
320 newConn->local = nl.rdaddr.v4;
321 newConn->local.SetPort(ntohs(nl.rdport));
322 debugs(89, 5, HERE << "address NAT: " << newConn);
323 return true;
3f38a55e 324 }
62e76326 325
51f4d36b 326#endif /* --enable-pf-transparent */
40d34a62 327 return false;
51f4d36b
AJ
328}
329
40d34a62
AJ
330bool
331Ip::Intercept::Lookup(const Comm::ConnectionPointer &newConn, const Comm::ConnectionPointer &listenConn)
51f4d36b 332{
af6a12ee
AJ
333 /* --enable-linux-netfilter */
334 /* --enable-ipfw-transparent */
335 /* --enable-ipf-transparent */
336 /* --enable-pf-transparent */
51f4d36b
AJ
337#if IPF_TRANSPARENT || LINUX_NETFILTER || IPFW_TRANSPARENT || PF_TRANSPARENT
338
51f4d36b
AJ
339#if 0
340 // Crop interception errors down to one per minute.
40d34a62 341 int silent = (squid_curtime - lastReported_ > 60 ? 0 : 1);
51f4d36b
AJ
342#else
343 // Show all interception errors.
344 int silent = 0;
345#endif
346
40d34a62
AJ
347 debugs(89, 5, HERE << "address BEGIN: me/client= " << newConn->local << ", destination/me= " << newConn->remote);
348
349 newConn->flags |= (listenConn->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION));
e9172f79 350
9bad3e09 351 /* NP: try TPROXY first, its much quieter than NAT when non-matching */
40d34a62
AJ
352 if (transparentActive_ && listenConn->flags&COMM_TRANSPARENT) {
353 if (NetfilterTransparent(newConn, silent)) return true;
9bad3e09
AJ
354 }
355
b9420956 356 /* NAT is only available in IPv4 */
40d34a62
AJ
357 if ( !newConn->local.IsIPv4() ) return false;
358 if ( !newConn->remote.IsIPv4() ) return false;
11298d0d 359
40d34a62 360 if (interceptActive_ && listenConn->flags&COMM_INTERCEPTION) {
e9172f79 361 /* NAT methods that use sock-opts to return client address */
40d34a62
AJ
362 if (NetfilterInterception(newConn, silent)) return true;
363 if (IpfwInterception(newConn, silent)) return true;
e9172f79
AJ
364
365 /* NAT methods that use ioctl to return client address AND destination address */
40d34a62
AJ
366 if (PfInterception(newConn, silent)) return true;
367 if (IpfInterception(newConn, silent)) return true;
51f4d36b 368 }
f1e0717c 369
51f4d36b 370#else /* none of the transparent options configured */
e9172f79 371 debugs(89, DBG_IMPORTANT, "WARNING: transparent proxying not supported");
3f38a55e 372#endif
373
40d34a62 374 return false;
f1e0717c 375}
34ec5c62 376
263f84f0 377bool
b7ac5457 378Ip::Intercept::ProbeForTproxy(Ip::Address &test)
263f84f0
AJ
379{
380 debugs(3, 3, "Detect TPROXY support on port " << test);
263f84f0
AJ
381
382#if defined(IP_TRANSPARENT)
383
384 int tos = 1;
385 int tmp_sock = -1;
386
263f84f0
AJ
387 /* Probe to see if the Kernel TPROXY support is IPv6-enabled */
388 if (test.IsIPv6()) {
389 debugs(3, 3, "...Probing for IPv6 TPROXY support.");
390
391 struct sockaddr_in6 tmp_ip6;
b7ac5457 392 Ip::Address tmp = "::2";
263f84f0
AJ
393 tmp.SetPort(0);
394 tmp.GetSockAddr(tmp_ip6);
395
396 if ( (tmp_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
f54f527e
AJ
397 setsockopt(tmp_sock, SOL_IP, IP_TRANSPARENT, (char *)&tos, sizeof(int)) == 0 &&
398 bind(tmp_sock, (struct sockaddr*)&tmp_ip6, sizeof(struct sockaddr_in6)) == 0 ) {
263f84f0
AJ
399
400 debugs(3, 3, "IPv6 TPROXY support detected. Using.");
fb5bffa3 401 close(tmp_sock);
263f84f0
AJ
402 return true;
403 }
404 if (tmp_sock >= 0) {
fb5bffa3 405 close(tmp_sock);
263f84f0
AJ
406 tmp_sock = -1;
407 }
408 }
409
410 if ( test.IsIPv6() && !test.SetIPv4() ) {
411 debugs(3, DBG_CRITICAL, "TPROXY lacks IPv6 support for " << test );
412 return false;
413 }
263f84f0
AJ
414
415 /* Probe to see if the Kernel TPROXY support is IPv4-enabled (aka present) */
416 if (test.IsIPv4()) {
417 debugs(3, 3, "...Probing for IPv4 TPROXY support.");
418
419 struct sockaddr_in tmp_ip4;
b7ac5457 420 Ip::Address tmp = "127.0.0.2";
263f84f0
AJ
421 tmp.SetPort(0);
422 tmp.GetSockAddr(tmp_ip4);
423
424 if ( (tmp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
f54f527e
AJ
425 setsockopt(tmp_sock, SOL_IP, IP_TRANSPARENT, (char *)&tos, sizeof(int)) == 0 &&
426 bind(tmp_sock, (struct sockaddr*)&tmp_ip4, sizeof(struct sockaddr_in)) == 0 ) {
263f84f0
AJ
427
428 debugs(3, 3, "IPv4 TPROXY support detected. Using.");
fb5bffa3 429 close(tmp_sock);
263f84f0
AJ
430 return true;
431 }
432 if (tmp_sock >= 0) {
fb5bffa3 433 close(tmp_sock);
263f84f0
AJ
434 }
435 }
436
437#else /* undefined IP_TRANSPARENT */
438 debugs(3, 3, "setsockopt(IP_TRANSPARENT) not supported on this platform. Disabling TPROXYv4.");
439#endif
263f84f0
AJ
440 return false;
441}