#include "comm/Connection.h"
#include "ip/Intercept.h"
#include "fde.h"
+#include "src/tools.h"
#if IPF_TRANSPARENT
#include <net/pfvar.h>
#endif /* HAVE_NET_PFVAR_H */
#endif /* PF_TRANSPARENT required headers */
-/* must be before including netfilter_ipv4.h */
+
+#if LINUX_NETFILTER
#if HAVE_LIMITS_H
+/* must be before including netfilter_ipv4.h */
#include <limits.h>
#endif
-#if LINUX_NETFILTER
#include <linux/netfilter_ipv4.h>
-#endif
+#endif /* LINUX_NETFILTER required headers */
// single global instance for access by other components.
Ip::Intercept Ip::Interceptor;
}
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
{
#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.
+ */
+ debugs(89, 5, HERE << "address NAT divert-to: " << newConn);
+ return true;
+
+#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 (TproxyTransparent(newConn, silent)) return true;
}
/* NAT is only available in IPv4 */
bool
Ip::Intercept::ProbeForTproxy(Ip::Address &test)
{
- debugs(3, 3, "Detect TPROXY support on port " << test);
+ bool doneSuid = false;
-#if defined(IP_TRANSPARENT)
+#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;
int tmp_sock = -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) {
}
}
-#else /* undefined IP_TRANSPARENT */
- debugs(3, 3, "setsockopt(IP_TRANSPARENT) not supported on this platform. Disabling TPROXYv4.");
+#else
+ debugs(3, 3, "TPROXY setsockopt() not supported on this platform. Disabling TPROXY.");
+
#endif
+ if (doneSuid)
+ leave_suid();
return false;
}