]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Bug #1630: TPROXY needs porting from 2.6 to 3.x
authorserassio <>
Sun, 15 Apr 2007 20:46:11 +0000 (20:46 +0000)
committerserassio <>
Sun, 15 Apr 2007 20:46:11 +0000 (20:46 +0000)
This patch adds Linux TPROXY support to Squid 3 forward
porting 2.6 code. Based on Steven Swilton work.

The changes are verified to not break non-TPROXY use, while the
TPROXY code is still untested.
If there will be issues in the optional TPROXY code we deal with
them as bugs later on.

16 files changed:
configure.in
include/autoconf.h.in
src/ICAP/ICAPXaction.cc
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/forward.cc
src/forward.h
src/globals.h
src/http.cc
src/main.cc
src/pconn.cc
src/pconn.h
src/protos.h
src/structs.h
src/tools.cc

index 899e960a34f63879336eae8fd6bde651bacc9f52..7169b597892b329395d4393dc91ccd3c450d894a 100644 (file)
@@ -1,7 +1,7 @@
 
 dnl  Configuration input file for Squid
 dnl
-dnl  $Id: configure.in,v 1.449 2007/04/12 23:51:55 wessels Exp $
+dnl  $Id: configure.in,v 1.450 2007/04/15 14:46:11 serassio Exp $
 dnl
 dnl
 dnl
@@ -11,7 +11,7 @@ AM_CONFIG_HEADER(include/autoconf.h)
 AC_CONFIG_AUX_DIR(cfgaux)
 AC_CONFIG_SRCDIR([src/main.cc])
 AM_INIT_AUTOMAKE([tar-ustar])
-AC_REVISION($Revision: 1.449 $)dnl
+AC_REVISION($Revision: 1.450 $)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
 AM_MAINTAINER_MODE
 
@@ -1274,6 +1274,21 @@ dnl "-64" from LDFLAGS
        esac
 fi
 
