How to route or exclude a network or service specific or China only traffic through a VPN using IP based split tunneling on GNU Linux?

How to route or exclude a network or service specific or China only traffic through a VPN using IP based split tunneling on GNU Linux?
Photo by Christian Wiediger / Unsplash

Long gone are the days when a website was hosted on a single server with a static IP for years. In today's web, redundancy and robust content delivery networks are taking over from traditional web server systems. I am sure you have often struggled with IPs and routing table tricks when trying to route or exclude certain websites when connected to a VPN.

Complex routing improves two aspects:

  • Privacy and security: route "bad" websites through VPN and use your connection for "good" websites and you getting a privacy bonus 'cause tracking a device with two IP addresses is harder.

  • Speed: surfing on normal "good" websites feels fast like before 'cause no VPN - no speed tax to pay.

  • Time: you don't have to waste your time turning your VPN on and off anymore, just enable it permanently.

Let us simplify the process for you, so you can easily route just a single network or service specific traffic over VPN with confidence and little effort.

Cloudflare

The Cloudflare IP ranges available here: IPv4 and IPv6. Let's collect them and use to change routing.

Wireguard

Wireguard supports the option AllowedIPs, which does two things: adds a route to the networks and will allows packets with the source IPs list to be routed from the given peer on the WireGuard interface. AllowedIPs accepts IP and subnets in the following format:

AllowedIPs=1.1.1.1,1.0.0.1/32,::1

We need to do a simple text conversion to get IP list, compatible with Wireguard format.

