Jump to content
Not connected, Your IP: 3.12.34.150
rohko

How to imitate WireGuard's policy routing in OpenVPN for robust reconnections

Recommended Posts

WireGuard binaries and tools in most Linux distributions come with the handy wg-quick, which is responsible for creating the routes for the tunnel. By default, it uses so-called policy routing, while OpenVPN 2.5 binaries seem to use the traditional routing. That is, unless an user omits the route pushed by the server (with directive route-nopull), OpenVPN usually creates a more specific route than the existing one. The route is given in the verbose output:

/sbin/ip link set dev tun0 up
/sbin/ip addr add dev tun0 10.X.Y.Z/24
/sbin/ip route add A.B.C.D/32 via 192.168.1.1
/sbin/ip route add 0.0.0.0/1 via 10.X.Y.1
/sbin/ip route add 128.0.0.0/1 via 10.X.Y.1
Here A.B.C.D is an AirVPN entry IP address and 10.X.Y.Z is a local OpenVPN IP address given by the server. As mentioned in https://www.wireguard.com/netns/ , the problem in the route above is that if the route 192.168.1.1 (can be a Wi-Fi router, for example) disappears or changes, the VPN traffic freezes. Neither OpenVPN understands the default route has been removed, nor it creates a new one.  Apparently, any of the OpenVPN directives keep-alive, ping, ping-restart, nobind, or float do not help in the routing matter. They are useful, if the route itself remains alive but the internet connection is otherwise unstable. Disabling directive persist-tun might help, but it can take minutes until the VPN connection is re-created and the old tun interface is destroyed.

Wg-quick in WireGuard uses a rather simple trick to solve the problem. It merely marks the WireGuard related packages with an integer identifier (51820). Then the packages with and without the identifiers are applied a different routing policy. To imitate the policy in OpenVPN, the directives
route-nopull
mark 51820
shall be added into a .ovpn configration file. Then the OpenVPN routing can be enforced with the same rules as WireGuard does:
sudo ip -4 route add 0.0.0.0/0 dev tun0 table 51820
sudo ip -4 rule add not fwmark 51820 table 51820
sudo ip -4 rule add table main suppress_prefixlength 0

Naturally, similar rules should be applied into IPv6 routing tables as well. A brief test revealed that the policy does work as intended. Removing or changing the default internet route briefly interrupted OpenVPN traffic, but it resumed very quickly - a thing that does not happen with the default OpenVPN routing rules.

However, there are some open questions. For the first, does the mark directive apply only in the client side or does it interfere with the AirVPN servers? It would be logical that the packages are marked for the local netfilter only, and the VPN server knows nothing about the client's extra directive.
For the second, the way how WireGuard updates the DNS servers used by systemd-resolved is not clear. It uses the command
sudo resolvconf -a tun.Air -m 0 -x
if the config file is Air.conf. How to imitate the DNS update in OpenVPN, is still a mystery. Without any actions, OpenVPN will use both the AirVPN and the default DNS servers specified for the base internet connection.

 

Share this post


