Linux: Bridge two NICs and route traffic through VPN tunnel except specific destinations and ports

iptableslinuxopenvpnrouting

I have a Linux PC (Debian Wheezy) with two Ethernet adapters. Adapter eth0 is connected to the Internet (modem/router/DHCP/firewall thingy) and eth1 is connected to an WiFi access point. All other computers use WiFi and connect to that AP.

I want to configure the network so that all traffic from the clients behind the AP is passed through an OpenVPN tunnel on tun0. However, I need port 587 and the IP ranges 192.168.0.0/16 and 10.0.0.0/24 to always get passed through eth0.

I tried to build something with iptables but I'm having little success. Nothing is logged anywhere for some reason, so I am not sure how to start debugging. To be honest I am not even really sure what I am doing. English also isn't my native language so that makes reading a documentation difficult.

The following is what I have right now. Would a kind person tell me what I am doing wrong? Is this a wrong approach?

sysctl -w net.ipv4.conf.tun0.rp_filter=2
iptables -F
iptables -X LOGDROP
iptables -t mangle -F

iptables -N LOGDROP
iptables -A LOGDROP -m limit --limit 5/m --limit-burst 10 -j LOG
iptables -A LOGDROP -j DROP

iptables -A PREROUTING -t mangle -i eth0 -d 192.168.0.0/16 -j MARK --set-mark 1
iptables -A PREROUTING -t mangle -i eth0 -d 10.0.0.0/24 -j MARK --set-mark 1
iptables -A PREROUTING -t mangle -i eth0 -p tcp --dport 587 -j MARK --set-mark 1
iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 2

iptables -I FORWARD -m state --state INVALID -j LOGDROP
iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables -A FORWARD -m mark --mark 1 --in-interface eth1 --out-interface eth0 -j ACCEPT
iptables -A FORWARD -m mark --mark 2 --in-interface eth1 --out-interface tun0 -j ACCEPT

iptables -A FORWARD --in-interface eth0 --out-interface eth1 -d 192.168.0.0/16 -j ACCEPT
iptables -A FORWARD --in-interface eth0 --out-interface eth1 -d 10.0.0.0/24 -j ACCEPT

iptables -A FORWARD -j LOGDROP

Best Answer

You're right about marking packets using the firewall, but you should not be routing them at that level — use multiple routing tables.

First, set up your main routing table so that it routes through eth0:

ip route add default via XXXX dev eth0

Once the tunnel is up, set up a secondary routing table that routes through tun0:

ip route add default via YYYY dev tun0 table 42

Now mark packets destined to tun0:

iptables -t mange -A PREROUTING ... --set-mark 54

and set up a routing rule so that marked packets go through table 42:

ip rule add priority 100 fwmark 54 table 42

In order to ensure that packets don't go through eth0 when the tunnel is down, you may optionally add a lower priority rule to drop any marked packets that failed to get routed by table 42:

ip rule add priority 110 fwmark 54 table default