/*
- * DEBUG: section 89 NAT / IP Interception
- * AUTHOR: Robert Collins
- * AUTHOR: Amos Jeffries
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
*
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+
+/* DEBUG: section 89 NAT / IP Interception */
+
+// Enable hack to workaround Solaris 10 IPFilter breakage
+#define BUILDING_SQUID_IP_INTERCEPT_CC 1
+
#include "squid.h"
#include "comm/Connection.h"
-#include "ip/Intercept.h"
#include "fde.h"
+#include "ip/Intercept.h"
#include "src/tools.h"
+#include <cerrno>
+
#if IPF_TRANSPARENT
+#if !defined(IPFILTER_VERSION)
+#define IPFILTER_VERSION 5000004
+#endif
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#if HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
+#if HAVE_NETINET_IP6_H
+#include <netinet/ip6.h>
+#endif
#if HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#elif HAVE_NETINET_IPL_H
#include <netinet/ipl.h>
#endif
+#if USE_SOLARIS_IPFILTER_MINOR_T_HACK
+#undef minor_t
+#endif
#if HAVE_IP_FIL_COMPAT_H
#include <ip_fil_compat.h>
#elif HAVE_NETINET_IP_FIL_COMPAT_H
#elif HAVE_NETINET_IP_NAT_H
#include <netinet/ip_nat.h>
#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
#endif /* IPF_TRANSPARENT required headers */
#endif /* PF_TRANSPARENT required headers */
#if LINUX_NETFILTER
-#if HAVE_LIMITS_H
-/* must be before including netfilter_ipv4.h */
-#include <limits.h>
-#endif
+/* <climits> must be before including netfilter_ipv4.h */
+#include <climits>
+#include <linux/if.h>
#include <linux/netfilter_ipv4.h>
+#if HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H
+/* 2013-07-01: Pablo the Netfilter maintainer is rejecting patches
+ * which will enable C++ compilers to build the Netfilter public headers.
+ * We can auto-detect its presence and attempt to use in case he ever
+ * changes his mind or things get cleaned up some other way.
+ * But until then are usually forced to hard-code the getsockopt() code
+ * for IPv6 NAT lookups.
+ */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#endif
+#if !defined(IP6T_SO_ORIGINAL_DST)
+#define IP6T_SO_ORIGINAL_DST 80 // stolen with prejudice from the above file.
+#endif
#endif /* LINUX_NETFILTER required headers */
// single global instance for access by other components.
Ip::Intercept::NetfilterInterception(const Comm::ConnectionPointer &newConn, int silent)
{
#if LINUX_NETFILTER
- struct sockaddr_in lookup;
- socklen_t len = sizeof(struct sockaddr_in);
- newConn->local.GetSockAddr(lookup);
+ struct sockaddr_storage lookup;
+ socklen_t len = newConn->local.isIPv6() ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
+ newConn->local.getSockAddr(lookup, AF_UNSPEC);
/** \par
* Try NAT lookup for REDIRECT or DNAT targets. */
- if ( getsockopt(newConn->fd, IPPROTO_IP, SO_ORIGINAL_DST, &lookup, &len) != 0) {
+ if ( getsockopt(newConn->fd,
+ newConn->local.isIPv6() ? IPPROTO_IPV6 : IPPROTO_IP,
+ newConn->local.isIPv6() ? IP6T_SO_ORIGINAL_DST : SO_ORIGINAL_DST,
+ &lookup,
+ &len) != 0) {
if (!silent) {
- debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed on " << newConn << ": " << xstrerror());
+ int xerrno = errno;
+ debugs(89, DBG_IMPORTANT, "ERROR: NF getsockopt(ORIGINAL_DST) failed on " << newConn << ": " << xstrerr(xerrno));
lastReported_ = squid_curtime;
}
- debugs(89, 9, HERE << "address: " << newConn);
+ debugs(89, 9, "address: " << newConn);
return false;
} else {
newConn->local = lookup;
- debugs(89, 5, HERE << "address NAT: " << newConn);
+ debugs(89, 5, "address NAT: " << newConn);
return true;
}
#endif
}
bool
-Ip::Intercept::TproxyTransparent(const Comm::ConnectionPointer &newConn, int silent)
+Ip::Intercept::TproxyTransparent(const Comm::ConnectionPointer &newConn, int)
{
#if (LINUX_NETFILTER && defined(IP_TRANSPARENT)) || \
(PF_TRANSPARENT && defined(SO_BINDANY)) || \
/* Trust the user configured properly. If not no harm done.
* We will simply attempt a bind outgoing on our own IP.
*/
- newConn->remote.SetPort(0); // allow random outgoing port to prevent address clashes
debugs(89, 5, HERE << "address TPROXY: " << newConn);
return true;
#else
}
bool
-Ip::Intercept::IpfwInterception(const Comm::ConnectionPointer &newConn, int silent)
+Ip::Intercept::IpfwInterception(const Comm::ConnectionPointer &newConn, int)
{
#if IPFW_TRANSPARENT
/* The getsockname() call performed already provided the TCP packet details.
// all fields must be set to 0
memset(&natLookup, 0, sizeof(natLookup));
// for NAT lookup set local and remote IP:port's
- natLookup.nl_inport = htons(newConn->local.GetPort());
- newConn->local.GetInAddr(natLookup.nl_inip);
- natLookup.nl_outport = htons(newConn->remote.GetPort());
- newConn->remote.GetInAddr(natLookup.nl_outip);
+ if (newConn->remote.isIPv6()) {
+#if HAVE_NATLOOKUP_NL_INIPADDR_IN6
+ natLookup.nl_v = 6;
+ newConn->local.getInAddr(natLookup.nl_inipaddr.in6);
+ newConn->remote.getInAddr(natLookup.nl_outipaddr.in6);
+ }
+ else {
+ natLookup.nl_v = 4;
+ newConn->local.getInAddr(natLookup.nl_inipaddr.in4);
+ newConn->remote.getInAddr(natLookup.nl_outipaddr.in4);
+ }
+#else
+ // warn once every 10 at critical level, then push down a level each repeated event
+ static int warningLevel = DBG_CRITICAL;
+ debugs(89, warningLevel, "Your IPF (IPFilter) NAT does not support IPv6. Please upgrade it.");
+ warningLevel = (warningLevel + 1) % 10;
+ return false;
+ }
+ newConn->local.getInAddr(natLookup.nl_inip);
+ newConn->remote.getInAddr(natLookup.nl_outip);
+#endif
+ natLookup.nl_inport = htons(newConn->local.port());
+ natLookup.nl_outport = htons(newConn->remote.port());
// ... and the TCP flag
natLookup.nl_flags = IPN_TCP;
if (natfd < 0) {
if (!silent) {
- debugs(89, DBG_IMPORTANT, "IPF (IPFilter) NAT open failed: " << xstrerror());
+ int xerrno = errno;
+ debugs(89, DBG_IMPORTANT, "IPF (IPFilter) NAT open failed: " << xstrerr(xerrno));
lastReported_ = squid_curtime;
return false;
}
#endif
if (x < 0) {
- if (errno != ESRCH) {
+ int xerrno = errno;
+ if (xerrno != ESRCH) {
if (!silent) {
- debugs(89, DBG_IMPORTANT, "IPF (IPFilter) NAT lookup failed: ioctl(SIOCGNATL) (v=" << IPFILTER_VERSION << "): " << xstrerror());
+ debugs(89, DBG_IMPORTANT, "IPF (IPFilter) NAT lookup failed: ioctl(SIOCGNATL) (v=" << IPFILTER_VERSION << "): " << xstrerr(xerrno));
lastReported_ = squid_curtime;
}
debugs(89, 9, HERE << "address: " << newConn);
return false;
} else {
+#if HAVE_NATLOOKUP_NL_REALIPADDR_IN6
+ if (newConn->remote.isIPv6())
+ newConn->local = natLookup.nl_realipaddr.in6;
+ else
+ newConn->local = natLookup.nl_realipaddr.in4;
+#else
newConn->local = natLookup.nl_realip;
- newConn->local.SetPort(ntohs(natLookup.nl_realport));
+#endif
+ newConn->local.port(ntohs(natLookup.nl_realport));
debugs(89, 5, HERE << "address NAT: " << newConn);
return true;
}
if (pffd < 0) {
if (!silent) {
- debugs(89, DBG_IMPORTANT, HERE << "PF open failed: " << xstrerror());
+ int xerrno = errno;
+ debugs(89, DBG_IMPORTANT, MYNAME << "PF open failed: " << xstrerr(xerrno));
lastReported_ = squid_curtime;
}
return false;
}
memset(&nl, 0, sizeof(struct pfioc_natlook));
- newConn->remote.GetInAddr(nl.saddr.v4);
- nl.sport = htons(newConn->remote.GetPort());
- newConn->local.GetInAddr(nl.daddr.v4);
- nl.dport = htons(newConn->local.GetPort());
+ if (newConn->remote.isIPv6()) {
+ newConn->remote.getInAddr(nl.saddr.v6);
+ newConn->local.getInAddr(nl.daddr.v6);
+ nl.af = AF_INET6;
+ } else {
+ newConn->remote.getInAddr(nl.saddr.v4);
+ newConn->local.getInAddr(nl.daddr.v4);
+ nl.af = AF_INET;
+ }
+
+ nl.sport = htons(newConn->remote.port());
+ nl.dport = htons(newConn->local.port());
- nl.af = AF_INET;
nl.proto = IPPROTO_TCP;
nl.direction = PF_OUT;
if (ioctl(pffd, DIOCNATLOOK, &nl)) {
- if (errno != ENOENT) {
+ int xerrno = errno;
+ if (xerrno != ENOENT) {
if (!silent) {
- debugs(89, DBG_IMPORTANT, HERE << "PF lookup failed: ioctl(DIOCNATLOOK)");
+ debugs(89, DBG_IMPORTANT, HERE << "PF lookup failed: ioctl(DIOCNATLOOK): " << xstrerr(xerrno));
lastReported_ = squid_curtime;
}
close(pffd);
debugs(89, 9, HERE << "address: " << newConn);
return false;
} else {
- newConn->local = nl.rdaddr.v4;
- newConn->local.SetPort(ntohs(nl.rdport));
+ if (newConn->remote.isIPv6())
+ newConn->local = nl.rdaddr.v6;
+ else
+ newConn->local = nl.rdaddr.v4;
+ newConn->local.port(ntohs(nl.rdport));
debugs(89, 5, HERE << "address NAT: " << newConn);
return true;
}
if (TproxyTransparent(newConn, silent)) return true;
}
- /* NAT is only available in IPv4 */
- if ( !newConn->local.IsIPv4() ) return false;
- if ( !newConn->remote.IsIPv4() ) return false;
-
if (interceptActive_ && listenConn->flags&COMM_INTERCEPTION) {
/* NAT methods that use sock-opts to return client address */
if (NetfilterInterception(newConn, silent)) return true;
int tmp_sock = -1;
/* Probe to see if the Kernel TPROXY support is IPv6-enabled */
- if (test.IsIPv6()) {
+ if (test.isIPv6()) {
debugs(3, 3, "...Probing for IPv6 TPROXY support.");
struct sockaddr_in6 tmp_ip6;
Ip::Address tmp = "::2";
- tmp.SetPort(0);
- tmp.GetSockAddr(tmp_ip6);
+ tmp.port(0);
+ tmp.getSockAddr(tmp_ip6);
if ( (tmp_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
setsockopt(tmp_sock, soLevel, soFlag, (char *)&tos, sizeof(int)) == 0 &&
}
}
- if ( test.IsIPv6() && !test.SetIPv4() ) {
+ if ( test.isIPv6() && !test.setIPv4() ) {
debugs(3, DBG_CRITICAL, "TPROXY lacks IPv6 support for " << test );
if (doneSuid)
leave_suid();
}
/* Probe to see if the Kernel TPROXY support is IPv4-enabled (aka present) */
- if (test.IsIPv4()) {
+ if (test.isIPv4()) {
debugs(3, 3, "...Probing for IPv4 TPROXY support.");
struct sockaddr_in tmp_ip4;
Ip::Address tmp = "127.0.0.2";
- tmp.SetPort(0);
- tmp.GetSockAddr(tmp_ip4);
+ tmp.port(0);
+ tmp.getSockAddr(tmp_ip4);
if ( (tmp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
setsockopt(tmp_sock, soLevel, soFlag, (char *)&tos, sizeof(int)) == 0 &&
leave_suid();
return false;
}
+