Link to post
dnsip=$(ifconfig tun0 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p')
dnsip=$(awk -F"." '{print $1"."$2"."$3".1"}'<<<$dnsip)
sudo systemd-resolve --interface tun0 --set-dns $dnsip                                                       
AirVPN servers' local DNS IP addresses look to end always with .1, and otherwise they equal the local IP address assigned to the client. The code above is a dirty way to introduce them to the systemd-resolved. Furthermore, the global DNS addresses can be disabled by setting their scope to local. The script shall be executed periodically, if the default route keeps resetting, because Networkmanager resets the local flag of the physical interfaces, in turn. I guess Hummingbird and Eddie employ D-Bus for that.

Now one question remains. Is usage of the directive mark allowed in client side? Perhaps @Staff knows?

 

Share this post


Link to post
@rohko

Hello!

We have not examined or tested the method you suggest, we're sorry. By the way, from the OpenVPN 2.4 manual:
--mark value
Mark encrypted packets being sent with value. The mark value can be matched in policy routing and packetfilter rules. This option is only supported in Linux and does nothing on other operating systems.
 

https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/

Kind regards
 

Share this post


Link to post
4 hours ago, Staff said:
@rohko

Hello!

We have not examined or tested the method you suggest, we're sorry. By the way, from the OpenVPN 2.4 manual:
--mark value
Mark encrypted packets being sent with value. The mark value can be matched in policy routing and packetfilter rules. This option is only supported in Linux and does nothing on other operating systems.

https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/

Kind regards

Thanks for the reply. Because the mark value only matters in Linux, the marked packages are marked only on the localhost, if I understood this statement correctly (https://docs.huihoo.com/hpc-cluster/linux-virtual-server/HOWTO/LVS-HOWTO.fwmark.html)
Quote

The fwmark is only a part of the packet while it stays in the skb of the machine which marked the packet (here the director). The fwmark is not on the packet when the packet is put out on the external network (i.e. the fwmark that is put on the packet when it is on the director is not on the packet when it arrives at the realserver).


The following script takes care of DNS switch and route activation. The latter part of the script could be executed periodically to guarantee that the reconnections won't change the DNS addresses. 🎃
#!/bin/bash

# The script to be run upon tunnel activation


tip=$(ifconfig $dev | sed -En -e 's/.*inet ([0-9.]+).*/\1/p')
tip=$(awk -F"." '{print $1"."$2"."$3".1"}'<<<$tip)
echo $tip

sudo systemd-resolve --interface $dev --set-dns $tip
sudo ip -4 route add 0.0.0.0/0 dev $dev table 51820 
sudo ip -4 rule add not fwmark 51820 table 51820
sudo ip -4 rule add table main suppress_prefixlength 0 
                                                  
                                                  
mapfile -t ifaces < <(ip -o link show | awk -F': ' '{print $2}')
arraylength=${#ifaces[@]}

for (( i=0; i<${arraylength}; i++ ));
do
   : 
   name="${ifaces[$i]}"  
   if [[ "$name" != *"lo"* ]]  && [[ "$name" != *"tun"* ]];
    then
        sudo systemd-resolve -i "$name" --set-domain local
        
   elif [[ "$name" != *"lo"* ]]  && [[ "$name" == *"tun"* ]];
    then   
        sudo systemd-resolve -i "$name" --set-domain ~.
   fi
   
done
                                                  

Share this post


Link to post

If the WiFi connection drops, and Networkmanager recreates the same route again for internet, the procedure above works well. All OpenVPN traffic almost immediately resumes after the route is up and running again. However, if the connection changes from a WiFi hotspot to another so that the external IP address of the local computer changes, then the traffic is halted for one minute, after which a new OpenVPN handshake takes place.

My question is, is there a way to avoid the one minute delay in case of a new external IP address is found for a connected client? I tried the float directive, but it does not help. @Staff has AirVPN enabled float in the server directives?
 

Quote

The --float option is a peculiar option. It tells the local openvpn process that the remote side can change public IP. So if your client side moves around, the server side would needs this option, or vice versa. The tricky thing here is that this type floating usually triggers a re-negotiation of the VPN tunnel.

That said, as of OpenVPN 2.4, if both local and remote side runs at least this version, there is an enhanced data channel which utilizes a "peer-id" reference. This allows the client side to float without renegotiating the tunnel.

For the peer-id feature, you need to use a PKI setup (--ca, --cert, --key options together with --tls-server or --tls-client). I'm not so sure about --float. But it should not matter where this option are located in your config file. The whole file is parsed as-is before options are being evaluated and conflicting options reported.

Quote
--float
Allow remote peer to change its IP address and/or port number, such as due to DHCP (this is the default if --remote is not used). --float when specified with --remote allows an OpenVPN session to initially connect to a peer at a known address, however if packets arrive from a new address and pass all authentication tests, the new address will take control of the session. This is useful when you are connecting to a peer which holds a dynamic address such as a dial-in user or DHCP client.Essentially, --float tells OpenVPN to accept authenticated packets from any address, not only the address which was specified in the --remote option.

Share this post


Link to post

I have studied this issue more. It seems that the float directive is not required, thanks to tls-crypt and tls-auth directives (as explained in the Reddit post I linked). I have set up my own OpenVPN server, and if the client's IP address changes, the procedure above (marking the VPN packets with an identifier for dynamic routing instead of a static route) takes care of re-routing the VPN traffic automatically. I have also confirmed the trick works by another VPN provider that offers OpenVPN.

However, change of the client's IP address does not work with AirVPN servers. More specifically, if the client's IP address changes, WireShark reveals that OpenVPN packets are routed to the internet and to the AirVPN server, but the server does not respond. If the client's IP address is changed back to the original IP address within a minute or so after the first IP address change, the server does respond back without re-negotiating the tunnel, and the VPN traffic flows normally.

As a conclusion, the dynamic routing that WireGuard utilizes is applicable in OpenVPN as well. Does AirVPN use some hardened directives in the servers that prohibit the IP change? Are the server side directives public? This is a bit odd since I cannot find such a directive in the OpenVPN reference manual that could in theory ban the IP address change of the client without re-negotiating the tunnel.

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Security Check
    Play CAPTCHA Audio
    Refresh Image

×
×
  • Create New...