------------------------------------------------------------ revno: 12524 revision-id: squid3@treenet.co.nz-20130402040735-33nh1s6y98cu2519 parent: squid3@treenet.co.nz-20130402040431-dtmvsr1i7kjkhbl9 author: Marios Makassikis committer: Amos Jeffries branch nick: 3.3 timestamp: Mon 2013-04-01 22:07:35 -0600 message: Add TPROXY support for OpenBSD, FreeBSD, NetBSD 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 ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: squid3@treenet.co.nz-20130402040735-33nh1s6y98cu2519 # target_branch: http://bzr.squid-cache.org/bzr/squid3/3.3 # testament_sha1: 50e4473899d37293080c4a0936b0496ec462dc2f # timestamp: 2013-04-02 04:10:54 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/3.3 # base_revision_id: squid3@treenet.co.nz-20130402040431-\ # dtmvsr1i7kjkhbl9 # # Begin patch === modified file 'src/comm.cc' --- src/comm.cc 2012-11-18 11:21:41 +0000 +++ src/comm.cc 2013-04-02 04:07:35 +0000 @@ -34,6 +34,7 @@ #include "squid.h" #include "base/AsyncCall.h" +#include "cbdata.h" #include "comm.h" #include "ClientInfo.h" #include "CommCalls.h" @@ -63,11 +64,12 @@ #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 #endif @@ -493,12 +495,13 @@ } /** - * 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 @@ /* 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 */ === modified file 'src/ip/Intercept.cc' --- src/ip/Intercept.cc 2012-08-16 07:11:05 +0000 +++ src/ip/Intercept.cc 2013-04-02 04:07:35 +0000 @@ -277,6 +277,21 @@ } 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) { #if PF_TRANSPARENT /* --enable-pf-transparent */ @@ -352,6 +367,7 @@ /* 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,10 +394,9 @@ bool Ip::Intercept::ProbeForTproxy(Ip::Address &test) { +#if defined(IP_TRANSPARENT) debugs(3, 3, "Detect TPROXY support on port " << test); -#if defined(IP_TRANSPARENT) - int tos = 1; int tmp_sock = -1; @@ -435,8 +450,51 @@ } } -#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; } === modified file 'src/ip/Intercept.h' --- src/ip/Intercept.h 2012-08-28 13:00:30 +0000 +++ src/ip/Intercept.h 2013-04-02 04:07:35 +0000 @@ -124,7 +124,7 @@ 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 @@ */ 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 */