12#define BUILDING_SQUID_IP_INTERCEPT_CC 1
24#if !defined(IPFILTER_VERSION)
25#define IPFILTER_VERSION 5000004
32#include <sys/ioccom.h>
38#include <netinet/ip6.h>
41#include <netinet/tcp.h>
48#elif HAVE_NETINET_IPL_H
49#include <netinet/ipl.h>
51#if USE_SOLARIS_IPFILTER_MINOR_T_HACK
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>
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>
77#include <sys/socket.h>
81#include <netinet/in.h>
82#if HAVE_NET_PF_PFVAR_H
83#include <net/pf/pfvar.h>
94#include <linux/netfilter_ipv4.h>
95#if HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H
103#include <linux/netfilter_ipv6/ip6_tables.h>
105#if !defined(IP6T_SO_ORIGINAL_DST)
106#define IP6T_SO_ORIGINAL_DST 80
126 struct sockaddr_storage lookup;
132 if ( getsockopt(newConn->
fd,
133 newConn->
local.
isIPv6() ? IPPROTO_IPV6 : IPPROTO_IP,
137 const auto xerrno = errno;
141 newConn->
local = lookup;
142 debugs(89, 5,
"address NAT: " << newConn);
157#if (LINUX_NETFILTER && defined(IP_TRANSPARENT)) || \
158 (PF_TRANSPARENT && defined(SO_BINDANY)) || \
159 (IPFW_TRANSPARENT && defined(IP_BINDANY))
160 transparentActive_ = 1;
162 throw TextException(
"requires TPROXY feature to be enabled by ./configure",
Here());
173#if IPF_TRANSPARENT || LINUX_NETFILTER || IPFW_TRANSPARENT || PF_TRANSPARENT
174 interceptActive_ = 1;
176 throw TextException(
"requires NAT Interception feature to be enabled by ./configure",
Here());
188 debugs(89, 5,
"address NAT: " << newConn);
201 struct natlookup natLookup;
202 static int natfd = -1;
206 memset(&natLookup, 0,
sizeof(natLookup));
209#if HAVE_STRUCT_NATLOOKUP_NL_INIPADDR_IN6
222 debugs(89, warningLevel,
"Your IPF (IPFilter) NAT does not support IPv6. Please upgrade it.");
223 warningLevel = (warningLevel + 1) % 10;
226 newConn->local.getInAddr(natLookup.nl_inip);
227 newConn->remote.getInAddr(natLookup.nl_outip);
229 natLookup.nl_inport = htons(newConn->local.port());
230 natLookup.nl_outport = htons(newConn->remote.port());
232 natLookup.nl_flags = IPN_TCP;
238 natfd = open(IPNAT_NAME, O_RDONLY, 0);
240 natfd = open(IPL_NAT, O_RDONLY, 0);
248 const auto xerrno = errno;
253#if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
255 memset(&obj, 0,
sizeof(obj));
256 obj.ipfo_rev = IPFILTER_VERSION;
257 obj.ipfo_size =
sizeof(natLookup);
258 obj.ipfo_ptr = &natLookup;
259 obj.ipfo_type = IPFOBJ_NATLOOKUP;
261 x = ioctl(natfd, SIOCGNATL, &obj);
270 static int siocgnatl_cmd = SIOCGNATL & 0xff;
271 if (63 == siocgnatl_cmd) {
272 struct natlookup *nlp = &natLookup;
273 x = ioctl(natfd, SIOCGNATL, &nlp);
275 x = ioctl(natfd, SIOCGNATL, &natLookup);
280 const auto xerrno = errno;
281 if (xerrno != ESRCH) {
282 debugs(89,
DBG_IMPORTANT,
"ERROR: IPF (IPFilter) NAT lookup failed: ioctl(SIOCGNATL) (v=" << IPFILTER_VERSION <<
"): " <<
xstrerr(xerrno));
287 debugs(89, 9,
"address: " << newConn);
290#if HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6
291 if (newConn->remote.isIPv6())
292 newConn->local = natLookup.nl_realipaddr.in6;
294 newConn->local = natLookup.nl_realipaddr.in4;
296 newConn->local = natLookup.nl_realip;
298 newConn->local.port(ntohs(natLookup.nl_realport));
299 debugs(89, 5,
"address NAT: " << newConn);
321 debugs(89, 5,
"address NAT divert-to: " << newConn);
326 struct pfioc_natlook nl;
327 static int pffd = -1;
330 pffd = open(
"/dev/pf", O_RDONLY);
333 const auto xerrno = errno;
338 memset(&nl, 0,
sizeof(
struct pfioc_natlook));
353 nl.proto = IPPROTO_TCP;
354 nl.direction = PF_OUT;
356 if (ioctl(pffd, DIOCNATLOOK, &nl)) {
357 const auto xerrno = errno;
358 if (xerrno != ENOENT) {
363 debugs(89, 9,
"address: " << newConn);
367 newConn->
local = nl.rdaddr.v6;
369 newConn->
local = nl.rdaddr.v4;
371 debugs(89, 5,
"address NAT: " << newConn);
384 debugs(89, 5,
"address BEGIN: me/client= " << aConn.
local <<
", destination/me= " << aConn.
remote);
388 return NetfilterInterception(newConn) || IpfwInterception(newConn) ||
389 PfInterception(newConn) || IpfInterception(newConn);
395 bool doneSuid =
false;
397#if _SQUID_LINUX_ && defined(IP_TRANSPARENT)
398# define soLevel SOL_IP
399# define soFlag IP_TRANSPARENT
401#elif defined(SO_BINDANY)
402# define soLevel SOL_SOCKET
403# define soFlag SO_BINDANY
407#elif defined(IP_BINDANY)
408# define soLevel IPPROTO_IP
409# define soFlag IP_BINDANY
415#if defined(soLevel) && defined(soFlag)
417 debugs(3, 3,
"Detect TPROXY support on port " << test);
424 debugs(3, 3,
"...Probing for IPv6 TPROXY support.");
426 struct sockaddr_in6 tmp_ip6;
429 tmp.getSockAddr(tmp_ip6);
431 if ( (tmp_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
432 setsockopt(tmp_sock, soLevel, soFlag, (
char *)&tos,
sizeof(
int)) == 0 &&
433 bind(tmp_sock, (
struct sockaddr*)&tmp_ip6,
sizeof(
struct sockaddr_in6)) == 0 ) {
435 debugs(3, 3,
"IPv6 TPROXY support detected. Using.");
456 debugs(3, 3,
"...Probing for IPv4 TPROXY support.");
458 struct sockaddr_in tmp_ip4;
461 tmp.getSockAddr(tmp_ip4);
463 if ( (tmp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
464 setsockopt(tmp_sock, soLevel, soFlag, (
char *)&tos,
sizeof(
int)) == 0 &&
465 bind(tmp_sock, (
struct sockaddr*)&tmp_ip4,
sizeof(
struct sockaddr_in)) == 0 ) {
467 debugs(3, 3,
"IPv4 TPROXY support detected. Using.");
479 debugs(3, 3,
"TPROXY setsockopt() not supported on this platform. Disabling TPROXY on port " << test);
#define Here()
source code location of the caller
#define IP6T_SO_ORIGINAL_DST
void getSockAddr(struct sockaddr_storage &addr, const int family) const
bool getInAddr(struct in_addr &) const
unsigned short port() const
bool NetfilterInterception(const Comm::ConnectionPointer &newConn)
bool PfInterception(const Comm::ConnectionPointer &newConn)
void StopTransparency(const char *str)
bool IpfwInterception(const Comm::ConnectionPointer &newConn)
bool ProbeForTproxy(Address &test)
bool LookupNat(const Comm::Connection &)
perform NAT lookups for the local address of the given connection
bool IpfInterception(const Comm::ConnectionPointer &newConn)
an std::runtime_error with thrower location info
#define debugs(SECTION, LEVEL, CONTENT)
const char * xstrerr(int error)