+dnl Enable Linux transparent proxy support
+AC_ARG_ENABLE(linux-tproxy,
+[  --enable-linux-tproxy
+                         Enable real Transparent Proxy support for Netfilter TPROXY.],
+[ if test "$enableval" = "yes" ; then
+       echo "Linux Netfilter/TPROXY enabled"
+       AC_DEFINE(LINUX_TPROXY, 1, [Enable real Transparent Proxy support for Netfilter TPROXY.])
+       LINUX_TPROXY="yes"
+        if test -z "$LINUX_NETFILTER"; then
+           echo "Linux-Netfilter Transparent Proxy automatically enabled"
+           LINUX_NETFILTER="yes"
+       fi
+  fi
+])
+
 AM_CONDITIONAL(MAKE_LEAKFINDER, false)
 dnl Enable Leak Finding Functions
 AC_ARG_ENABLE(leakfinder,
@@ -1951,7 +1966,10 @@ AC_CHECK_HEADERS( \
        db_185.h
 )
 
-AC_CHECK_HEADERS(linux/netfilter_ipv4.h,,,
+AC_CHECK_HEADERS(
+       linux/netfilter_ipv4.h \
+       linux/netfilter_ipv4/ip_tproxy.h \
+,,,
 SQUID_DEFAULT_INCLUDES
 #if HAVE_LIMITS_H
 #include <limits.h>
@@ -2785,6 +2803,27 @@ if test "$LINUX_NETFILTER" = "no" ; then
     sleep 10
 fi
 
+dnl Linux Netfilter/TPROXY support requires some specific header files
+dnl Shamelessly copied from shamelessly copied from above
+if test "$LINUX_TPROXY" ; then
+    AC_MSG_CHECKING(if TPROXY header files are installed)
+    # hold on to your hats...
+    if test "$ac_cv_header_linux_netfilter_ipv4_ip_tproxy_h" = "yes" && test "$LINUX_NETFILTER" = "yes"; then
+       LINUX_TPROXY="yes"
+       AC_DEFINE(LINUX_TPROXY, 1, [Enable real Transparent Proxy support for Netfilter TPROXY.])
+    else
+       LINUX_TPROXY="no"
+       AC_DEFINE(LINUX_TPROXY, 0, [Enable real Transparent Proxy support for Netfilter TPROXY.])
+    fi
+    AC_MSG_RESULT($LINUX_TPROXY)
+fi
+if test "$LINUX_TPROXY" = "no" && test "$LINUX_NETFILTER" = "yes"; then
+    echo "WARNING: Cannot find TPROXY headers, you need to install the"
+    echo "tproxy package from:"
+    echo " - lynx http://www.balabit.com/downloads/tproxy/"
+    sleep 10
+fi
+
 if test -z "$USE_GNUREGEX" ; then
     case "$host" in
     *-sun-solaris2.[[0-4]])
index 1b8ae14aa6c6fd51ae4c6e32b909756f4fe4c095..e8efc39f8195ce30f153eca47fae41d8b5f2bdd6 100644 (file)
 /* Define to 1 if you have the <linux/netfilter_ipv4.h> header file. */
 #undef HAVE_LINUX_NETFILTER_IPV4_H
 
+/* Define to 1 if you have the <linux/netfilter_ipv4/ip_tproxy.h> header file. */
+#undef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H
+
 /* long is defined in system headers */
 #undef HAVE_LONG
 
 /* Enable support for Transparent Proxy on Linux (Netfilter) systems */
 #undef LINUX_NETFILTER
 
+/* Enable real Transparent Proxy support for Netfilter TPROXY. */
+#undef LINUX_TPROXY
+
 /* If we need to declare sys_errlist[] as external */
 #undef NEED_SYS_ERRLIST
 
index 359e845de3560641bf91a9b14f901eab5ae840e6..bc2e260238cc7fe768e44169b5c787124a55edfd 100644 (file)
@@ -104,7 +104,7 @@ void ICAPXaction::openConnection()
 {
     const ICAPServiceRep &s = service();
     // TODO: check whether NULL domain is appropriate here
-    connection = icapPconnPool->pop(s.host.buf(), s.port, NULL);
+    connection = icapPconnPool->pop(s.host.buf(), s.port, NULL, NULL);
 
     if (connection >= 0) {
         debugs(93,3, HERE << "reused pconn FD " << connection);
@@ -169,7 +169,7 @@ void ICAPXaction::closeConnection()
         if (reuseConnection) {
             debugs(93,3, HERE << "pushing pconn" << status());
             commSetTimeout(connection, -1, NULL, NULL);
-            icapPconnPool->push(connection, theService->host.buf(), theService->port, NULL);
+            icapPconnPool->push(connection, theService->host.buf(), theService->port, NULL, NULL);
         } else {
             debugs(93,3, HERE << "closing pconn" << status());
             // comm_close will clear timeout
index 2c5af9d00c0f9e492f429a7ffdde79f87f8bf164..61757c962856af28a0e984f5495c612b309aeb24 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: cache_cf.cc,v 1.507 2007/04/11 22:57:34 wessels Exp $
+ * $Id: cache_cf.cc,v 1.508 2007/04/15 14:46:12 serassio Exp $
  *
  * DEBUG: section 3     Configuration File Parsing
  * AUTHOR: Harvest Derived
@@ -2739,6 +2739,14 @@ parse_http_port_option(http_port_list * s, char *token)
             s->disable_pmtu_discovery = DISABLE_PMTU_ALWAYS;
         else
             self_destruct();
+
+#if LINUX_TPROXY
+
+    } else if (strcmp(token, "tproxy") == 0) {
+        s->tproxy = 1;
+        need_linux_tproxy = 1;
+#endif
+
     } else {
         self_destruct();
     }
index ff5495f8940cce85d9a6685f1a543dd3c398090b..fa5bf21914e597eca9f667c7c14bf8492283a7fe 100644 (file)
@@ -1,6 +1,6 @@
 
 #
-# $Id: cf.data.pre,v 1.431 2007/04/07 09:35:38 serassio Exp $
+# $Id: cf.data.pre,v 1.432 2007/04/15 14:46:13 serassio Exp $
 #
 #
 # SQUID Web Proxy Cache                http://www.squid-cache.org/
@@ -100,6 +100,10 @@ DOC_START
           protocol=    Protocol to reconstruct accelerated requests with.
                        Defaults to http
 
+          tproxy       Support Linux TPROXY for spoofing
+                       outgoing connections using the client
+                       IP address.
+
           disable-pmtu-discovery=
                        Control Path-MTU discovery usage:
                            off         lets OS decide on what to do (default).
index 6176c8f99c43ee25fa4b741ca88e517e86e86979..63b2a9b8374034015a887c97d9b9b46fbc50c93f 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: client_side.cc,v 1.745 2007/04/06 04:50:05 rousskov Exp $
+ * $Id: client_side.cc,v 1.746 2007/04/15 14:46:15 serassio Exp $
  *
  * DEBUG: section 33    Client-side Routines
  * AUTHOR: Duane Wessels
@@ -1271,6 +1271,7 @@ clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
         http->al.reply = HTTPMSGLOCK(rep);
         context->sendStartOfMessage(rep, recievedData);
     }
+
     PROF_stop(clientSocketRecipient);
 }
 
@@ -1549,6 +1550,7 @@ void
 ClientSocketContext::initiateClose(const char *reason)
 {
     debugs(33, 5, HERE << "initiateClose: closing for " << reason);
+
     if (http != NULL) {
         ConnStateData::Pointer conn = http->getConn();
 
@@ -1563,7 +1565,7 @@ ClientSocketContext::initiateClose(const char *reason)
                     debugs(33, 2, HERE << "avoiding double-closing " << conn);
                     return;
                 }
-                    
+
                 /*
                 * XXX We assume the reply fits in the TCP transmit
                 * window.  If not the connection may stall while sending
@@ -1573,6 +1575,7 @@ ClientSocketContext::initiateClose(const char *reason)
                 * this may be an issue.
                 */
                 conn->startClosing(reason);
+
                 return;
             }
         }
@@ -1594,6 +1597,7 @@ ClientSocketContext::writeComplete(int fd, char *bufnotused, size_t size, comm_e
     assert (this->fd() == fd);
 
     /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
+
     if (errflag == COMM_ERR_CLOSING)
         return;
 
@@ -1841,13 +1845,16 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho
 
     /* Attempt to parse the first line; this'll define the method, url, version and header begin */
     r = HttpParserParseReqLine(hp);
+
     if (r == 0) {
         debug(33, 5) ("Incomplete request, waiting for end of request line\n");
-    return NULL;
+        return NULL;
     }
+
     if (r == -1) {
         return parseHttpRequestAbort(conn, "error:invalid-request");
     }
+
     /* Request line is valid here .. */
     *http_ver = HttpVersion(hp->v_maj, hp->v_min);
 
@@ -1865,10 +1872,13 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho
     /* We know the whole request is in hp->buf now */
 
     assert(req_sz <= (size_t) hp->bufsiz);
+
     /* Will the following be true with HTTP/0.9 requests? probably not .. */
     /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
     assert(req_sz > 0);
+
     hp->hdr_end = req_sz - 1;
+
     hp->hdr_start = hp->req_end + 1;
 
     /* Enforce max_request_size */
@@ -1879,10 +1889,11 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho
 
     /* Set method_p */
     *method_p = HttpRequestMethod(&hp->buf[hp->m_start], &hp->buf[hp->m_end]);
+
     if (*method_p == METHOD_NONE) {
-    /* XXX need a way to say "this many character length string" */
+        /* XXX need a way to say "this many character length string" */
         debug(33, 1) ("clientParseRequestMethod: Unsupported method in request '%s'\n", hp->buf);
-    /* XXX where's the method set for this error? */
+        /* XXX where's the method set for this error? */
         return parseHttpRequestAbort(conn, "error:unsupported-request-method");
     }
 
@@ -1892,7 +1903,9 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho
      * below needs to be modified to not expect a mutable nul-terminated string.
      */
     url = (char *)xmalloc(hp->u_end - hp->u_start + 16);
+
     memcpy(url, hp->buf + hp->u_start, hp->u_end - hp->u_start + 1);
+
     url[hp->u_end - hp->u_start + 1] = '\0';
 
     /*
@@ -1901,13 +1914,16 @@ parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * metho
      */
     /* XXX this code should be modified to take a const char * later! */
     req_hdr = (char *) hp->buf + hp->req_end + 1;
+
     debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
+
     end = (char *) hp->buf + hp->hdr_end;
+
     debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
 
     if (strstr(req_hdr, "\r\r\n")) {
         debug(33, 1) ("WARNING: suspicious HTTP request contains double CR\n");
-    xfree(url);
+        xfree(url);
         return parseHttpRequestAbort(conn, "error:double-CR");
     }
 
@@ -2140,7 +2156,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC
         assert(context->http->out.offset == 0);
         context->pullData();
         conn->flags.readMoreRequests = false;
-    goto finish;
+        goto finish;
     }
 
     if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
@@ -2154,7 +2170,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC
         assert(context->http->out.offset == 0);
         context->pullData();
         conn->flags.readMoreRequests = false;
-    goto finish;
+        goto finish;
     }
 
     /* compile headers */
