]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add TPROXY support for OpenBSD
authorMarios Makassikis <mmakassikis@gmail.com>
Fri, 29 Mar 2013 05:48:19 +0000 (23:48 -0600)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 29 Mar 2013 05:48:19 +0000 (23:48 -0600)
This adds support for the OpenBSD 'divert' target in PF 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 teh TPROXY behaviour.

To enable these features Squid is configured the same as Linux TPROXY:
  http_port 1234 tproxy

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

index 05eaadac5c9261b4cb9d494469b0350c16921580..e0f5528487706f05744b4fb9df2289d8e13944d5 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
@@ -498,7 +500,7 @@ comm_set_v6only(int fd, int tos)
 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 +508,18 @@ comm_set_transparent(int fd)
         /* mark the socket as having transparent options */
         fd_table[fd].flags.transparent = true;
     }
+
+#elif _SQUID_OPENBSD_ && 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..3d6517cbda3ce9e75bcdbc642d79e0f12d1811cf 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,55 @@ 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);
+
+        enter_suid();
+        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)) {
+            leave_suid();
+            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);
+
+        enter_suid();
+        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)) {
+            leave_suid();
+            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 */