Routing traffic from a specific IP through a VPN

Hi. I'd like to route only the traffic of a specific LAN IP 192.168.1.100 through a VPN. I've already imported and enabled the VPN client in OpenWRT. Now I need to configure such a rule. I can't to install luci-app-pbr due to a kernel incompatibility of one of its dependencies.

Is there another simple way to get what I need?

Sure, do you use OpenVPN or WireGuard?

1 Like

I use an OpenVPN client

See: https://github.com/egc112/OpenWRT-egc-add-on/tree/main/openvpn-pbr

1 Like

Do I need to create a specifc interface? Looking into the script, it looks it's required. I have just an OpenVPN client up & running:

config openvpn 'my_client'
	option config '/etc/openvpn/my_client.ovpn'
	option enabled '1'

No you do not need to create an interface as that is done by OpenVPN itself.

Usually you have in your OpenVPN config: dev tun
OpenVPN will then create a tun interface with the lowest available number e.g. tun0

So normally if you use tun0 as interface in the script it will do, but otherwise post your OpenVPN config

1 Like

Got it! I wasn't finding any tun* interface, and I've just found out that the problem seems primarily related to the OpenVPN client connection (Couldn't see it in LuCi):

...
Validating certificate extended key usage
++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
VERIFY EKU OK
VERIFY OK: depth=0, CN=server
Connection reset, restarting [0]

Strangely enough, the same config used to work fine on a previous version of OpenWRT

You probably need to add: remote-cert-tls server

Also if your VPN provider started using IPv6 and you have IPv6 not implemented then you need to block IPv6:

#Block IPv6, newer clients could default to IPv6
pull-filter ignore "route-ipv6"
pull-filter ignore "ifconfig-ipv6"
block-ipv6

In the mean time I updated the script to search automatically for the right interface :slight_smile:

1 Like

I won't be able to retry until tomorrow. In the meanwhile, I confirm that:

  • the same config used to work with my previous build of OpenWRT (current: OpenWrt SNAPSHOT r24936-60ffcfdabc)
  • IPv6 is not involved

Here's an extended log with increased verbosity:

daemon.notice: TCPv4_CLIENT WRITE [127] to [AF_INET]<VPN_SERVER_IP>:1194: P_CONTROL_V1 kid=0 [ 2 1 0 ] pid=2 DATA len=93
daemon.notice: TCPv4_CLIENT READ [22] from [AF_INET]<VPN_SERVER_IP>:1194: P_ACK_V1 kid=0 [ 2 ] DATA len=0
daemon.notice: TCPv4_CLIENT READ [65] from [AF_INET]<VPN_SERVER_IP>:1194: P_CONTROL_V1 kid=0 [ ] pid=3 DATA len=51
daemon.notice: TCPv4_CLIENT WRITE [487] to [AF_INET]<VPN_SERVER_IP>:1194: P_CONTROL_V1 kid=0 [ 3 2 1 0 ] pid=3 DATA len=449
daemon.notice: TCPv4_CLIENT READ [22] from [AF_INET]<VPN_SERVER_IP>:1194: P_ACK_V1 kid=0 [ 3 ] DATA len=0
daemon.err: Connection reset, restarting [0]

To troubleshoot it helps if we can see the whole picture.

If you have not added yet please add to the openvpn config:
verb 5

Please connect to your OpenWRT device using ssh and copy the output of the following commands and post it here using the "Preformatted text </> " button:

Remember to redact passwords, MAC addresses and any public IP addresses you may have:

ubus call system board
cat /etc/config/network
cat /etc/config/firewall
ifconfig
ip route show
logread | grep openvpn

and lastly the openvpn config file: cat /etc/openvpn/my_client.ovpn

1 Like

Hi @egc, sure. Sorry for the scattered information.

So, here is /etc/openvpn/my_client.ovpn:

client
dev tun
remote <VPN_SERVER_IP> 1194 tcp
tun-mtu 1500
tls-client
nobind
user nobody
group nogroup
ping 15
ping-restart 45
persist-tun
persist-key
mute-replay-warnings
verb 5
cipher AES-128-CBC
auth SHA1
pull
auth-user-pass /etc/openvpn/my_client.auth
connect-retry 1
reneg-sec 3600
remote-cert-tls server
redirect-gateway def1
<ca>
</ca>

ubus call system board

{
	"kernel": "5.15.147",
	"hostname": "OpenWrt",
	"system": "ARMv8 Processor rev 4",
	"model": "Zyxel EX5601-T0 ubootmod",
	"board_name": "zyxel,ex5601-t0-ubootmod",
	"rootfs_type": "squashfs",
	"release": {
		"distribution": "OpenWrt",
		"version": "SNAPSHOT",
		"revision": "r24936-60ffcfdabc",
		"target": "mediatek/filogic",
		"description": "OpenWrt SNAPSHOT r24936-60ffcfdabc"
	}
}

/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 ula_prefix '...'

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

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

config interface 'wan'
	option device 'eth1'
	option proto 'dhcp'

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

config device 'guest_dev'
	option type 'bridge'
	option name 'br-guest'

config interface 'guest'
	option proto 'static'
	option device 'br-guest'
	list ipaddr '192.168.2.1/24'

/etc/config/firewall

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

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

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

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

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 'Guest'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	list network 'guest'

config forwarding
	option src 'Guest'
	option dest 'wan'

config forwarding
	option src 'lan'
	option dest 'Guest'

config rule 'guest_dns'
	option name 'Allow-DNS-Guest'
	option src 'guest'
	option dest_port '53'
	option proto 'tcp udp'
	option target 'ACCEPT'

config rule 'guest_dhcp'
	option name 'Allow-DHCP-Guest'
	option src 'guest'
	option dest_port '67'
	option proto 'udp'
	option family 'ipv4'
	option target 'ACCEPT'

Here is a complete log (verbosity 5) for a VPN connection attempt:

daemon.warn openvpn(my_client)[9376]: DEPRECATED OPTION: --cipher set to 'AES-128-CBC' but missing in --data-ciphers (AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305). OpenVPN ignores --cipher for cipher negotiations.
daemon.notice openvpn(my_client)[9376]: OpenVPN 2.6.8 aarch64-openwrt-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [MH/PKTINFO] [AEAD]
daemon.notice openvpn(my_client)[9376]: library versions: OpenSSL 3.0.12 24 Oct 2023, LZO 2.10
daemon.warn openvpn(my_client)[9376]: NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
daemon.notice openvpn(my_client)[9376]: Control Channel MTU parms [ mss_fix:0 max_frag:0 tun_mtu:1250 tun_max_mtu:0 headroom:126 payload:1600 tailroom:126 ET:0 ]
daemon.notice openvpn(my_client)[9376]: Data Channel MTU parms [ mss_fix:0 max_frag:0 tun_mtu:1500 tun_max_mtu:1600 headroom:136 payload:1768 tailroom:562 ET:0 ]
daemon.notice openvpn(my_client)[9376]: TCP/UDP: Preserving recently used remote address: [AF_INET]<VPN_SERVER_IP>:1194
daemon.notice openvpn(my_client)[9376]: Socket Buffers: R=[131072->131072] S=[16384->16384]
daemon.notice openvpn(my_client)[9376]: Attempting to establish TCP connection with [AF_INET]<VPN_SERVER_IP>:1194
daemon.notice openvpn(my_client)[9376]: TCP connection established with [AF_INET]<VPN_SERVER_IP>:1194
daemon.notice openvpn(my_client)[9376]: TCPv4_CLIENT link local: (not bound)
daemon.notice openvpn(my_client)[9376]: TCPv4_CLIENT link remote: [AF_INET]<VPN_SERVER_IP>:1194
daemon.notice openvpn(my_client)[9376]: NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay
daemon.notice openvpn(my_client)[9376]: TLS: Initial packet from [AF_INET]<VPN_SERVER_IP>:1194, sid=b15c0e49 36125f1d
daemon.warn openvpn(my_client)[9376]: WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
daemon.notice openvpn(my_client)[9376]: VERIFY OK: depth=1, CN=myCa
daemon.notice openvpn(my_client)[9376]: VERIFY KU OK
daemon.notice openvpn(my_client)[9376]: Validating certificate extended key usage
daemon.notice openvpn(my_client)[9376]: ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
daemon.notice openvpn(my_client)[9376]: VERIFY EKU OK
daemon.notice openvpn(my_client)[9376]: VERIFY OK: depth=0, CN=server
daemon.err openvpn(my_client)[9376]: Connection reset, restarting [0]

ifconfig and ip route show don't show anything interesting, since the VPN client can't connect anymore.

OK I am missing the tun0 interface in the firewall.

The shortcut is to just add it to the WAN zone:

Furthermore remove from the openvpn config: redirect-gateway def1

Reboot and post openvpn log again

1 Like

Just tried but the problem is still the same error during the connection process:

daemon.err openvpn(my_client)[9376]: Connection reset, restarting [0]

The client seems not connecting, so there's any tun* interface:

# ifconfig -a | sed 's/[ \t].*//;/^$/d'
br-lan
eth0
eth1
lan1
lan2
lan3
lan4
lo

Please note that:

  • the same config used to work in OpenWRT before the update (event with redirect-gateway def1). No changes have occurred on the VPN server side.
  • the same config works with another OpenVPN client

Newer OpenVPN 2.6.8 might reject using older ciphers.
Add to the OpenVPN config:

data-ciphers AES-256-GCM:AES-128-GCM:AES-128-CBC
data-ciphers-fallback AES-128-CBC
1 Like

Perfect. That was the problem. Adding ciphers fixed. Now the client connects via tun0.

Now, I've added to the WAN zone: list device 'tun0'.

I've also customized and chmodded scritps:

ovpn-pbr-up

#!/bin/sh
# set clients ip addresses to route via the VPN, use CIDR notation, change to your need
IPSOURCE="192.168.1.200/32"

# Interface used by the OpenVPN tunnel, usually tun0, trying to get it from environment with $dev
[[ -z "$dev" ]] && TUN="tun0" || TUN="$dev"
#logger -t egc "$(echo $(basename $0)) uses interface:[$dev] = $TUN"

# Table number
TABLE=101

# Start script add default route to table
ip route add default dev $TUN table $TABLE
#ip route add default via $route_vpn_gateway dev $TUN table $TABLE
# Add local routes
ip route show | grep -Ev '^default |^0.0.0.0/1 |^128.0.0.0/1 ' | while read route; do
    ip route add $route table $TABLE >/dev/null 2>&1
done
# Add rules, you can add more rules with e.g.: interface:iif, sourceport:sport etc., see ip rule man page
ip rule add from $IPSOURCE table $TABLE
# examples
# ip rule add iif br-guest table $TABLE

ovpn-pbr-down

#!/bin/sh
# Table number
TABLE=101

# Start script cleaning up
ip route flush table $TABLE
while ip rule delete from 0/0 to 0/0 table $TABLE; do true; done
# restart routing optional
service network restart

my-client.conf now has the following directives:

pull-filter ignore "redirect-gateway"
redirect-gateway def1
data-ciphers AES-256-GCM:AES-128-GCM:AES-128-CBC
data-ciphers-fallback AES-128-CBC
route-up /etc/openvpn/ovpn-pbr-up
down /etc/openvpn/ovpn-pbr-down

However, it looks like I'm not done yet since all the traffic from the LAN is routed through the VPN. It seems that the pbr policy doesn't kick in.

Remove this as this will route everything via the VPN

1 Like

Ok done! Thank you :slight_smile: However the down command seems triggering errors:

daemon.err openvpn(my_client)[16285]:event_wait : Interrupted system call (fd=-1,code=4)
daemon.notice openvpn(my_client)[16285]:TCP/UDP: Closing socket
daemon.notice openvpn(my_client)[16285]:/usr/libexec/openvpn-hotplug route-pre-down my_client tun0 1500 0 192.168.10.50 255.255.255.0 init
daemon.notice openvpn(my_client)[16285]:Closing TUN/TAP interface
daemon.notice openvpn(my_client)[16285]:net_addr_v4_del: 192.168.10.50 dev tun0
daemon.notice openvpn(my_client)[16285]:/usr/libexec/openvpn-hotplug down my_client tun0 1500 0 192.168.10.50 255.255.255.0 init
daemon.err openvpn(my_client)[16285]:WARNING: Failed running command (--up/--down): external program exited with error status: 1

How can I fix this and make sure that the pbr routing has been properly destroyed? I changed the IPSOURCE several times, but it seems that previous routing policies (e.g., different runs with different IPSOURCEs) are still kicking in.

Did you make the script executable?
e.g.:
chmod +x /etc/openvpn/ovpn-pbr-down

1 Like

Yes, I confirm that

-rwxr-xr-x    1 root     root           207 Feb  9 12:10 ovpn-pbr-down*
-rwxr-xr-x    1 root     root           883 Feb  9 13:20 ovpn-pbr-up*

Here is what happens if (i) I remove the scripts execution from the client config:

# route-up /etc/openvpn/ovpn-pbr-up # commented out
# down /etc/openvpn/ovpn-pbr-down   # commented out

and (ii) I run them manually with the tunnel up & running:

$ ./ovpn-pbr-up # no output
$ ./ovpn-pbr-down
ip: RTNETLINK answers: No such file or directory
'radio0' is disabled
'radio1' is disabled
Command failed: Not found