@@ -2171,13 +2187,18 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC
         assert(context->http->out.offset == 0);
         context->pullData();
         conn->flags.readMoreRequests = false;
-    goto finish;
+        goto finish;
     }
 
     request->flags.accelerated = http->flags.accel;
 
     request->flags.transparent = http->flags.transparent;
 
+#if LINUX_TPROXY
+
+    request->flags.tproxy = conn->port->tproxy;
+#endif
+
     if (internalCheck(request->urlpath.buf())) {
         if (internalHostnameIs(request->host) &&
                 request->port == getMyPort()) {
@@ -2214,7 +2235,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC
         assert(context->http->out.offset == 0);
         context->pullData();
         conn->flags.readMoreRequests = false;
-    goto finish;
+        goto finish;
     }
 
 
@@ -2228,13 +2249,14 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC
         assert(context->http->out.offset == 0);
         context->pullData();
         conn->flags.readMoreRequests = false;
-    goto finish;
+        goto finish;
     }
 
     http->request = HTTPMSGLOCK(request);
     clientSetKeepaliveFlag(http);
 
     /* Do we expect a request-body? */
+
     if (request->content_length > 0) {
         request->body_pipe = conn->expectRequestBody(request->content_length);
 
@@ -2243,6 +2265,7 @@ clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketC
         notedUseOfBuffer = true;
 
         conn->handleRequestBodyData();
+
         if (!request->body_pipe->exhausted())
             conn->readSomeData();
 
@@ -2312,6 +2335,7 @@ ssize_t
 ConnStateData::bodySizeLeft()
 {
     // XXX: this logic will not work for chunked requests with unknown sizes
+
     if (bodyPipe != NULL)
         return bodyPipe->unproducedSize();
 
@@ -2339,9 +2363,10 @@ clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read)
     while (conn->in.notYetUsed > 0 && conn->bodySizeLeft() == 0) {
         connStripBufferWhitespace (conn);
 
-    /* Don't try to parse if the buffer is empty */
-    if (conn->in.notYetUsed == 0)
-        break;
+        /* Don't try to parse if the buffer is empty */
+
+        if (conn->in.notYetUsed == 0)
+            break;
 
         /* Limit the number of concurrent requests to 2 */
 
@@ -2353,13 +2378,15 @@ clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read)
         /* Terminate the string */
         conn->in.buf[conn->in.notYetUsed] = '\0';
 
-    /* Begin the parsing */
-    HttpParserInit(&hp, conn->in.buf, conn->in.notYetUsed);
+        /* Begin the parsing */
+        HttpParserInit(&hp, conn->in.buf, conn->in.notYetUsed);
 
         /* Process request */
-    PROF_start(parseHttpRequest);
+        PROF_start(parseHttpRequest);
+
         context = parseHttpRequest(conn, &hp, &method, &http_ver);
-    PROF_stop(parseHttpRequest);
+
+        PROF_stop(parseHttpRequest);
 
         /* partial or incomplete request */
         if (!context) {
@@ -2394,6 +2421,7 @@ clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read)
             continue;          /* while offset > 0 && conn->bodySizeLeft() == 0 */
         }
     }                          /* while offset > 0 && conn->bodySizeLeft() == 0 */
+
     /* XXX where to 'finish' the parsing pass? */
 
     return parsed_req;
@@ -2494,6 +2522,7 @@ ConnStateData::handleReadData(char *buf, size_t size)
         xmemmove(current_buf, buf, size);
 
     in.notYetUsed += size;
+
     in.buf[in.notYetUsed] = '\0'; /* Terminate the string */
 
     // if we are reading a body, stuff data into the body pipe
