Iptables – Route only packets from specific interface over VPN

interfaceiptablesroutevpn

So i have 3 interfaces on a host:

  • Interface 1 is facing the external network
  • interface 2 is the tun0 vpn interface
  • interface 3 the local network interface

My goal is to route all the traffic from interface 3 (the local network) through the vpn interface and all the traffic from the host through the default interface1

# ip route
default via 192.168.1.1 dev eth0
10.8.8.0/24 dev tun0 proto kernel scope link src 10.8.8.27
128.0.0.0/1 via 10.8.8.1 dev tun0
192.168.0.0/24 dev eth2 proto kernel scope link src 192.168.0.1
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.56

(I have read other posts here but none of them were really helpful)

Best Answer

First: the "half-default" route 128.0.0.0/1 (without its other half 0.0.0.0/1 ??) shouldn't exist if the router won't use the tunnel:

# ip route del 128.0.0.0/1 via 10.8.8.1 dev tun0

Since it's most likely set from the tunnel software, deal with its configuration.

Now create a new table (random choice: 100) with a default route through the tunnel, and use it only for traffic from eth2:

# ip route add default via 10.8.8.1 dev tun0 table 100
# ip rule add iif eth2 lookup 100

Tests:

Host is using the usual default route whatever its source IP (as per the question, the selector for the tunnel is from the interface, not the IP):

# ip -o route get 8.8.8.8
8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.56 \    cache
# ip -o route get 8.8.8.8 from 192.168.0.1
8.8.8.8 from 192.168.0.1 via 192.168.1.1 dev eth0 \    cache 

The traffic from eth2 will get handled differently:

# ip -o route get 8.8.8.8 from 192.168.0.2 iif eth2
8.8.8.8 from 192.168.0.2 via 10.8.8.1 dev tun0 table 100 \    cache  iif eth2

If you also want the local 192.168.0.1 IP going through the tunnel. It's a bit more complex because 192.168.0.1 needs to send IP packets to its own LAN if needed (and not to tun0 in this case). Requires copying the LAN route also to table 100, adding a rule based on source IP (and then removing the useless previous rule based on interface):

# ip route add 192.168.0.0/24 dev eth2 table 100
# ip rule add from 192.168.0.0/24 lookup 100
( # ip rule del iif eth2 lookup 100 )

Which now gives also:

# ip -o route get 8.8.8.8 from 192.168.0.1
8.8.8.8 from 192.168.0.1 via 10.8.8.1 dev tun0 table 100 \    cache

That's it for routing. Note that without NAT the other end of the tunnel needs a route to 192.168.0.0/24 via 10.8.8.27 or it won't work. This should be the preferred choice.

If this route on remote isn't set, and NAT is good enough instead, configure it with:

# iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o tun0 -j MASQUERADE