]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add TPROXY support for OpenBSD, FreeBSD, NetBSD
authorMarios Makassikis <mmakassikis@gmail.com>
Tue, 2 Apr 2013 04:07:35 +0000 (22:07 -0600)
committerAmos Jeffries <squid3@treenet.co.nz>
Tue, 2 Apr 2013 04:07:35 +0000 (22:07 -0600)
This adds support for the PF 'divert-to' target which presents the
client and remote IPs directly to Squid in accept() parameters the
way Linux TPROXY target does.

It also adds support for the SO_BINDANY option on outgoing traffic for
client IP address spoofing which completes the TPROXY behaviour.

To enable these features Squid built with --enable-pf-transparent can
be configured with:
  http_port 1234 tproxy

src/comm.cc
src/ip/Intercept.cc
src/ip/Intercept.h

index 8020e0c193243fb4d0786acb6da88c77cf4ca4f2..3f107c420398a8700cae0d4a6f998a6bcd3eaa80 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "squid.h"
 #include "base/AsyncCall.h"
+#include "cbdata.h"
 #include "comm.h"
 #include "ClientInfo.h"
 #include "CommCalls.h"
 #include "SquidTime.h"
 #include "StatCounters.h"
 #include "StoreIOBuffer.h"
+#include "tools.h"
+
 #if USE_SSL
 #include "ssl/support.h"
 #endif
 
-#include "cbdata.h"
 #if _SQUID_CYGWIN_
 #include <sys/ioctl.h>
 #endif
@@ -493,12 +495,13 @@ comm_set_v6only(int fd, int tos)
 }
 
 /**
- * Set the socket IP_TRANSPARENT option for Linux TPROXY v4 support.
+ * Set the socket IP_TRANSPARENT option for Linux TPROXY v4 support,
+ * or set the socket SO_BINDANY option for BSD divert-to support.
  */
 void
 comm_set_transparent(int fd)
 {
-#if defined(IP_TRANSPARENT)
+#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());
@@ -506,6 +509,18 @@ comm_set_transparent(int fd)
         /* mark the socket as having transparent options */
         fd_table[fd].flags.transparent = 1;
     }
+
+#elif defined(SO_BINDANY)
+    int tos = 1;
+    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());
+    } 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 */
index b00ff27596b4d24c75a6b07ca363c006a1e2847e..182b1f6c61150e9b120564f43a41bed481f86b54 100644 (file)
@@ -276,6 +276,21 @@ Ip::Intercept::IpfInterception(const Comm::ConnectionPointer &newConn, int silen
     return false;
 }
 
+bool
+Ip::Intercept::PfTransparent(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.
+     */
+    newConn->remote.SetPort(0); // allow random outgoing port to prevent address clashes
+    debugs(89, 5, HERE << "address DIVERT: " << newConn);
+    return true;
+#else
+    return false;
+#endif
+}
+
 bool
 Ip::Intercept::PfInterception(const Comm::ConnectionPointer &newConn, int silent)
 {
@@ -352,6 +367,7 @@ Ip::Intercept::Lookup(const Comm::ConnectionPointer &newConn, const Comm::Connec
     /* 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;
     }
 
     /* NAT is only available in IPv4 */
@@ -378,9 +394,8 @@ Ip::Intercept::Lookup(const Comm::ConnectionPointer &newConn, const Comm::Connec
 bool
 Ip::Intercept::ProbeForTproxy(Ip::Address &test)
 {
-    debugs(3, 3, "Detect TPROXY support on port " << test);
-
 #if defined(IP_TRANSPARENT)
+    debugs(3, 3, "Detect TPROXY support on port " << test);
 
     int tos = 1;
     int tmp_sock = -1;
@@ -435,8 +450,51 @@ Ip::Intercept::ProbeForTproxy(Ip::Address &test)
         }
     }
 
-#else /* undefined IP_TRANSPARENT */
-    debugs(3, 3, "setsockopt(IP_TRANSPARENT) not supported on this platform. Disabling TPROXYv4.");
+#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
     return false;
 }
index 6f8444f11cddc56be8d6347d4cfdcafc9917354f..f65d8ba8da85554208c0f30293441eeef381e72c 100644 (file)
@@ -124,7 +124,7 @@ private:
     bool IpfInterception(const Comm::ConnectionPointer &newConn, int silent);
 
     /**
-     * perform Lookups on PF interception.
+     * perform Lookups on PF interception target (REDIRECT).
      *
      * \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.
@@ -132,6 +132,15 @@ private:
      */
     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 */