@@ -3192,7 +3221,7 @@ ConnStateData::closing() const
     return closing_;
 }
 
-// Called by ClientSocketContext to give the connection a chance to read 
+// Called by ClientSocketContext to give the connection a chance to read
 // the entire body before closing the socket.
 void
 ConnStateData::startClosing(const char *reason)
index 5b0fc156fc082c5b5673cd6f7cf770a0852c7937..07c9f25ffffc214a170d693970522a13a1253590 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: forward.cc,v 1.155 2007/04/12 14:07:10 rousskov Exp $
+ * $Id: forward.cc,v 1.156 2007/04/15 14:46:16 serassio Exp $
  *
  * DEBUG: section 17    Request Forwarding
  * AUTHOR: Duane Wessels
 #include "SquidTime.h"
 #include "Store.h"
 
+#if LINUX_TPROXY
+#include <linux/netfilter_ipv4/ip_tproxy.h>
+#endif
+
 static PSC fwdStartCompleteWrapper;
 static PF fwdServerClosedWrapper;
 #if USE_SSL
@@ -102,7 +106,8 @@ FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r)
 }
 
 // Called once, right after object creation, when it is safe to set self
-void FwdState::start(Pointer aSelf) {
+void FwdState::start(Pointer aSelf)
+{
     // Protect ourselves from being destroyed when the only Server pointing
     // to us is gone (while we expect to talk to more Servers later).
     // Once we set self, we are responsible for clearing it when we do not
@@ -115,18 +120,19 @@ void FwdState::start(Pointer aSelf) {
     storeRegisterAbort(entry, FwdState::abort, this);
     peerSelect(request, entry, fwdStartCompleteWrapper, this);
 
-    // TODO: set self _after_ the peer is selected because we do not need 
+    // TODO: set self _after_ the peer is selected because we do not need
     // self until we start talking to some Server.
 }
 
 void
 FwdState::completed()
 {
-       if (flags.forward_completed == 1) {
-               debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
-               return;
-       }
-       flags.forward_completed = 1;
+    if (flags.forward_completed == 1) {
+        debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
+        return;
+    }
+
+    flags.forward_completed = 1;
 
 #if URL_CHECKSUM_DEBUG
 
@@ -157,8 +163,9 @@ FwdState::completed()
 FwdState::~FwdState()
 {
     debugs(17, 3, HERE << "FwdState destructor starting");
+
     if (! flags.forward_completed)
-           completed();
+        completed();
 
     serversFree(&servers);
 
@@ -181,6 +188,7 @@ FwdState::~FwdState()
         debug(17, 3) ("fwdStateFree: closing FD %d\n", fd);
         comm_close(fd);
     }
+
     debugs(17, 3, HERE << "FwdState destructor done");
 }
 
@@ -260,6 +268,14 @@ FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request)
 
     default:
         FwdState::Pointer fwd = new FwdState(client_fd, entry, request);
+#if LINUX_TPROXY
+        /* If we need to transparently proxy the request
+         * then we need the client source address and port */
+        fwd->src.sin_family = AF_INET;
+        fwd->src.sin_addr = request->client_addr;
+        fwd->src.sin_port = request->client_port;
+#endif
+
         fwd->start(fwd);
         return;
     }
@@ -332,9 +348,11 @@ FwdState::complete()
         debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
                       entry->getReply()->sline.status);
         EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
-       entry->complete();
-       if (server_fd < 0)
-               completed();
+        entry->complete();
+
+        if (server_fd < 0)
+            completed();
+
         self = NULL; // refcounted
     }
 }
@@ -721,9 +739,15 @@ FwdState::connectStart()
     const char *domain = NULL;
     int ctimeout;
     int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
+#if LINUX_TPROXY
+
+    struct in_tproxy itp;
+#endif
 
     struct IN_ADDR outgoing;
     unsigned short tos;
+
+    struct IN_ADDR *client_addr = NULL;
     assert(fs);
     assert(server_fd == -1);
     debug(17, 3) ("fwdConnectStart: %s\n", url);
@@ -742,13 +766,19 @@ FwdState::connectStart()
         ctimeout = Config.Timeout.connect;
     }
 
+#if LINUX_TPROXY
+    if (request->flags.tproxy)
+        client_addr = &request->client_addr;
+
+#endif
+
     if (ftimeout < 0)
         ftimeout = 5;
 
     if (ftimeout < ctimeout)
         ctimeout = ftimeout;
 
-    if ((fd = fwdPconnPool->pop(host, port, domain)) >= 0) {
+    if ((fd = fwdPconnPool->pop(host, port, domain, client_addr)) >= 0) {
         if (checkRetriable()) {
             debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
             server_fd = fd;
@@ -822,10 +852,42 @@ FwdState::connectStart()
 
     commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
 
-    if (fs->_peer)
+    if (fs->_peer) {
         hierarchyNote(&request->hier, fs->code, fs->_peer->host);
-    else
+    } else {
+#if LINUX_TPROXY
+
+        if (request->flags.tproxy) {
+            itp.v.addr.faddr.s_addr = src.sin_addr.s_addr;
+            itp.v.addr.fport = 0;
+
+            /* If these syscalls fail then we just fallback to connecting
+             * normally by simply ignoring the errors...
+             */
+            itp.op = TPROXY_ASSIGN;
+
+            if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
+                debug(20, 1) ("tproxy ip=%s,0x%x,port=%d ERROR ASSIGN\n",
+                              inet_ntoa(itp.v.addr.faddr),
+                              itp.v.addr.faddr.s_addr,
+                              itp.v.addr.fport);
+                request->flags.tproxy = 0;
+            } else {
+                itp.op = TPROXY_FLAGS;
+                itp.v.flags = ITP_CONNECT;
+
+                if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
+                    debug(20, 1) ("tproxy ip=%x,port=%d ERROR CONNECT\n",
+                                  itp.v.addr.faddr.s_addr,
+                                  itp.v.addr.fport);
+                    request->flags.tproxy = 0;
+                }
+            }
+        }
+
+#endif
         hierarchyNote(&request->hier, fs->code, request->host);
+    }
 
     commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
 }
