]> git.ipfire.org Git - thirdparty/squid.git/blame - src/IPInterception.cc
Updates for running on squid-cache.org
[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 */
a74968c2 35#include "config.h"
fc27cd70 36#include "IPInterception.h"
b3166404 37#include "fde.h"
fc27cd70 38
b3166404 39#if 0
c8be6d7b 40#include "squid.h"
41#include "clientStream.h"
b3166404 42#endif
34ec5c62 43
c8be6d7b 44#if IPF_TRANSPARENT
34ec5c62 45
c8be6d7b 46#if HAVE_SYS_IOCTL_H
47#include <sys/ioctl.h>
48#endif
e9e7c285 49#if HAVE_NETINET_TCP_H
c8be6d7b 50#include <netinet/tcp.h>
e9e7c285 51#endif
52#if HAVE_NET_IF_H
c8be6d7b 53#include <net/if.h>
e9e7c285 54#endif
dbc5782a 55#ifdef HAVE_IPL_H
56#include <ipl.h>
57#elif HAVE_NETINET_IPL_H
58#include <netinet/ipl.h>
59#endif
c8be6d7b 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>
68#endif
69#if HAVE_IP_FIL_H
70#include <ip_fil.h>
71#elif HAVE_NETINET_IP_FIL_H
72#include <netinet/ip_fil.h>
73#endif
74#if HAVE_IP_NAT_H
75#include <ip_nat.h>
76#elif HAVE_NETINET_IP_NAT_H
77#include <netinet/ip_nat.h>
78#endif
34ec5c62
AJ
79
80#endif /* IPF_TRANSPARENT required headers */
c8be6d7b 81
82#if PF_TRANSPARENT
83#include <sys/types.h>
84#include <sys/socket.h>
85#include <sys/ioctl.h>
86#include <sys/fcntl.h>
87#include <net/if.h>
88#include <netinet/in.h>
89#include <net/pfvar.h>
34ec5c62 90#endif /* PF_TRANSPARENT required headers */
c8be6d7b 91
92#if LINUX_NETFILTER
ca6f2174 93#include <linux/types.h>
c8be6d7b 94#include <linux/netfilter_ipv4.h>
95#endif
96
34ec5c62
AJ
97#if LINUX_TPROXY2
98#ifdef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H
99#include <linux/netfilter_ipv4/ip_tproxy.h>
100#else
101#error " TPROXY v2 Header file missing: linux/netfilter_ipv4/ip_tproxy.h. Perhapse you meant to use TPROXY v4 ? "
102#endif
103#endif
104
0fc2952e
AJ
105
106// single global instance for access by other components.
107IPIntercept IPInterceptor;
108
04f87469
AJ
109void
110IPIntercept::StopTransparency(const char *str) {
788625af
AJ
111 if(transparent_active) {
112 debugs(89, DBG_IMPORTANT, "Stopping full transparency: " << str);
113 transparent_active = 0;
114 }
04f87469
AJ
115}
116
117void
118IPIntercept::StopInterception(const char *str) {
788625af
AJ
119 if(intercept_active) {
120 debugs(89, DBG_IMPORTANT, "Stopping IP interception: " << str);
121 intercept_active = 0;
122 }
04f87469 123}
0fc2952e 124
7b0a0d1f
AJ
125int
126IPIntercept::NetfilterInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
127{
128#if LINUX_NETFILTER
7b0a0d1f
AJ
129 struct addrinfo *lookup = NULL;
130
131 dst.GetAddrInfo(lookup,AF_INET);
132
133 /** \par
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) {
136 if(!silent) {
d6ae5979 137 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed on FD " << fd << ": " << xstrerror());
7b0a0d1f
AJ
138 last_reported = squid_curtime;
139 }
140 }
141 else {
142 dst = *lookup;
143 }
144
145 dst.FreeAddrInfo(lookup);
146
147 if(me != dst) {
148 debugs(89, 5, HERE << "address: " << dst);
149 return 0;
150 }
151
83707d8a 152 debugs(89, 9, HERE << "address: me= " << me << ", dst= " << dst);
7b0a0d1f
AJ
153#endif
154 return -1;
155}
156
157int
482a5c64 158IPIntercept::NetfilterTransparent(int fd, const IPAddress &me, IPAddress &client, int silent)
7b0a0d1f
AJ
159{
160#if LINUX_NETFILTER
7b0a0d1f 161
acaa7194
AJ
162 /* Trust the user configured properly. If not no harm done.
163 * We will simply attempt a bind outgoing on our own IP.
acaa7194 164 */
68d57d84 165 if(fd_table[fd].flags.transparent) {
482a5c64 166 client.SetPort(0); // allow random outgoing port to prevent address clashes
68d57d84
AJ
167 return 0;
168 }
acaa7194 169#endif
68d57d84 170 return -1;
7b0a0d1f
AJ
171}
172
d8b5bcbc
AJ
173int
174IPIntercept::IPFWInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
175{
176#if IPFW_TRANSPARENT
177 struct addrinfo *lookup = NULL;
178
179 dst.GetAddrInfo(lookup,AF_INET);
180
181 /** \par
182 * Try lookup for IPFW interception. */
183 if( getsockname(fd, lookup->ai_addr, &lookup->ai_addrlen) != 0 ) {
184 if( !silent ) {
185 debugs(89, DBG_IMPORTANT, HERE << " IPFW getsockname(...) failed: " << xstrerror());
186 last_reported = squid_curtime;
187 }
188 }
189 else {
190 dst = *lookup;
191 }
192
193 dst.FreeAddrInfo(lookup);
194
195 if(me != dst) {
196 debugs(89, 5, HERE << "address: " << dst);
197 return 0;
198 }
199
200 debugs(89, 9, HERE << "address: me= " << me << ", dst= " << dst);
201#endif
202 return -1;
203}
0fc2952e 204
3f38a55e 205int
482a5c64 206IPIntercept::NatLookup(int fd, const IPAddress &me, const IPAddress &peer, IPAddress &client, IPAddress &dst)
3f38a55e 207{
f1e0717c 208#if IPF_TRANSPARENT /* --enable-ipf-transparent */
482a5c64 209 client = me;
cc192b50 210 if( !me.IsIPv4() ) return -1;
211 if( !peer.IsIPv4() ) return -1;
62e76326 212
dbc5782a 213#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
214
215 struct ipfobj obj;
08a746ab 216#else
217
218 static int siocgnatl_cmd = SIOCGNATL & 0xff;
219
dbc5782a 220#endif
221
c8be6d7b 222 struct natlookup natLookup;
223 static int natfd = -1;
c8be6d7b 224 int x;
3f38a55e 225
dbc5782a 226#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
227
228 obj.ipfo_rev = IPFILTER_VERSION;
229 obj.ipfo_size = sizeof(natLookup);
230 obj.ipfo_ptr = &natLookup;
231 obj.ipfo_type = IPFOBJ_NATLOOKUP;
232 obj.ipfo_offset = 0;
233#endif
234
cc192b50 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);
c8be6d7b 239 natLookup.nl_flags = IPN_TCP;
62e76326 240
241 if (natfd < 0)
242 {
243 int save_errno;
244 enter_suid();
dbc5782a 245#ifdef IPNAT_NAME
98a3bc99 246
dbc5782a 247 natfd = open(IPNAT_NAME, O_RDONLY, 0);
98a3bc99 248#else
249
62e76326 250 natfd = open(IPL_NAT, O_RDONLY, 0);
98a3bc99 251#endif
252
62e76326 253 save_errno = errno;
254 leave_suid();
255 errno = save_errno;
c8be6d7b 256 }
62e76326 257
258 if (natfd < 0)
259 {
d6026916 260 if (squid_curtime - last_reported > 60) {
bf8fe701 261 debugs(89, 1, "clientNatLookup: NAT open failed: " << xstrerror());
d6026916 262 last_reported = squid_curtime;
263 return -1;
264 }
c8be6d7b 265 }
62e76326 266
dbc5782a 267#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
268 x = ioctl(natfd, SIOCGNATL, &obj);
269
270#else
3f38a55e 271 /*
dbc5782a 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.
277 */
62e76326 278 if (63 == siocgnatl_cmd)
279 {
280
281 struct natlookup *nlp = &natLookup;
282 x = ioctl(natfd, SIOCGNATL, &nlp);
283 } else
284 {
285 x = ioctl(natfd, SIOCGNATL, &natLookup);
286 }
287
dbc5782a 288#endif
62e76326 289 if (x < 0)
290 {
291 if (errno != ESRCH) {
d6026916 292 if (squid_curtime - last_reported > 60) {
bf8fe701 293 debugs(89, 1, "clientNatLookup: NAT lookup failed: ioctl(SIOCGNATL)");
d6026916 294 last_reported = squid_curtime;
295 }
296
62e76326 297 close(natfd);
298 natfd = -1;
299 }
300
301 return -1;
302 } else
303 {
cc192b50 304 if (me != natLookup.nl_realip) {
482a5c64 305 client = natLookup.nl_realip;
62e76326 306
482a5c64 307 client.SetPort(ntohs(natLookup.nl_realport));
cc192b50 308 }
309 // else. we already copied it.
62e76326 310
311 return 0;
c8be6d7b 312 }
62e76326 313
f1e0717c
AJ
314
315
7b0a0d1f 316#elif LINUX_NETFILTER || IPFW_TRANSPARENT /* --enable-linux-netfilter OR --enable-ipfw-transparent */
f1e0717c 317
7b0a0d1f
AJ
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.
320 */
62e76326 321
482a5c64
AJ
322 client = me;
323 dst = peer;
cc192b50 324
7b0a0d1f
AJ
325 if( !me.IsIPv4() ) return -1;
326 if( !peer.IsIPv4() ) return -1;
d6026916 327
ba8d006b
AJ
328#if 0
329 // Crop interception errors down to one per minute.
330 int silent = (squid_curtime - last_reported > 60 ? 0 : 1);
331#else
332 // Show all interception errors.
333 int silent = 0;
334#endif
335
7b0a0d1f 336 if(intercept_active) {
482a5c64
AJ
337 if( NetfilterInterception(fd, me, client, silent) == 0) return 0;
338 if( IPFWInterception(fd, me, client, silent) == 0) return 0;
7b0a0d1f
AJ
339 }
340 if(transparent_active) {
ca45fe65 341 if( NetfilterTransparent(fd, me, dst, silent) == 0) return 0;
d6026916 342 }
62e76326 343
7b0a0d1f 344 return -1;
62e76326 345
f1e0717c 346#elif PF_TRANSPARENT /* --enable-pf-transparent */
62e76326 347
3f38a55e 348 struct pfioc_natlook nl;
349 static int pffd = -1;
62e76326 350
cc192b50 351 if( !me.IsIPv4() ) return -1;
352 if( !peer.IsIPv4() ) return -1;
353
62e76326 354 if (pffd < 0)
d14c6ef2 355 pffd = open("/dev/pf", O_RDONLY);
62e76326 356
c8be6d7b 357 if (pffd < 0)
62e76326 358 {
d6026916 359 if (squid_curtime - last_reported > 60) {
bf8fe701 360 debugs(89, 1, "clientNatLookup: PF open failed: " << xstrerror());
d6026916 361 last_reported = squid_curtime;
362 }
363
62e76326 364 return -1;
d6026916 365
c8be6d7b 366 }
62e76326 367
482a5c64 368 client.SetEmpty();
62e76326 369
c8be6d7b 370 memset(&nl, 0, sizeof(struct pfioc_natlook));
cc192b50 371 peer.GetInAddr(nl.saddr.v4);
372 nl.sport = htons(peer.GetPort());
373
374 me.GetInAddr(nl.daddr.v4);
375 nl.dport = htons(me.GetPort());
376
c8be6d7b 377 nl.af = AF_INET;
378 nl.proto = IPPROTO_TCP;
379 nl.direction = PF_OUT;
62e76326 380
381 if (ioctl(pffd, DIOCNATLOOK, &nl))
382 {
383 if (errno != ENOENT) {
d6026916 384 if (squid_curtime - last_reported > 60) {
bf8fe701 385 debugs(89, 1, "clientNatLookup: PF lookup failed: ioctl(DIOCNATLOOK)");
d6026916 386 last_reported = squid_curtime;
387 }
388
62e76326 389 close(pffd);
390 pffd = -1;
391 }
392
393 return -1;
394 } else
395 {
cc192b50 396 int natted = (me != nl.rdaddr.v4);
482a5c64
AJ
397 client = nl.rdaddr.v4;
398 client.SetPort(ntohs(nl.rdport));
62e76326 399
400 if (natted)
401 return 0;
402 else
403 return -1;
3f38a55e 404 }
62e76326 405
f1e0717c 406
f1e0717c
AJ
407#else /* none of the transparent options configured */
408
cc192b50 409 debugs(89, 1, "WARNING: transparent proxying not supported");
410 return -1;
f1e0717c 411
3f38a55e 412#endif
413
f1e0717c 414}
34ec5c62
AJ
415
416#if LINUX_TPROXY2
417IPIntercept::SetTproxy2OutgoingAddr(int fd, const IPAddress &src)
418{
419 IPAddress addr;
420 struct in_tproxy itp;
421
422 src.GetInAddr(itp.v.addr.faddr);
423 itp.v.addr.fport = 0;
424
425 /* If these syscalls fail then we just fallback to connecting
426 * normally by simply ignoring the errors...
427 */
428 itp.op = TPROXY_ASSIGN;
429
430 addr = (struct in_addr)itp.v.addr.faddr;
431 addr.SetPort(itp.v.addr.fport);
432
433 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
434 debugs(20, 1, "tproxy ip=" << addr << " ERROR ASSIGN");
435 return -1;
436 } else {
437 itp.op = TPROXY_FLAGS;
438 itp.v.flags = ITP_CONNECT;
439
440 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
441 debugs(20, 1, "tproxy ip=" << addr << " ERROR CONNECT");
442 return -1;
443 }
444 }
445
446 return 0;
447}
448#endif