V4=$(curl -s https://www.cloudflare.com/ips-v4 | tr '\n' ',')
V6=$(curl -s https://www.cloudflare.com/ips-v6 | tr '\n' ',')
echo "AllowedIPs=$V4,$v6"

We use tr command to join multiple lines into single line.
Finally, we got the completed Wireguard configuration:

AllowedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,2405:8100::/32,2a06:98c0::/29,2c0f:f248::/32

Now add this line to your wg-quick.conf file or /etc/NetworkManager/system-connections/CONNECTION_ID.nmconnection if you are using NetworkManager on Linux. The Network manager option name is slightly different - allowed-ips, the format is still the same.

OpenVPN

For OpenVPN we will use network routing and change it with the ip route command. Let's create script called vpn-cloudflare.sh:

#!/bin/bash
V4=$(curl -s https://www.cloudflare.com/ips-v4 | tr '\n' ' ')
V6=$(curl -s https://www.cloudflare.com/ips-v6 | tr '\n' ' ')

# VPN gateway
GW=111.111.111.111

for H in $V4
do
ip route add "$H" via "$GW"
done

for H in $V6
do
ip -6 route add "$H" via "$GW"
done

Let's give it executeable permission and run:

chmod +x vpn-cloudflare.sh && sudo ./vpn-cloudlfare.sh

As you can see, there are minimal changes in comparision with Wireguard: we use the tr '\n' ' command to replace newline characters with spaces and use two loops - one with IPv4 addresses, the second with IPv6 addresses.

Let's create another script to undo all changes called vpn-cloudflare-reset.sh:

#!/bin/bash
V4=$(curl -s https://www.cloudflare.com/ips-v4 | tr '\n' ' ')
V6=$(curl -s https://www.cloudflare.com/ips-v6 | tr '\n' ' ')

# VPN gateway
GW=111.111.111.111

for H in $V4
do
ip route del "$H" via "$GW"
done

for H in $V6
do
ip -6 route del "$H" via "$GW"
done

Give it the executeable permission and run:

chmod +x vpn-cloudflare-reset.sh && sudo ./vpn-cloudlfare-reset.sh

Google

Google IP addresses are publicly available in two lists:

Wireguard

Let's extract IP addresses from both lists and convert them into Wireguard configuration format:

LIST1=$(curl -s https://www.gstatic.com/ipranges/goog.json | grep -oP '"*Prefix": "\K(.*)(?=")' | tr '\n' ',')
LIST2=$(curl -s https://www.gstatic.com/ipranges/cloud.json | grep -oP '"*Prefix": "\K(.*)(?=")' | tr '\n' ',')

echo "AllowedIPs=$LIST1,$LIST2"

Regular expressions explanations:

  • \K resets the starting point of the reported match
  • (.*) 1st Capturing Group - matches any characters between zero and unlimited times
  • (?=") positive localhead matches the character "

We got the complete configuration for Wireguard:

AllowedIPs=8.8.4.0/24,8.8.8.0/24,8.34.208.0/20,8.35.192.0/20,23.236.48.0/20...

Now add this line to your wg-quick.conf file or /etc/NetworkManager/system-connections/CONNECTION_ID.nmconnection if you use NetworkManager on Linux.

OpenVPN

The OpenVPN script called vpn-google.sh will be very similar to Cloudflare version:

#!/bin/bash

LIST1V4=$(curl -s https://www.gstatic.com/ipranges/goog.json | grep -oP '"ipv4Prefix": "\K(.*)(?=")' | tr '\n' ',')
LIST2V4=$(curl -s https://www.gstatic.com/ipranges/cloud.json | grep -oP '"ipv4Prefix": "\K(.*)(?=")' | tr '\n' ',')

LIST1V6=$(curl -s https://www.gstatic.com/ipranges/goog.json | grep -oP '"ipv6Prefix": "\K(.*)(?=")' | tr '\n' ',')
LIST2V6=$(curl -s https://www.gstatic.com/ipranges/cloud.json | grep -oP '"ipv6Prefix": "\K(.*)(?=")' | tr '\n' ',')

# VPN gateway
GW=111.111.111.111

for H in "$LIST1V4 $LIST2V4"
do
ip route add "$H" via "$GW"
done

for H in "$LIST1V46 $LIST2V6"
do
ip -6 route add "$H" via "$GW"
done

Let's give it the executeable permission and run:

chmod +x vpn-cloudflare.sh && sudo ./vpn-cloudlfare.sh

China

Well, we hope you are all aware of the privacy and human rights situation in China. If you don't want to give Winnie the Pooh your personal information, it's time to get a VPN.

Chinese IP can be obtained from several sources, but we recommend the nice repository on Github - 17mon/china_ip_list.

Wireguard

LIST=$(curl -s -L https://github.com/17mon/china_ip_list/raw/master/china_ip_list.txt | tr '\n' ',')
echo "AllowedIPs=$LIST"

We're using tr command to join multiple lines into single line. And got the Wireguard configuration:

AllowedIPs=1.0.1.0/24,1.0.2.0/23,1.0.8.0/21,1.0.32.0/19,1.1.0.0/24...

Now add this line to your wg-quick.conf file or /etc/NetworkManager/system-connections/CONNECTION_ID.nmconnection if you use NetworkManager on Linux.

OpenVPN

For OpenVPN we will use network routing and change it with the ip route command. Let's create script called vpn-cloudflare.sh:

#!/bin/bash

LIST=$(curl -s https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt | tr '\n' ' ')

# VPN gateway
GW=111.111.111.111

for H in $LIST
do
ip route add "$H" via "$GW"
done

Let's give it the executeable permission and run:

chmod +x vpn-china.sh && sudo ./vpn-china.sh

Let's create the another sript to undo all changes called vpn-china-reset.sh:

#!/bin/bash

LIST=$(curl -s https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt | tr '\n' ' ')


# VPN gateway
GW=111.111.111.111

for H in "$LIST"
do
ip route del "$H" via "$GW"
done

Give it the executeable permission and run:

chmod +x vpn-china-reset.sh && sudo ./vpn-cloudlfare-reset.sh

How to exclude Netflix only traffic VPN Gateway?

If you're using VPN - Netflix hates you! They invest a lot of resources for VPN detection and improve it regularly. We can't call them the greedy service 'cause different regions with different prices and different laws, so Netflix should handle this well.

All Netflix IP addresses are available on their Help Center.

OpenVPN

Let's collect all IP addresses from Neflix website and store them in a variable called ADDRS. GW is the default gateway when VPN is down ('cause we will route all Netflix traffix through this gateway), you can get it from the ip route show command output. METRIC is for route priority, lower is better.

And save all into script called netflix-out-vpn.sh:

#!/bin/sh

ADDRS="52.0.131.132/32 3.221.228.214/32 18.207.84.236 54.204.25.0/28 23.23.189.144/28 34.195.253.0/25 52.89.201.64/32 54.149.90.167/32 54.186.44.22/32 54.201.152.16 35.165.74.28/32 35.167.141.219/32 44.224.174.227/32"
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  sudo ip route add "$IP" via "$GW" metric "$METRIC"
done

To undo all changes, let's create another script called netflix-out-vpn-reset.sh:

#!/bin/sh

ADDRS="52.0.131.132/32 3.221.228.214/32 18.207.84.236 54.204.25.0/28 23.23.189.144/28 34.195.253.0/25 52.89.201.64/32 54.149.90.167/32 54.186.44.22/32 54.201.152.16 35.165.74.28/32 35.167.141.219/32 44.224.174.227/32"
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  sudo ip route del "$IP" via "$GW" metric "$METRIC"
done

Wireguard

Wireguard config supports PostUp and PreDown route options. For example:

PostUp = ip route add 192.168.1.42/24 via 192.168.1.1, ip route add 192.168.1.42/32 via 192.168.1.1;

PreDown is similar, so we can use ip route del commands here.

Let's rewrite our netflix-out-vpn.sh script to get the generated PostUp and PreDown lines.

#!/bin/sh

ADDRS="52.0.131.132/32 3.221.228.214/32 18.207.84.236 54.204.25.0/28 23.23.189.144/28 34.195.253.0/25 52.89.201.64/32 54.149.90.167/32 54.186.44.22/32 54.201.152.16 35.165.74.28/32 35.167.141.219/32 44.224.174.227/32"
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  echo "PostUp=ip route add $i via $GW metric $METRIC;"
  echo "PreDown=ip route del $i via $GW metric $METRIC;"
done

We are going to get a big list of PostUp and PreDown commands, just copy it and paste it into your Wireguard configuration file.

How to exclude Microsoft Exchange only traffic from a VPN Gateway?

The Exchange services are not happy when you use them with VPN, so we can make your life easier with better routing.

OpenVPN

For OpenVPN we'll reuse our Netflix knownledge with ADDRS, GW and METRIC. MS Exchange IP subnet list can be found here, this list also available in JSON format.

We need to copy all IPv4 addresses to the ADDRS and all IPv6 addresses to the ADDRS6 variables.
Save all the logic in to exchange-out-vpn.sh script:

#!/bin/sh

ADDRS="13.107.6.152/31 13.107.18.10/31 13.107.128.0/22"
ADDRS6="2620:1ec:4::152/128 2620:1ec:4::153/128 2620:1ec:c::10/128"
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  sudo ip route add "$IP" via "$GW" metric "$METRIC"
done

for IP in $ADDRS6
do
  sudo ip -6 route add "$IP" via "$GW" metric "$METRIC"
done

And exchange-out-vpn-reset.sh script to undo all changes:

#!/bin/sh

ADDRS="13.107.6.152/31 13.107.18.10/31 13.107.128.0/22"
ADDRS6="2620:1ec:4::152/128 2620:1ec:4::153/128 2620:1ec:c::10/128"
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  sudo ip route del "$IP" via "$GW" metric "$METRIC"
done

for IP in $ADDRS6
do
  sudo ip -6 route del "$IP" via "$GW" metric "$METRIC"
done

Wireguard

Wireguard is very similar to the Netflix section, but here we need handle IPv6.

#!/bin/sh

ADDRS="13.107.6.152/31 13.107.18.10/31 13.107.128.0/22"
ADDRS6="2620:1ec:4::152/128 2620:1ec:4::153/128 2620:1ec:c::10/128"
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  echo "PostUp=ip route add $i via $GW metric $METRIC;"
  echo "PreDown=ip route del $i via $GW metric $METRIC;"
done

for IP in $ADDRS6
do
  echo "PostUp=ip -6 route add $i via $GW metric $METRIC;"
  echo "PreDown=ip -6 route del $i via $GW metric $METRIC;"
done

You can now copy the output to the Wireguard configuration file.

How to exclude local Chinese traffic from a VPN Gateway when connecting to outside from China?

Wireguard

Create the next script:

#!/bin/sh

ADDRS=$(curl -s -L https://github.com/17mon/china_ip_list/raw/master/china_ip_list.txt | tr '\n' ' ')
GW=192.168.1.1
METRIC=7

for IP in $ADDRS
do
  echo "PostUp=ip route add $i via $GW metric $METRIC;"
  echo "PreDown=ip route del $i via $GW metric $METRIC;"
done

We're using tr command to join multiple lines into single line and printing multiple PostUp and PreDown options for each subnet.

Now run this script and lines from the output to your wg-quick.conf file or /etc/NetworkManager/system-connections/CONNECTION_ID.nmconnection if you use NetworkManager on Linux.

OpenVPN

For OpenVPN we will use network routing and change it with the ip route command. Make sure your gateway works when VPN is down, check it with ip route show command. Let's create script called vpn-cloudflare.sh:

#!/bin/bash

LIST=$(curl -s https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt | tr '\n' ' ')

# VPN gateway
GW=192.168.1.1

# Metric
METRIC=7


for H in $LIST
do
ip route add "$H" via "$GW" metric "$METRIC"
done

Let's give it the executeable permission and run:

chmod +x vpn-china.sh && sudo ./vpn-china.sh

And create the another sript to undo all changes called vpn-china-reset.sh:

#!/bin/bash

LIST=$(curl -s https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt | tr '\n' ' ')


# VPN gateway
GW=192.168.1.1

for H in "$LIST"
do
ip route del "$H" via "$GW"
done

Give it the executeable permission and run:

chmod +x vpn-china-reset.sh && sudo ./vpn-cloudlfare-reset.sh

Stay safe and thank you for reading!

Read more