AP routing by VPN and rest by default

I have an orangepi (armbian) with an Ethernet interface (eth0) where I set up an interface (wlan0) for an AccessPoint and installed a VPN client (tun0).
The objective is:
All traffic from the wlan0 AP is directed to the vpn (tun0) and vice versa
All traffic from system processes, programs, etc. is routed by default to 192.168.1.1 ISP destination

                     -------------------
                     |                 |
       10.8.3.2/24---|tun0       wlan0 |----192.168.2.1/24
   192.168.1.31/24---|eth0             |    WIFI AP
                     |                 |
                     -------------------

I have activated Forwarding: net.ipv4.ip_forward=1

And added rule in iptables: iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

Result: Access through AP OK and always routed through the VPN OK

Problem:
All traffic is routed through the VPN and the idea is that the traffic generated by the processes is routed through the default 192.168.1.1

If from a terminal I perform ping, traceroute, etc. it routes tun0, I understand why it assigns tun0’s 10.8.3.2 as the source IP and I think this is where the problem is, if it assigned eth0’s 192.168.1.31, it would route by default 192.168. 1.1 and it would be solved.

The VPN client creates the default:

  0.0.0.0         10.8.3.1        128.0.0.0       UG        0 0          0 tun0

Even if I delete it, everything remains the same.

What needs to be configured so that the rest of the traffic is routed by default to 192.168.1.1 ISP destination?

Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.8.3.1        128.0.0.0       UG        0 0          0 tun0
0.0.0.0         192.168.1.1     0.0.0.0         UG        0 0          0 eth0
10.8.3.0        0.0.0.0         255.255.255.0   U         0 0          0 tun0
128.0.0.0       10.8.3.1        128.0.0.0       UG        0 0          0 tun0
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 wlan0
178.239.165.30  192.168.1.1     255.255.255.255 UGH       0 0          0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0          0 eth0
192.168.2.0     0.0.0.0         255.255.255.0   U         0 0          0 wlan0

eth0: flags=4163 <UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.31  netmask 255.255.255.0  broadcast 192.168.1.255
ether 02:81:91:07:0c:a5  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
inet 127.0.0.1  netmask 255.0.0.0
inet6 ::1  prefixlen 128  scopeid 0x10<host>

tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
inet 10.8.3.2  netmask 255.255.255.0  destination 10.8.3.2
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
inet 192.168.2.1  netmask 255.255.255.0  broadcast 192.168.2.255
ether 00:e0:4c:81:79:8a  txqueuelen 1000  (Ethernet)

Note: ifconfig and route should not be used anymore on Linux because they use a deprecated and obsolete Linux kernel API. They should be replaced with ip link, ip addr and ip route (and various additional commands from the iproute2 suite).

Policy-based routing cannot be achieved with the deprecated commands on Linux but only with the iproute2 suite’s commands.


The current routing setup takes care of overriding the original default route by using two "half-default" routes instead of a default route (for example this is called def1 by OpenVPN when the server pushes a route or the client overrides it), so the actual VPN routes are both:

  0.0.0.0         10.8.3.1        128.0.0.0       UG        0 0          0 tun0
  128.0.0.0       10.8.3.1        128.0.0.0       UG        0 0          0 tun0

and because they are narrower than the default route, they override it for any destination (not in an other routing entry, such as the VPN remote endpoint explicitly added to avoid a routing loop for the tunnel envelope).

So removing only the first entry will still affect about half of Internet (eg: if www.armbian.com resolves as 5.161.66.254 it won’t be reached through the tunnel anymore, but if www.debian.org resolves as 128.31.0.62 then it will reached through the tunnel). Removing both of course will just more or less disable using the tunnel. These actions would affect the host and the nodes behind wlan0.

Here policy-based routing can be used to distinguish routed/forwarded traffic and traffic originating from the host itself to have a different routing outcome.

OP’s goal is to have the VPN used by the nodes behind the host (at the LAN behind wlan0) but not the host itself. Without any other change to the current setup, this can be manually accomplished by policy routing rules + a routing table overriding the current override: only for the host ignore routes with CIDR of 0 (0.0.0.0/0) or 1 (0.0.0.0/1 and 128.0.0.0/1: OpenVPN’s def1 method) and respecify a new default route (which will be the original default route).

The 3 commands below are sufficient to solve this:

ip route add default via 192.168.1.1 dev eth0 table 1000
ip rule add pref 1000 iif lo lookup main suppress_prefixlength 1
ip rule add pref 1001 iif lo lookup 1000

This will:

  • only for the host itself (special syntax iif lo for host-originating traffic) but not for routed/forwarded traffic (which would match iif wlan0, iif eth0 or iif tun0 depending on where from, instead) use the main routing table to retrieve a route as long as the route doesn’t come from the default route or the two def1 "half-default" routes, thanks to the suppress_prefixlength suppressor.

  • still only for the host itself if no route was found then use the original default route that was duplicated in routing table 1000.

    thus by default allowing the host to reach Internet without using the VPN.

Beside this:

  • any routed/forwarded traffic is left unaffected by this setting

    and will thus continue as before to reach Internet through the VPN.

  • return traffic to the host itself is always handled first by the local routing table so unaffected by this setup. Indeed the very first routing rule at preference 0 handles the local routing table, as seen below:

    # ip rule
    0:      from all lookup local
    1000:   from all iif lo lookup main suppress_prefixlength 1
    1001:   from all iif lo lookup 1000
    32766:  from all lookup main
    32767:  from all lookup default
    
  • the host can still reach nodes at 10.8.3.0/24 as usual using the VPN or nodes at 192.168.2.0/24 through wireless since their routes have a CIDR of 24 which is superior to 1 and is thus not suppressed.

Caveat: whenever the interface eth0 is administratively put down (then up) the entry in routing table 1000 has to be put back because it will be deleted, just as what happens in the main routing table: this should be integrated in the eth0 interface setup rather than just at boot.


Other methods that don’t need to know explicitly the gateway or would automate this certainly exist, but they would likely involve the VPN setup. Either these commands could be run as a script by the VPN tool (which would probably be able to provide the gateway in a variable) or the VPN’s configuration might be able to provide direct solutions to solve this. I can’t know with available information.

Answered By: A.B
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.