]> git.ipfire.org Git - thirdparty/squid.git/blame - src/IPInterception.cc
Restore old tproxy enabling back to original option name.
[thirdparty/squid.git] / src / IPInterception.cc
CommitLineData
c8be6d7b 1/*
6d96c80d 2 * $Id: IPInterception.cc,v 1.20 2008/02/05 22:38:24 amosjeffries Exp $
c8be6d7b 3 *
4 * DEBUG: section 89 NAT / IP Interception
5 * AUTHOR: Robert Collins
04f87469 6 * AUTHOR: Amos Jeffries
c8be6d7b 7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36#include "squid.h"
37#include "clientStream.h"
68991eab 38#include "IPInterception.h"
9fed22b7 39#include "SquidTime.h"
7b0a0d1f 40#include "fde.h"
34ec5c62 41
c8be6d7b 42#if IPF_TRANSPARENT
34ec5c62 43
c8be6d7b 44#if HAVE_SYS_IOCTL_H
45#include <sys/ioctl.h>
46#endif
e9e7c285 47#if HAVE_NETINET_TCP_H
c8be6d7b 48#include <netinet/tcp.h>
e9e7c285 49#endif
50#if HAVE_NET_IF_H
c8be6d7b 51#include <net/if.h>
e9e7c285 52#endif
dbc5782a 53#ifdef HAVE_IPL_H
54#include <ipl.h>
55#elif HAVE_NETINET_IPL_H
56#include <netinet/ipl.h>
57#endif
c8be6d7b 58#if HAVE_IP_FIL_COMPAT_H
59#include <ip_fil_compat.h>
60#elif HAVE_NETINET_IP_FIL_COMPAT_H
61#include <netinet/ip_fil_compat.h>
62#elif HAVE_IP_COMPAT_H
63#include <ip_compat.h>
64#elif HAVE_NETINET_IP_COMPAT_H
65#include <netinet/ip_compat.h>
66#endif
67#if HAVE_IP_FIL_H
68#include <ip_fil.h>
69#elif HAVE_NETINET_IP_FIL_H
70#include <netinet/ip_fil.h>
71#endif
72#if HAVE_IP_NAT_H
73#include <ip_nat.h>
74#elif HAVE_NETINET_IP_NAT_H
75#include <netinet/ip_nat.h>
76#endif
34ec5c62
AJ
77
78#endif /* IPF_TRANSPARENT required headers */
c8be6d7b 79
80#if PF_TRANSPARENT
81#include <sys/types.h>
82#include <sys/socket.h>
83#include <sys/ioctl.h>
84#include <sys/fcntl.h>
85#include <net/if.h>
86#include <netinet/in.h>
87#include <net/pfvar.h>
34ec5c62 88#endif /* PF_TRANSPARENT required headers */
c8be6d7b 89
90#if LINUX_NETFILTER
91#include <linux/netfilter_ipv4.h>
92#endif
93
34ec5c62
AJ
94#if LINUX_TPROXY2
95#ifdef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H
96#include <linux/netfilter_ipv4/ip_tproxy.h>
97#else
98#error " TPROXY v2 Header file missing: linux/netfilter_ipv4/ip_tproxy.h. Perhapse you meant to use TPROXY v4 ? "
99#endif
100#endif
101
0fc2952e
AJ
102
103// single global instance for access by other components.
104IPIntercept IPInterceptor;
105
04f87469
AJ
106void
107IPIntercept::StopTransparency(const char *str) {
788625af
AJ
108 if(transparent_active) {
109 debugs(89, DBG_IMPORTANT, "Stopping full transparency: " << str);
110 transparent_active = 0;
111 }
04f87469
AJ
112}
113
114void
115IPIntercept::StopInterception(const char *str) {
788625af
AJ
116 if(intercept_active) {
117 debugs(89, DBG_IMPORTANT, "Stopping IP interception: " << str);
118 intercept_active = 0;
119 }
04f87469 120}
0fc2952e 121
7b0a0d1f
AJ
122int
123IPIntercept::NetfilterInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
124{
125#if LINUX_NETFILTER
7b0a0d1f
AJ
126 struct addrinfo *lookup = NULL;
127
128 dst.GetAddrInfo(lookup,AF_INET);
129
130 /** \par
131 * Try NAT lookup for REDIRECT or DNAT targets. */
132 if( getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, lookup->ai_addr, &lookup->ai_addrlen) == 0) {
133 if(!silent) {
08e15b9c 134 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed: " << xstrerror());
7b0a0d1f
AJ
135 last_reported = squid_curtime;
136 }
137 }
138 else {
139 dst = *lookup;
140 }
141
142 dst.FreeAddrInfo(lookup);
143
144 if(me != dst) {
145 debugs(89, 5, HERE << "address: " << dst);
146 return 0;
147 }
148
149#endif
150 return -1;
151}
152
153int
154IPIntercept::NetfilterTransparent(int fd, const IPAddress &me, IPAddress &dst, int silent)
155{
156#if LINUX_NETFILTER
7b0a0d1f
AJ
157 struct addrinfo *lookup = NULL;
158
159 if( ! fd_table[fd].flags.transparent) return -1;
160
161 dst.GetAddrInfo(lookup,AF_INET);
162
163 /** \par
164 * Try lookup for TPROXY targets. BUT, only if the FD is flagged for transparent operations. */
165 if(getsockopt(fd, SOL_IP, IP_TRANSPARENT, lookup->ai_addr, &lookup->ai_addrlen) != 0) {
166 if(!silent) {
08e15b9c 167 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(IP_TRANSPARENT) failed: " << xstrerror());
7b0a0d1f
AJ
168 last_reported = squid_curtime;
169 }
170 }
171 else {
172 dst = *lookup;
173 }
174
175 dst.FreeAddrInfo(lookup);
176
177 if(me != dst) {
178 debugs(89, 5, HERE << "address: " << dst);
179 return 0;
180 }
181
182#endif
183 return -1;
184}
185
186int
187IPIntercept::IPFWInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
188{
189#if IPFW_TRANSPARENT
7b0a0d1f
AJ
190 struct addrinfo *lookup = NULL;
191
192 dst.GetAddrInfo(lookup,AF_INET);
193
194 /** \par
195 * Try lookup for IPFW interception. */
196 if( getsockname(fd, lookup->ai_addr, &lookup->ai_addrlen) >= 0 ) {
197 if( !silent ) {
08e15b9c 198 debugs(89, DBG_IMPORTANT, HERE << " IPFW getsockname(...) failed: " << xstrerror());
7b0a0d1f
AJ
199 last_reported = squid_curtime;
200 }
201 }
202 else {
203 dst = *lookup;
204 }
205
206 dst.FreeAddrInfo(lookup);
207
208 if(me != dst) {
209 debugs(89, 5, HERE << "address: " << dst);
210 return 0;
211 }
212
213#endif
214 return -1;
215}
216
217
0fc2952e
AJ
218// TODO split this one call into one per transparency method
219// with specific switching at run-time ??
220
3f38a55e 221int
0fc2952e 222IPIntercept::NatLookup(int fd, const IPAddress &me, const IPAddress &peer, IPAddress &dst)
3f38a55e 223{
f1e0717c 224#if IPF_TRANSPARENT /* --enable-ipf-transparent */
cc192b50 225 dst = me;
226 if( !me.IsIPv4() ) return -1;
227 if( !peer.IsIPv4() ) return -1;
62e76326 228
dbc5782a 229#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
230
231 struct ipfobj obj;
08a746ab 232#else
233
234 static int siocgnatl_cmd = SIOCGNATL & 0xff;
235
dbc5782a 236#endif
237
c8be6d7b 238 struct natlookup natLookup;
239 static int natfd = -1;
c8be6d7b 240 int x;
3f38a55e 241
dbc5782a 242#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
243
244 obj.ipfo_rev = IPFILTER_VERSION;
245 obj.ipfo_size = sizeof(natLookup);
246 obj.ipfo_ptr = &natLookup;
247 obj.ipfo_type = IPFOBJ_NATLOOKUP;
248 obj.ipfo_offset = 0;
249#endif
250
cc192b50 251 natLookup.nl_inport = htons(me.GetPort());
252 natLookup.nl_outport = htons(peer.GetPort());
253 me.GetInAddr(natLookup.nl_inip);
254 peer.GetInAddr(natLookup.nl_outip);
c8be6d7b 255 natLookup.nl_flags = IPN_TCP;
62e76326 256
257 if (natfd < 0)
258 {
259 int save_errno;
260 enter_suid();
dbc5782a 261#ifdef IPNAT_NAME
98a3bc99 262
dbc5782a 263 natfd = open(IPNAT_NAME, O_RDONLY, 0);
98a3bc99 264#else
265
62e76326 266 natfd = open(IPL_NAT, O_RDONLY, 0);
98a3bc99 267#endif
268
62e76326 269 save_errno = errno;
270 leave_suid();
271 errno = save_errno;
c8be6d7b 272 }
62e76326 273
274 if (natfd < 0)
275 {
d6026916 276 if (squid_curtime - last_reported > 60) {
bf8fe701 277 debugs(89, 1, "clientNatLookup: NAT open failed: " << xstrerror());
d6026916 278 last_reported = squid_curtime;
279 return -1;
280 }
c8be6d7b 281 }
62e76326 282
dbc5782a 283#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
284 x = ioctl(natfd, SIOCGNATL, &obj);
285
286#else
3f38a55e 287 /*
dbc5782a 288 * IP-Filter changed the type for SIOCGNATL between
289 * 3.3 and 3.4. It also changed the cmd value for
290 * SIOCGNATL, so at least we can detect it. We could
291 * put something in configure and use ifdefs here, but
292 * this seems simpler.
293 */
62e76326 294 if (63 == siocgnatl_cmd)
295 {
296
297 struct natlookup *nlp = &natLookup;
298 x = ioctl(natfd, SIOCGNATL, &nlp);
299 } else
300 {
301 x = ioctl(natfd, SIOCGNATL, &natLookup);
302 }
303
dbc5782a 304#endif
62e76326 305 if (x < 0)
306 {
307 if (errno != ESRCH) {
d6026916 308 if (squid_curtime - last_reported > 60) {
bf8fe701 309 debugs(89, 1, "clientNatLookup: NAT lookup failed: ioctl(SIOCGNATL)");
d6026916 310 last_reported = squid_curtime;
311 }
312
62e76326 313 close(natfd);
314 natfd = -1;
315 }
316
317 return -1;
318 } else
319 {
cc192b50 320 if (me != natLookup.nl_realip) {
321 dst = natLookup.nl_realip;
62e76326 322
cc192b50 323 dst.SetPort(ntohs(natLookup.nl_realport));
324 }
325 // else. we already copied it.
62e76326 326
327 return 0;
c8be6d7b 328 }
62e76326 329
f1e0717c
AJ
330
331
7b0a0d1f 332#elif LINUX_NETFILTER || IPFW_TRANSPARENT /* --enable-linux-netfilter OR --enable-ipfw-transparent */
f1e0717c 333
7b0a0d1f
AJ
334 /* Netfilter and IPFW share almost identical lookup methods for their NAT tables.
335 * This allows us to perform a nice clean failover sequence for them.
336 */
337 int silent = (squid_curtime - last_reported > 60 ? 0 : 1);
62e76326 338
7b0a0d1f 339 dst = me;
cc192b50 340
7b0a0d1f
AJ
341 if( !me.IsIPv4() ) return -1;
342 if( !peer.IsIPv4() ) return -1;
d6026916 343
7b0a0d1f 344 if(intercept_active) {
ca45fe65
AJ
345 if( NetfilterInterception(fd, me, dst, silent) == 0) return 0;
346 if( IPFWInterception(fd, me, dst, silent) == 0) return 0;
7b0a0d1f
AJ
347 }
348 if(transparent_active) {
ca45fe65 349 if( NetfilterTransparent(fd, me, dst, silent) == 0) return 0;
d6026916 350 }
62e76326 351
7b0a0d1f 352 return -1;
62e76326 353
f1e0717c 354#elif PF_TRANSPARENT /* --enable-pf-transparent */
62e76326 355
3f38a55e 356 struct pfioc_natlook nl;
357 static int pffd = -1;
62e76326 358
cc192b50 359 if( !me.IsIPv4() ) return -1;
360 if( !peer.IsIPv4() ) return -1;
361
62e76326 362 if (pffd < 0)
363 pffd = open("/dev/pf", O_RDWR);
364
c8be6d7b 365 if (pffd < 0)
62e76326 366 {
d6026916 367 if (squid_curtime - last_reported > 60) {
bf8fe701 368 debugs(89, 1, "clientNatLookup: PF open failed: " << xstrerror());
d6026916 369 last_reported = squid_curtime;
370 }
371
62e76326 372 return -1;
d6026916 373
c8be6d7b 374 }
62e76326 375
cc192b50 376 dst.SetEmpty();
62e76326 377
c8be6d7b 378 memset(&nl, 0, sizeof(struct pfioc_natlook));
cc192b50 379 peer.GetInAddr(nl.saddr.v4);
380 nl.sport = htons(peer.GetPort());
381
382 me.GetInAddr(nl.daddr.v4);
383 nl.dport = htons(me.GetPort());
384
c8be6d7b 385 nl.af = AF_INET;
386 nl.proto = IPPROTO_TCP;
387 nl.direction = PF_OUT;
62e76326 388
389 if (ioctl(pffd, DIOCNATLOOK, &nl))
390 {
391 if (errno != ENOENT) {
d6026916 392 if (squid_curtime - last_reported > 60) {
bf8fe701 393 debugs(89, 1, "clientNatLookup: PF lookup failed: ioctl(DIOCNATLOOK)");
d6026916 394 last_reported = squid_curtime;
395 }
396
62e76326 397 close(pffd);
398 pffd = -1;
399 }
400
401 return -1;
402 } else
403 {
cc192b50 404 int natted = (me != nl.rdaddr.v4);
405 dst = nl.rdaddr.v4;
406 dst.SetPort(ntohs(nl.rdport));
62e76326 407
408 if (natted)
409 return 0;
410 else
411 return -1;
3f38a55e 412 }
62e76326 413
f1e0717c 414
f1e0717c
AJ
415#else /* none of the transparent options configured */
416
cc192b50 417 debugs(89, 1, "WARNING: transparent proxying not supported");
418 return -1;
f1e0717c 419
3f38a55e 420#endif
421
f1e0717c 422}
34ec5c62
AJ
423
424#if LINUX_TPROXY2
425IPIntercept::SetTproxy2OutgoingAddr(int fd, const IPAddress &src)
426{
427 IPAddress addr;
428 struct in_tproxy itp;
429
430 src.GetInAddr(itp.v.addr.faddr);
431 itp.v.addr.fport = 0;
432
433 /* If these syscalls fail then we just fallback to connecting
434 * normally by simply ignoring the errors...
435 */
436 itp.op = TPROXY_ASSIGN;
437
438 addr = (struct in_addr)itp.v.addr.faddr;
439 addr.SetPort(itp.v.addr.fport);
440
441 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
442 debugs(20, 1, "tproxy ip=" << addr << " ERROR ASSIGN");
443 return -1;
444 } else {
445 itp.op = TPROXY_FLAGS;
446 itp.v.flags = ITP_CONNECT;
447
448 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
449 debugs(20, 1, "tproxy ip=" << addr << " ERROR CONNECT");
450 return -1;
451 }
452 }
453
454 return 0;
455}
456#endif