[unrecognized argument to --enable-pf-transparent: $enableval])
])
#will be AC_DEFINEd later, after checking for appropriate infrastructure
-AC_MSG_NOTICE([PF-based transparent proxying requested: ${enable_pf_transparent:=auto}])
+AC_MSG_NOTICE([PF-based transparent proxying requested: ${enable_pf_transparent:=no}])
+
+dnl Enable /dev/pf support for older PF Transparent Proxy systems (OpenBSD 4.x and older)
+AC_ARG_WITH(nat-devpf,
+ AS_HELP_STRING([--with-nat-devpf],
+ [Enable /dev/pf support for NAT on older OpenBSD and FreeBSD kernels.]), [
+ SQUID_YESNO([$enableval],
+ [unrecognized argument to --with-nat-devpf: $enableval])
+])
+#will be AC_DEFINEd later, after checking for appropriate infrastructure
+AC_MSG_NOTICE([NAT lookups via /dev/pf: ${with_nat_devpf:=no}])
# Linux Netfilter Transparent Proxy
AC_ARG_ENABLE(linux-netfilter,
CXXFLAGS="-DSOLARIS2=$solrev $CXXFLAGS"
fi
-dnl PF support requires a header file.
-if test "x$enable_pf_transparent" != "xno" ; then
+dnl PF /dev/pf support requires a header file.
+if test "x$with_nat_devpf" != "xno" ; then
if test "x$ac_cv_header_net_pfvar_h" = "xyes" -o \
"x$ac_cv_header_net_pf_pfvar_h" = "xyes"; then
- if test "x$enable_pf_transparent" = "xauto" ; then
- enable_pf_transparent="yes"
+ if test "x$with_nat_devpf" = "xauto" ; then
+ with_nat_devpf="no"
fi
else
- if test "x$enable_pf_transparent" = "xyes" ; then
- AC_MSG_ERROR([PF-based transparent proxy requested but needed header not found])
+ if test "x$with_nat_devpf" = "xyes" ; then
+ AC_MSG_ERROR([PF /dev/pf based NAT requested but needed header not found])
fi
- enable_pf_transparent="no"
+ with_nat_devpf="no"
fi
fi
-SQUID_DEFINE_BOOL(PF_TRANSPARENT,$enable_pf_transparent,
+SQUID_DEFINE_BOOL(PF_TRANSPARENT,${enable_pf_transparent:=no},
[Enable support for PF-style transparent proxying])
+SQUID_DEFINE_BOOL(USE_NAT_DEVPF,${with_nat_devpf:=no},
+ [Enable support for /dev/pf NAT lookups])
if test "x$enable_linux_netfilter" != "xno" ; then
if test "x$ac_cv_header_linux_netfilter_ipv4_h" = "xyes"; then
<itemize>
<item>Helper protocol extensions
<item>SSL Server Certificate Validator
+ <item>TPROXY Support for OpenBSD 5.1+ and FreeBSD 9+
</itemize>
Most user-facing changes are reflected in squid.conf (see below).
<em>ssl_crtd</em> related options.
+<sect1>TPROXY Support for OpenBSD 5.1+ and FreeBSD 9+
+<p>Details at <url url="http://wiki.squid-cache.org/ConfigExamples/Intercept/OpenBsdPf">.
+
+<p>The Packet Filter (PF) firewall in OpenBSD 4.4 and later offers traffic interception
+ using several very simple methods. One of which is the <em>divert-to</em> rule type
+ which acts as a simple routing diversion instead of performing NAT packet alterations.
+
+<p>The IP Firewall (IPFW) on FreeBSD 9+ contains a port of the Linux Netfilter TPROXY feature.
+
+<p>This version of Squid adds support for these features through the ./configure
+ options --enable-pf-transparent and --enable-ipfw-transparent when Squid is built on
+ systems with the required support. No special extras are required to enable
+ <em>http_port ... tproxy</em> configuration to work.
+
+<p>NOTE: To resolve NAT lookup issues on recent PF firewall versions the code behind
+ <em>./configure --enable-pf-transparent</em> has been altered and is expected to
+ break on the version of PF firewall shipped with BSD systems such as NetBSD and FreeBSD
+ which do not yet support the getsockname() API.
+ These systems require <em>--with-nat-devpf</em> to enable /dev/pf support when using PF firewall.
+
+
<sect>Changes to squid.conf since Squid-3.3
<p>
There have been changes to Squid's configuration file since Squid-3.3.
<p>New result code <em>BH</em> to signal helper internal errors
<p>Details at <url url="http://wiki.squid-cache.org/Features/AddonHelpers">.
+ <tag>http_port</tag>
+ <p>Support <em>tproxy</em> mode traffic on BSD systems with BINDANY support
+ (OpenBSD 5+, FreeBSD 9+ so far).
+ <p>Changed build options behind <em>intercept</em> traffic mode handling on BSD.
+ see <em>--enable-pf-transparent</em> for more details.
+
<tag>logformat</tag>
<p>New format code <em>%note</em> to log a transaction annotation linked to the
transaction by ICAP, eCAP, a helper, or the <em>note</em> squid.conf directive.
<sect1>New options<label id="newoptions">
<p>
<descrip>
- <p><em>There are no new ./configure options in Squid-3.4.</em>
+ <tag>--with-nat-pf</tag>
+ <p>New option to alter the behaviour of <em>http_port ... intercept</em> option
+ in squid.conf.
+ <p>When this option is used Squid performs the /dev/pf lookups required to
+ support PF <em>rdr-to</em> rules. Otherwise Squid will perform perform the
+ getsockname() API calls to support PF <em>divert-to</em> rules.
+ <p>NOTE: systems such as NetBSD and FreeBSD which do not yet support
+ the getsockname() API in recent PF versions require this option.
</descrip>
<sect1>Changes to existing options<label id="modifiedoptions">
<p>
<descrip>
- <p><em>There are no changed ./configure options in Squid-3.4.</em>
+ <tag>--enable-pf-transparent</tag>
+ <p>NAT table support updated to use the getsockname() API provided by the
+ latest PF versions <em>divert-to</em>. This allows <em>http_port</em>
+ in squid.conf to support both <em>intercept</em> and <em>tproxy</em> traffic
+ and to silence NAT lookup failure messages on recent BSD.
+ <p>NOTE: systems such as NetBSD and FreeBSD which do not yet support
+ the getsockname() API in recent PF versions require <em>--with-nat-devpf</em>
+ to re-enable /dev/pf support when using PF firewall.
</descrip>
</p>
}
/**
- * Set the socket IP_TRANSPARENT option for Linux TPROXY v4 support,
- * or set the socket SO_BINDANY option for BSD divert-to support.
+ * Set the socket option required for TPROXY spoofing for:
+ * - Linux TPROXY v4 support,
+ * - OpenBSD divert-to support,
+ * - FreeBSD IPFW TPROXY v4 support.
*/
void
comm_set_transparent(int fd)
{
-#if _SQUID_LINUX_ && defined(IP_TRANSPARENT)
- int tos = 1;
- if (setsockopt(fd, SOL_IP, IP_TRANSPARENT, (char *) &tos, sizeof(int)) < 0) {
- debugs(50, DBG_IMPORTANT, "comm_open: setsockopt(IP_TRANSPARENT) on FD " << fd << ": " << xstrerror());
- } else {
- /* mark the socket as having transparent options */
- fd_table[fd].flags.transparent = true;
- }
+ bool doneSuid = false;
+#if _SQUID_LINUX_ && defined(IP_TRANSPARENT) // Linux
+# define soLevel SOL_IP
+# define soFlag IP_TRANSPARENT
-#elif defined(SO_BINDANY)
- int tos = 1;
+#elif defined(SO_BINDANY) // OpenBSD 4.7+ and NetBSD with PF
+# define soLevel SOL_SOCKET
+# define soFlag SO_BINDANY
+ enter_suid();
+ doneSuid = true;
+
+#elif defined(IP_BINDANY) // FreeBSD with IPFW
+# define soLevel IPPROTO_IP
+# define soFlag IP_BINDANY
enter_suid();
- if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, (char *) &tos, sizeof(int)) < 0) {
- debugs(50, DBG_IMPORTANT, "comm_open: setsockopt(SO_BINDANY) on FD " << fd << ": " << xstrerror());
+ doneSuid = true;
+
+#else
+ debugs(50, DBG_CRITICAL, "WARNING: comm_open: setsockopt(TPROXY) not supported on this platform");
+#endif /* sockopt */
+
+#if defined(soLevel) && defined(soFlag)
+ int tos = 1;
+ if (setsockopt(fd, soLevel, soFlag, (char *) &tos, sizeof(int)) < 0) {
+ debugs(50, DBG_IMPORTANT, "comm_open: setsockopt(TPROXY) on FD " << fd << ": " << xstrerror());
} else {
/* mark the socket as having transparent options */
fd_table[fd].flags.transparent = true;
}
- leave_suid();
-
-#else
- debugs(50, DBG_CRITICAL, "WARNING: comm_open: setsockopt(IP_TRANSPARENT) not supported on this platform");
-#endif /* sockopt */
+ if (doneSuid)
+ leave_suid();
+#endif
}
/**
}
bool
-Ip::Intercept::NetfilterTransparent(const Comm::ConnectionPointer &newConn, int silent)
+Ip::Intercept::TproxyTransparent(const Comm::ConnectionPointer &newConn, int silent)
{
-#if LINUX_NETFILTER
+#if (LINUX_NETFILTER && defined(IP_TRANSPARENT)) || \
+ (PF_TRANSPARENT && defined(SO_BINDANY)) || \
+ (IPFW_TRANSPARENT && defined(IP_BINDANY))
+
/* Trust the user configured properly. If not no harm done.
* We will simply attempt a bind outgoing on our own IP.
*/
Ip::Intercept::IpfwInterception(const Comm::ConnectionPointer &newConn, int silent)
{
#if IPFW_TRANSPARENT
- struct sockaddr_storage lookup;
- socklen_t len = sizeof(struct sockaddr_storage);
- newConn->local.GetSockAddr(lookup, AF_INET);
-
- /** \par
- * Try lookup for IPFW interception. */
- if ( getsockname(newConn->fd, (struct sockaddr*)&lookup, &len) != 0 ) {
- if ( !silent ) {
- debugs(89, DBG_IMPORTANT, HERE << " IPFW getsockname(...) failed: " << xstrerror());
- lastReported_ = squid_curtime;
- }
- debugs(89, 9, HERE << "address: " << newConn);
- return false;
- } else {
- newConn->local = lookup;
- debugs(89, 5, HERE << "address NAT: " << newConn);
- return true;
- }
-#endif
+ /* The getsockname() call performed already provided the TCP packet details.
+ * There is no way to identify whether they came from NAT or not.
+ * Trust the user configured properly.
+ */
+ debugs(89, 5, HERE << "address NAT: " << newConn);
+ return true;
+#else
return false;
+#endif
}
bool
}
bool
-Ip::Intercept::PfTransparent(const Comm::ConnectionPointer &newConn, int silent)
+Ip::Intercept::PfInterception(const Comm::ConnectionPointer &newConn, int silent)
{
-#if 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.
+#if PF_TRANSPARENT /* --enable-pf-transparent */
+
+#if !USE_NAT_DEVPF
+ /* On recent PF versions the getsockname() call performed already provided
+ * the required TCP packet details.
+ * There is no way to identify whether they came from NAT or not.
+ *
+ * Trust the user configured properly.
*/
- newConn->remote.SetPort(0); // allow random outgoing port to prevent address clashes
- debugs(89, 5, HERE << "address DIVERT: " << newConn);
+ debugs(89, 5, HERE << "address NAT divert-to: " << newConn);
return true;
-#else
- return false;
-#endif
-}
-bool
-Ip::Intercept::PfInterception(const Comm::ConnectionPointer &newConn, int silent)
-{
-#if PF_TRANSPARENT /* --enable-pf-transparent */
+#else /* USE_NAT_DEVPF / --with-nat-devpf */
struct pfioc_natlook nl;
static int pffd = -1;
debugs(89, 5, HERE << "address NAT: " << newConn);
return true;
}
-
+#endif /* --with-nat-devpf */
#endif /* --enable-pf-transparent */
return false;
}
/* NP: try TPROXY first, its much quieter than NAT when non-matching */
if (transparentActive_ && listenConn->flags&COMM_TRANSPARENT) {
- if (NetfilterTransparent(newConn, silent)) return true;
- if (PfTransparent(newConn, silent)) return true;
+ if (TproxyTransparent(newConn, silent)) return true;
}
/* NAT is only available in IPv4 */
bool
Ip::Intercept::ProbeForTproxy(Ip::Address &test)
{
-#if defined(IP_TRANSPARENT)
+ bool doneSuid = false;
+
+#if _SQUID_LINUX_ && defined(IP_TRANSPARENT) // Linux
+# define soLevel SOL_IP
+# define soFlag IP_TRANSPARENT
+
+#elif defined(SO_BINDANY) // OpenBSD 4.7+ and NetBSD with PF
+# define soLevel SOL_SOCKET
+# define soFlag SO_BINDANY
+ enter_suid();
+ doneSuid = true;
+
+#elif defined(IP_BINDANY) // FreeBSD with IPFW
+# define soLevel IPPROTO_IP
+# define soFlag IP_BINDANY
+ enter_suid();
+ doneSuid = true;
+
+#endif
+
+#if defined(soLevel) && defined(soFlag)
+
debugs(3, 3, "Detect TPROXY support on port " << test);
int tos = 1;
tmp.GetSockAddr(tmp_ip6);
if ( (tmp_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
- setsockopt(tmp_sock, SOL_IP, IP_TRANSPARENT, (char *)&tos, sizeof(int)) == 0 &&
+ setsockopt(tmp_sock, soLevel, soFlag, (char *)&tos, sizeof(int)) == 0 &&
bind(tmp_sock, (struct sockaddr*)&tmp_ip6, sizeof(struct sockaddr_in6)) == 0 ) {
debugs(3, 3, "IPv6 TPROXY support detected. Using.");
close(tmp_sock);
+ if (doneSuid)
+ leave_suid();
return true;
}
if (tmp_sock >= 0) {
if ( test.IsIPv6() && !test.SetIPv4() ) {
debugs(3, DBG_CRITICAL, "TPROXY lacks IPv6 support for " << test );
+ if (doneSuid)
+ leave_suid();
return false;
}
tmp.GetSockAddr(tmp_ip4);
if ( (tmp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) >= 0 &&
- setsockopt(tmp_sock, SOL_IP, IP_TRANSPARENT, (char *)&tos, sizeof(int)) == 0 &&
+ setsockopt(tmp_sock, soLevel, soFlag, (char *)&tos, sizeof(int)) == 0 &&
bind(tmp_sock, (struct sockaddr*)&tmp_ip4, sizeof(struct sockaddr_in)) == 0 ) {
debugs(3, 3, "IPv4 TPROXY support detected. Using.");
close(tmp_sock);
+ if (doneSuid)
+ leave_suid();
return true;
}
if (tmp_sock >= 0) {
}
}
-#elif defined(SO_BINDANY)
- debugs(3, 3, "Detect BINDANY support on port " << test);
-
- int tos = 1;
- int tmp_sock = -1;
-
- if (test.IsIPv6()) {
- debugs(3, 3, "...Probing for IPv6 SO_BINDANY support.");
-
- struct sockaddr_in6 tmp_ip6;
- Ip::Address tmp = "::2";
- tmp.SetPort(0);
- tmp.GetSockAddr(tmp_ip6);
-
- if ((tmp_sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) >=0 &&
- (setsockopt(tmp_sock, SOL_SOCKET, SO_BINDANY, (char *)&tos,
- sizeof(tos)) == 0) &&
- (bind(tmp_sock, (struct sockaddr*)&tmp_ip6, sizeof(struct sockaddr_in6)) == 0)) {
- debugs(3, 3, "IPv6 BINDANY support detected. Using.");
- close(tmp_sock);
- return true;
- }
- }
-
- if (test.IsIPv4()) {
- debugs(3, 3, "...Probing for IPv4 SO_BINDANY support.");
-
- struct sockaddr_in tmp_ip4;
- Ip::Address tmp = "127.0.0.2";
- tmp.SetPort(0);
- tmp.GetSockAddr(tmp_ip4);
-
- if ((tmp_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) >=0 &&
- (setsockopt(tmp_sock, SOL_SOCKET, SO_BINDANY, (char *)&tos,
- sizeof(tos)) == 0) &&
- (bind(tmp_sock, (struct sockaddr*)&tmp_ip4, sizeof(struct sockaddr_in)) == 0)) {
- debugs(3, 3, "IPv4 BINDANY support detected. Using.");
- close(tmp_sock);
- return true;
- }
- }
-
#else
debugs(3, 3, "TPROXY setsockopt() not supported on this platform. Disabling TPROXY.");
#endif
+ if (doneSuid)
+ leave_suid();
return false;
}
private:
/**
- * perform Lookups on Netfilter interception targets (REDIRECT, DNAT).
+ * perform Lookups on fully-transparent interception targets (TPROXY).
+ * Supports Netfilter, PF and IPFW.
*
* \param silent 0 if errors are to be displayed. 1 if errors are to be hidden.
* \param newConn Details known, to be updated where relevant.
* \return Whether successfuly located the new address.
*/
- bool NetfilterInterception(const Comm::ConnectionPointer &newConn, int silent);
+ bool TproxyTransparent(const Comm::ConnectionPointer &newConn, int silent);
/**
- * perform Lookups on Netfilter fully-transparent interception targets (TPROXY).
+ * perform Lookups on Netfilter interception targets (REDIRECT, DNAT).
*
* \param silent 0 if errors are to be displayed. 1 if errors are to be hidden.
* \param newConn Details known, to be updated where relevant.
* \return Whether successfuly located the new address.
*/
- bool NetfilterTransparent(const Comm::ConnectionPointer &newConn, int silent);
+ bool NetfilterInterception(const Comm::ConnectionPointer &newConn, int silent);
/**
* perform Lookups on IPFW interception.
*/
bool PfInterception(const Comm::ConnectionPointer &newConn, int silent);
- /**
- * perform Lookups on PF fully-transparent interception target (DIVERT).
- *
- * \param silent 0 if errors are to be displayed. 1 if errors are to be hidden.
- * \param newConn Details known, to be updated where relevant.
- * \return Whether successfuly located the new address.
- */
- bool PfTransparent(const Comm::ConnectionPointer &newConn, int silent);
-
int transparentActive_;
int interceptActive_;
time_t lastReported_; /**< Time of last error report. Throttles NAT error display to 1 per minute */