]>
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 "ip/Intercept.h"
40 #include <sys/ioctl.h>
42 #if HAVE_NETINET_TCP_H
43 #include <netinet/tcp.h>
50 #elif HAVE_NETINET_IPL_H
51 #include <netinet/ipl.h>
53 #if HAVE_IP_FIL_COMPAT_H
54 #include <ip_fil_compat.h>
55 #elif HAVE_NETINET_IP_FIL_COMPAT_H
56 #include <netinet/ip_fil_compat.h>
57 #elif HAVE_IP_COMPAT_H
58 #include <ip_compat.h>
59 #elif HAVE_NETINET_IP_COMPAT_H
60 #include <netinet/ip_compat.h>
64 #elif HAVE_NETINET_IP_FIL_H
65 #include <netinet/ip_fil.h>
69 #elif HAVE_NETINET_IP_NAT_H
70 #include <netinet/ip_nat.h>
73 #endif /* IPF_TRANSPARENT required headers */
76 #include <sys/socket.h>
77 #include <sys/ioctl.h>
78 #include <sys/fcntl.h>
80 #include <netinet/in.h>
81 #if HAVE_NET_PF_PFVAR_H
82 #include <net/pf/pfvar.h>
83 #endif /* HAVE_NET_PF_PFVAR_H */
85 #include <net/pfvar.h>
86 #endif /* HAVE_NET_PFVAR_H */
87 #endif /* PF_TRANSPARENT required headers */
90 #include <linux/netfilter_ipv4.h>
93 // single global instance for access by other components.
94 Ip::Intercept
Ip::Interceptor
;
97 Ip::Intercept::StopTransparency(const char *str
)
99 if (transparent_active
) {
100 debugs(89, DBG_IMPORTANT
, "Stopping full transparency: " << str
);
101 transparent_active
= 0;
106 Ip::Intercept::StopInterception(const char *str
)
108 if (intercept_active
) {
109 debugs(89, DBG_IMPORTANT
, "Stopping IP interception: " << str
);
110 intercept_active
= 0;
115 Ip::Intercept::NetfilterInterception(int fd
, const Ip::Address
&me
, Ip::Address
&dst
, int silent
)
118 struct addrinfo
*lookup
= NULL
;
120 dst
.GetAddrInfo(lookup
,AF_INET
);
123 * Try NAT lookup for REDIRECT or DNAT targets. */
124 if ( getsockopt(fd
, IPPROTO_IP
, SO_ORIGINAL_DST
, lookup
->ai_addr
, &lookup
->ai_addrlen
) != 0) {
126 debugs(89, DBG_IMPORTANT
, HERE
<< " NF getsockopt(SO_ORIGINAL_DST) failed on FD " << fd
<< ": " << xstrerror());
127 last_reported
= squid_curtime
;
133 Address::FreeAddrInfo(lookup
);
136 debugs(89, 5, HERE
<< "address NAT: me= " << me
<< ", dst= " << dst
);
140 debugs(89, 9, HERE
<< "address: me= " << me
<< ", dst= " << dst
);
146 Ip::Intercept::NetfilterTransparent(int fd
, const Ip::Address
&me
, Ip::Address
&client
, int silent
)
150 /* Trust the user configured properly. If not no harm done.
151 * We will simply attempt a bind outgoing on our own IP.
153 if (fd_table
[fd
].flags
.transparent
) {
154 client
.SetPort(0); // allow random outgoing port to prevent address clashes
155 debugs(89, 5, HERE
<< "address TPROXY: me= " << me
<< ", client= " << client
);
159 debugs(89, 9, HERE
<< "address: me= " << me
<< ", client= " << client
);
165 Ip::Intercept::IpfwInterception(int fd
, const Ip::Address
&me
, Ip::Address
&dst
, int silent
)
168 struct addrinfo
*lookup
= NULL
;
170 dst
.GetAddrInfo(lookup
,AF_INET
);
173 * Try lookup for IPFW interception. */
174 if ( getsockname(fd
, lookup
->ai_addr
, &lookup
->ai_addrlen
) != 0 ) {
176 debugs(89, DBG_IMPORTANT
, HERE
<< " IPFW getsockname(...) failed: " << xstrerror());
177 last_reported
= squid_curtime
;
183 Address::FreeAddrInfo(lookup
);
186 debugs(89, 5, HERE
<< "address NAT: me= " << me
<< ", dst= " << dst
);
190 debugs(89, 9, HERE
<< "address: me= " << me
<< ", dst= " << dst
);
196 Ip::Intercept::IpfInterception(int fd
, const Ip::Address
&me
, Ip::Address
&client
, Ip::Address
&dst
, int silent
)
198 #if IPF_TRANSPARENT /* --enable-ipf-transparent */
200 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
203 static int siocgnatl_cmd
= SIOCGNATL
& 0xff;
205 struct natlookup natLookup
;
206 static int natfd
= -1;
209 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
211 obj
.ipfo_rev
= IPFILTER_VERSION
;
212 obj
.ipfo_size
= sizeof(natLookup
);
213 obj
.ipfo_ptr
= &natLookup
;
214 obj
.ipfo_type
= IPFOBJ_NATLOOKUP
;
218 natLookup
.nl_inport
= htons(me
.GetPort());
219 natLookup
.nl_outport
= htons(dst
.GetPort());
220 me
.GetInAddr(natLookup
.nl_inip
);
221 dst
.GetInAddr(natLookup
.nl_outip
);
222 natLookup
.nl_flags
= IPN_TCP
;
228 natfd
= open(IPNAT_NAME
, O_RDONLY
, 0);
230 natfd
= open(IPL_NAT
, O_RDONLY
, 0);
239 debugs(89, DBG_IMPORTANT
, HERE
<< "NAT open failed: " << xstrerror());
240 last_reported
= squid_curtime
;
245 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
246 x
= ioctl(natfd
, SIOCGNATL
, &obj
);
249 * IP-Filter changed the type for SIOCGNATL between
250 * 3.3 and 3.4. It also changed the cmd value for
251 * SIOCGNATL, so at least we can detect it. We could
252 * put something in configure and use ifdefs here, but
253 * this seems simpler.
255 if (63 == siocgnatl_cmd
) {
256 struct natlookup
*nlp
= &natLookup
;
257 x
= ioctl(natfd
, SIOCGNATL
, &nlp
);
259 x
= ioctl(natfd
, SIOCGNATL
, &natLookup
);
264 if (errno
!= ESRCH
) {
266 debugs(89, DBG_IMPORTANT
, HERE
<< "NAT lookup failed: ioctl(SIOCGNATL)");
267 last_reported
= squid_curtime
;
276 if (client
!= natLookup
.nl_realip
) {
277 client
= natLookup
.nl_realip
;
278 client
.SetPort(ntohs(natLookup
.nl_realport
));
280 // else. we already copied it.
282 debugs(89, 5, HERE
<< "address NAT: me= " << me
<< ", client= " << client
<< ", dst= " << dst
);
286 debugs(89, 9, HERE
<< "address: me= " << me
<< ", client= " << client
<< ", dst= " << dst
);
288 #endif /* --enable-ipf-transparent */
293 Ip::Intercept::PfInterception(int fd
, const Ip::Address
&me
, Ip::Address
&client
, Ip::Address
&dst
, int silent
)
295 #if PF_TRANSPARENT /* --enable-pf-transparent */
297 struct pfioc_natlook nl
;
298 static int pffd
= -1;
301 pffd
= open("/dev/pf", O_RDONLY
);
305 debugs(89, DBG_IMPORTANT
, HERE
<< "PF open failed: " << xstrerror());
306 last_reported
= squid_curtime
;
311 memset(&nl
, 0, sizeof(struct pfioc_natlook
));
312 dst
.GetInAddr(nl
.saddr
.v4
);
313 nl
.sport
= htons(dst
.GetPort());
315 me
.GetInAddr(nl
.daddr
.v4
);
316 nl
.dport
= htons(me
.GetPort());
319 nl
.proto
= IPPROTO_TCP
;
320 nl
.direction
= PF_OUT
;
322 if (ioctl(pffd
, DIOCNATLOOK
, &nl
)) {
323 if (errno
!= ENOENT
) {
325 debugs(89, DBG_IMPORTANT
, HERE
<< "PF lookup failed: ioctl(DIOCNATLOOK)");
326 last_reported
= squid_curtime
;
332 int natted
= (client
!= nl
.rdaddr
.v4
);
333 client
= nl
.rdaddr
.v4
;
334 client
.SetPort(ntohs(nl
.rdport
));
337 debugs(89, 5, HERE
<< "address NAT: me= " << me
<< ", client= " << client
<< ", dst= " << dst
);
342 debugs(89, 9, HERE
<< "address: me= " << me
<< ", client= " << client
<< ", dst= " << dst
);
344 #endif /* --enable-pf-transparent */
350 Ip::Intercept::NatLookup(int fd
, const Ip::Address
&me
, const Ip::Address
&peer
, Ip::Address
&client
, Ip::Address
&dst
)
352 /* --enable-linux-netfilter */
353 /* --enable-ipfw-transparent */
354 /* --enable-ipf-transparent */
355 /* --enable-pf-transparent */
356 #if IPF_TRANSPARENT || LINUX_NETFILTER || IPFW_TRANSPARENT || PF_TRANSPARENT
362 // Crop interception errors down to one per minute.
363 int silent
= (squid_curtime
- last_reported
> 60 ? 0 : 1);
365 // Show all interception errors.
369 debugs(89, 5, HERE
<< "address BEGIN: me= " << me
<< ", client= " << client
<<
370 ", dst= " << dst
<< ", peer= " << peer
);
372 /* NP: try TPROXY first, its much quieter than NAT when non-matching */
373 if (transparent_active
) {
374 if ( NetfilterTransparent(fd
, me
, dst
, silent
) == 0) return 0;
377 /* NAT is only available in IPv4 */
378 if ( !me
.IsIPv4() ) return -1;
379 if ( !peer
.IsIPv4() ) return -1;
381 if (intercept_active
) {
382 /* NAT methods that use sock-opts to return client address */
383 if ( NetfilterInterception(fd
, me
, client
, silent
) == 0) return 0;
384 if ( IpfwInterception(fd
, me
, client
, silent
) == 0) return 0;
386 /* NAT methods that use ioctl to return client address AND destination address */
387 if ( PfInterception(fd
, me
, client
, dst
, silent
) == 0) return 0;
388 if ( IpfInterception(fd
, me
, client
, dst
, silent
) == 0) return 0;
391 #else /* none of the transparent options configured */
392 debugs(89, DBG_IMPORTANT
, "WARNING: transparent proxying not supported");
399 Ip::Intercept::ProbeForTproxy(Ip::Address
&test
)
401 debugs(3, 3, "Detect TPROXY support on port " << test
);
403 #if defined(IP_TRANSPARENT)
408 /* Probe to see if the Kernel TPROXY support is IPv6-enabled */
410 debugs(3, 3, "...Probing for IPv6 TPROXY support.");
412 struct sockaddr_in6 tmp_ip6
;
413 Ip::Address tmp
= "::2";
415 tmp
.GetSockAddr(tmp_ip6
);
417 if ( (tmp_sock
= socket(PF_INET6
, SOCK_STREAM
, IPPROTO_TCP
)) >= 0 &&
418 setsockopt(tmp_sock
, SOL_IP
, IP_TRANSPARENT
, (char *)&tos
, sizeof(int)) == 0 &&
419 bind(tmp_sock
, (struct sockaddr
*)&tmp_ip6
, sizeof(struct sockaddr_in6
)) == 0 ) {
421 debugs(3, 3, "IPv6 TPROXY support detected. Using.");
431 if ( test
.IsIPv6() && !test
.SetIPv4() ) {
432 debugs(3, DBG_CRITICAL
, "TPROXY lacks IPv6 support for " << test
);
436 /* Probe to see if the Kernel TPROXY support is IPv4-enabled (aka present) */
438 debugs(3, 3, "...Probing for IPv4 TPROXY support.");
440 struct sockaddr_in tmp_ip4
;
441 Ip::Address tmp
= "127.0.0.2";
443 tmp
.GetSockAddr(tmp_ip4
);
445 if ( (tmp_sock
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
)) >= 0 &&
446 setsockopt(tmp_sock
, SOL_IP
, IP_TRANSPARENT
, (char *)&tos
, sizeof(int)) == 0 &&
447 bind(tmp_sock
, (struct sockaddr
*)&tmp_ip4
, sizeof(struct sockaddr_in
)) == 0 ) {
449 debugs(3, 3, "IPv4 TPROXY support detected. Using.");
458 #else /* undefined IP_TRANSPARENT */
459 debugs(3, 3, "setsockopt(IP_TRANSPARENT) not supported on this platform. Disabling TPROXYv4.");