[tproxy] [stunnel patch] transparent proxy on linux 2.6 using cttproxy patch

MAtteo HCE Valsasna MAtteo.Valsasna at uninsubria.it
Mon Aug 22 12:29:49 CEST 2005


hi folks:

as suggested in http://www.stunnel.org/faq/transparent.html#ToC3, I
modified the stunnel code to use the transparent proxy funcionality on
2.6 (and possibly 2.4) linux kernels with the cttproxy patch
(http://www.balabit.com/products/oss/tproxy/, I
used )cttproxy-2.6.12-2.0.2. on a clean 2.6.12.5 kernel)

the patch is higly incomplete, but it works (see TODO). 

please try it if you are interested in transparent stunnel. 

any feedback il welcome.

MAtteo


the code is heavly based on the examples distributed along with the
cttproxy patch, many thanks to Balázs Scheidler for its great work and
for making it GPL.

with this patch, I was able to run a stunnel in server mode with the
transparent option, and the application server I connected to saw the IP
address of the stunnel client.

of course, this code il also redistributed under the GPL.


conditions for running stunnel in transparent mode:

if you want to try this patch, you MUST modify the hardcoded IP address
in client.c to the IP address of the stunnel server (look for DOIT! in
the source code). any improvements wellcome.

the stunnel server must run a linux kernel patched with the cttproxy
patch

the stunnel server and the application server cannot be the same host

the application server must believe that it has to route all packets of
transparently tunneled connections via the stunnel server. thus the two
server should be on the same subnet.


I was also able to use linux policy routing to avoid modifying the
general routing on the application server: transparently tunneled
connections are directed to a different IP on the application server,
where a policy rule applies a specific routing table to packets with
that source address (see at the end of this message).


TODO:

stunnel must explicitly bind to an *existing* local address (not
loopback) before calling setsockopt(SOL_IP, IP_TPROXY, TPROXY_ASSIGN)").
now the address is hardcoded in the patch, it should 
automatically find a local address to bind to (or get it from the config
file)

now the code will only run with tproxy patch version 2.0.2
it should be less strict about tproxy version (make it a configurable
option in stunnel?). 
which tproxy version will it run with?

auto detect or configure kernel version (2.2 vs 2.4 or 2.6 with
cttproxy), or better add it in .configure

----begin patch-----
*** /tmp/stunnel-4.11/src/client.c	2005-07-02 08:55:58.000000000 +0200
--- stunnel-4.11/src/client.c	2005-08-22 12:19:57.000000000 +0200
***************
*** 44,49 ****
--- 44,60 ----
  #define SHUT_RDWR 2
  #endif
  
+ #define LINUX_NETFILTER
+ #define LINUX_TPROXY
+ 
+ /* transparent proxy */
+ #ifdef LINUX_NETFILTER
+ #include <linux/netfilter_ipv4.h>
+ #endif
+ #ifdef LINUX_TPROXY
+ #include <linux/netfilter_ipv4/ip_tproxy.h>
+ #endif
+ 
  /* TCP wrapper */
  #ifdef USE_LIBWRAP
  #include <tcpd.h>
***************
*** 921,926 ****
--- 932,944 ----
      int s; /* destination socket */
      u16 i;
  
+     //    struct in_addr *local=NULL;
+ #ifdef LINUX_TPROXY
+     int f;
+     struct in_tproxy itp;
+     struct sockaddr_in tproxy_sin;
+ #endif
+ 
      /* setup address_list */
      if(c->opt->option.delayed_lookup) {
          resolved_list.num=0;
***************
*** 945,960 ****
          if(alloc_fd(s))
              return -1;
  
!         if(c->bind_addr.num) { /* explicit local bind or transparent proxy */
!             memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION));
!             if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) {
!                 sockerror("bind transparent");
!                 closesocket(s);
!                 return -1;
!             }
!         }
! 
!         /* try to connect for the 1st time */
          s_ntop(c->connecting_address, &addr);
          s_log(LOG_DEBUG, "%s connecting %s",
              c->opt->servname, c->connecting_address);
--- 963,1053 ----
          if(alloc_fd(s))
              return -1;
  
