]> git.ipfire.org Git - thirdparty/squid.git/blob - src/IPInterception.cc
Merged from trunk.
[thirdparty/squid.git] / src / IPInterception.cc
1 /*
2 * $Id: IPInterception.cc,v 1.20 2008/02/05 22:38:24 amosjeffries Exp $
3 *
4 * DEBUG: section 89 NAT / IP Interception
5 * AUTHOR: Robert Collins
6 * AUTHOR: Amos Jeffries
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 #include "config.h"
36 #include "IPInterception.h"
37 #include "fde.h"
38
39 #if 0
40 #include "squid.h"
41 #include "clientStream.h"
42 #endif
43
44 #if IPF_TRANSPARENT
45
46 #if HAVE_SYS_IOCTL_H
47 #include <sys/ioctl.h>
48 #endif
49 #if HAVE_NETINET_TCP_H
50 #include <netinet/tcp.h>
51 #endif
52 #if HAVE_NET_IF_H
53 #include <net/if.h>
54 #endif
55 #ifdef HAVE_IPL_H
56 #include <ipl.h>
57 #elif HAVE_NETINET_IPL_H
58 #include <netinet/ipl.h>
59 #endif
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
79
80 #endif /* IPF_TRANSPARENT required headers */
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>
90 #endif /* PF_TRANSPARENT required headers */
91
92 #if LINUX_NETFILTER
93 #include <linux/types.h>
94 #include <linux/netfilter_ipv4.h>
95 #endif
96
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
105
106 // single global instance for access by other components.
107 IPIntercept IPInterceptor;
108
109 void
110 IPIntercept::StopTransparency(const char *str) {
111 if(transparent_active) {
112 debugs(89, DBG_IMPORTANT, "Stopping full transparency: " << str);
113 transparent_active = 0;
114 }
115 }
116
117 void
118 IPIntercept::StopInterception(const char *str) {
119 if(intercept_active) {
120 debugs(89, DBG_IMPORTANT, "Stopping IP interception: " << str);
121 intercept_active = 0;
122 }
123 }
124
125 int
126 IPIntercept::NetfilterInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
127 {
128 #if LINUX_NETFILTER
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) {
137 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed on FD " << fd << ": " << xstrerror());
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
152 debugs(89, 9, HERE << "address: me= " << me << ", dst= " << dst);
153 #endif
154 return -1;
155 }
156
157 int
158 IPIntercept::NetfilterTransparent(int fd, const IPAddress &me, IPAddress &client, int silent)
159 {
160 #if LINUX_NETFILTER
161
162 /* Trust the user configured properly. If not no harm done.
163 * We will simply attempt a bind outgoing on our own IP.
164 */
165 if(fd_table[fd].flags.transparent) {
166 client.SetPort(0); // allow random outgoing port to prevent address clashes
167 return 0;
168 }
169 #endif
170 return -1;
171 }
172
173 int
174 IPIntercept::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 }
204
205 int
206 IPIntercept::NatLookup(int fd, const IPAddress &me, const IPAddress &peer, IPAddress &client, IPAddress &dst)
207 {
208 #if IPF_TRANSPARENT /* --enable-ipf-transparent */
209 client = me;
210 if( !me.IsIPv4() ) return -1;
211 if( !peer.IsIPv4() ) return -1;
212
213 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
214
215 struct ipfobj obj;
216 #else
217
218 static int siocgnatl_cmd = SIOCGNATL & 0xff;
219
220 #endif
221
222 struct natlookup natLookup;
223 static int natfd = -1;
224 int x;
225
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
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;
240
241 if (natfd < 0)
242 {
243 int save_errno;
244 enter_suid();
245 #ifdef IPNAT_NAME
246
247 natfd = open(IPNAT_NAME, O_RDONLY, 0);
248 #else
249
250 natfd = open(IPL_NAT, O_RDONLY, 0);
251 #endif
252
253 save_errno = errno;
254 leave_suid();
255 errno = save_errno;
256 }
257
258 if (natfd < 0)
259 {
260 if (squid_curtime - last_reported > 60) {
261 debugs(89, 1, "clientNatLookup: NAT open failed: " << xstrerror());
262 last_reported = squid_curtime;
263 return -1;
264 }
265 }
266
267 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
268 x = ioctl(natfd, SIOCGNATL, &obj);
269
270 #else
271 /*
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 */
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
288 #endif
289 if (x < 0)
290 {
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;
295 }
296
297 close(natfd);
298 natfd = -1;
299 }
300
301 return -1;
302 } else
303 {
304 if (me != natLookup.nl_realip) {
305 client = natLookup.nl_realip;
306
307 client.SetPort(ntohs(natLookup.nl_realport));
308 }
309 // else. we already copied it.
310
311 return 0;
312 }
313
314
315
316 #elif LINUX_NETFILTER || IPFW_TRANSPARENT /* --enable-linux-netfilter OR --enable-ipfw-transparent */
317
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 */
321
322 client = me;
323 dst = peer;
324
325 if( !me.IsIPv4() ) return -1;
326 if( !peer.IsIPv4() ) return -1;
327
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
336 if(intercept_active) {
337 if( NetfilterInterception(fd, me, client, silent) == 0) return 0;
338 if( IPFWInterception(fd, me, client, silent) == 0) return 0;
339 }
340 if(transparent_active) {
341 if( NetfilterTransparent(fd, me, dst, silent) == 0) return 0;
342 }
343
344 return -1;
345
346 #elif PF_TRANSPARENT /* --enable-pf-transparent */
347
348 struct pfioc_natlook nl;
349 static int pffd = -1;
350
351 if( !me.IsIPv4() ) return -1;
352 if( !peer.IsIPv4() ) return -1;
353
354 if (pffd < 0)
355 pffd = open("/dev/pf", O_RDONLY);
356
357 if (pffd < 0)
358 {
359 if (squid_curtime - last_reported > 60) {
360 debugs(89, 1, "clientNatLookup: PF open failed: " << xstrerror());
361 last_reported = squid_curtime;
362 }
363
364 return -1;
365
366 }
367
368 client.SetEmpty();
369
370 memset(&nl, 0, sizeof(struct pfioc_natlook));
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
377 nl.af = AF_INET;
378 nl.proto = IPPROTO_TCP;
379 nl.direction = PF_OUT;
380
381 if (ioctl(pffd, DIOCNATLOOK, &nl))
382 {
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;
387 }
388
389 close(pffd);
390 pffd = -1;
391 }
392
393 return -1;
394 } else
395 {
396 int natted = (me != nl.rdaddr.v4);
397 client = nl.rdaddr.v4;
398 client.SetPort(ntohs(nl.rdport));
399
400 if (natted)
401 return 0;
402 else
403 return -1;
404 }
405
406
407 #else /* none of the transparent options configured */
408
409 debugs(89, 1, "WARNING: transparent proxying not supported");
410 return -1;
411
412 #endif
413
414 }
415
416 #if LINUX_TPROXY2
417 IPIntercept::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