]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ip/Intercept.cc
2 * DEBUG: section 89 NAT / IP Interception
3 * AUTHOR: Robert Collins
4 * AUTHOR: Amos Jeffries
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
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.
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.
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.
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.
34 #include "comm/Connection.h"
35 #include "ip/Intercept.h"
41 #include <sys/ioctl.h>
43 #if HAVE_NETINET_TCP_H
44 #include <netinet/tcp.h>
51 #elif HAVE_NETINET_IPL_H
52 #include <netinet/ipl.h>
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>
65 #elif HAVE_NETINET_IP_FIL_H
66 #include <netinet/ip_fil.h>
70 #elif HAVE_NETINET_IP_NAT_H
71 #include <netinet/ip_nat.h>
74 #endif /* IPF_TRANSPARENT required headers */
77 #include <sys/socket.h>
78 #include <sys/ioctl.h>
79 #include <sys/fcntl.h>
81 #include <netinet/in.h>
82 #if HAVE_NET_PF_PFVAR_H
83 #include <net/pf/pfvar.h>
84 #endif /* HAVE_NET_PF_PFVAR_H */
86 #include <net/pfvar.h>
87 #endif /* HAVE_NET_PFVAR_H */
88 #endif /* PF_TRANSPARENT required headers */
91 #include <linux/netfilter_ipv4.h>
94 // single global instance for access by other components.
95 Ip::Intercept
Ip::Interceptor
;
98 Ip::Intercept::StopTransparency(const char *str
)
100 if (transparentActive_
) {
101 debugs(89, DBG_IMPORTANT
, "Stopping full transparency: " << str
);
102 transparentActive_
= 0;
107 Ip::Intercept::StopInterception(const char *str
)
109 if (interceptActive_
) {
110 debugs(89, DBG_IMPORTANT
, "Stopping IP interception: " << str
);
111 interceptActive_
= 0;
116 Ip::Intercept::NetfilterInterception(const Comm::ConnectionPointer
&newConn
, int silent
)
119 struct sockaddr_in lookup
;
120 socklen_t len
= sizeof(struct sockaddr_in
);
121 newConn
->local
.GetSockAddr(lookup
);
124 * Try NAT lookup for REDIRECT or DNAT targets. */
125 if ( getsockopt(newConn
->fd
, IPPROTO_IP
, SO_ORIGINAL_DST
, &lookup
, &len
) != 0) {
127 debugs(89, DBG_IMPORTANT
, HERE
<< " NF getsockopt(SO_ORIGINAL_DST) failed on " << newConn
<< ": " << xstrerror());
128 lastReported_
= squid_curtime
;
130 debugs(89, 9, HERE
<< "address: " << newConn
);
133 newConn
->local
= lookup
;
134 debugs(89, 5, HERE
<< "address NAT: " << newConn
);
142 Ip::Intercept::NetfilterTransparent(const Comm::ConnectionPointer
&newConn
, int silent
)
145 /* Trust the user configured properly. If not no harm done.
146 * We will simply attempt a bind outgoing on our own IP.
148 newConn
->remote
.SetPort(0); // allow random outgoing port to prevent address clashes
149 debugs(89, 5, HERE
<< "address TPROXY: " << newConn
);
157 Ip::Intercept::IpfwInterception(const Comm::ConnectionPointer
&newConn
, int silent
)
160 struct sockaddr_storage lookup
;
161 socklen_t len
= sizeof(struct sockaddr_storage
);
162 newConn
->local
.GetSockAddr(lookup
, AF_INET
);
165 * Try lookup for IPFW interception. */
166 if ( getsockname(newConn
->fd
, (struct sockaddr
*)&lookup
, &len
) != 0 ) {
168 debugs(89, DBG_IMPORTANT
, HERE
<< " IPFW getsockname(...) failed: " << xstrerror());
169 lastReported_
= squid_curtime
;
171 debugs(89, 9, HERE
<< "address: " << newConn
);
174 newConn
->local
= lookup
;
175 debugs(89, 5, HERE
<< "address NAT: " << newConn
);
183 Ip::Intercept::IpfInterception(const Comm::ConnectionPointer
&newConn
, int silent
)
185 #if IPF_TRANSPARENT /* --enable-ipf-transparent */
187 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
190 static int siocgnatl_cmd
= SIOCGNATL
& 0xff;
192 struct natlookup natLookup
;
193 static int natfd
= -1;
196 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
198 obj
.ipfo_rev
= IPFILTER_VERSION
;
199 obj
.ipfo_size
= sizeof(natLookup
);
200 obj
.ipfo_ptr
= &natLookup
;
201 obj
.ipfo_type
= IPFOBJ_NATLOOKUP
;
205 natLookup
.nl_inport
= htons(newConn
->local
.GetPort());
206 newConn
->local
.GetInAddr(natLookup
.nl_inip
);
207 natLookup
.nl_outport
= htons(newConn
->remote
.GetPort());
208 newConn
->remote
.GetInAddr(natLookup
.nl_outip
);
209 natLookup
.nl_flags
= IPN_TCP
;
215 natfd
= open(IPNAT_NAME
, O_RDONLY
, 0);
217 natfd
= open(IPL_NAT
, O_RDONLY
, 0);
226 debugs(89, DBG_IMPORTANT
, HERE
<< "NAT open failed: " << xstrerror());
227 lastReported_
= squid_curtime
;
232 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
233 x
= ioctl(natfd
, SIOCGNATL
, &obj
);
236 * IP-Filter changed the type for SIOCGNATL between
237 * 3.3 and 3.4. It also changed the cmd value for
238 * SIOCGNATL, so at least we can detect it. We could
239 * put something in configure and use ifdefs here, but
240 * this seems simpler.
242 if (63 == siocgnatl_cmd
) {
243 struct natlookup
*nlp
= &natLookup
;
244 x
= ioctl(natfd
, SIOCGNATL
, &nlp
);
246 x
= ioctl(natfd
, SIOCGNATL
, &natLookup
);
251 if (errno
!= ESRCH
) {
253 debugs(89, DBG_IMPORTANT
, HERE
<< "NAT lookup failed: ioctl(SIOCGNATL)");
254 lastReported_
= squid_curtime
;
261 debugs(89, 9, HERE
<< "address: " << newConn
);
264 newConn
->local
= natLookup
.nl_realip
;
265 newConn
->local
.SetPort(ntohs(natLookup
.nl_realport
));
266 debugs(89, 5, HERE
<< "address NAT: " << newConn
);
270 #endif /* --enable-ipf-transparent */
275 Ip::Intercept::PfInterception(const Comm::ConnectionPointer
&newConn
, int silent
)
277 #if PF_TRANSPARENT /* --enable-pf-transparent */
279 struct pfioc_natlook nl
;
280 static int pffd
= -1;
283 pffd
= open("/dev/pf", O_RDONLY
);
287 debugs(89, DBG_IMPORTANT
, HERE
<< "PF open failed: " << xstrerror());
288 lastReported_
= squid_curtime
;
293 memset(&nl
, 0, sizeof(struct pfioc_natlook
));
294 newConn
->remote
.GetInAddr(nl
.saddr
.v4
);
295 nl
.sport
= htons(newConn
->remote
.GetPort());
297 newConn
->local
.GetInAddr(nl
.daddr
.v4
);
298 nl
.dport
= htons(newConn
->local
.GetPort());
301 nl
.proto
= IPPROTO_TCP
;
302 nl
.direction
= PF_OUT
;
304 if (ioctl(pffd
, DIOCNATLOOK
, &nl
)) {
305 if (errno
!= ENOENT
) {
307 debugs(89, DBG_IMPORTANT
, HERE
<< "PF lookup failed: ioctl(DIOCNATLOOK)");
308 lastReported_
= squid_curtime
;
313 debugs(89, 9, HERE
<< "address: " << newConn
);
316 newConn
->local
= nl
.rdaddr
.v4
;
317 newConn
->local
.SetPort(ntohs(nl
.rdport
));
318 debugs(89, 5, HERE
<< "address NAT: " << newConn
);
322 #endif /* --enable-pf-transparent */
327 Ip::Intercept::Lookup(const Comm::ConnectionPointer
&newConn
, const Comm::ConnectionPointer
&listenConn
)
329 /* --enable-linux-netfilter */
330 /* --enable-ipfw-transparent */
331 /* --enable-ipf-transparent */
332 /* --enable-pf-transparent */
333 #if IPF_TRANSPARENT || LINUX_NETFILTER || IPFW_TRANSPARENT || PF_TRANSPARENT
336 // Crop interception errors down to one per minute.
337 int silent
= (squid_curtime
- lastReported_
> 60 ? 0 : 1);
339 // Show all interception errors.
343 debugs(89, 5, HERE
<< "address BEGIN: me/client= " << newConn
->local
<< ", destination/me= " << newConn
->remote
);
345 newConn
->flags
|= (listenConn
->flags
& (COMM_TRANSPARENT
|COMM_INTERCEPTION
));
347 /* NP: try TPROXY first, its much quieter than NAT when non-matching */
348 if (transparentActive_
&& listenConn
->flags
&COMM_TRANSPARENT
) {
349 if (NetfilterTransparent(newConn
, silent
)) return true;
352 /* NAT is only available in IPv4 */
353 if ( !newConn
->local
.IsIPv4() ) return false;
354 if ( !newConn
->remote
.IsIPv4() ) return false;
356 if (interceptActive_
&& listenConn
->flags
&COMM_INTERCEPTION
) {
357 /* NAT methods that use sock-opts to return client address */
358 if (NetfilterInterception(newConn
, silent
)) return true;
359 if (IpfwInterception(newConn
, silent
)) return true;
361 /* NAT methods that use ioctl to return client address AND destination address */
362 if (PfInterception(newConn
, silent
)) return true;
363 if (IpfInterception(newConn
, silent
)) return true;
366 #else /* none of the transparent options configured */
367 debugs(89, DBG_IMPORTANT
, "WARNING: transparent proxying not supported");
374 Ip::Intercept::ProbeForTproxy(Ip::Address
&test
)
376 debugs(3, 3, "Detect TPROXY support on port " << test
);
378 #if defined(IP_TRANSPARENT)
383 /* Probe to see if the Kernel TPROXY support is IPv6-enabled */
385 debugs(3, 3, "...Probing for IPv6 TPROXY support.");
387 struct sockaddr_in6 tmp_ip6
;
388 Ip::Address tmp
= "::2";
390 tmp
.GetSockAddr(tmp_ip6
);
392 if ( (tmp_sock
= socket(PF_INET6
, SOCK_STREAM
, IPPROTO_TCP
)) >= 0 &&
393 setsockopt(tmp_sock
, SOL_IP
, IP_TRANSPARENT
, (char *)&tos
, sizeof(int)) == 0 &&
394 bind(tmp_sock
, (struct sockaddr
*)&tmp_ip6
, sizeof(struct sockaddr_in6
)) == 0 ) {
396 debugs(3, 3, "IPv6 TPROXY support detected. Using.");
406 if ( test
.IsIPv6() && !test
.SetIPv4() ) {
407 debugs(3, DBG_CRITICAL
, "TPROXY lacks IPv6 support for " << test
);
411 /* Probe to see if the Kernel TPROXY support is IPv4-enabled (aka present) */
413 debugs(3, 3, "...Probing for IPv4 TPROXY support.");
415 struct sockaddr_in tmp_ip4
;
416 Ip::Address tmp
= "127.0.0.2";
418 tmp
.GetSockAddr(tmp_ip4
);
420 if ( (tmp_sock
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
)) >= 0 &&
421 setsockopt(tmp_sock
, SOL_IP
, IP_TRANSPARENT
, (char *)&tos
, sizeof(int)) == 0 &&
422 bind(tmp_sock
, (struct sockaddr
*)&tmp_ip4
, sizeof(struct sockaddr_in
)) == 0 ) {
424 debugs(3, 3, "IPv4 TPROXY support detected. Using.");
433 #else /* undefined IP_TRANSPARENT */
434 debugs(3, 3, "setsockopt(IP_TRANSPARENT) not supported on this platform. Disabling TPROXYv4.");