Route each LAN port through a separate VPN

I have a router with 3 LAN ports and I would like to configure each port to use a seperate VPN.

Right now I have it set to use a single VPN for all ports + wifi, but I can't get the details right for multiple VPNs.
I want the ports to be able to only access the internet throgh their own VPN connection, not being able to bypass or access any of the other's VPN connection.

Current setup:

I assme I have to create a new interface for each port, then somehow remove them from the bridge(?) and route their traffic through one of the VPNs using the firewall, but as I said, I can't quite get it working.

I don't have an answer for you, but this thread might help. Configure OpenVPN only on some LAN ports
Note: this thread is a little old, and might not help since vpn-policy-routing is not supported in the latest version.

Sharing the device and type of VPN (OpenVPN, Wireguard?) may be useful for someone to help.

Sharing current config may also be helpful
Please copy the output of the following commands and post it here using the "Preformatted text </> " button:
grafik
Remember to redact passwords, MAC addresses and any public IP addresses you may have:

cat /etc/config/network
cat /etc/config/firewall

It's an ASUS RT-AX53U and I'm using OpenVPN, but it would be neat to allow a mix of different VPN types.

root@OpenWrt:~# cat /etc/config/network

config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option packet_steering '1'
	option ula_prefix 'fd32:8fe2:1025::/48'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'

config interface 'lan'
	option proto 'static'
	option ipaddr '192.168.1.1'
	option netmask '255.255.255.0'
	option ip6assign '60'
	option device 'br-lan'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'
	option peerdns '0'

config interface 'wan6'
	option device 'wan'
	option proto 'dhcpv6'

config interface 'VPN'
	option proto 'none'
	option device 'tun0'

root@OpenWrt:~# cat /etc/config/firewall

config defaults
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option synflood_protect '1'

config zone
	option name 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'lan'

config zone
	option name 'wan'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	list network 'wan'
	list network 'wan6'

config rule
	option name 'Allow-DHCP-Renew'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option target 'ACCEPT'
	option family 'ipv4'

config rule
	option name 'Allow-Ping'
	option src 'wan'
	option proto 'icmp'
	option icmp_type 'echo-request'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-IGMP'
	option src 'wan'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-DHCPv6'
	option src 'wan'
	option proto 'udp'
	option dest_port '546'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-MLD'
	option src 'wan'
	option proto 'icmp'
	option src_ip 'fe80::/10'
	list icmp_type '130/0'
	list icmp_type '131/0'
	list icmp_type '132/0'
	list icmp_type '143/0'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config zone
	option name 'VPN_FW'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	list network 'VPN'

config forwarding
	option dest 'VPN_FW'
	option src 'lan'

I used this tutorial:

Make a new bridge for every LAN port you want to separate each on its own subnet just like the already existing br-lan

Use Policy Based Routing (PBR) or for an overview, to route the subnets you created via the tunnel you want

I have a guest network made which works similar but includes separate wifi and one of the LAN ports and route only this guest network via the VPN tunnel with PBR.

1 Like

I created a new bridge device and removed port 1 from the existing br-lan:

config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option packet_steering '1'
	option ula_prefix 'fd32:8fe2:1025::/48'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan2'
	list ports 'lan3'

config interface 'lan'
	option proto 'static'
	option ipaddr '192.168.1.1'
	option netmask '255.255.255.0'
	option ip6assign '60'
	option device 'br-lan'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'
	option peerdns '0'

config interface 'wan6'
	option device 'wan'
	option proto 'dhcpv6'

config interface 'VPN'
	option proto 'none'
	option device 'tun0'

config device
	option type 'bridge'
	option name 'port1'
	list ports 'lan1'

config interface 'port1nw'
	option proto 'static'
	option device 'port1'
	option ipaddr '192.168.4.1'
	option netmask '255.255.255.0'

Honestly flying blind when it comes to the PBR though.

config defaults
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option synflood_protect '1'

config zone
	option name 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'lan'

config zone
	option name 'wan'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	list network 'wan'
	list network 'wan6'

config rule
	option name 'Allow-DHCP-Renew'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option target 'ACCEPT'
	option family 'ipv4'

config rule
	option name 'Allow-Ping'
	option src 'wan'
	option proto 'icmp'
	option icmp_type 'echo-request'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-IGMP'
	option src 'wan'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-DHCPv6'
	option src 'wan'
	option proto 'udp'
	option dest_port '546'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-MLD'
	option src 'wan'
	option proto 'icmp'
	option src_ip 'fe80::/10'
	list icmp_type '130/0'
	list icmp_type '131/0'
	list icmp_type '132/0'
	list icmp_type '143/0'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config zone
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'
	list network 'port1nw'
	option name 'port1'

config forwarding
	option dest 'wan'

config forwarding
	option dest 'wan'

config forwarding
	option src 'lan'
	option dest 'wan'