@@ -923,6 +985,7 @@ FwdState::dispatch()
             break;
 
         case PROTO_WAIS:       /* Not implemented */
+
         default:
             debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
                           storeUrl(entry));
@@ -1047,9 +1110,10 @@ FwdState::reforwardableStatus(http_status s)
 }
 
 void
-FwdState::pconnPush(int fd, const char *host, int port, const char *domain)
+
+FwdState::pconnPush(int fd, const char *host, int port, const char *domain, struct IN_ADDR *client_addr)
 {
-    fwdPconnPool->push(fd, host, port, domain);
+    fwdPconnPool->push(fd, host, port, domain, client_addr);
 }
 
 void
index 19b796148009143ca06ad13d984e1a23f4c682bd..483af54ed8cdeb47b3893dd72b9388632025a67a 100644 (file)
@@ -43,7 +43,7 @@ public:
     bool checkRetry();
     bool checkRetriable();
     void dispatch();
-    void pconnPush(int fd, const char *host, int port, const char *domain);
+    void pconnPush(int fd, const char *host, int port, const char *domain, struct IN_ADDR *client_addr);
 
     bool dontRetry() { return flags.dont_retry; }
 
@@ -103,6 +103,9 @@ unsigned int forward_completed:1;
     }
 
     flags;
+#if LINUX_NETFILTER
+    struct sockaddr_in src;
+#endif
 
 };
 
index 9a58646f6b1748bfba5342c910e2f5e53eff7c26..74100c2705b69f735becf4bf72a1060c575f3d32 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: globals.h,v 1.139 2006/09/13 18:55:10 serassio Exp $
+ * $Id: globals.h,v 1.140 2007/04/15 14:46:16 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -183,6 +183,10 @@ extern "C"
     extern const char *external_acl_message;      /* NULL */
     extern int opt_send_signal;        /* -1 */
     extern int opt_no_daemon; /* 0 */
+#if LINUX_TPROXY
+    extern int need_linux_tproxy; /* 0 */
+#endif
+
 
 #ifdef __cplusplus
 }
index 0a357687416d9587554306e00bd0fb5ac6746eb4..67780b077ef917f5df448d4c6b8ddd6c4078da2b 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: http.cc,v 1.512 2007/04/06 12:15:51 serassio Exp $
+ * $Id: http.cc,v 1.513 2007/04/15 14:46:16 serassio Exp $
  *
  * DEBUG: section 11    Hypertext Transfer Protocol (HTTP)
  * AUTHOR: Harvest Derived
@@ -75,7 +75,7 @@ static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data)
 #endif
 
 HttpStateData::HttpStateData(FwdState *theFwdState) : ServerStateData(theFwdState),
-    header_bytes_read(0), reply_bytes_read(0)
+        header_bytes_read(0), reply_bytes_read(0)
 {
     debugs(11,5,HERE << "HttpStateData " << this << " created");
     ignoreCacheControl = false;
@@ -152,7 +152,8 @@ HttpStateData::~HttpStateData()
 }
 
 int
-HttpStateData::dataDescriptor() const {
+HttpStateData::dataDescriptor() const
+{
     return fd;
 }
 
@@ -466,9 +467,9 @@ HttpStateData::cacheableReply()
          */
 
         if (!refreshIsCachable(entry)) {
-        debug(22, 3) ("refreshIsCachable() returned non-cacheable..\n");
+            debug(22, 3) ("refreshIsCachable() returned non-cacheable..\n");
             return 0;
-    }
+        }
 
         /* don't cache objects from peers w/o LMT, Date, or Expires */
         /* check that is it enough to check headers @?@ */
@@ -844,6 +845,7 @@ no_cache:
 
 #if HEADERS_LOG
     headersLog(1, 0, request->method, getReply());
+
 #endif
 
     ctx_exit(ctx);
@@ -902,10 +904,12 @@ HttpStateData::persistentConnStatus() const
 
     /* If we haven't seen the end of reply headers, we are not done */
     debug(11,5)("persistentConnStatus: flags.headers_parsed=%d\n", flags.headers_parsed);
+
     if (!flags.headers_parsed)
         return INCOMPLETE_MSG;
 
     const int clen = reply->bodySize(request->method);
+
     debug(11,5)("persistentConnStatus: clen=%d\n", clen);
 
     /* If the body size is unknown we must wait for EOF */
@@ -918,7 +922,7 @@ HttpStateData::persistentConnStatus() const
         // if (entry->mem_obj->endOffset() < reply->content_length + reply->hdr_sz)
         const int body_bytes_read = reply_bytes_read - header_bytes_read;
         debugs(11,5, "persistentConnStatus: body_bytes_read=" <<
-            body_bytes_read << " content_length=" << reply->content_length);
+               body_bytes_read << " content_length=" << reply->content_length);
 
         if (body_bytes_read < reply->content_length)
             return INCOMPLETE_MSG;
@@ -1041,11 +1045,10 @@ HttpStateData::readReply (size_t len, comm_err_t flag, int xerrno)
             * definately at EOF, so we want to process the reply
             * headers.
              */
