]> git.ipfire.org Git - thirdparty/squid.git/blob - src/IPInterception.cc
Summary: Synced with libecap, adopted pass-all-changes-through transactions
[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
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/netfilter_ipv4.h>
94 #endif
95
96 #if LINUX_TPROXY2
97 #ifdef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H
98 #include <linux/netfilter_ipv4/ip_tproxy.h>
99 #else
100 #error " TPROXY v2 Header file missing: linux/netfilter_ipv4/ip_tproxy.h. Perhapse you meant to use TPROXY v4 ? "
101 #endif
102 #endif
103
104
105 // single global instance for access by other components.
106 IPIntercept IPInterceptor;
107
108 void
109 IPIntercept::StopTransparency(const char *str) {
110 if(transparent_active) {
111 debugs(89, DBG_IMPORTANT, "Stopping full transparency: " << str);
112 transparent_active = 0;
113 }
114 }
115
116 void
117 IPIntercept::StopInterception(const char *str) {
118 if(intercept_active) {
119 debugs(89, DBG_IMPORTANT, "Stopping IP interception: " << str);
120 intercept_active = 0;
121 }
122 }
123
124 int
125 IPIntercept::NetfilterInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
126 {
127 #if LINUX_NETFILTER
128 struct addrinfo *lookup = NULL;
129
130 dst.GetAddrInfo(lookup,AF_INET);
131
132 /** \par
133 * Try NAT lookup for REDIRECT or DNAT targets. */
134 if( getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, lookup->ai_addr, &lookup->ai_addrlen) == 0) {
135 if(!silent) {
136 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed: " << xstrerror());
137 last_reported = squid_curtime;
138 }
139 }
140 else {
141 dst = *lookup;
142 }
143
144 dst.FreeAddrInfo(lookup);
145
146 if(me != dst) {
147 debugs(89, 5, HERE << "address: " << dst);
148 return 0;
149 }
150
151 debugs(89, 9, HERE << "address: me= " << me << ", dst= " << dst);
152 #endif
153 return -1;
154 }
155
156 int
157 IPIntercept::NetfilterTransparent(int fd, const IPAddress &me, IPAddress &dst, int silent)
158 {
159 #if LINUX_NETFILTER
160 struct addrinfo *lookup = NULL;
161
162 if( ! fd_table[fd].flags.transparent) return -1;
163
164 dst.GetAddrInfo(lookup,AF_INET);
165
166 /** \par
167 * Try lookup for TPROXY targets. BUT, only if the FD is flagged for transparent operations. */
168 if(getsockopt(fd, SOL_IP, IP_TRANSPARENT, lookup->ai_addr, &lookup->ai_addrlen) != 0) {
169 if(!silent) {
170 debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(IP_TRANSPARENT) failed: " << xstrerror());
171 last_reported = squid_curtime;
172 }
173 }
174 else {
175 dst = *lookup;
176 }
177
178 dst.FreeAddrInfo(lookup);
179
180 if(me != dst) {
181 debugs(89, 5, HERE << "address: " << dst);
182 return 0;
183 }
184
185 debugs(89, 9, HERE << "address: me= " << me << ", dst= " << dst);
186 #endif
187 return -1;
188 }
189
190 int
191 IPIntercept::IPFWInterception(int fd, const IPAddress &me, IPAddress &dst, int silent)
192 {
193 #if IPFW_TRANSPARENT
194 struct addrinfo *lookup = NULL;
195
196 dst.GetAddrInfo(lookup,AF_INET);
197
198 /** \par
199 * Try lookup for IPFW interception. */
200 if( getsockname(fd, lookup->ai_addr, &lookup->ai_addrlen) >= 0 ) {
201 if( !silent ) {
202 debugs(89, DBG_IMPORTANT, HERE << " IPFW getsockname(...) failed: " << xstrerror());
203 last_reported = squid_curtime;
204 }
205 }
206 else {
207 dst = *lookup;
208 }
209
210 dst.FreeAddrInfo(lookup);
211
212 if(me != dst) {
213 debugs(89, 5, HERE << "address: " << dst);
214 return 0;
215 }
216
217 debugs(89, 9, HERE << "address: me= " << me << ", dst= " << dst);
218 #endif
219 return -1;
220 }
221
222
223 // TODO split this one call into one per transparency method
224 // with specific switching at run-time ??
225
226 int
227 IPIntercept::NatLookup(int fd, const IPAddress &me, const IPAddress &peer, IPAddress &dst)
228 {
229 #if IPF_TRANSPARENT /* --enable-ipf-transparent */
230 dst = me;
231 if( !me.IsIPv4() ) return -1;
232 if( !peer.IsIPv4() ) return -1;
233
234 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
235
236 struct ipfobj obj;
237 #else
238
239 static int siocgnatl_cmd = SIOCGNATL & 0xff;
240
241 #endif
242
243 struct natlookup natLookup;
244 static int natfd = -1;
245 int x;
246
247 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
248
249 obj.ipfo_rev = IPFILTER_VERSION;
250 obj.ipfo_size = sizeof(natLookup);
251 obj.ipfo_ptr = &natLookup;
252 obj.ipfo_type = IPFOBJ_NATLOOKUP;
253 obj.ipfo_offset = 0;
254 #endif
255
256 natLookup.nl_inport = htons(me.GetPort());
257 natLookup.nl_outport = htons(peer.GetPort());
258 me.GetInAddr(natLookup.nl_inip);
259 peer.GetInAddr(natLookup.nl_outip);
260 natLookup.nl_flags = IPN_TCP;
261
262 if (natfd < 0)
263 {
264 int save_errno;
265 enter_suid();
266 #ifdef IPNAT_NAME
267
268 natfd = open(IPNAT_NAME, O_RDONLY, 0);
269 #else
270
271 natfd = open(IPL_NAT, O_RDONLY, 0);
272 #endif
273
274 save_errno = errno;
275 leave_suid();
276 errno = save_errno;
277 }
278
279 if (natfd < 0)
280 {
281 if (squid_curtime - last_reported > 60) {
282 debugs(89, 1, "clientNatLookup: NAT open failed: " << xstrerror());
283 last_reported = squid_curtime;
284 return -1;
285 }
286 }
287
288 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
289 x = ioctl(natfd, SIOCGNATL, &obj);
290
291 #else
292 /*
293 * IP-Filter changed the type for SIOCGNATL between
294 * 3.3 and 3.4. It also changed the cmd value for
295 * SIOCGNATL, so at least we can detect it. We could
296 * put something in configure and use ifdefs here, but
297 * this seems simpler.
298 */
299 if (63 == siocgnatl_cmd)
300 {
301
302 struct natlookup *nlp = &natLookup;
303 x = ioctl(natfd, SIOCGNATL, &nlp);
304 } else
305 {
306 x = ioctl(natfd, SIOCGNATL, &natLookup);
307 }
308
309 #endif
310 if (x < 0)
311 {
312 if (errno != ESRCH) {
313 if (squid_curtime - last_reported > 60) {
314 debugs(89, 1, "clientNatLookup: NAT lookup failed: ioctl(SIOCGNATL)");
315 last_reported = squid_curtime;
316 }
317
318 close(natfd);
319 natfd = -1;
320 }
321
322 return -1;
323 } else
324 {
325 if (me != natLookup.nl_realip) {
326 dst = natLookup.nl_realip;
327
328 dst.SetPort(ntohs(natLookup.nl_realport));
329 }
330 // else. we already copied it.
331
332 return 0;
333 }
334
335
336
337 #elif LINUX_NETFILTER || IPFW_TRANSPARENT /* --enable-linux-netfilter OR --enable-ipfw-transparent */
338
339 /* Netfilter and IPFW share almost identical lookup methods for their NAT tables.
340 * This allows us to perform a nice clean failover sequence for them.
341 */
342 int silent = (squid_curtime - last_reported > 60 ? 0 : 1);
343
344 dst = me;
345
346 if( !me.IsIPv4() ) return -1;
347 if( !peer.IsIPv4() ) return -1;
348
349 if(intercept_active) {
350 if( NetfilterInterception(fd, me, dst, silent) == 0) return 0;
351 if( IPFWInterception(fd, me, dst, silent) == 0) return 0;
352 }
353 if(transparent_active) {
354 if( NetfilterTransparent(fd, me, dst, silent) == 0) return 0;
355 }
356
357 return -1;
358
359 #elif PF_TRANSPARENT /* --enable-pf-transparent */
360
361 struct pfioc_natlook nl;
362 static int pffd = -1;
363
364 if( !me.IsIPv4() ) return -1;
365 if( !peer.IsIPv4() ) return -1;
366
367 if (pffd < 0)
368 pffd = open("/dev/pf", O_RDWR);
369
370 if (pffd < 0)
371 {
372 if (squid_curtime - last_reported > 60) {
373 debugs(89, 1, "clientNatLookup: PF open failed: " << xstrerror());
374 last_reported = squid_curtime;
375 }
376
377 return -1;
378
379 }
380
381 dst.SetEmpty();
382
383 memset(&nl, 0, sizeof(struct pfioc_natlook));
384 peer.GetInAddr(nl.saddr.v4);
385 nl.sport = htons(peer.GetPort());
386
387 me.GetInAddr(nl.daddr.v4);
388 nl.dport = htons(me.GetPort());
389
390 nl.af = AF_INET;
391 nl.proto = IPPROTO_TCP;
392 nl.direction = PF_OUT;
393
394 if (ioctl(pffd, DIOCNATLOOK, &nl))
395 {
396 if (errno != ENOENT) {
397 if (squid_curtime - last_reported > 60) {
398 debugs(89, 1, "clientNatLookup: PF lookup failed: ioctl(DIOCNATLOOK)");
399 last_reported = squid_curtime;
400 }
401
402 close(pffd);
403 pffd = -1;
404 }
405
406 return -1;
407 } else
408 {
409 int natted = (me != nl.rdaddr.v4);
410 dst = nl.rdaddr.v4;
411 dst.SetPort(ntohs(nl.rdport));
412
413 if (natted)
414 return 0;
415 else
416 return -1;
417 }
418
419
420 #else /* none of the transparent options configured */
421
422 debugs(89, 1, "WARNING: transparent proxying not supported");
423 return -1;
424
425 #endif
426
427 }
428
429 #if LINUX_TPROXY2
430 IPIntercept::SetTproxy2OutgoingAddr(int fd, const IPAddress &src)
431 {
432 IPAddress addr;
433 struct in_tproxy itp;
434
435 src.GetInAddr(itp.v.addr.faddr);
436 itp.v.addr.fport = 0;
437
438 /* If these syscalls fail then we just fallback to connecting
439 * normally by simply ignoring the errors...
440 */
441 itp.op = TPROXY_ASSIGN;
442
443 addr = (struct in_addr)itp.v.addr.faddr;
444 addr.SetPort(itp.v.addr.fport);
445
446 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
447 debugs(20, 1, "tproxy ip=" << addr << " ERROR ASSIGN");
448 return -1;
449 } else {
450 itp.op = TPROXY_FLAGS;
451 itp.v.flags = ITP_CONNECT;
452
453 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
454 debugs(20, 1, "tproxy ip=" << addr << " ERROR CONNECT");
455 return -1;
456 }
457 }
458
459 return 0;
460 }
461 #endif