The patch adds a new onoff config 'linux_tproxy' (if you configure with --enable-linux-tproxy) which, when set, will spoof the source address of outgoing server connections to be the same as the original client address. It now also works with persistent sever connections. To take advantage of this code you need Linux 2.4 with netfilter and with the TPROXY patches installed from: http://www.balabit.com/downloads/tproxy/linux-2.4/ There are two small nits with the code: 1. You must supply a tcp_outgoing_address in your squid.conf, this is because of some deep magic in the Linux TCP/IP stack. 2. Squid must run as root in order to do the connection spoofing bits. TODO: o Port all changes to cvs HEAD branch. o Attempt to fix connect(2) problem in kernel which requires bind(2) to local address. o Hack kernel space code so squid doesn't need to run as root. diff -ur --exclude configure squid-2.5.STABLE1.orig/acconfig.h squid-2.5.STABLE1/acconfig.h --- squid-2.5.STABLE1.orig/acconfig.h Mon Jul 1 09:55:11 2002 +++ squid-2.5.STABLE1/acconfig.h Mon Mar 10 16:54:46 2003 @@ -331,6 +331,11 @@ */ #undef LINUX_NETFILTER +/* + * Enable real Transparent Proxy support for Netfilter TPROXY. + */ +#undef LINUX_TPROXY + /* * Do we have unix sockets? (required for the winbind ntlm helper */ diff -ur --exclude configure squid-2.5.STABLE1.orig/configure.in squid-2.5.STABLE1/configure.in --- squid-2.5.STABLE1.orig/configure.in Tue Sep 24 21:00:03 2002 +++ squid-2.5.STABLE1/configure.in Mon Mar 10 16:07:42 2003 @@ -746,6 +746,17 @@ 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) + LINUX_TPROXY="yes" + fi +]) + AM_CONDITIONAL(MAKE_LEAKFINDER, false) dnl Enable Leak Finding Functions AC_ARG_ENABLE(leakfinder, @@ -1153,6 +1164,7 @@ libc.h \ limits.h \ linux/netfilter_ipv4.h \ + linux/netfilter_ipv4/ip_tproxy.h \ malloc.h \ math.h \ memory.h \ @@ -1764,6 +1776,27 @@ 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"; then + LINUX_TPROXY="yes" + AC_DEFINE(LINUX_TPROXY, 1) + else + LINUX_TPROXY="no" + AC_DEFINE(LINUX_TPROXY, 0) + fi + AC_MSG_RESULT($LINUX_TPROXY) +fi +if test "$LINUX_TPROXY" = "no" ; then + echo "WARNING: Cannot find TPROXY headers, you need to install the" + echo "tproxy package from:" + echo " - lynx http://www.balabit.com/downloads/tproxy/linux-2.4/" + sleep 10 +fi + if test -z "$USE_GNUREGEX" ; then case "$host" in *-sun-solaris2.[[0-4]]) diff -ur --exclude configure squid-2.5.STABLE1.orig/include/autoconf.h.in squid-2.5.STABLE1/include/autoconf.h.in --- squid-2.5.STABLE1.orig/include/autoconf.h.in Mon Jul 15 22:29:47 2002 +++ squid-2.5.STABLE1/include/autoconf.h.in Tue Mar 11 11:46:33 2003 @@ -350,6 +350,11 @@ */ #undef LINUX_NETFILTER +/* + * Enable real Transparent Proxy support for Netfilter TPROXY. + */ +#undef LINUX_TPROXY + /* * Do we have unix sockets? (required for the winbind ntlm helper */ @@ -645,6 +650,9 @@ /* Define if you have the header file. */ #undef HAVE_LINUX_NETFILTER_IPV4_H +/* Define if you have the header file. */ +#undef HAVE_LINUX_NETFILTER_IPV4_IP_TPROXY_H + /* Define if you have the header file. */ #undef HAVE_MALLOC_H diff -ur --exclude configure squid-2.5.STABLE1.orig/src/cf.data.pre squid-2.5.STABLE1/src/cf.data.pre --- squid-2.5.STABLE1.orig/src/cf.data.pre Wed Sep 4 14:35:01 2002 +++ squid-2.5.STABLE1/src/cf.data.pre Tue Mar 11 11:48:48 2003 @@ -3837,4 +3837,20 @@ until all the child processes have been started. DOC_END +NAME: linux_tproxy +IFDEF: LINUX_TPROXY +COMMENT: on|off +TYPE: onoff +LOC: Config.onoff.linux_tproxy +DEFAULT: off +DOC_START + If you have Linux 2.4 with netfilter and TPROXY support and you + have compiled squid with the correct options then you can enable + this option to allow squid to spoof the source address of + outgoing connections to servers so that they see connections from + the original client IP addresses. Enable this only if you know + what you are doing. You will need to set a valid + tcp_outgoing_address. +DOC_END + EOF diff -ur --exclude configure squid-2.5.STABLE1.orig/src/client_side.c squid-2.5.STABLE1/src/client_side.c --- squid-2.5.STABLE1.orig/src/client_side.c Mon Sep 23 05:04:03 2002 +++ squid-2.5.STABLE1/src/client_side.c Mon Mar 10 16:06:03 2003 @@ -322,6 +322,7 @@ new_request->http_ver = old_request->http_ver; httpHeaderAppend(&new_request->header, &old_request->header); new_request->client_addr = old_request->client_addr; + new_request->client_port = old_request->client_port; new_request->my_addr = old_request->my_addr; new_request->my_port = old_request->my_port; new_request->flags.redirected = 1; @@ -2977,6 +2978,7 @@ safe_free(http->log_uri); http->log_uri = xstrdup(urlCanonicalClean(request)); request->client_addr = conn->peer.sin_addr; + request->client_port = conn->peer.sin_port; request->my_addr = conn->me.sin_addr; request->my_port = ntohs(conn->me.sin_port); request->http_ver = http->http_ver; diff -ur --exclude configure squid-2.5.STABLE1.orig/src/forward.c squid-2.5.STABLE1/src/forward.c --- squid-2.5.STABLE1.orig/src/forward.c Mon Apr 1 13:51:27 2002 +++ squid-2.5.STABLE1/src/forward.c Wed Mar 12 15:18:20 2003 @@ -36,6 +36,13 @@ #include "squid.h" +#if LINUX_NETFILTER +#include +#endif +#if LINUX_TPROXY +#include +#endif + static PSC fwdStartComplete; static void fwdDispatch(FwdState *); static void fwdConnectStart(void *); /* should be same as EVH */ @@ -331,6 +338,15 @@ time_t ctimeout; struct in_addr outgoing; unsigned short tos; + struct in_addr *local=NULL; +#if LINUX_TPROXY + int f=ITP_CONNECT; + struct in_tproxy itp; + + if ( Config.onoff.linux_tproxy ) + local=&fwdState->src.sin_addr; +#endif + assert(fs); assert(fwdState->server_fd == -1); debug(17, 3) ("fwdConnectStart: %s\n", url); @@ -349,7 +365,7 @@ port = fwdState->request->port; ctimeout = Config.Timeout.connect; } - if ((fd = pconnPop(host, port)) >= 0) { + if ((fd = pconnPop(host, port, local)) >= 0) { debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd); fwdState->server_fd = fd; fwdState->n_tries++; @@ -396,6 +412,18 @@ ctimeout, fwdConnectTimeout, fwdState); +#if LINUX_TPROXY + if ( Config.onoff.linux_tproxy ) { + itp.itp_faddr.s_addr = fwdState->src.sin_addr.s_addr; + itp.itp_fport = fwdState->src.sin_port; + + /* If these syscalls fail then we just fallback to connecting + * normally by simply ignoring the errors... + */ + setsockopt(fd, SOL_IP, IP_TPROXY_ASSIGN, &itp, sizeof(itp)); + setsockopt(fd, SOL_IP, IP_TPROXY_FLAGS, &f, sizeof(f)); + } +#endif commConnectStart(fd, host, port, fwdConnectDone, fwdState); } @@ -610,6 +638,15 @@ fwdState->server_fd = -1; fwdState->request = requestLink(r); fwdState->start = squid_curtime; + +#if LINUX_TPROXY + /* If we need to transparently proxy the request + * then we need the client source address and port */ + fwdState->src.sin_family = AF_INET; + fwdState->src.sin_addr = r->client_addr; + fwdState->src.sin_port = r->client_port; +#endif + storeLockObject(e); EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT); storeRegisterAbort(e, fwdAbort, fwdState); diff -ur --exclude configure squid-2.5.STABLE1.orig/src/http.c squid-2.5.STABLE1/src/http.c --- squid-2.5.STABLE1.orig/src/http.c Sat Sep 7 23:52:10 2002 +++ squid-2.5.STABLE1/src/http.c Wed Mar 12 13:50:50 2003 @@ -542,6 +542,7 @@ int bin; int clen; size_t read_sz; + struct in_addr *local=NULL; #if DELAY_POOLS delay_id delay_id; @@ -551,6 +552,12 @@ else delay_id = delayMostBytesAllowed(entry->mem_obj); #endif + +#if LINUX_TPROXY + if ( Config.onoff.linux_tproxy ) + local=&httpState->request->client_addr; +#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { comm_close(fd); return; @@ -657,7 +664,7 @@ #endif comm_remove_close_handler(fd, httpStateFree, httpState); fwdUnregister(fd, httpState->fwd); - pconnPush(fd, request->host, request->port); + pconnPush(fd, request->host, request->port, local); fwdComplete(httpState->fwd); httpState->fd = -1; httpStateFree(fd, httpState); diff -ur --exclude configure squid-2.5.STABLE1.orig/src/main.c squid-2.5.STABLE1/src/main.c --- squid-2.5.STABLE1.orig/src/main.c Sun Jul 14 18:20:49 2002 +++ squid-2.5.STABLE1/src/main.c Tue Mar 11 11:48:18 2003 @@ -422,6 +422,16 @@ #ifdef _SQUID_OS2_ return; #endif + + /* Transparent proxy support requires squid to run as root which + * kinda sucks. I will look towards implementing a solution in + * kernel space to combat this, perhaps a sysctl to set a certain + * GID to be able to use the TPROXY stuff */ +#if LINUX_TPROXY + if ( Config.onoff.linux_tproxy ) + return; +#endif + if (geteuid() == 0) { debug(0, 0) ("Squid is not safe to run as root! If you must\n"); debug(0, 0) ("start Squid as root, then you must configure\n"); diff -ur --exclude configure squid-2.5.STABLE1.orig/src/pconn.c squid-2.5.STABLE1/src/pconn.c --- squid-2.5.STABLE1.orig/src/pconn.c Sat Apr 14 01:03:23 2001 +++ squid-2.5.STABLE1/src/pconn.c Wed Mar 12 15:20:04 2003 @@ -49,7 +49,6 @@ static PF pconnRead; static PF pconnTimeout; -static const char *pconnKey(const char *host, u_short port); static hash_table *table = NULL; static struct _pconn *pconnNew(const char *key); static void pconnDelete(struct _pconn *p); @@ -58,12 +57,17 @@ static MemPool *pconn_data_pool = NULL; static MemPool *pconn_fds_pool = NULL; -static const char * -pconnKey(const char *host, u_short port) +#define PCONN_KEYLEN (SQUIDHOSTNAMELEN + 24) + +static inline const int +pconnKey(char *buf, const char *peer, u_short port, struct in_addr *local) { - LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN + 10); - snprintf(buf, SQUIDHOSTNAMELEN + 10, "%s.%d", host, (int) port); - return buf; + if ( local == NULL ) { + return snprintf(buf, PCONN_KEYLEN, "%s.%d", peer, (int) port); + }else{ + return snprintf(buf, PCONN_KEYLEN, "%s.%d.%s", + peer, (int) port, inet_ntoa(*local)); + } } static struct _pconn * @@ -184,11 +188,11 @@ } void -pconnPush(int fd, const char *host, u_short port) +pconnPush(int fd, const char *peer, u_short port, struct in_addr *local) { struct _pconn *p; int *old; - LOCAL_ARRAY(char, key, SQUIDHOSTNAMELEN + 10); + LOCAL_ARRAY(char, key, PCONN_KEYLEN); LOCAL_ARRAY(char, desc, FD_DESC_SZ); if (fdNFree() < (RESERVED_FD << 1)) { debug(48, 3) ("pconnPush: Not many unused FDs\n"); @@ -199,7 +203,7 @@ return; } assert(table != NULL); - strcpy(key, pconnKey(host, port)); + pconnKey(key, peer, port, local); p = (struct _pconn *) hash_lookup(table, key); if (p == NULL) p = pconnNew(key); @@ -217,20 +221,20 @@ p->fds[p->nfds++] = fd; commSetSelect(fd, COMM_SELECT_READ, pconnRead, p, 0); commSetTimeout(fd, Config.Timeout.pconn, pconnTimeout, p); - snprintf(desc, FD_DESC_SZ, "%s idle connection", host); + snprintf(desc, FD_DESC_SZ, "%s idle connection", peer); fd_note(fd, desc); debug(48, 3) ("pconnPush: pushed FD %d for %s\n", fd, key); } int -pconnPop(const char *host, u_short port) +pconnPop(const char *peer, u_short port, struct in_addr *local) { struct _pconn *p; hash_link *hptr; int fd = -1; - LOCAL_ARRAY(char, key, SQUIDHOSTNAMELEN + 10); + LOCAL_ARRAY(char, key, PCONN_KEYLEN); assert(table != NULL); - strcpy(key, pconnKey(host, port)); + pconnKey(key, peer, port, local); hptr = hash_lookup(table, key); if (hptr != NULL) { p = (struct _pconn *) hptr; diff -ur --exclude configure squid-2.5.STABLE1.orig/src/protos.h squid-2.5.STABLE1/src/protos.h --- squid-2.5.STABLE1.orig/src/protos.h Sat Sep 7 16:13:05 2002 +++ squid-2.5.STABLE1/src/protos.h Wed Mar 12 13:37:31 2003 @@ -1142,8 +1142,8 @@ extern int errorReservePageId(const char *page_name); extern ErrorState *errorCon(err_type type, http_status); -extern void pconnPush(int, const char *host, u_short port); -extern int pconnPop(const char *host, u_short port); +extern void pconnPush(int, const char *peer, u_short port, struct in_addr *local); +extern int pconnPop(const char *peer, u_short port, struct in_addr *local); extern void pconnInit(void); extern int asnMatchIp(void *, struct in_addr); diff -ur --exclude configure squid-2.5.STABLE1.orig/src/structs.h squid-2.5.STABLE1/src/structs.h --- squid-2.5.STABLE1.orig/src/structs.h Sun Sep 8 00:11:23 2002 +++ squid-2.5.STABLE1/src/structs.h Mon Mar 10 16:06:03 2003 @@ -585,6 +585,9 @@ int ie_refresh; int vary_ignore_expire; int pipeline_prefetch; +#if LINUX_NETFILTER + int linux_tproxy; +#endif } onoff; acl *aclList; struct { @@ -1645,6 +1648,7 @@ int imslen; int max_forwards; /* these in_addr's could probably be sockaddr_in's */ + in_port_t client_port; struct in_addr client_addr; struct in_addr my_addr; unsigned short my_port; @@ -1962,6 +1966,9 @@ unsigned int dont_retry:1; unsigned int ftp_pasv_failed:1; } flags; +#if LINUX_NETFILTER + struct sockaddr_in src; +#endif }; #if USE_HTCP