Go to the documentation of this file.
12 #define BUILDING_SQUID_IP_INTERCEPT_CC 1
27 #if !defined(IPFILTER_VERSION)
28 #define IPFILTER_VERSION 5000004
32 #include <sys/param.h>
35 #include <sys/ioccom.h>
38 #include <sys/ioctl.h>
40 #if HAVE_NETINET_IP6_H
41 #include <netinet/ip6.h>
43 #if HAVE_NETINET_TCP_H
44 #include <netinet/tcp.h>
51 #elif HAVE_NETINET_IPL_H
52 #include <netinet/ipl.h>
54 #if USE_SOLARIS_IPFILTER_MINOR_T_HACK
57 #if HAVE_IP_FIL_COMPAT_H
58 #include <ip_fil_compat.h>
59 #elif HAVE_NETINET_IP_FIL_COMPAT_H
60 #include <netinet/ip_fil_compat.h>
61 #elif HAVE_IP_COMPAT_H
62 #include <ip_compat.h>
63 #elif HAVE_NETINET_IP_COMPAT_H
64 #include <netinet/ip_compat.h>
68 #elif HAVE_NETINET_IP_FIL_H
69 #include <netinet/ip_fil.h>
73 #elif HAVE_NETINET_IP_NAT_H
74 #include <netinet/ip_nat.h>
80 #include <sys/ioctl.h>
81 #include <sys/fcntl.h>
83 #include <netinet/in.h>
84 #if HAVE_NET_PF_PFVAR_H
85 #include <net/pf/pfvar.h>
88 #include <net/pfvar.h>
96 #include <linux/netfilter_ipv4.h>
97 #if HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H
105 #include <linux/netfilter_ipv6/ip6_tables.h>
107 #if !defined(IP6T_SO_ORIGINAL_DST)
108 #define IP6T_SO_ORIGINAL_DST 80 // stolen with prejudice from the above file.
128 struct sockaddr_storage lookup;
135 newConn->
local.
isIPv6() ? IPPROTO_IPV6 : IPPROTO_IP,
139 const auto xerrno = errno;
143 newConn->
local = lookup;
144 debugs(89, 5,
"address NAT: " << newConn);
159 #if (LINUX_NETFILTER && defined(IP_TRANSPARENT)) || \
160 (PF_TRANSPARENT && defined(SO_BINDANY)) || \
161 (IPFW_TRANSPARENT && defined(IP_BINDANY))
162 transparentActive_ = 1;
164 throw TextException(
"requires TPROXY feature to be enabled by ./configure",
Here());
175 #if IPF_TRANSPARENT || LINUX_NETFILTER || IPFW_TRANSPARENT || PF_TRANSPARENT
176 interceptActive_ = 1;
178 throw TextException(
"requires NAT Interception feature to be enabled by ./configure",
Here());
186 return UseInterceptionAddressesLookedUpEarlier(__FUNCTION__, newConn);
200 #if LINUX_NETFILTER && PF_TRANSPARENT && !USE_NAT_DEVPF
201 static_assert(!
"--enable-linux-netfilter is incompatible with --enable-pf-transparent --without-nat-devpf");
203 #if LINUX_NETFILTER && IPFW_TRANSPARENT
204 static_assert(!
"--enable-linux-netfilter is incompatible with --enable-ipfw-transparent");
208 debugs(89, 5, caller <<
" uses " << newConn);
217 struct natlookup natLookup;
218 static int natfd = -1;
222 memset(&natLookup, 0,
sizeof(natLookup));
225 #if HAVE_STRUCT_NATLOOKUP_NL_INIPADDR_IN6
238 debugs(89, warningLevel,
"Your IPF (IPFilter) NAT does not support IPv6. Please upgrade it.");
239 warningLevel = (warningLevel + 1) % 10;
242 newConn->local.getInAddr(natLookup.nl_inip);
243 newConn->remote.getInAddr(natLookup.nl_outip);
245 natLookup.nl_inport = htons(newConn->local.port());
246 natLookup.nl_outport = htons(newConn->remote.port());
248 natLookup.nl_flags = IPN_TCP;
254 natfd =
xopen(IPNAT_NAME, O_RDONLY, 0);
256 natfd =
xopen(IPL_NAT, O_RDONLY, 0);
264 const auto xerrno = errno;
269 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
271 memset(&obj, 0,
sizeof(obj));
272 obj.ipfo_rev = IPFILTER_VERSION;
273 obj.ipfo_size =
sizeof(natLookup);
274 obj.ipfo_ptr = &natLookup;
275 obj.ipfo_type = IPFOBJ_NATLOOKUP;
277 x = ioctl(natfd, SIOCGNATL, &obj);
286 static int siocgnatl_cmd = SIOCGNATL & 0xff;
287 if (63 == siocgnatl_cmd) {
288 struct natlookup *nlp = &natLookup;
289 x = ioctl(natfd, SIOCGNATL, &nlp);
291 x = ioctl(natfd, SIOCGNATL, &natLookup);
296 const auto xerrno = errno;
297 if (xerrno != ESRCH) {
298 debugs(89,
DBG_IMPORTANT,
"ERROR: IPF (IPFilter) NAT lookup failed: ioctl(SIOCGNATL) (v=" << IPFILTER_VERSION <<
"): " <<
xstrerr(xerrno));
303 debugs(89, 9,
"address: " << newConn);
306 #if HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6
307 if (newConn->remote.isIPv6())
308 newConn->local = natLookup.nl_realipaddr.in6;
310 newConn->local = natLookup.nl_realipaddr.in4;
312 newConn->local = natLookup.nl_realip;
314 newConn->local.port(ntohs(natLookup.nl_realport));
315 debugs(89, 5,
"address NAT: " << newConn);
331 return UseInterceptionAddressesLookedUpEarlier(
"recent PF version", newConn);
335 struct pfioc_natlook nl;
336 static int pffd = -1;
339 pffd =
xopen(
"/dev/pf", O_RDONLY);
342 const auto xerrno = errno;
347 memset(&nl, 0,
sizeof(
struct pfioc_natlook));
362 nl.proto = IPPROTO_TCP;
363 nl.direction = PF_OUT;
365 if (ioctl(pffd, DIOCNATLOOK, &nl)) {
366 const auto xerrno = errno;
367 if (xerrno != ENOENT) {
372 debugs(89, 9,
"address: " << newConn);
376 newConn->
local = nl.rdaddr.v6;
378 newConn->
local = nl.rdaddr.v4;
380 debugs(89, 5,
"address NAT: " << newConn);
393 debugs(89, 5,
"address BEGIN: me/client= " << aConn.
local <<
", destination/me= " << aConn.
remote);
397 return NetfilterInterception(newConn) || IpfwInterception(newConn) ||
398 PfInterception(newConn) || IpfInterception(newConn);
404 bool doneSuid =
false;
406 #if _SQUID_LINUX_ && defined(IP_TRANSPARENT) // Linux
407 # define soLevel SOL_IP
408 # define soFlag IP_TRANSPARENT
410 #elif defined(SO_BINDANY) // OpenBSD 4.7+ and NetBSD with PF
411 # define soLevel SOL_SOCKET
412 # define soFlag SO_BINDANY
416 #elif defined(IP_BINDANY) // FreeBSD with IPFW
417 # define soLevel IPPROTO_IP
418 # define soFlag IP_BINDANY
424 #if defined(soLevel) && defined(soFlag)
426 debugs(3, 3,
"Detect TPROXY support on port " << test);
429 debugs(3,
DBG_CRITICAL,
"Cannot use TPROXY for " << test <<
" because IPv6 support is disabled");
440 debugs(3, 3,
"...Probing for IPv6 TPROXY support.");
442 struct sockaddr_in6 tmp_ip6;
445 tmp.getSockAddr(tmp_ip6);
447 if ( (tmp_sock =
xsocket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
448 xsetsockopt(tmp_sock, soLevel, soFlag, &tos,
sizeof(
int)) == 0 &&
449 xbind(tmp_sock, (
struct sockaddr*)&tmp_ip6,
sizeof(
struct sockaddr_in6)) == 0 ) {
451 debugs(3, 3,
"IPv6 TPROXY support detected. Using.");
472 debugs(3, 3,
"...Probing for IPv4 TPROXY support.");
474 struct sockaddr_in tmp_ip4;
477 tmp.getSockAddr(tmp_ip4);
479 if ( (tmp_sock =
xsocket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
480 xsetsockopt(tmp_sock, soLevel, soFlag, &tos,
sizeof(
int)) == 0 &&
481 xbind(tmp_sock, (
struct sockaddr*)&tmp_ip4,
sizeof(
struct sockaddr_in)) == 0 ) {
483 debugs(3, 3,
"IPv4 TPROXY support detected. Using.");
495 debugs(3, 3,
"TPROXY setsockopt() not supported on this platform. Disabling TPROXY on port " << test);
const char * xstrerr(int error)
#define Here()
source code location of the caller
void StopTransparency(const char *str)
bool NetfilterInterception(const Comm::ConnectionPointer &newConn)
bool getInAddr(struct in_addr &) const
bool IpfwInterception(const Comm::ConnectionPointer &newConn)
bool UseInterceptionAddressesLookedUpEarlier(const char *, const Comm::ConnectionPointer &)
unsigned short port() const
int xsetsockopt(int socketFd, int level, int option, const void *value, socklen_t valueLength)
POSIX setsockopt(2) equivalent.
bool IpfInterception(const Comm::ConnectionPointer &newConn)
int xgetsockopt(int socketFd, int level, int optionName, void *optionValue, socklen_t *optionLength)
POSIX getsockopt(2) equivalent.
bool ProbeForTproxy(Address &test)
int xsocket(int domain, int type, int protocol)
POSIX socket(2) equivalent.
an std::runtime_error with thrower location info
bool LookupNat(const Comm::Connection &)
#define IP6T_SO_ORIGINAL_DST
int xbind(int socketFd, const struct sockaddr *sa, socklen_t saLength)
POSIX bind(2) equivalent.
int xopen(const char *filename, int oflag, int pmode=0)
POSIX open(2) equivalent.
bool PfInterception(const Comm::ConnectionPointer &newConn)
int xclose(int fd)
POSIX close(2) equivalent.
int EnableIpv6
Whether IPv6 is supported and type of support.
void getSockAddr(struct sockaddr_storage &addr, const int family) const
#define debugs(SECTION, LEVEL, CONTENT)