linux-tproxy

Transparent Proxy

Overview

Transparent proxy allows to bind a non-local address(address belong to none interface), so that packet can be sent out with non-local address, meanwhile, with proper iptables and ip rule, incoming packet for transparent socket(non-local dst) can be received properly.

Implementation

In order to support this, changes needed as below

  • Change kernel to allow bind non-local address.
  • Add a netfilter plugin to match transparent socket based on five tuples(src, sport, dst, dport, proto), tag the matched packet to make it go local by ip rule.

kernel commit for tproxy

1
2
3
4
5
6
7
# allow bind non-local address
f5715aea: ipv4: Implement IP_TRANSPARENT socket option
b9fb1506: ipv4: Allow binding to non-local addresses if IP_TRANSPARENT is set

# with socket match(transparent socket), redirect packet to local with ip rule
# hence packet goes to local, then received by tcp/udp
136cdc71: netfilter: iptables socket match

Example to use

make sure your kernel is built with such config

  • CONFIG_NETFILTER_XT_MATCH_SOCKET
  • CONFIG_NF_TPROXY_IPV4
  • CONFIG_NETFILTER_XT_TARGET_TPROXY

output, allow bind non-local

1
2
3
4
5
6
7
8
9
10
11
fd = socket(AF_INET, SOCK_STREAM, 0);

int value = 1;
setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));

name.sin_family = AF_INET;
name.sin_port = htons(0xCAFE);
name.sin_addr.s_addr = htonl(0xDEADBEEF);

//bind non-local address after setting IP_TRANSPARENT for this socket
bind(fd, &name, sizeof(name));

input packet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# create a new chain
$ iptables -t mangle -N DIVERT

# if packet matches tcp socket(non-local or not) of this host, go to that chain in PREROUTING
$ iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT # -j jump to DIVERT chain

# set mark with 1 for the matched packet(non-local or not)
# this mark used for ip rule.
$ iptables -t mangle -A DIVERT -j MARK --set-mark 1
$ iptables -t mangle -A DIVERT -j ACCEPT
# later on packet goes to routing

# skb with mark 1, lookup route table 100
$ ip rule add fwmark 1 lookup 100 # policy routing
# all skb goes to local, up to Lay4 even it's local dst or not.
# add a route at table 100, for all packets(as 0.0.0.0/0 matches all) sent to local(passed to upper layer), skb->dev with loopback.
$ ip route add local 0.0.0.0/0 dev lo table 100

REF