2 * $Id: IPInterception.cc,v 1.20 2008/02/05 22:38:24 amosjeffries Exp $
4 * DEBUG: section 89 NAT / IP Interception
5 * AUTHOR: Robert Collins
6 * AUTHOR: Amos Jeffries
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
36 #include "IPInterception.h"
41 #include "clientStream.h"
47 #include <sys/ioctl.h>
49 #if HAVE_NETINET_TCP_H
50 #include <netinet/tcp.h>
57 #elif HAVE_NETINET_IPL_H
58 #include <netinet/ipl.h>
60 #if HAVE_IP_FIL_COMPAT_H
61 #include <ip_fil_compat.h>
62 #elif HAVE_NETINET_IP_FIL_COMPAT_H
63 #include <netinet/ip_fil_compat.h>
64 #elif HAVE_IP_COMPAT_H
65 #include <ip_compat.h>
66 #elif HAVE_NETINET_IP_COMPAT_H
67 #include <netinet/ip_compat.h>
71 #elif HAVE_NETINET_IP_FIL_H
72 #include <netinet/ip_fil.h>
76 #elif HAVE_NETINET_IP_NAT_H
77 #include <netinet/ip_nat.h>
80 #endif /* IPF_TRANSPARENT required headers */
83 #include <sys/types.h>
84 #include <sys/socket.h>
85 #include <sys/ioctl.h>
86 #include <sys/fcntl.h>
88 #include <netinet/in.h>
89 #include <net/pfvar.h>
90 #endif /* PF_TRANSPARENT required headers */
93 #include <linux/types.h>
94 #include <linux/netfilter_ipv4.h>
98 #ifdef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H
99 #include <linux/netfilter_ipv4/ip_tproxy.h>
101 #error " TPROXY v2 Header file missing: linux/netfilter_ipv4/ip_tproxy.h. Perhapse you meant to use TPROXY v4 ? "
106 // single global instance for access by other components.
107 IPIntercept IPInterceptor
;
110 IPIntercept::StopTransparency(const char *str
) {
111 if(transparent_active
) {
112 debugs(89, DBG_IMPORTANT
, "Stopping full transparency: " << str
);
113 transparent_active
= 0;
118 IPIntercept::StopInterception(const char *str
) {
119 if(intercept_active
) {
120 debugs(89, DBG_IMPORTANT
, "Stopping IP interception: " << str
);
121 intercept_active
= 0;
126 IPIntercept::NetfilterInterception(int fd
, const IPAddress
&me
, IPAddress
&dst
, int silent
)
129 struct addrinfo
*lookup
= NULL
;
131 dst
.GetAddrInfo(lookup
,AF_INET
);
134 * Try NAT lookup for REDIRECT or DNAT targets. */
135 if( getsockopt(fd
, SOL_IP
, SO_ORIGINAL_DST
, lookup
->ai_addr
, &lookup
->ai_addrlen
) == 0) {
137 debugs(89, DBG_IMPORTANT
, HERE
<< " NF getsockopt(SO_ORIGINAL_DST) failed on FD " << fd
<< ": " << xstrerror());
138 last_reported
= squid_curtime
;
145 dst
.FreeAddrInfo(lookup
);
148 debugs(89, 5, HERE
<< "address: " << dst
);
152 debugs(89, 9, HERE
<< "address: me= " << me
<< ", dst= " << dst
);
158 IPIntercept::NetfilterTransparent(int fd
, const IPAddress
&me
, IPAddress
&client
, int silent
)
162 /* Trust the user configured properly. If not no harm done.
163 * We will simply attempt a bind outgoing on our own IP.
165 if(fd_table
[fd
].flags
.transparent
) {
166 client
.SetPort(0); // allow random outgoing port to prevent address clashes
174 IPIntercept::IPFWInterception(int fd
, const IPAddress
&me
, IPAddress
&dst
, int silent
)
177 struct addrinfo
*lookup
= NULL
;
179 dst
.GetAddrInfo(lookup
,AF_INET
);
182 * Try lookup for IPFW interception. */
183 if( getsockname(fd
, lookup
->ai_addr
, &lookup
->ai_addrlen
) != 0 ) {
185 debugs(89, DBG_IMPORTANT
, HERE
<< " IPFW getsockname(...) failed: " << xstrerror());
186 last_reported
= squid_curtime
;
193 dst
.FreeAddrInfo(lookup
);
196 debugs(89, 5, HERE
<< "address: " << dst
);
200 debugs(89, 9, HERE
<< "address: me= " << me
<< ", dst= " << dst
);
206 IPIntercept::NatLookup(int fd
, const IPAddress
&me
, const IPAddress
&peer
, IPAddress
&client
, IPAddress
&dst
)
208 #if IPF_TRANSPARENT /* --enable-ipf-transparent */
210 if( !me
.IsIPv4() ) return -1;
211 if( !peer
.IsIPv4() ) return -1;
213 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
218 static int siocgnatl_cmd
= SIOCGNATL
& 0xff;
222 struct natlookup natLookup
;
223 static int natfd
= -1;
226 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
228 obj
.ipfo_rev
= IPFILTER_VERSION
;
229 obj
.ipfo_size
= sizeof(natLookup
);
230 obj
.ipfo_ptr
= &natLookup
;
231 obj
.ipfo_type
= IPFOBJ_NATLOOKUP
;
235 natLookup
.nl_inport
= htons(me
.GetPort());
236 natLookup
.nl_outport
= htons(peer
.GetPort());
237 me
.GetInAddr(natLookup
.nl_inip
);
238 peer
.GetInAddr(natLookup
.nl_outip
);
239 natLookup
.nl_flags
= IPN_TCP
;
247 natfd
= open(IPNAT_NAME
, O_RDONLY
, 0);
250 natfd
= open(IPL_NAT
, O_RDONLY
, 0);
260 if (squid_curtime
- last_reported
> 60) {
261 debugs(89, 1, "clientNatLookup: NAT open failed: " << xstrerror());
262 last_reported
= squid_curtime
;
267 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
268 x
= ioctl(natfd
, SIOCGNATL
, &obj
);
272 * IP-Filter changed the type for SIOCGNATL between
273 * 3.3 and 3.4. It also changed the cmd value for
274 * SIOCGNATL, so at least we can detect it. We could
275 * put something in configure and use ifdefs here, but
276 * this seems simpler.
278 if (63 == siocgnatl_cmd
)
281 struct natlookup
*nlp
= &natLookup
;
282 x
= ioctl(natfd
, SIOCGNATL
, &nlp
);
285 x
= ioctl(natfd
, SIOCGNATL
, &natLookup
);
291 if (errno
!= ESRCH
) {
292 if (squid_curtime
- last_reported
> 60) {
293 debugs(89, 1, "clientNatLookup: NAT lookup failed: ioctl(SIOCGNATL)");
294 last_reported
= squid_curtime
;
304 if (me
!= natLookup
.nl_realip
) {
305 client
= natLookup
.nl_realip
;
307 client
.SetPort(ntohs(natLookup
.nl_realport
));
309 // else. we already copied it.
316 #elif LINUX_NETFILTER || IPFW_TRANSPARENT /* --enable-linux-netfilter OR --enable-ipfw-transparent */
318 /* Netfilter and IPFW share almost identical lookup methods for their NAT tables.
319 * This allows us to perform a nice clean failover sequence for them.
325 if( !me
.IsIPv4() ) return -1;
326 if( !peer
.IsIPv4() ) return -1;
329 // Crop interception errors down to one per minute.
330 int silent
= (squid_curtime
- last_reported
> 60 ? 0 : 1);
332 // Show all interception errors.
336 if(intercept_active
) {
337 if( NetfilterInterception(fd
, me
, client
, silent
) == 0) return 0;
338 if( IPFWInterception(fd
, me
, client
, silent
) == 0) return 0;
340 if(transparent_active
) {
341 if( NetfilterTransparent(fd
, me
, dst
, silent
) == 0) return 0;
346 #elif PF_TRANSPARENT /* --enable-pf-transparent */
348 struct pfioc_natlook nl
;
349 static int pffd
= -1;
351 if( !me
.IsIPv4() ) return -1;
352 if( !peer
.IsIPv4() ) return -1;
355 pffd
= open("/dev/pf", O_RDONLY
);
359 if (squid_curtime
- last_reported
> 60) {
360 debugs(89, 1, "clientNatLookup: PF open failed: " << xstrerror());
361 last_reported
= squid_curtime
;
370 memset(&nl
, 0, sizeof(struct pfioc_natlook
));
371 peer
.GetInAddr(nl
.saddr
.v4
);
372 nl
.sport
= htons(peer
.GetPort());
374 me
.GetInAddr(nl
.daddr
.v4
);
375 nl
.dport
= htons(me
.GetPort());
378 nl
.proto
= IPPROTO_TCP
;
379 nl
.direction
= PF_OUT
;
381 if (ioctl(pffd
, DIOCNATLOOK
, &nl
))
383 if (errno
!= ENOENT
) {
384 if (squid_curtime
- last_reported
> 60) {
385 debugs(89, 1, "clientNatLookup: PF lookup failed: ioctl(DIOCNATLOOK)");
386 last_reported
= squid_curtime
;
396 int natted
= (me
!= nl
.rdaddr
.v4
);
397 client
= nl
.rdaddr
.v4
;
398 client
.SetPort(ntohs(nl
.rdport
));
407 #else /* none of the transparent options configured */
409 debugs(89, 1, "WARNING: transparent proxying not supported");
417 IPIntercept::SetTproxy2OutgoingAddr(int fd
, const IPAddress
&src
)
420 struct in_tproxy itp
;
422 src
.GetInAddr(itp
.v
.addr
.faddr
);
423 itp
.v
.addr
.fport
= 0;
425 /* If these syscalls fail then we just fallback to connecting
426 * normally by simply ignoring the errors...
428 itp
.op
= TPROXY_ASSIGN
;
430 addr
= (struct in_addr
)itp
.v
.addr
.faddr
;
431 addr
.SetPort(itp
.v
.addr
.fport
);
433 if (setsockopt(fd
, SOL_IP
, IP_TPROXY
, &itp
, sizeof(itp
)) == -1) {
434 debugs(20, 1, "tproxy ip=" << addr
<< " ERROR ASSIGN");
437 itp
.op
= TPROXY_FLAGS
;
438 itp
.v
.flags
= ITP_CONNECT
;
440 if (setsockopt(fd
, SOL_IP
, IP_TPROXY
, &itp
, sizeof(itp
)) == -1) {
441 debugs(20, 1, "tproxy ip=" << addr
<< " ERROR CONNECT");