-        PROF_start(HttpStateData_processReplyHeader);
+            PROF_start(HttpStateData_processReplyHeader);
             processReplyHeader();
-        PROF_stop(HttpStateData_processReplyHeader);
-    }
-        else if (getReply()->sline.status == HTTP_INVALID_HEADER && HttpVersion(0,9) != getReply()->sline.version) {
+            PROF_stop(HttpStateData_processReplyHeader);
+        } else if (getReply()->sline.status == HTTP_INVALID_HEADER && HttpVersion(0,9) != getReply()->sline.version) {
             fwd->fail(errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, fwd->request));
             flags.do_next_read = 0;
         } else {
@@ -1061,9 +1064,9 @@ HttpStateData::readReply (size_t len, comm_err_t flag, int xerrno)
         }
     } else {
         if (!flags.headers_parsed) {
-        PROF_start(HttpStateData_processReplyHeader);
+            PROF_start(HttpStateData_processReplyHeader);
             processReplyHeader();
-        PROF_stop(HttpStateData_processReplyHeader);
+            PROF_stop(HttpStateData_processReplyHeader);
 
             if (flags.headers_parsed) {
                 bool fail = reply == NULL;
@@ -1101,21 +1104,26 @@ HttpStateData::writeReplyBody()
     int len = readBuf->contentSize();
 
 #if ICAP_CLIENT
+
     if (virginBodyDestination != NULL) {
         const size_t putSize = virginBodyDestination->putMoreData(data, len);
         readBuf->consume(putSize);
         return;
     }
+
     // Even if we are done with sending the virgin body to ICAP, we may still
     // be waiting for adapted headers. We need them before writing to store.
     if (adaptedHeadSource != NULL) {
         debugs(11,5, HERE << "need adapted head from " << adaptedHeadSource);
         return;
     }
+
 #endif
 
     entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
+
     readBuf->consume(len);
+
     currentOffset += len;
 }
 
@@ -1129,6 +1137,9 @@ HttpStateData::writeReplyBody()
 void
 HttpStateData::processReplyBody()
 {
+
+    struct IN_ADDR *client_addr = NULL;
+
     if (!flags.headers_parsed) {
         flags.do_next_read = 1;
         maybeReadVirginBody();
@@ -1138,6 +1149,7 @@ HttpStateData::processReplyBody()
 #if ICAP_CLIENT
     if (icapAccessCheckPending)
         return;
+
 #endif
 
     /*
@@ -1178,14 +1190,20 @@ HttpStateData::processReplyBody()
 
             comm_remove_close_handler(fd, httpStateFree, this);
             fwd->unregister(fd);
+#if LINUX_TPROXY
+
+            if (orig_request->flags.tproxy)
+                client_addr = &orig_request->client_addr;
+
+#endif
 
             if (_peer) {
                 if (_peer->options.originserver)
-                    fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->host);
+                    fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->host, client_addr);
                 else
-                    fwd->pconnPush(fd, _peer->name, _peer->http_port, NULL);
+                    fwd->pconnPush(fd, _peer->name, _peer->http_port, NULL, client_addr);
             } else {
-                fwd->pconnPush(fd, request->host, request->port, NULL);
+                fwd->pconnPush(fd, request->host, request->port, NULL, client_addr);
             }
 
             fd = -1;
@@ -1211,6 +1229,7 @@ HttpStateData::maybeReadVirginBody()
 #if RE_ENABLE_THIS_IF_NEEDED_OR_DELETE
     // This code is not broken, but is probably not needed because we
     // probably can read more than will fit into the BodyPipe buffer.
+
     if (virginBodyDestination != NULL) {
         /*
          * BodyPipe buffer has a finite size limit.  We
@@ -1227,11 +1246,12 @@ HttpStateData::maybeReadVirginBody()
         if (icap_space < read_sz)
             read_sz = icap_space;
     }
+
 #endif
 #endif
 
     debugs(11,9, HERE << (flags.do_next_read ? "may" : "wont") <<
-        " read up to " << read_sz << " bytes from FD " << fd);
+           " read up to " << read_sz << " bytes from FD " << fd);
 
     /*
      * why <2? Because delayAwareRead() won't actually read if
@@ -1300,6 +1320,7 @@ void
 HttpStateData::closeServer()
 {
     debugs(11,5, HERE << "closing HTTP server FD " << fd << " this " << this);
+
     if (fd >= 0) {
         fwd->unregister(fd);
         comm_remove_close_handler(fd, httpStateFree, this);
@@ -1732,11 +1753,13 @@ HttpStateData::sendRequest()
 
     if (orig_request->body_pipe != NULL) {
         requestBodySource = orig_request->body_pipe;
+
         if (!requestBodySource->setConsumerIfNotLate(this)) {
             debugs(32,3, HERE << "aborting on partially consumed body");
             requestBodySource = NULL;
             return false;
         }
+
         requestSender = HttpStateData::sentRequestBodyWrapper;
         debugs(32,3, HERE << "expecting request body on pipe " << requestBodySource);
     } else {
@@ -1847,8 +1870,10 @@ HttpStateData::handleMoreRequestBodyAvailable()
     }
 
     assert(requestBodySource != NULL);
+
     if (requestBodySource->buf().hasContent()) {
         // XXX: why does not this trigger a debug message on every request?
+
         if (flags.headers_parsed && !flags.abuse_detected) {
             flags.abuse_detected = 1;
             debug(11, 1) ("http handleMoreRequestBodyAvailable: Likely proxy abuse detected '%s' -> '%s'\n",
@@ -1880,6 +1905,7 @@ HttpStateData::sentRequestBody(int fd, size_t size, comm_err_t errflag)
 {
     if (size > 0)
         kb_incr(&statCounter.server.http.kbytes_out, size);
+
     ServerStateData::sentRequestBody(fd, size, errflag);
 }
 
@@ -1891,6 +1917,7 @@ HttpStateData::abortTransaction(const char *reason)
 {
     debugs(11,5, HERE << "aborting transaction for " << reason <<
            "; FD " << fd << ", this " << this);
+
     if (fd >= 0)
         comm_close(fd);
     else
index 48194e404b24ab2fd712cd79a4b0678a06f979df..b69d380951c5aced754580999474af3435738a36 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: main.cc,v 1.444 2007/04/13 23:12:31 wessels Exp $
+ * $Id: main.cc,v 1.445 2007/04/15 14:46:17 serassio Exp $
  *
  * DEBUG: section 1     Startup and Main Loop
  * AUTHOR: Harvest Derived
@@ -165,6 +165,7 @@ bool
 SignalDispatcher::dispatch()
 {
     PROF_start(SignalDispatcher_dispatch);
+
     if (do_reconfigure) {
         mainReconfigure();
         do_reconfigure = 0;
@@ -334,6 +335,7 @@ mainParseOptions(int argc, char *argv[])
             break;
 
         case 'k':
+
             if ((int) strlen(optarg) < 1)
                 usage();
 
@@ -387,10 +389,10 @@ mainParseOptions(int argc, char *argv[])
         case 'm':
             if (optarg) {
 #if MALLOC_DBG
-                    malloc_debug_level = atoi(optarg);
+                malloc_debug_level = atoi(optarg);
 #else
 
-                    fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
+                fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
 #endif
 
             } else {
@@ -726,6 +728,7 @@ mainRotate(void)
 static void
 setEffectiveUser(void)
 {
+    keepCapabilities();
     leave_suid();              /* Run as non privilegied user */
 #ifdef _SQUID_OS2_
 
@@ -962,7 +965,7 @@ mainInitialize(void)
         storeDigestRegisterWithCacheManager(manager);
         StoreFileSystem::RegisterAllFsWithCacheManager(manager);
         storeRegisterWithCacheManager(manager);
-       storeLogRegisterWithCacheManager(manager);
+        storeLogRegisterWithCacheManager(manager);
 #if DEBUGSTRINGS
 
         StringRegistry::Instance().registerWithCacheManager(manager);
@@ -988,6 +991,7 @@ mainInitialize(void)
     serverConnectionsOpen();
 
     neighbors_init();
+
     neighborsRegisterWithCacheManager(manager);
 
     if (Config.chroot_dir)
@@ -1644,6 +1648,7 @@ SquidShutdown()
 
     unlinkdClose();      /* after sync/flush */
 #endif
+
     storeDirWriteCleanLogs(0);
     PrintRusage();
     dumpMallocStats();
index 39715a2a6e7ddafbcd5d8d1400941e391991dab7..7d072ae93fb82f2890c619bc6521b60a1df800b4 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: pconn.cc,v 1.47 2006/09/03 21:05:20 hno Exp $
+ * $Id: pconn.cc,v 1.48 2007/04/15 14:46:17 serassio Exp $
  *
  * DEBUG: section 48    Persistent Connections
  * AUTHOR: Duane Wessels
@@ -175,12 +175,17 @@ IdleConnList::timeout(int fd, void *data)
 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
 
 const char *
-PconnPool::key(const char *host, u_short port, const char *domain)
+
+PconnPool::key(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address)
 {
     LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 2 + 10);
 
-    if (domain)
+    if (domain && client_address)
+        snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s/%s", host, (int) port, inet_ntoa(*client_address), domain);
+    else if (domain && (!client_address))
         snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d/%s", host, (int) port, domain);
+    else if ((!domain) && client_address)
+        snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s", host, (int) port, inet_ntoa(*client_address));
     else
         snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d", host, (int) port);
 
@@ -222,27 +227,31 @@ PconnPool::PconnPool(const char *aDescr) : table(NULL), descr(aDescr)
 }
 
 void
-PconnPool::push(int fd, const char *host, u_short port, const char *domain)
+
+PconnPool::push(int fd, const char *host, u_short port, const char *domain, struct IN_ADDR *client_address)
 {
 
     IdleConnList *list;
     const char *aKey;
     LOCAL_ARRAY(char, desc, FD_DESC_SZ);
 
-    if (fdUsageHigh()) {
+    if (fdUsageHigh())
+    {
         debug(48, 3) ("PconnPool::push: Not many unused FDs\n");
         comm_close(fd);
         return;
-    } else if (shutting_down) {
+    } else if (shutting_down)
+    {
         comm_close(fd);
         return;
     }
 
-    aKey = key(host, port, domain);
+    aKey = key(host, port, domain, client_address);
 
     list = (IdleConnList *) hash_lookup(table, aKey);
 
-    if (list == NULL) {
+    if (list == NULL)
+    {
         list = new IdleConnList(aKey, this);
         debug(48, 3) ("pconnNew: adding %s\n", hashKeyStr(&list->hash));
         hash_join(table, &list->hash);
@@ -260,10 +269,11 @@ PconnPool::push(int fd, const char *host, u_short port, const char *domain)
  * return a pconn fd for host:port, or -1 if none are available
  */
 int
-PconnPool::pop(const char *host, u_short port, const char *domain)
+
+PconnPool::pop(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address)
 {
     IdleConnList *list;
-    const char * aKey = key(host, port, domain);
+    const char * aKey = key(host, port, domain, client_address);
     list = (IdleConnList *)hash_lookup(table, aKey);
 
     if (list == NULL)
@@ -271,7 +281,8 @@ PconnPool::pop(const char *host, u_short port, const char *domain)
 
     int fd = list->findUseableFD();
 
-    if (fd >= 0) {
+    if (fd >= 0)
+    {
         list->clearHandlers(fd);
         list->removeFD(fd);    /* might delete list */
     }
index 41a1584d7b346b103da6e266be120a16a613b3b9..0d3ec5d8a9d74d4e6eb1c10e7540502cf55694cd 100644 (file)
@@ -48,15 +48,15 @@ public:
     PconnPool(const char *);
 
     void moduleInit();
-    void push(int fd, const char *host, u_short port, const char *domain);
-    int pop(const char *host, u_short port, const char *domain);
+    void push(int fd, const char *host, u_short port, const char *domain, struct IN_ADDR *client_address);
+    int pop(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address);
     void count(int uses);
     void dumpHist(StoreEntry *e);
     void unlinkList(IdleConnList *list) const;
 
 private:
 
-    static const char *key(const char *host, u_short port, const char *domain);
+    static const char *key(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address);
 
     int hist[PCONN_HIST_SZ];
     hash_table *table;
index 4e14eb9bbb8fe61c2ac17728f4c2051088d8e89e..fc9785d6d94b4fb997198e0e0a1468dc6ee3baf7 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: protos.h,v 1.539 2007/04/13 23:12:31 wessels Exp $
+ * $Id: protos.h,v 1.540 2007/04/15 14:46:17 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -606,6 +606,7 @@ SQUIDCEXTERN int percent(int, int);
 SQUIDCEXTERN double dpercent(double, double);
 SQUIDCEXTERN void squid_signal(int sig, SIGHDLR *, int flags);
 SQUIDCEXTERN pid_t readPidFile(void);
+SQUIDCEXTERN void keepCapabilities(void);
 
 SQUIDCEXTERN struct IN_ADDR inaddrFromHostent(const struct hostent *hp);
 SQUIDCEXTERN int intAverage(int, int, int, int);
index 9a42b0dafc3555186e5fc3b1f8af32afdcc36c8f..9ba0287f2b04ad76b9e330e902d287d7eaf82f86 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: structs.h,v 1.553 2007/04/07 09:35:38 serassio Exp $
+ * $Id: structs.h,v 1.554 2007/04/15 14:46:17 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -150,6 +150,10 @@ unsigned int vhost:
 
     int vport;                 /* virtual port support, -1 for dynamic, >0 static*/
     int disable_pmtu_discovery;
+#if LINUX_TPROXY
+unsigned int tproxy:
+    1; /* spoof client ip using tproxy */
+#endif
 };
 
 
@@ -1237,6 +1241,9 @@ struct request_flags
 #if HTTP_VIOLATIONS
         nocache_hack = 0;
 #endif
+#if LINUX_TPROXY
+       tproxy = 0;
+#endif
 
     }
 
@@ -1287,6 +1294,10 @@ unsigned int accelerated:
 unsigned int transparent:
     1;
 
+#if LINUX_TPROXY
+unsigned int tproxy:
+    1; /* spoof client ip using tproxy */
+#endif
 unsigned int internal:
     1;
 
index 4e81a13f480d2ad2bbb1cd63259bf8c6573ee11d..f6326de50d13fc44cfca284db16074e415c472f5 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: tools.cc,v 1.272 2006/11/04 17:10:43 hno Exp $
+ * $Id: tools.cc,v 1.273 2007/04/15 14:46:17 serassio Exp $
  *
  * DEBUG: section 21    Misc Functions
  * AUTHOR: Harvest Derived
 #include "wordlist.h"
 #include "SquidTime.h"
 
+#if LINUX_TPROXY
+#undef _POSIX_SOURCE
+/* Ugly glue to get around linux header madness colliding with glibc */
+#define _LINUX_TYPES_H
+#define _LINUX_FS_H
+typedef uint32_t __u32;
+#include <sys/capability.h>
+#endif
+
 #if HAVE_SYS_PRCTL_H
 #include <sys/prctl.h>
 #endif
@@ -706,6 +715,27 @@ leave_suid(void)
     if (setuid(Config2.effectiveUserID) < 0)
         debug(50, 0) ("ALERT: setuid: %s\n", xstrerror());
 
+#endif
+#if LINUX_TPROXY
+
+    if (need_linux_tproxy) {
+        cap_user_header_t head = (cap_user_header_t) xcalloc(1, sizeof(cap_user_header_t));
+        cap_user_data_t cap = (cap_user_data_t) xcalloc(1, sizeof(cap_user_data_t));
+
+        head->version = _LINUX_CAPABILITY_VERSION;
+        head->pid = 0;
+        cap->inheritable = cap->permitted = cap->effective = (1 << CAP_NET_ADMIN) + (1 << CAP_NET_BIND_SERVICE) + (1 << CAP_NET_BROADCAST);
+
+        if (capset(head, cap) != 0) {
+            xfree(head);
+            xfree(cap);
+            fatal("Error giving up capabilities");
+        }
+
+        xfree(head);
+        xfree(cap);
+    }
+
 #endif
 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
     /* Set Linux DUMPABLE flag */
@@ -1292,3 +1322,17 @@ strwordquote(MemBuf * mb, const char *str)
     if (quoted)
         mb->append("\"", 1);
 }
+
+void
+keepCapabilities(void)
+{
+#if LINUX_TPROXY
+
+    if (need_linux_tproxy) {
+        if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
+            debug(1, 1) ("Error - tproxy support requires capability setting which has failed.  Continuing without tproxy support\n");
+        }
+    }
+
+#endif
+}