!         if(c->bind_addr.num && !c->opt->option.transparent) { 
! 	  /* explicit local bind, not transparent mode */
! 	  
! 	  memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION));
! 	  if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) {
! 	    sockerror("bind transparent");
! 	    closesocket(s);
! 	    return -1;
! 	  }
! 	  sockerror("local bind succeeded");
! 	}
!         
! 	if (c->opt->option.transparent){
! 
! 	  /* transparent proxy support on 2.[46] linux kernels */
! 	  /* added by MAtteo HCE Valsasna (matteo.valsasna at uninsubria.it), 20050822 */
! 	  /* requires tproxy kernel patch (http://www.balabit.com/products/oss/tproxy */
! 	  /* based on examples in cttproxy patch */
! 
! 	  /* FIXME: should detect 2.2 kernel or 2.[46] + cttproxy patch and use appropriate code */
! 
! 	  memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION));
! 
! 	  /* check tproxy version */
! 	  itp.op = TPROXY_VERSION;
! 	  itp.v.version = 0x00000000;
! 
! 	  getsockopt(s, SOL_IP, IP_TPROXY, &itp, &f);
! 	  s_log(LOG_NOTICE, "pre getsockopt(s, SOL_IP, IP_TPROXY): %x", itp.v.version);
! 
! 	  /* FIXME: should accept a range of tproxy versions - which? */
! 
! 	  if (itp.v.version != 0x02000002) {
! 	    itp.v.version = 0x02000002;
! 	    if (setsockopt(s, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1)
! 	      {
! 		sockerror("setsockopt(SOL_IP, IP_TPROXY, TPROXY_VERSION)");
! 		getsockopt(s, SOL_IP, IP_TPROXY, &itp, &f);
! 		s_log(LOG_NOTICE, "err getsockopt(s, SOL_IP, IP_TPROXY): %x", itp.v.version);
! 		return -1;
! 	      }
! 	  }
! 	  else{
! 	    s_log(LOG_NOTICE, "getsockopt(SOL_IP, IP_TPROXY, TPROXY_VERSION) returned right version");
! 	  }
! 
! 	  /* bind to local address */
! 	  /* FIXME: must become dynamic */
! 	  tproxy_sin.sin_family = AF_INET;
! 	  /* DOIT! add a local IP address here */
! 	  inet_aton("192.168.179.71", &tproxy_sin.sin_addr);
! 	  /* do not request a specific source port */
! 	  tproxy_sin.sin_port = htons(0);
! 
! 	  if (bind(s, (struct sockaddr *) &tproxy_sin, sizeof(tproxy_sin)) == -1)
! 	    {
! 	      sockerror("bind");
! 	      return -1;
! 	    }
! 	  s_log(LOG_NOTICE, "bound to local: %s", inet_ntoa(tproxy_sin.sin_addr));
! 
! 	  /* assign foreign address */
! 	  itp.op = TPROXY_ASSIGN;
! 	  itp.v.addr.faddr = bind_addr.in.sin_addr;
! 	  itp.v.addr.fport = 0;
! 
! 	  if (setsockopt(s, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1)
! 	    {
! 	      s_log(LOG_NOTICE, "error assigning foreign address: %s", inet_ntoa(bind_addr.in.sin_addr));
! 	      sockerror("setsockopt(SOL_IP, IP_TPROXY, TPROXY_ASSIGN)");
! 	      return -1;
! 	    }
!   	  s_log(LOG_NOTICE, "assigned foreign address: %s", inet_ntoa(bind_addr.in.sin_addr));
! 
! 	  /* set connect flag on socket */
! 	  itp.op = TPROXY_FLAGS;
! 	  itp.v.flags = ITP_CONNECT|ITP_ONCE;
! 	  if (setsockopt(s, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1)
! 	    {
! 	      sockerror("setsockopt(SOL_IP, IP_TPROXY, TPROXY_FLAGS)");
! 	      return -1;
! 	    }
! 	}
! 	
! 	/* try to connect for the 1st time */
          s_ntop(c->connecting_address, &addr);
          s_log(LOG_DEBUG, "%s connecting %s",
              c->opt->servname, c->connecting_address);

----end patch-----

EXTRAS

source routing on the application server

intended usage: the application server is a squid proxy with pubblic
adresses. its default gw is an HW-based router (not the transparent
stunnel server).

we want normal traffic (i.e. not handled by transparent stunnel) to
continue to use normal routing. only traffic handled by stunnel should
be routed back to the stunnel server.

a new IP address (10.0.0.1) is added on the application server
specifically for this purpose. stunnel server will forward connections
to this address, normal clients will use another address. then, only
traffic originating from this address needs to be routed back to the
stunnel server.

the stunnel server has IP 10.0.0.254 (but it can have more, of course)

in /etc/iproute2/rt_tables add a line defining a new routing table:
10      inr.tproxy

# add a rule to the new routing table
ip r a table inr.tproxy default via 10.0.0.254

# create ip policy routing rule: packets with source address =
# 10.0.0.1 should be routed according to the inr.tproxy table

ip rule add type unicast from 10.0.0.1 table inr.tproxy priority 220
# apply policy routing changes
ip route flush cache




More information about the tproxy mailing list