config zone
	option name 'VPN_FW'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'
	list network 'VPN'
	option masq '1'
	option mtu_fix '1'

config forwarding
	option src 'port1'
	option dest 'VPN_FW'

First get all your interfaces with separate LAN ports running.

The PBR doc: https://docs.openwrt.melmac.net/vpn-policy-routing/ is well written start with the How to Install section :slight_smile:

It is just a package you can install and can be set in the LuCi (Services > Policy Routing)

First get all your interfaces with separate LAN ports running.

Yea let's start with that.

I'm now trying to just move port3 to it's own interface, while keeping all the other functionality the same.

Here's my new /network config:

config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option packet_steering '1'
	option ula_prefix 'fd32:8fe2:1025::/48'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'

config device
	option name 'br-lan3'
	option type 'bridge'
	list ports 'lan3'

config interface 'lan'
	option proto 'static'
	option ipaddr '192.168.1.1'
	option netmask '255.255.255.0'
	option ip6assign '60'
	option device 'br-lan'

config interface 'lan3'
	option proto 'static'
	option ipaddr '192.168.1.2'
	option netmask '255.255.255.0'
	option ip6assign '60'
	option device 'br-lan3'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'
	option peerdns '0'

config interface 'wan6'
	option device 'wan'
	option proto 'dhcpv6'

config interface 'VPN'
	option proto 'none'
	option device 'tun0'

I'm already confused as to what the correct IP address for my lan3 interface would be.

And here is the new firewall config:

config defaults
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option synflood_protect '1'

config zone
	option name 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'lan'

config zone
	option name 'lan3'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'lan3'

config zone
	option name 'wan'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	list network 'wan'
	list network 'wan6'

config rule
	option name 'Allow-DHCP-Renew'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option target 'ACCEPT'
	option family 'ipv4'

config rule
	option name 'Allow-Ping'
	option src 'wan'
	option proto 'icmp'
	option icmp_type 'echo-request'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-IGMP'
	option src 'wan'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-DHCPv6'
	option src 'wan'
	option proto 'udp'
	option dest_port '546'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-MLD'
	option src 'wan'
	option proto 'icmp'
	option src_ip 'fe80::/10'
	list icmp_type '130/0'
	list icmp_type '131/0'
	list icmp_type '132/0'
	list icmp_type '143/0'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'


config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan3'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan3'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config zone
	option name 'VPN_FW'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	list network 'VPN'

config forwarding
	option dest 'VPN_FW'
	option src 'lan'

config forwarding
	option dest 'VPN_FW'
	option src 'lan3'

With this setup, anything plugged into port3 can't even access the LuCi interface.

I think I got one step closer to the solution:

I enabled DHCP on the br-lan3, gave it a static address of 192.168.5.1 and pointed it to the same DHCP server as br-lan. (6,10.8.0.1)

I can now access the internet through the VPN, and the LuCi interface using port3.
Now I believe i just have to configure 3 separate VPNs in the same way I have setup the first.

Will report back.

Your new interface should be on its own subnet e.g.:
option ipaddr '192.168.2.1'

Furthermore consider starting simple for the firewall add network lan3 to the LAN zone instead of using its own zone.
If it works you can then transfer it to its own zone

Add list network 'lan3' and remove lan3 from the zone lan3

I think I'm 95% there now.

I have a separate interface running for port3, routed through it's own VPN tunnel.

Now my only remaining issue: I can't seem to connect to two VPNs at the same time.
Either tun0 or tun1 work and provide internet, it appears like only the connection that gets started last works.

I'm using a piad proton VPN plan which should allow me to connect up to 10 devices at the same time.

How do I go about diagnosing what's going wrong?

proton_client (tun0) is assigned to lan1, lan2 and wifi
vpn3 (tun1) is assigned to lan3

In this state, lan1 has internet through the VPN

Once I start vpn3, lan1 loses internet access and lan3 gains internet access.

I think That is normal as each VPN tries to setup default route via its own tunnel.

Normally both VPN should connect but there is only one default route.

Just check your VPN log.

A possible way to deal with this is to add to the OpenVPN configs (both configs):
route-nopull

This means that no route will be set, You should have two active tunnels but nothing goes through it by default.

So you have to make extra routing tables one for each tunnel and then decide which clients uses which routing table (and thus which tunnel).
Fortunately you do not have to do it yourself as the PBR package, you can install, should do that for you :slight_smile:
See: https://docs.openwrt.melmac.net/vpn-policy-routing

That does appear to work!
But after a router reboot, I always get Failed to set up 'VPN/tun0/10.30.0.2'!

I then have to manuall Restart the PBR service, then it seems do do exactly what I want it to.

I do not know where that could come from at the moment.

When I have some more time I will try to duplicate your problem.

At first glance, its probably a dependency that isn't fully loaded yet.
So he could add some lines to rc.local in the mean while.

sleep 10
service restart pbr