{"id":765,"date":"2022-04-10T22:29:43","date_gmt":"2022-04-10T20:29:43","guid":{"rendered":"https:\/\/caipirinha.spdns.org\/wp\/?p=765"},"modified":"2023-03-18T21:41:20","modified_gmt":"2023-03-18T20:41:20","slug":"setting-up-client-vpns-policy-routing","status":"publish","type":"post","link":"https:\/\/caipirinha.spdns.org\/wp\/?p=765","title":{"rendered":"Setting up Client VPNs, Policy Routing"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"executive-summary\">Executive Summary<\/h2>\n\n\n\n<p>This blog post is the continuation my previous blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a> and explains how I use <strong>client VPNs<\/strong> together with simple <strong>Policy Routing<\/strong> on my Linux server in order to relegate outgoing connections to various network interfaces and, ultimately, to different countries. The examples use IPv4 only.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"background\">Background<\/h2>\n\n\n\n<p>The approach was originally developed back in 2011&#8230;2014 when I lived in China and maintained several outgoing VPN connections from my Linux server to end points &#8220;in the West&#8221; so that I could circumvent internet censorship in China [<a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=56\" target=\"_blank\">8<\/a>]. With the VPN service described <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a>, it was then possible for me to be in town and to connect the smartphone to my Linux server (in the same town).  From there, the connections to sites blocked in China would run over the client VPNs of the Linux server so that I could use Google Maps on my smartphone, for example (which at that time had already been blocked in China).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"preconditions\">Preconditions<\/h2>\n\n\n\n<p>Routing in Linux follows some very clever approaches which can be combined in mighty ways. Those readers who want to understand <em>all<\/em> of the underlying theory, are encouraged to study the (older) documents [<a rel=\"noreferrer noopener\" href=\"https:\/\/tldp.org\/HOWTO\/Adv-Routing-HOWTO\/index.html\" target=\"_blank\">1<\/a>], [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.frozentux.net\/iptables-tutorial\/iptables-tutorial.html\" target=\"_blank\">2<\/a>], [<a rel=\"noreferrer noopener\" href=\"http:\/\/linux-ip.net\/html\/\" target=\"_blank\">3<\/a>], even if <em>parts<\/em> of the content might not be relevant any more. Those readers who just want to follow and replicate the approach in this blog, should <em>at least<\/em> study the documents [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\">4<\/a>], [<a rel=\"noreferrer noopener\" href=\"http:\/\/www.policyrouting.org\/PolicyRoutingBook\/ONLINE\/TOC.html\" target=\"_blank\">5<\/a>], [<a rel=\"noreferrer noopener\" href=\"https:\/\/ro-che.info\/articles\/2021-02-27-linux-routing\" target=\"_blank\">6<\/a>].<\/p>\n\n\n\n<p>Apart from that, in order to replicate the approach described here, you should:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>&#8230; fulfil all preconditions listed in the blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a><\/li>\n\n\n\n<li>&#8230; have running the setup similar to the one described in the blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a><\/li>\n\n\n\n<li>&#8230; have access to a commercial VPN provider allowing you to run several client connections on the same machine<\/li>\n\n\n\n<li>&#8230; have at least read the documents [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\">4<\/a>], [<a rel=\"noreferrer noopener\" href=\"http:\/\/www.policyrouting.org\/PolicyRoutingBook\/ONLINE\/TOC.html\" target=\"_blank\">5<\/a>], [<a rel=\"noreferrer noopener\" href=\"https:\/\/ro-che.info\/articles\/2021-02-27-linux-routing\" target=\"_blank\">6<\/a>]<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"description-and-usage\">Description and Usage<\/h2>\n\n\n\n<p>The graph below shows the setup on my machine caipirinha.spdns.org with. The 5 <strong>VPN services<\/strong> (blue, green color) were already described in blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a>. Now, we have a close look at the 3 <strong>VPN clients<\/strong> which use a commercial VPN service (ocker color) in order to connect to VPN end points in 3 different countries (Portugal, Singapore, Thailand).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/VPN-Diagramm-2-1024x576.jpg\" alt=\"\" class=\"wp-image-800\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/VPN-Diagramm-2-1024x576.jpg 1024w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/VPN-Diagramm-2-300x169.jpg 300w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/VPN-Diagramm-2-768x432.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/VPN-Diagramm-2.jpg 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">VPN Setup on my Linux server<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"enabling-routing\">Enabling Routing<\/h3>\n\n\n\n<p>Routing needs to be enabled on the Linux server. I personally also decided to switch off the privacy extensions on the Linux server, but that is a personal matter of taste:<\/p>\n\n\n\n<pre id=\"enable-loose-reverse-path-filtering-and-prohibit-icmp-redirects\" class=\"wp-block-code\"><code># Enable \"loose\" reverse path filtering and prohibit icmp redirects\nsysctl -w net.ipv4.conf.all.rp_filter=2\nsysctl -w net.ipv4.conf.all.send_redirects=0\nsysctl -w net.ipv4.conf.eth0.send_redirects=0\nsysctl -w net.ipv4.icmp_errors_use_inbound_ifaddr=1\n\n# Enable IPv6 routing, but keep SLAAC for eth0\nsysctl -w net.ipv6.conf.eth0.accept_ra=2\nsysctl -w net.ipv6.conf.all.forwarding=1\n\n# Switch off the privacy extensions\nsysctl -w net.ipv6.conf.eth0.use_tempaddr=0<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Routing Tables<\/h3>\n\n\n\n<p>We now must have a closer look at the concept of the <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Routing_table\" target=\"_blank\">routing table<\/a>. A routing tables basically lists routes to particular network destinations. An example is the routing table <strong>main<\/strong> on my Linux server. It reads:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>ip route list table main<\/strong>\ndefault via 192.168.2.1 dev eth0 proto dhcp\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 dev tun4 proto kernel scope link src 192.168.10.1\n192.168.11.0\/24 dev tun5 proto kernel scope link src 192.168.11.1\n192.168.12.0\/24 dev tun6 proto kernel scope link src 192.168.12.1\n192.168.13.0\/24 dev tun7 proto kernel scope link src 192.168.13.1\n192.168.14.0\/24 dev wg0 proto kernel scope link src 192.168.14.1<\/code><\/pre>\n\n\n\n<p>This table has 7 entries, and they have this meaning:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>(&#8220;default via&#8230;&#8221;) Connections to IP addresses that do <strong>not have<\/strong> a corresponding entry in the routing table shall be forwarded via the interface <strong>eth0<\/strong> and to the router IP address <em>192.168.2.1<\/em> (an <a rel=\"noreferrer noopener\" href=\"https:\/\/avm.de\/produkte\/fritzbox\/\" target=\"_blank\">AVM Fritz! Box<\/a>).<\/li>\n\n\n\n<li>Connections to the network <em>192.168.2.0\/24<\/em> shall be forwarded via the interface <strong>eth0<\/strong> using the source IP address <em>192.168.2.3<\/em> (the Linux server itself).<\/li>\n\n\n\n<li>Connections to the network <em>192.168.10.0\/24<\/em> shall be forwarded via the interface <strong>tun4<\/strong> using the source IP address <em>192.168.10.1<\/em> (the Linux server itself). This network belongs to one of the 5 VPN services on my Linux server.<\/li>\n\n\n\n<li>Connections to the network <em>192.168.11.0\/24<\/em> shall be forwarded via the interface <strong>tun5<\/strong> using the source IP address <em>192.168.11.1<\/em> (the Linux server itself). This network belongs to one of the 5 VPN services on my Linux server.<\/li>\n\n\n\n<li>Connections to the network <em>192.168.12.0\/24<\/em> shall be forwarded via the interface <strong>tun6<\/strong> using the source IP address <em>192.168.12.1<\/em> (the Linux server itself). This network belongs to one of the 5 VPN services on my Linux server.<\/li>\n\n\n\n<li>Connections to the network <em>192.168.13.0\/24<\/em> shall be forwarded via the interface <strong>tun7<\/strong> using the source IP address <em>192.168.13.1<\/em> (the Linux server itself). This network belongs to one of the 5 VPN services on my Linux server.<\/li>\n\n\n\n<li>Connections to the network <em>192.168.14.0\/24<\/em> shall be forwarded via the interface <strong>wg0<\/strong> using the source IP address <em>192.168.14.1<\/em> (the Linux server itself). This network belongs to one of the 5 VPN services on my Linux server.<\/li>\n<\/ol>\n\n\n\n<p>Usually, a routing table should have a <strong>default<\/strong> entry which sends all IP traffic that is not explicitly routed to other network interfaces to the <em>default router<\/em> of a network. Otherwise, no meaningful internet access is possible.<\/p>\n\n\n\n<p>A Linux system can have up to 256 routing tables which are defined in <strong>\/etc\/iproute2\/rt_tables<\/strong>. They can either be used by their number or by their name. On my Linux server, I have set up 3 additional routing tables, named &#8220;Portugal&#8221;, &#8220;Singapur&#8221;, &#8220;Thailand&#8221;. You can see in the file <strong>\/etc\/iproute2\/rt_tables<\/strong> that besides the table <strong>main<\/strong>, the tables <strong>local<\/strong>, <strong>default<\/strong>, and <strong>unspec<\/strong> do already exist, but they are not of interest for our purposes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#\n# reserved values\n#\n255 local\n254 main\n253 default\n0 unspec\n#\n# local\n#\n#1  inr.ruhep\n\n240 Portugal\n241 Singapur\n242 Thailand<\/code><\/pre>\n\n\n\n<p>Right now (before we set up the client VPNs), all 3 routing tables look the same as shown here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>ip route list table Portugal<\/strong>\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 via 192.168.10.1 dev tun4\n192.168.11.0\/24 via 192.168.11.1 dev tun5\n192.168.12.0\/24 via 192.168.12.1 dev tun6\n192.168.13.0\/24 via 192.168.13.1 dev tun7\n192.168.14.0\/24 via 192.168.14.1 dev wg0\ncaipirinha:~ # <strong>ip route list table Singapur<\/strong>\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 via 192.168.10.1 dev tun4\n192.168.11.0\/24 via 192.168.11.1 dev tun5\n192.168.12.0\/24 via 192.168.12.1 dev tun6\n192.168.13.0\/24 via 192.168.13.1 dev tun7\n192.168.14.0\/24 via 192.168.14.1 dev wg0\ncaipirinha:~ # <strong>ip route list table Thailand<\/strong>\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 via 192.168.10.1 dev tun4\n192.168.11.0\/24 via 192.168.11.1 dev tun5\n192.168.12.0\/24 via 192.168.12.1 dev tun6\n192.168.13.0\/24 via 192.168.13.1 dev tun7\n192.168.14.0\/24 via 192.168.14.1 dev wg0<\/code><\/pre>\n\n\n\n<p>The content of routing tables can be listed with the command <strong>ip route list table ${tablename}<\/strong>, and <strong>${tablename}<\/strong> needs to exist in <strong>\/etc\/iproute2\/rt_tables<\/strong>. It is important to notice that so far, none of these 3 routing tables have a default route. They only contain the home network and the networks of the 5 VPN services. Right now, these tables are not yet useful. In case you wonder how it comes that these 3 routing tables are populated with their entries. That needs to done either manually or by a script (see next chapter). <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">OpenVPN Server Configuration (Update)<\/h3>\n\n\n\n<p>Now that we have 3 additional routing tables, we must ensure that the networks of our 5 VPN services are also inserted in these 3 routing tables. Therefore, we modify the configuration files described in the blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a> so that a script runs when the VPN service is started. In the configuration files for the&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a> configuration, we insert the statement:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>up \/etc\/openvpn\/start_vpn.sh<\/code><\/pre>\n\n\n\n<p>In the configuration files for the&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/www.wireguard.com\/\" target=\"_blank\">WireGuard<\/a>&nbsp;configuration, we insert the statement:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PostUp = \/etc\/openvpn\/start_vpn.sh %i - - 192.168.14.1<\/code><\/pre>\n\n\n\n<p>The effect of these statements is that the script <strong>\/etc\/openvpn\/start_vpn.sh<\/strong> is executed when the VPN service has been set up. If no arguments are specified, <a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a> hands over 5 arguments to the scripts (see [<a rel=\"noreferrer noopener\" href=\"https:\/\/openvpn.net\/community-resources\/reference-manual-for-openvpn-2-4\/\" target=\"_blank\">9<\/a>], section &#8220;&#8211;up cmd&#8221;). In the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.wireguard.com\/\" target=\"_blank\">WireGuard<\/a>&nbsp;configuration, we have to explicitly specify the arguments, the &#8220;%i&#8221; means the interface (see [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.man7.org\/linux\/man-pages\/man8\/wg-quick.8.html\" target=\"_blank\">10<\/a>], &#8220;PostUp&#8221;). In my case, &#8220;%i&#8221; hence stands for <strong>wg0<\/strong>.<\/p>\n\n\n\n<p>The script <strong>\/etc\/openvpn\/start_vpn.sh<\/strong> was originally developed for the <a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a> configuration and therefore intakes all the default arguments that <a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a> transmits, although only the first and the fourth argument are used. Therefore, in the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.wireguard.com\/\" target=\"_blank\">WireGuard<\/a>&nbsp;configuration, there are two &#8220;-&#8221; inserted as bogus arguments. That is surely something that can be solved more elegantly.<\/p>\n\n\n\n<p>What does this script do? It essentially writes the same entry that is done automatically in the routing table <strong>main<\/strong> to the 3 additional routing tables <strong>Portugal<\/strong>, <strong>Singapur<\/strong>, and <strong>Thailand<\/strong>. It assumes that VPN services have a \/24 network (true in my own case, not necessarily for other setups).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n#\n# This script sets the VPN parameters in the routing tables \"Portugal\", \"Singapur\" and \"Thailand\" once the server has been started successfully.\n\n# Set the correct PATH environment\nPATH='\/sbin:\/usr\/sbin:\/bin:\/usr\/bin'\n\nVPN_DEV=\"${1}\"\nVPN_SRC=\"${4}\"\nVPN_NET=$(echo \"${VPN_SRC}\" | cut -d . -f 1-3)\".0\/24\"\n\nfor TABLE in Portugal Singapur Thailand; do\n    ip route add ${VPN_NET} dev ${VPN_DEV} via ${VPN_SRC} table ${TABLE}\ndone<\/code><\/pre>\n\n\n\n<p>For our experiments, we now also need to allocate 3 dedicated IP addresses to 3 devices in one of the VPN services on the Linux server so that the devices always get the same IP address by the VPN service when they connect (pseudo-static IP configuration). As described in the blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a>, section &#8220;Dedicated Configurations&#8221;, we can achieve this by creating 3 files with the common names of the devices (gabriel-SM-G991B, gabriel-SM-N960F, gabriel-SM-T580) that were used to create their certificates. I did that for the UDP-based VPN, full tunneling <a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a>, and the 3 configuration files are listed here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>cat \/etc\/openvpn\/conf-1194\/gabriel-SM-G991B<\/strong>\n# Spezielle Konfigurationsdatei f\u00fcr Gabriels Galaxy S20 (gabriel-SM-G991B)\n#\n\nifconfig-push      192.168.10.250  255.255.255.0\nifconfig-ipv6-push fd01:0:0:a:0:0:1:fa\/111 fd01:0:0:a::1\ncaipirinha:~ # <strong>cat \/etc\/openvpn\/conf-1194\/gabriel-SM-N960F<\/strong>\n# Spezielle Konfigurationsdatei f\u00fcr Gabriels Galaxy Note 9 (gabriel-SM-N960F)\n#\n\nifconfig-push      192.168.10.251  255.255.255.0\nifconfig-ipv6-push fd01:0:0:a:0:0:1:fb\/111 fd01:0:0:a::1\ncaipirinha:~ # <strong>cat \/etc\/openvpn\/conf-1194\/gabriel-SM-T580<\/strong>\n# Spezielle Konfigurationsdatei f\u00fcr Gabriels Galaxy Tablet A (gabriel-SM-T580)\n#\n\nifconfig-push      192.168.10.252  255.255.255.0\nifconfig-ipv6-push fd01:0:0:a:0:0:1:fc\/111 fd01:0:0:a::1<\/code><\/pre>\n\n\n\n<p>One can easily identify the respective IPv4 and IPv6 addresses which shall be allocated to the 3 named devices:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>gabriel-SM-G991B<\/em> shall get the IPv4 <em>192.168.10.250<\/em> and the IPv6 <em>fd01:0:0:a:0:0:1:fa<\/em>.<\/li>\n\n\n\n<li><em>gabriel-SM-N960F<\/em> shall get the IPv4 <em>192.168.10.251<\/em> and the IPv6 <em>fd01:0:0:a:0:0:1:fb<\/em>.<\/li>\n\n\n\n<li><em>gabriel-SM-T580<\/em> shall get the IPv4 <em>192.168.10.252<\/em> and the IPv6 <em>fd01:0:0:a:0:0:1:fc<\/em>.<\/li>\n<\/ul>\n\n\n\n<p>Let us not forget that this is the configuration for only one out of the 5 VPN services. If the devices connect to a VPN service different from the UDP-based VPN, full tunneling <a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a>, then, these configurations do not have any effect.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"client-configuration\">OpenVPN Client Configuration<\/h3>\n\n\n\n<p>For the experiments below, we will set up 3 client VPN connections to different countries. As I do not have infrastructure outside of Germany, I use a commercial VPN provider, in my case this is <a rel=\"noreferrer noopener\" href=\"https:\/\/www.purevpn.com\/\" target=\"_blank\">PureVPN\u2122<\/a> (as I once got an affordable 5-years subscription). Choosing a suitable VPN provider is not easy, and I strongly recommend to research test reports and forums which deal with the configuration on Linux before you choose any subscription to a commercial VPN provider. In my case, the provider (<a rel=\"noreferrer noopener\" href=\"https:\/\/www.purevpn.com\/\" target=\"_blank\">PureVPN\u2122<\/a>) offers <a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a> Linux configuration as a download. I just had to make some modifications as otherwise, the VPN wants to be the default connection for all internet traffic; this is not what we want when we do our own policy routing. I chose the TCP configuration as the UDP configuration, which is normally preferred, did not run in a stable fashion at the time of writing this article. The client configuration files also contain the <strong>ca<\/strong>, the <strong>certificate<\/strong>, and the <strong>key<\/strong> file at the end (not shown here).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"udp-based-vpn-full-tunneling-1\">TCP-based split VPN to Portugal<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code># Konfigurationsdatei f\u00fcr den openVPN-Client auf CAIPIRINHA zur Verbindung nach PureVPN (Portugal)\n\nauth-user-pass       \/etc\/openvpn\/purevpn.login\nauth-nocache \nauth-retry           nointeract\nclient\ncomp-lzo\ndev                  tun0\nifconfig-nowarn\nkey-direction        1\nlog                  \/var\/log\/openvpn_PT.log\nlport                5456\nmute                 20\nproto                tcp\npersist-key\npersist-tun\nremote               pt2-auto-tcp.ptoserver.com 80\nremote-cert-tls      server\nroute-nopull\nscript-security      2\nstatus               \/var\/run\/openvpn\/status_PT\nup                   \/etc\/openvpn\/start_purevpn.sh\ndown                 \/etc\/openvpn\/stop_purevpn.sh\nverb                 3\n\n&lt;ca>\n-----BEGIN CERTIFICATE-----\nMIIE6DCCA9CgAwIBAgIJAMjXFoeo5uSlMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYD\n...\n4ZjTr9nMn6WdAHU2\n-----END CERTIFICATE-----\n&lt;\/ca>\n&lt;cert>\n-----BEGIN CERTIFICATE-----\nMIIEnzCCA4egAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBqDELMAkGA1UEBhMCSEsx\n...\n21oww875KisnYdWjHB1FiI+VzQ1\/gyoDsL5kPTJVuu2CoG8=\n-----END CERTIFICATE-----\n&lt;\/cert>\n&lt;key>\n-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbJ8p+L+scQz57g\n...\nd7q7xhec5WHlng==\n-----END PRIVATE KEY-----\n&lt;\/key>\n&lt;tls-auth>\n#\n# 2048 bit OpenVPN static key\n#\n-----BEGIN OpenVPN Static key V1-----\ne30af995f56d07426d9ba1f824730521\n...\ndd94498b4d7133d3729dd214a16b27fb\n-----END OpenVPN Static key V1-----\n&lt;\/tls-auth><\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"tcp-based-vpn-full-tunneling-1\">TCP-based split VPN to Singapore<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code># Konfigurationsdatei f\u00fcr den openVPN-Client auf CAIPIRINHA zur Verbindung nach PureVPN (Singapur)\n\nauth-user-pass       \/etc\/openvpn\/purevpn.login\nauth-nocache\nauth-retry           nointeract\nclient\ncomp-lzo\ndev                  tun1\nifconfig-nowarn\nkey-direction        1\nlog                  \/var\/log\/openvpn_SG.log\nlport                5457\nmute                 20\nproto                tcp\npersist-key\npersist-tun\nremote               sg2-auto-tcp.ptoserver.com 80\nremote-cert-tls      server\nroute-nopull\nscript-security      2\nstatus               \/var\/run\/openvpn\/status_SG\nup                   \/etc\/openvpn\/start_purevpn.sh\ndown                 \/etc\/openvpn\/stop_purevpn.sh\nverb                 3\n...<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">TCP-based split VPN to Thailand<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code># Konfigurationsdatei f\u00fcr den openVPN-Client auf CAIPIRINHA zur Verbindung nach PureVPN (Thailand)\n\nauth-user-pass       \/etc\/openvpn\/purevpn.login\nauth-nocache\nauth-retry           nointeract\nclient\ncomp-lzo\ndev                  tun2\nifconfig-nowarn\nkey-direction        1\nlog                  \/var\/log\/openvpn_TH.log\nlport                5458\nmute                 20\nproto                tcp\npersist-key\npersist-tun\nremote               th2-auto-tcp.ptoserver.com 80\nremote-cert-tls      server\nroute-nopull\nscript-security      2\nstatus               \/var\/run\/openvpn\/status_TH\nup                   \/etc\/openvpn\/start_purevpn.sh\ndown                 \/etc\/openvpn\/stop_purevpn.sh\nverb                 3\n...<\/code><\/pre>\n\n\n\n<p>I stored these configurations in the files:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\/etc\/openvpn\/client_PT.conf<\/li>\n\n\n\n<li>\/etc\/openvpn\/client_SG.conf<\/li>\n\n\n\n<li>\/etc\/openvpn\/client_TH.conf<\/li>\n<\/ul>\n\n\n\n<p>Let us discuss some configuration items:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>auth-user-pass<\/strong> refers to the file <strong>\/etc\/openvpn\/purevpn.login<\/strong> which contains the login and password for my VPN service. It is referenced here so that I do not have to enter them when I start the connection or when the connection restarts after a breakdown.<\/li>\n\n\n\n<li><strong>cipher<\/strong> refers to an algorithm that <a rel=\"noreferrer noopener\" href=\"https:\/\/www.purevpn.com\/\" target=\"_blank\">PureVPN\u2122<\/a> uses on their server side.<\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/www.purevpn.com\/\" target=\"_blank\">PureVPN\u2122<\/a> also uses compression on the VPN connection, and this is turned on by the line <strong>comp-lzo<\/strong>.<\/li>\n\n\n\n<li>As we want to do policy routing, we need to know which VPN we are dealing with. Therefore, I attribute a dedicated <strong>tun<\/strong> device as well as a dedicated <strong>lport<\/strong> (source port) to each connection.<\/li>\n\n\n\n<li><strong>remote<\/strong> names the server and port given in the downloaded configuration files.<\/li>\n\n\n\n<li><strong>route-nopull<\/strong> is very important as otherwise, the default route would be changed. However, for our purposes, we do not want any routes to be changed automatically, we will do that by policy routing later.<\/li>\n\n\n\n<li><strong>up<\/strong> and <strong>down<\/strong> name a start and a stop script. The start script is executed after the connection has been established, and the stop script is executed when the connection is disbanded. As the scripts use various command, we need to set <strong>script-security<\/strong> accordingly.<\/li>\n\n\n\n<li>The initial configuration always takes some time, and so I have set <strong>verb<\/strong> to &#8220;3&#8221; in order to have more verbosity in the log file, for debugging purposes.<\/li>\n<\/ul>\n\n\n\n<p>Let&#8217;s now look at the start script <strong>\/etc\/openvpn\/start_purevpn.sh<\/strong>. This script depends on the installation of the tool library <strong>ipcalc<\/strong> as this library eases some computations.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n#\n# This script sets the VPN parameters in the routing tables \"main\", \"Portugal\", \"Singapur\" and \"Thailand\" once the connection has been successfully established.\n# This script requires the tool \"ipcalc\" which needs to be installed on the target system.\n\n# Set the correct PATH environment\nPATH='\/sbin:\/usr\/sbin:\/bin:\/usr\/bin'\n\nVPN_DEV=$1\nVPN_SRC=$4\nVPN_MSK=$5\n\nVPN_GW=$(ipcalc ${VPN_SRC}\/${VPN_MSK} | sed -n 's\/^HostMin:\\s*\\(&#91;0-9]\\{1,3\\}\\.&#91;0-9]\\{1,3\\}\\.&#91;0-9]\\{1,3\\}\\.&#91;0-9]\\{1,3\\}\\).*\/\\1\/p')\nVPN_NET=$(ipcalc ${VPN_SRC}\/${VPN_MSK} | sed -n 's\/^Network:\\s*\\(&#91;0-9]\\{1,3\\}\\.&#91;0-9]\\{1,3\\}\\.&#91;0-9]\\{1,3\\}\\.&#91;0-9]\\{1,3\\}\\\/&#91;0-9]\\{1,2\\}\\).*\/\\1\/p')\n\ncase \"${VPN_DEV}\" in\n  \"tun0\") ROUTING_TABLE='Portugal';;\n  \"tun1\") ROUTING_TABLE='Singapur';;\n  \"tun2\") ROUTING_TABLE='Thailand';;\nesac\n\niptables -t filter -A INPUT   -i ${VPN_DEV} -m state --state NEW,INVALID -j DROP\niptables -t filter -A FORWARD -i ${VPN_DEV} -m state --state NEW,INVALID -j DROP\n\nip route add ${VPN_NET} dev ${VPN_DEV} proto kernel scope link src ${VPN_SRC} table ${ROUTING_TABLE}\nip route replace default dev ${VPN_DEV} via ${VPN_GW} table ${ROUTING_TABLE}<\/code><\/pre>\n\n\n\n<p>What does this script do? It executes these steps:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It blocks connections with the state NEW or INVALID in the <strong>filter<\/strong> chains <strong>INPUT<\/strong> and <strong>FORWARD<\/strong>. Later (down in this article), this shall be explained more in detail. For now, it suffices to know that we want to avoid those connections that originate from the commercial VPN network shall be blocked. We must keep in mind that by using commercial VPN connections, we make the Linux server vulnerable to connections that might come from these networks. If everything was correctly configured on the side of the VPN provider, there should never be such a connection that originates from the network because individual VPN users should not be able to &#8220;see&#8221; each other. There should only be connections that originate from our Linux server, and subsequently, we will get reply packets, of course, and have a bidirectional communication. Nevertheless, my own experience with various VPN providers has shown that there is a certain amount of unrelated stray packets that reach the Linux server, and I want to filter those out.<\/li>\n\n\n\n<li>It adds the client network (here, a \/27 network) to the respective routing table Portugal, Singapore, or Thailand.<\/li>\n\n\n\n<li>It sets the default route in the respective routing table to the VPN endpoint. Ultimately, every routing table gets a default route if all 3 client VPNs are engaged. I use <em>ip route replace<\/em> rather than <em>ip route add<\/em> because <em>ip route replace<\/em> does not throw an error if there is already a default route in the routing table.<\/li>\n<\/ul>\n\n\n\n<p>Consequently, the script <strong>\/etc\/openvpn\/stop_purevpn.sh<\/strong> serves to clean up the entries in the <strong>filter<\/strong> table. We do not have to remove the entries in the 3 additional routing tables as they disappear automatically when the VPN connection is disbanded. This script is somewhat smaller:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n#\n# This script removes some routing table entries when the connection is terminated.\n\n# Set the correct PATH environment\nPATH='\/sbin:\/usr\/sbin:\/bin:\/usr\/bin'\n\nVPN_DEV=$1\n\niptables -t filter -D INPUT   -i ${VPN_DEV} -m state --state NEW,INVALID -j DROP\niptables -t filter -D FORWARD -i ${VPN_DEV} -m state --state NEW,INVALID -j DROP<\/code><\/pre>\n\n\n\n<p>Now, that we have all these pieces together, we start the 3 client VPNs with the commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl start openvpn@client_PT\nsystemctl start openvpn@client_SG\nsystemctl start openvpn@client_TH<\/code><\/pre>\n\n\n\n<p>After some seconds, the 3 client VPN connections should have fully been set up, and the respective network devices <strong>tun0<\/strong>, <strong>tun1<\/strong>, <strong>tun2<\/strong> should exist. Similar to what was described in the blog post <a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=734\" target=\"_blank\">Setting up Dual Stack VPNs<\/a>, we must configure network address translation for the 3 client VPNs so that outgoing packets get modified in a way that they have the source IP address of the Linux server for the specific interface over which those packets shall travel. That is done with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE\niptables -t nat -A POSTROUTING -o tun1 -j MASQUERADE\niptables -t nat -A POSTROUTING -o tun2 -j MASQUERADE<\/code><\/pre>\n\n\n\n<p>We use <strong>MASQUERADE<\/strong> in this case because the IP address of the Linux server can change at each VPN connection, and we do not know the source address beforehand. Otherwise <strong>SNAT<\/strong> would be the better option that consumes less CPU power.<\/p>\n\n\n\n<p>Now, we should be able to ping a machine (in this example, <a href=\"https:\/\/www.google.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Google<\/a>&#8216;s DNS) via each of the 3 client VPN connections, as shown here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>ping -c 3 -I tun0 8.8.8.8<\/strong>\nPING 8.8.8.8 (8.8.8.8) from 172.17.66.34 tun0: 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=119 time=57.7 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=119 time=54.5 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=119 time=54.7 ms\n\n--- 8.8.8.8 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min\/avg\/max\/mdev = 54.516\/55.649\/57.727\/1.483 ms\ncaipirinha:~ # <strong>ping -c 3 -I tun1 8.8.8.8<\/strong>\nPING 8.8.8.8 (8.8.8.8) from 10.12.42.41 tun1: 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=58 time=249 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=58 time=247 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=58 time=247 ms\n\n--- 8.8.8.8 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2002ms\nrtt min\/avg\/max\/mdev = 247.120\/247.972\/249.111\/1.015 ms\ncaipirinha:~ # <strong>ping -c 3 -I tun2 8.8.8.8<\/strong>\nPING 8.8.8.8 (8.8.8.8) from 10.31.6.38 tun2: 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=13.9 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=14.2 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=117 time=22.6 ms\n\n--- 8.8.8.8 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2001ms\nrtt min\/avg\/max\/mdev = 13.910\/16.934\/22.641\/4.039 ms\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>traceroute -i eth0 8.8.8.8<\/strong>\ntraceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets\n 1  Router-EZ (192.168.2.1)  3.039 ms  2.962 ms  2.927 ms\n 2  fra1813aihr002.versatel.de (62.214.63.145)  15.440 ms  16.978 ms  18.866 ms\n 3  62.72.71.113 (62.72.71.113)  16.116 ms  19.534 ms  19.506 ms\n 4  89.246.109.249 (89.246.109.249)  24.717 ms  25.460 ms  24.659 ms\n 5  72.14.204.148 (72.14.204.148)  20.530 ms  20.602 ms 89.246.109.250 (89.246.109.250)  24.573 ms\n 6  * * *\n 7  dns.google (8.8.8.8)  20.265 ms  16.966 ms  14.751 ms\ncaipirinha:~ # <strong>traceroute -i tun0 8.8.8.8<\/strong>\ntraceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets\n 1  10.96.10.33 (10.96.10.33)  50.579 ms  101.574 ms  102.216 ms\n 2  91.205.230.65 (91.205.230.65)  121.175 ms  121.171 ms  151.320 ms\n 3  cr1.lis1.edgoo.net (193.163.151.1)  102.156 ms  102.155 ms  102.150 ms\n 4  Google.AS15169.gigapix.pt (193.136.250.20)  102.145 ms  103.099 ms  103.145 ms\n 5  74.125.245.100 (74.125.245.100)  103.166 ms 74.125.245.118 (74.125.245.118)  103.156 ms 74.125.245.117 (74.125.245.117)  103.071 ms\n 6  142.250.237.83 (142.250.237.83)  120.681 ms 142.250.237.29 (142.250.237.29)  149.742 ms 142.251.55.151 (142.251.55.151)  110.302 ms\n 7  74.125.242.161 (74.125.242.161)  108.651 ms 108.170.253.241 (108.170.253.241)  108.594 ms 108.170.235.178 (108.170.235.178)  108.426 ms\n 8  74.125.242.161 (74.125.242.161)  108.450 ms  108.429 ms 142.250.239.27 (142.250.239.27)  107.505 ms\n 9  142.251.54.149 (142.251.54.149)  107.406 ms 142.251.60.115 (142.251.60.115)  108.446 ms 142.251.54.151 (142.251.54.151)  157.613 ms\n10  dns.google (8.8.8.8)  107.380 ms  89.640 ms  73.506 ms\ncaipirinha:~ # <strong>traceroute -i tun1 8.8.8.8<\/strong>\ntraceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets\n 1  10.12.34.1 (10.12.34.1)  295.583 ms  561.820 ms  625.195 ms\n 2  146.70.67.65 (146.70.67.65)  687.601 ms  793.792 ms  825.806 ms\n 3  193.27.15.178 (193.27.15.178)  1130.988 ms  1198.522 ms  1260.560 ms\n 4  37.120.220.218 (37.120.220.218)  1383.152 ms 37.120.220.230 (37.120.220.230)  825.525 ms 37.120.220.218 (37.120.220.218)  925.081 ms\n 5  103.231.152.50 (103.231.152.50)  1061.923 ms  1061.945 ms 15169.sgw.equinix.com (27.111.228.150)  993.095 ms\n 6  108.170.240.225 (108.170.240.225)  1320.654 ms 74.125.242.33 (74.125.242.33)  1164.303 ms 108.170.254.225 (108.170.254.225)  1008.590 ms\n 7  74.125.251.205 (74.125.251.205)  1009.043 ms 74.125.251.207 (74.125.251.207)  993.251 ms 142.251.49.191 (142.251.49.191)  969.879 ms\n 8  dns.google (8.8.8.8)  1001.502 ms  1065.558 ms  1073.731 ms\ncaipirinha:~ # <strong>traceroute -i tun2 8.8.8.8<\/strong>\ntraceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets\n 1  10.31.3.33 (10.31.3.33)  189.134 ms  399.914 ms  399.941 ms\n 2  * * *\n 3  * * *\n 4  * * *\n 5  198.84.50.182.static-corp.jastel.co.th (182.50.84.198)  411.679 ms  411.729 ms  411.662 ms\n 6  72.14.222.138 (72.14.222.138)  433.761 ms 74.125.48.212 (74.125.48.212)  444.509 ms 72.14.223.80 (72.14.223.80)  444.554 ms\n 7  108.170.250.17 (108.170.250.17)  647.768 ms * 108.170.249.225 (108.170.249.225)  439.883 ms\n 8  142.250.62.59 (142.250.62.59)  635.318 ms 142.251.224.15 (142.251.224.15)  417.842 ms dns.google (8.8.8.8)  600.600 ms<\/code><\/pre>\n\n\n\n<p>A traceroute to <a rel=\"noreferrer noopener\" href=\"https:\/\/www.google.com\/\" target=\"_blank\">Google<\/a>&#8216;s DNS via the 3 client VPN connections shows us the route the packets travel; the first example shows the route via the default connection (<em>eth0<\/em>):<\/p>\n\n\n\n<p>Finally, we look at the routing tables that have changed after we have established the 3 client VPN connections:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>ip route list table main<\/strong>\ndefault via 192.168.2.1 dev eth0 proto dhcp\n10.12.42.32\/27 dev tun1 proto kernel scope link src 10.12.42.41\n10.31.6.32\/27 dev tun2 proto kernel scope link src 10.31.6.38\n172.17.66.32\/27 dev tun0 proto kernel scope link src 172.17.66.34\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 dev tun4 proto kernel scope link src 192.168.10.1\n192.168.11.0\/24 dev tun5 proto kernel scope link src 192.168.11.1\n192.168.12.0\/24 dev tun6 proto kernel scope link src 192.168.12.1\n192.168.13.0\/24 dev tun7 proto kernel scope link src 192.168.13.1\n192.168.14.0\/24 dev wg0 proto kernel scope link src 192.168.14.1\ncaipirinha:~ # <strong>ip route list table Portugal<\/strong>\ndefault via 172.17.66.33 dev tun0\n172.17.66.32\/27 dev tun0 proto kernel scope link src 172.17.66.34\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 via 192.168.10.1 dev tun4\n192.168.11.0\/24 via 192.168.11.1 dev tun5\n192.168.12.0\/24 via 192.168.12.1 dev tun6\n192.168.13.0\/24 via 192.168.13.1 dev tun7\n192.168.14.0\/24 via 192.168.14.1 dev wg0\ncaipirinha:~ # <strong>ip route list table Singapur<\/strong>\ndefault via 10.12.42.33 dev tun1\n10.12.42.32\/27 dev tun1 proto kernel scope link src 10.12.42.41\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 via 192.168.10.1 dev tun4\n192.168.11.0\/24 via 192.168.11.1 dev tun5\n192.168.12.0\/24 via 192.168.12.1 dev tun6\n192.168.13.0\/24 via 192.168.13.1 dev tun7\n192.168.14.0\/24 via 192.168.14.1 dev wg0\ncaipirinha:~ # <strong>ip route list table Thailand<\/strong>\ndefault via 10.31.6.33 dev tun2\n10.31.6.32\/27 dev tun2 proto kernel scope link src 10.31.6.38\n192.168.2.0\/24 dev eth0 proto kernel scope link src 192.168.2.3\n192.168.10.0\/24 via 192.168.10.1 dev tun4\n192.168.11.0\/24 via 192.168.11.1 dev tun5\n192.168.12.0\/24 via 192.168.12.1 dev tun6\n192.168.13.0\/24 via 192.168.13.1 dev tun7\n192.168.14.0\/24 via 192.168.14.1 dev wg0<\/code><\/pre>\n\n\n\n<p>In the routing tables, we can observe the following new items:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Each client VPN connection has added a \/27 network to the routing table <strong>main<\/strong>.<\/li>\n\n\n\n<li>The script <strong>\/etc\/openvpn\/start_purevpn.sh<\/strong> has added the \/27 networks to the corresponding routing tables <strong>Portugal<\/strong>, <strong>Singapur<\/strong>, <strong>Thailand<\/strong> so that each routing table only has the \/27 network of the connection that leads to the corresponding destination.<\/li>\n\n\n\n<li>The script <strong>\/etc\/openvpn\/start_purevpn.sh<\/strong> has also modified the default route of each of the routing tables <strong>Portugal<\/strong>, <strong>Singapur<\/strong>, <strong>Thailand<\/strong> so that each routing table has the default route of the connection that leads to the corresponding destination.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Routing Policies<\/h3>\n\n\n\n<p>Now, we are all set to define routing policies and do our first steps in the field of <strong>policy routing<\/strong>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Simple Policy Routing<\/h4>\n\n\n\n<p>In the first example, we will &#8220;place&#8221; each device (gabriel-SM-G991B, gabriel-SM-N960F, gabriel-SM-T580) in a different country. Let us recall that, when each of these devices connects to the Linux server via the UDP-based, full tunneling\u00a0<a rel=\"noreferrer noopener\" href=\"https:\/\/graphviz.org\/\" target=\"_blank\">openvpn<\/a>, then each device gets a defined IP address. This allows us to define routing policies based on the IP address [<a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/ip-rule.8.html\" target=\"_blank\">11<\/a>]. In order to modify the <strong>routing policy database<\/strong> of the Linux server, we enter the commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip rule add from 192.168.10.250\/32 table Portugal priority 2000\nip rule add from 192.168.10.251\/32 table Singapur priority 2000\nip rule add from 192.168.10.252\/32 table Thailand priority 2000<\/code><\/pre>\n\n\n\n<p>The resulting routing policy database looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:~ # <strong>ip rule list<\/strong>\n0:      from all lookup local\n2000:   from 192.168.10.250 lookup Portugal\n2000:   from 192.168.10.251 lookup Singapur\n2000:   from 192.168.10.252 lookup Thailand\n32766:  from all lookup main\n32767:  from all lookup default<\/code><\/pre>\n\n\n\n<p>The number at the beginning of each line in the routing policy database is the priority; this allows us to define routing policies in a defined order. As soon as the selector of a rule matches the a packet, the corresponding action is executed, and no further rules are checked for this packet. [<a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/ip-rule.8.html\" target=\"_blank\">11<\/a>] lists the possible selectors and actions, and we can see that there are a lot of possibilities, especially when we combine different matching criteria. In the case shown here, our rules tell the Linux server the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Packets with the source IP <em>192.168.10.250<\/em> (device <em>gabriel-SM-G991B<\/em>) shall be processed in the routing table <em>Portugal<\/em>.<\/li>\n\n\n\n<li>Packets with the source IP <em>192.168.10.251<\/em> (device <em>gabriel-SM-N960F<\/em>) shall be processed in the routing table <em>Singapur<\/em>.<\/li>\n\n\n\n<li>Packets with the source IP <em>192.168.10.252<\/em> (device <em>gabriel-SM-T580<\/em>) shall be processed in the routing table <em>Thailand<\/em>.<\/li>\n<\/ul>\n\n\n\n<p>An important rule is the one with the priority 32766; this one tells all packets to use the routing table <em>main<\/em>. This rule has a very low priority because we want to enable administrators to create many other rules with higher priority that match packets and that are subsequently dealt with in a special way. The rules with the priorities 0, 32766, 32767 are already in the system by default.<\/p>\n\n\n\n<p>When we place the 3 devices gabriel-SM-G991B, gabriel-SM-N960F, and gabriel-SM-T580 outside the home network, either in a different WiFi network or in a mobile network and connect to the Linux server via the VPN services, then, because of the routing policy defined above, the devices will appear in:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Portugal (gabriel-SM-G991B)<\/li>\n\n\n\n<li>Singapore (gabriel-SM-N960F)<\/li>\n\n\n\n<li>Thailand (gabriel-SM-T580)<\/li>\n<\/ul>\n\n\n\n<p>We can test this with one of the websites that display IP geolocation, for example <a href=\"https:\/\/www.iplocation.net\/\">[<\/a><a rel=\"noreferrer noopener\" href=\"https:\/\/www.iplocation.net\/\" target=\"_blank\">13<\/a>], and the result will look like this:<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"733\" height=\"1024\" data-id=\"784\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-733x1024.jpg\" alt=\"\" class=\"wp-image-784\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-733x1024.jpg 733w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-215x300.jpg 215w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-768x1072.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon.jpg 1080w\" sizes=\"auto, (max-width: 733px) 100vw, 733px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-G991B is in Lisbon<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"716\" height=\"1024\" data-id=\"783\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-716x1024.jpg\" alt=\"\" class=\"wp-image-783\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-716x1024.jpg 716w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-210x300.jpg 210w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-768x1098.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-1074x1536.jpg 1074w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore.jpg 1080w\" sizes=\"auto, (max-width: 716px) 100vw, 716px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F is in Singapore<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"713\" data-id=\"782\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-1024x713.jpg\" alt=\"\" class=\"wp-image-782\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-1024x713.jpg 1024w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-300x209.jpg 300w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-768x534.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang.jpg 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-T580 is in Bangkok (Khlong Luang)<\/figcaption><\/figure>\n<figcaption class=\"blocks-gallery-caption wp-element-caption\">Each device appears to be in a different country<\/figcaption><\/figure>\n\n\n\n<p>We must keep in mind that this kind of routing policy routes <em>all<\/em> outgoing traffic from the 3 devices to the respective countries, irrespective whether this is web or email or any other traffic. This is true for any protocol, and so, a traceroute to <a rel=\"noreferrer noopener\" href=\"https:\/\/www.google.com\/\" target=\"_blank\">Google<\/a>&#8216;s DNS (8.8.8.8) will <em>really<\/em> go via the respective country. The images below compare the device gabriel-SM-N960F <em>without<\/em> VPN (4G mobile network) and <em>with<\/em> the VPN to the Linux server which then routes the connection via Singapore. One can easily recognize the much higher latency via Singapore. The traceroutes were taken with [<a rel=\"noreferrer noopener\" href=\"https:\/\/play.google.com\/store\/apps\/details?id=ua.com.streamsoft.pingtools\" target=\"_blank\">14<\/a>].<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"386\" height=\"1024\" data-id=\"785\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-386x1024.jpg\" alt=\"\" class=\"wp-image-785\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-386x1024.jpg 386w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-113x300.jpg 113w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-768x2037.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-579x1536.jpg 579w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-772x2048.jpg 772w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-scaled.jpg 965w\" sizes=\"auto, (max-width: 386px) 100vw, 386px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F in the network of O2<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"414\" height=\"1024\" data-id=\"786\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-414x1024.jpg\" alt=\"\" class=\"wp-image-786\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-414x1024.jpg 414w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-121x300.jpg 121w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-768x1901.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-621x1536.jpg 621w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-827x2048.jpg 827w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG.jpg 972w\" sizes=\"auto, (max-width: 414px) 100vw, 414px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F in Singapore<\/figcaption><\/figure>\n<figcaption class=\"blocks-gallery-caption wp-element-caption\">traceroutes without VPN, and with VPN (and via Singapore)<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Policy Routing with Firewall Marking<\/h4>\n\n\n\n<p>While the <strong>ip-rule<\/strong> command [<a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/ip-rule.8.html\" target=\"_blank\">11<\/a>] already offers a lot of possible combinations for the selection of packets, sometimes, one needs more elaborate selection criteria. This is when we use policy routing using <strong>firewall marking<\/strong> and the <strong>mangle table<\/strong> [<a rel=\"noreferrer noopener\" href=\"https:\/\/programmerall.com\/article\/31282042528\/\" target=\"_blank\">15<\/a>]. We first delete our rule set from above with the sequence:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip rule del from 192.168.10.250\/32 table Portugal priority 2000\nip rule del from 192.168.10.251\/32 table Singapur priority 2000\nip rule del from 192.168.10.252\/32 table Thailand priority 2000<\/code><\/pre>\n\n\n\n<p>Then, we enter new rules. Instead of using IP addresses in the selector, we use a so-called &#8220;firewall mark&#8221; (<strong>fwmark<\/strong>). We tell the Linux server to process packets that have a special mark in the routing tables mentioned in the action field of ip-rule:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip rule add from all fwmark 0x1 priority 5000 lookup Portugal\nip rule add from all fwmark 0x2 priority 5000 lookup Singapur\nip rule add from all fwmark 0x3 priority 5000 lookup Thailand<\/code><\/pre>\n\n\n\n<p>But how do we mark packets? This is done in the <strong>mangle table<\/strong>, one of the 4 tables of the iptables [<a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/iptables.8.html\" target=\"_blank\">12<\/a>] command. With command listed below we specify the marking of TCP packets originating from the listed IP address and going to the destination ports 80 (http) and 443 (https). All other traffic from the device with the listed IP address (e.g., smtp, imap, UDP, ICMP, &#8230;) will not be marked.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>iptables -t mangle -F\niptables -t mangle -A PREROUTING -j CONNMARK --restore-mark\niptables -t mangle -A PREROUTING -m mark ! --mark 0 -j ACCEPT\niptables -t mangle -A PREROUTING -s 192.168.10.250\/32 -p tcp -m multiport --dports 80,443 -m state --state NEW,RELATED -j MARK --set-mark 1\niptables -t mangle -A PREROUTING -s 192.168.10.251\/32 -p tcp -m multiport --dports 80,443 -m state --state NEW,RELATED -j MARK --set-mark 2\niptables -t mangle -A PREROUTING -s 192.168.10.252\/32 -p tcp -m multiport --dports 80,443 -m state --state NEW,RELATED -j MARK --set-mark 3\niptables -t mangle -A PREROUTING -j CONNMARK --save-mark<\/code><\/pre>\n\n\n\n<p>Let us have a closer look at the 7 iptables commands:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>This command flushes all chains of the mangle table so that the mangle table is empty.<\/li>\n\n\n\n<li>This command restores the marks of packets. Here, one must know that the mark of a packet is not stored in the packet itself, as the IP header does not contain a field for such a mark. Rather than that, the Linux Kernel keeps track of the mark and the packet it belongs to. However, when the Linux server sends out a packet to its destination, and the computer at the destination (e.g., a web server) answers with his own packets, then when these packets arrive at our Linux server, we want to mark them, too, because they belong to a data connection whose packets were initially marked and we might need the mark in order to process them correctly. Therefore, we &#8220;restore&#8221; the mark in the PREROUTING chain of the mangle table.<\/li>\n\n\n\n<li>This command accepts all packets that have a non-zero mark. I am not really sure if that command is needed at all (should be tested).<\/li>\n\n\n\n<li>This command sets the mark &#8220;1&#8221; to those packets that fulfil all these requirements:\n<ol class=\"wp-block-list\">\n<li>It comes from the source IP address <em>192.168.10.250<\/em>.<\/li>\n\n\n\n<li>It uses TCP.<\/li>\n\n\n\n<li>It goes to one of the destination ports <em>80<\/em> (http) or <em>443<\/em> (https).<\/li>\n\n\n\n<li>It constitutes a NEW or RELATED connection.<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>This command sets the mark &#8220;2&#8221; to those packets that fulfil all these requirements:\n<ol class=\"wp-block-list\">\n<li>It comes from the source IP address <em>192.168.10.251<\/em>.<\/li>\n\n\n\n<li>It uses TCP.<\/li>\n\n\n\n<li>It goes to one of the destination ports <em>80<\/em> (http) or <em>443<\/em> (https).<\/li>\n\n\n\n<li>It constitutes a NEW or RELATED connection.<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>This command sets the mark &#8220;3&#8221; to those packets that fulfil all these requirements:\n<ol class=\"wp-block-list\">\n<li>It comes from the source IP address <em>192.168.10.252<\/em>.<\/li>\n\n\n\n<li>It uses TCP.<\/li>\n\n\n\n<li>It goes to one of the destination ports <em>80<\/em> (http) or <em>443<\/em> (https).<\/li>\n\n\n\n<li>It constitutes a NEW or RELATED connection.<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>This command stores in the mark of the packets in the connection tracking table.<\/li>\n<\/ol>\n\n\n\n<p>After we have entered these commands, the mangle table should look somewhat like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:\/etc\/openvpn # <strong>iptables -t mangle -L -n -v<\/strong>\nChain PREROUTING (policy ACCEPT 210K packets, 106M bytes)\n pkts bytes target     prot opt in     out     source               destination\n 953K  384M CONNMARK   all  --  *      *       0.0.0.0\/0            0.0.0.0\/0            CONNMARK restore\n 177K   88M ACCEPT     all  --  *      *       0.0.0.0\/0            0.0.0.0\/0            mark match ! 0x0\n  989 76505 MARK       tcp  --  *      *       192.168.10.250       0.0.0.0\/0            multiport dports 80,443 state NEW,RELATED MARK set 0x1\n 1233 82791 MARK       tcp  --  *      *       192.168.10.251       0.0.0.0\/0            multiport dports 80,443 state NEW,RELATED MARK set 0x2\n 1017 72624 MARK       tcp  --  *      *       192.168.10.252       0.0.0.0\/0            multiport dports 80,443 state NEW,RELATED MARK set 0x3\n 776K  296M CONNMARK   all  --  *      *       0.0.0.0\/0            0.0.0.0\/0            CONNMARK save\n\nChain INPUT (policy ACCEPT 203K packets, 104M bytes)\n pkts bytes target     prot opt in     out     source               destination\n\nChain FORWARD (policy ACCEPT 137K packets, 69M bytes)\n pkts bytes target     prot opt in     out     source               destination\n\nChain OUTPUT (policy ACCEPT 199K packets, 107M bytes)\n pkts bytes target     prot opt in     out     source               destination\n\nChain POSTROUTING (policy ACCEPT 335K packets, 176M bytes)\n pkts bytes target     prot opt in     out     source               destination<\/code><\/pre>\n\n\n\n<p>The values in the columns <strong>pkts<\/strong>, <strong>bytes<\/strong> will most probably be different in your case. They show how many IP packets and bytes have matched this rule and can help in controlling or debugging the configuration and the traffic flows.<\/p>\n\n\n\n<p>The entries in the mangle table consequently mark the packets that traverse our Linux router and that are from one of the devices gabriel-SM-G991B, gabriel-SM-N960F, or gabriel-SM-T580 and that are destined to a web server (TCP ports 80, 443) with either the <strong>fwmark<\/strong> &#8220;1&#8221;, &#8220;2&#8221;, or &#8220;3&#8221;. Based on this fwmark, the packets are then sent to the routing tables Portugal, Singapur or Thailand. Using both the <strong>routing policy database<\/strong> and the <strong>mangle table<\/strong> is a powerful instrument for selecting packets and connections that shall be routed in a special way which gives us a lot of flexibility.<\/p>\n\n\n\n<p>For example, if we only had one device to be considered (Gabriel-SM-G991B), the two command that we have issues before:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip rule add <strong>from all<\/strong> fwmark 0x1 priority 5000 lookup Portugal\niptables -t mangle -A PREROUTING <strong>-s 192.168.10.250\/32<\/strong> -p tcp -m multiport --dports 80,443 -m state --state NEW,RELATED -j MARK --set-mark 1<\/code><\/pre>\n\n\n\n<p>have the same effect as these two commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip rule add <strong>from 192.168.10.250\/32<\/strong> fwmark 0x1 priority 5000 lookup Portugal\niptables -t mangle -A PREROUTING -p tcp -m multiport --dports 80,443 -m state --state NEW,RELATED -j MARK --set-mark 1<\/code><\/pre>\n\n\n\n<p>In the first case, the source address selection is done in the <em>iptables<\/em> command, in the second case it is done in the <em>ip rule<\/em> command. With the 3 devices that have, the second way is not a solution as it would create the fwmark &#8220;1&#8221; on all packets that go to a web server and the subsequent entries in the mangle table would not be executed any more. We therefore have to create rules with extreme caution, in order not to jeopardize our intended routing behavior. I therefore recommend being as specific as possible already in the <em>iptables<\/em> command, also in order to avoid excessive packet marking as this complicates your life when you have to debug your setup.<\/p>\n\n\n\n<p>When we have the setup described in this chapter active, the 3 devices (gabriel-SM-G991B, gabriel-SM-N960F, gabriel-SM-T580) will appear to be in the respective countries, similar to the setup in the previous chapter where we only modified the routing policy database. We can test this with one of the websites that display IP geolocation, for example <a href=\"https:\/\/www.iplocation.net\/\">[<\/a><a rel=\"noreferrer noopener\" href=\"https:\/\/www.iplocation.net\/\" target=\"_blank\">13<\/a>], and the result will look like this:<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"733\" height=\"1024\" data-id=\"784\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-733x1024.jpg\" alt=\"\" class=\"wp-image-784\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-733x1024.jpg 733w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-215x300.jpg 215w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon-768x1072.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM-G991B-is-in-Lisbon.jpg 1080w\" sizes=\"auto, (max-width: 733px) 100vw, 733px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-G991B is in Lisbon<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"716\" height=\"1024\" data-id=\"783\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-716x1024.jpg\" alt=\"\" class=\"wp-image-783\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-716x1024.jpg 716w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-210x300.jpg 210w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-768x1098.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore-1074x1536.jpg 1074w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-SM960F-is-in-Singapore.jpg 1080w\" sizes=\"auto, (max-width: 716px) 100vw, 716px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F is in Singapore<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"713\" data-id=\"782\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-1024x713.jpg\" alt=\"\" class=\"wp-image-782\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-1024x713.jpg 1024w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-300x209.jpg 300w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang-768x534.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Gabriel-Tablet-is-in-Bangkok-Khlong-Luang.jpg 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-T580 is in Bangkok (Khlong Luang)<\/figcaption><\/figure>\n<figcaption class=\"blocks-gallery-caption wp-element-caption\">Each device appears to be in a different country<\/figcaption><\/figure>\n\n\n\n<p>However, if we execute a <strong>traceroute<\/strong> command on one of the devices, the traceroute does not go via the respective country because it uses the protocol <strong>ICMP<\/strong> rather than <strong>TCP<\/strong> and is therefore not marked and consequently is not routed via any of the client VPNs. This can be seen in the rightest image in the following gallery where a traceroute to <a rel=\"noreferrer noopener\" href=\"https:\/\/www.google.com\/\" target=\"_blank\">Google<\/a>&#8216;s DNS (8.8.8.8) has been made. The traceroutes were taken with [<a rel=\"noreferrer noopener\" href=\"https:\/\/play.google.com\/store\/apps\/details?id=ua.com.streamsoft.pingtools\" target=\"_blank\">14<\/a>].<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-4 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"965\" height=\"2560\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-scaled.jpg\" alt=\"\" class=\"wp-image-785\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-scaled.jpg 965w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-113x300.jpg 113w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-386x1024.jpg 386w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-768x2037.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-579x1536.jpg 579w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-from-O2-4G-Network-772x2048.jpg 772w\" sizes=\"auto, (max-width: 965px) 100vw, 965px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F in the network of O2<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"972\" height=\"2406\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG.jpg\" alt=\"\" class=\"wp-image-786\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG.jpg 972w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-121x300.jpg 121w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-414x1024.jpg 414w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-768x1901.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-621x1536.jpg 621w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-SG-827x2048.jpg 827w\" sizes=\"auto, (max-width: 972px) 100vw, 972px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F in Singapore<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"972\" height=\"2406\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE.jpg\" alt=\"\" class=\"wp-image-788\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE.jpg 972w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE-121x300.jpg 121w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE-414x1024.jpg 414w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE-768x1901.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE-621x1536.jpg 621w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/Google-DNS-via-VPN-to-caipirinha-then-internet-via-DE-827x2048.jpg 827w\" sizes=\"auto, (max-width: 972px) 100vw, 972px\" \/><figcaption class=\"wp-element-caption\">gabriel-SM-N960F in Germany<\/figcaption><\/figure>\n<figcaption class=\"blocks-gallery-caption wp-element-caption\">traceroutes without VPN, with VPN and routing policy database only (previous chapter), and with VPN, routing policy and mangle table (this chapter)<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Connection Tracking<\/h3>\n\n\n\n<p>In the previous chapter, the <em>iptables<\/em> statements for the mangle table apply to NEW or RELATED connections only. Let us therefore look into the concept of <strong>connection tracking<\/strong> [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\">4<\/a><a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\"><\/a>], [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.rigacci.org\/wiki\/lib\/exe\/fetch.php\/doc\/appunti\/linux\/sa\/iptables\/conntrack.html\" target=\"_blank\">21<\/a>]. This is achieved by the <strong>netfilter<\/strong> component [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.netfilter.org\/\" target=\"_blank\">16<\/a>] which is linked to the Linux Kernel keeps track of stateful connections in the connection tracking table, very similar to a stateful firewall [<a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Stateful_firewall#:~:text=In%20computing%2C%20a%20stateful%20firewall%20is%20a%20network-based,feature%20often%20used%20in%20non-commercial%20and%20business%20networks.\" target=\"_blank\">17<\/a>]. Important states are [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\">4<\/a><a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\"><\/a>]:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>NEW: The packet does not belong to an existing connection.<\/li>\n\n\n\n<li>ESTABLISHED: The packet belongs to an &#8220;established&#8221; connection. A connection is changed from NEW to ESTABLISHED when it receives a valid response in the opposite direction.<\/li>\n\n\n\n<li>RELATED: Packets that are not part of an existing connection but are associated with a connection already in the system are labeled RELATED. An example are <strong>ftp<\/strong> connections which open connections adjacent to the initial one [<a rel=\"noreferrer noopener\" href=\"https:\/\/slacksite.com\/other\/ftp.html\" target=\"_blank\">18<\/a>].<\/li>\n\n\n\n<li>INVALID: Packets can be marked INVALID if they are not associated with an existing connection and are not appropriate for opening a new connection.<\/li>\n<\/ul>\n\n\n\n<p>The mighty conntrack toolset [<a rel=\"noreferrer noopener\" href=\"https:\/\/conntrack-tools.netfilter.org\/manual.html\" target=\"_blank\">19<\/a>] contains the command <strong>conntrack<\/strong> [<a rel=\"noreferrer noopener\" href=\"https:\/\/manpages.debian.org\/jessie\/conntrack\/conntrack.8.en.html\" target=\"_blank\">20<\/a>] which can be used to see the connection tracking table (actually, there are also different tables) and to inspect various behaviors around the connection tracking table. We can, for example, examine which connections have the fwmark &#8220;2&#8221; set, that is, which connections have been set up by the device <em>Gabriel-Tablet<\/em> using the source IP address <em>192.168.10.251<\/em>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>caipirinha:\/etc\/openvpn # <strong>conntrack -L -m2<\/strong>\ntcp      6 431952 ESTABLISHED src=192.168.10.251 dst=172.217.19.74 sport=38330 dport=443 src=172.217.19.74 dst=10.12.42.37 sport=443 dport=38330 &#91;ASSURED] mark=2 use=1\ntcp      6 431832 ESTABLISHED src=192.168.10.251 dst=34.107.165.5 sport=38924 dport=443 src=34.107.165.5 dst=10.12.42.37 sport=443 dport=38924 &#91;ASSURED] mark=2 use=1\ntcp      6 431955 ESTABLISHED src=192.168.10.251 dst=142.250.186.170 sport=58652 dport=443 src=142.250.186.170 dst=10.12.42.37 sport=443 dport=58652 &#91;ASSURED] mark=2 use=1\ntcp      6 65 TIME_WAIT src=192.168.10.251 dst=142.250.185.66 sport=57856 dport=443 src=142.250.185.66 dst=10.12.42.37 sport=443 dport=57856 &#91;ASSURED] mark=2 use=1\ntcp      6 431954 ESTABLISHED src=192.168.10.251 dst=142.250.186.170 sport=58640 dport=443 src=142.250.186.170 dst=10.12.42.37 sport=443 dport=58640 &#91;ASSURED] mark=2 use=1\nconntrack v1.4.5 (conntrack-tools): 5 flow entries have been shown.<\/code><\/pre>\n\n\n\n<p>Each line contains a whole set of information which is explained in detail in [<a rel=\"noreferrer noopener\" href=\"https:\/\/www.frozentux.net\/iptables-tutorial\/iptables-tutorial.html\" target=\"_blank\">2<\/a>], \u00a7 7. For a quick orientation, we have to know the following points:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Each line has two parts. The first part lists the IP header information of the newly initiated connection; in our case, we observe:\n<ul class=\"wp-block-list\">\n<li>The source IP address is always <em>192.168.10.251<\/em> (Gabriel-Tablet).<\/li>\n\n\n\n<li>The destination port is either <em>80<\/em> or <em>443<\/em> as we change the routing for exactly these destination ports only<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>The second part lists the IP header information of the expected or received (as an answer) packets, and, we observe:\n<ul class=\"wp-block-list\">\n<li>The <em>source<\/em> IP address of the answering packet is the <em>destination<\/em> IP address of the initiating packet which makes sense.<\/li>\n\n\n\n<li>The <em>source<\/em> port of the <em>answering<\/em> packet is the <em>destination<\/em> port of the <em>initiating<\/em> packet which again makes sense.<\/li>\n\n\n\n<li>The destination IP address of the answering packet is not <em>192.168.10.251<\/em>, but <em>10.12.42.37<\/em>. This is because <em>10.12.42.37<\/em> is the IP address of the <em>tun1<\/em> device of the Linux server. When we send out the <em>initiating<\/em> packet from <em>192.168.10.251<\/em>, the packet will go to the Linux server who acts as router. In the server, the packet will be changed, and the server will use its source address on the outgoing interface <em>tun1<\/em> as source address on the packet as the remote end point of the client VPN connection that we use would not know how to route a packet to <em>192.168.10.251<\/em> (the remote end point does not know anything of the network <em>192.168.10.0\/24<\/em> on our side).<\/li>\n\n\n\n<li>[ASSURED] means that the connection has already seen traffic in <em>both<\/em> directions, the connections has therefore been set up successfully.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Coincidentally, in our example, we only have TCP connections; however, the connection tracking table can also comprise UDP or ICMP connections.<\/li>\n<\/ul>\n\n\n\n<p>The command <strong>conntrack<\/strong> [<a rel=\"noreferrer noopener\" href=\"https:\/\/manpages.debian.org\/jessie\/conntrack\/conntrack.8.en.html\" target=\"_blank\">20<\/a>] offers much more opportunities and even allows to change entries in the connection tracking table. So, we barely scratched the surface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>In this blog post, we have used client VPN connections to execute some experiments on policy routing in which we make different devices appear to be located in different countries. We touched the concepts of <strong>routing tables<\/strong>, the <strong>routing policy database<\/strong>, the <strong>mangle table<\/strong>, and the <strong>connection tracking table<\/strong>. The possibilities of all these items go far beyond of what we discussed in this blog post. The interested reader is referred to the sources listed below to get in-depth knowledge and to understand the vast possibilities that these items offer to the expert.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"sources\">Sources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/tldp.org\/HOWTO\/Adv-Routing-HOWTO\/index.html\" target=\"_blank\">1<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/tldp.org\/HOWTO\/Adv-Routing-HOWTO\/index.html\" target=\"_blank\">Linux Advanced Routing &amp; Traffic Control HOWTO<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/www.frozentux.net\/iptables-tutorial\/iptables-tutorial.html\" target=\"_blank\">2<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/www.frozentux.net\/iptables-tutorial\/iptables-tutorial.html\" target=\"_blank\">Iptables Tutorial 1.2.2<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"http:\/\/linux-ip.net\/html\/\" target=\"_blank\">3<\/a>] = <a rel=\"noreferrer noopener\" href=\"http:\/\/linux-ip.net\/html\/\" target=\"_blank\">Guide to IP Layer Network Administration with Linux<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\">4<\/a><a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\"><\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/a-deep-dive-into-iptables-and-netfilter-architecture\" target=\"_blank\">A Deep Dive into Iptables and Netfilter Architecture<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"http:\/\/www.policyrouting.org\/PolicyRoutingBook\/ONLINE\/TOC.html\" target=\"_blank\">5<\/a>] = <a rel=\"noreferrer noopener\" href=\"http:\/\/www.policyrouting.org\/PolicyRoutingBook\/ONLINE\/TOC.html\" target=\"_blank\">Policy Routing With Linux &#8211; Online Edition<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/ro-che.info\/articles\/2021-02-27-linux-routing\" target=\"_blank\">6<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/ro-che.info\/articles\/2021-02-27-linux-routing\" target=\"_blank\">Understanding modern Linux routing (and wg-quick)<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/home.regit.org\/netfilter-en\/netfilter-connmark\/\" target=\"_blank\">7<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/home.regit.org\/netfilter-en\/netfilter-connmark\/\" target=\"_blank\">Netfilter Connmark<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=56\" target=\"_blank\">8<\/a>] =&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/caipirinha.spdns.org\/wp\/?p=56\" target=\"_blank\">Internet Censorship in China<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/openvpn.net\/community-resources\/reference-manual-for-openvpn-2-4\/\" target=\"_blank\">9<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/openvpn.net\/community-resources\/reference-manual-for-openvpn-2-4\/\" target=\"_blank\">Reference manual for OpenVPN 2.4<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/www.man7.org\/linux\/man-pages\/man8\/wg-quick.8.html\" target=\"_blank\">10<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/www.man7.org\/linux\/man-pages\/man8\/wg-quick.8.html\" target=\"_blank\">man 8 wg-quick<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/ip-rule.8.html\" target=\"_blank\">11<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/ip-rule.8.html\" target=\"_blank\">man 8 ip-rule<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/iptables.8.html\" target=\"_blank\">12<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/iptables.8.html\" target=\"_blank\">man 8 iptables<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/www.iplocation.net\/\" target=\"_blank\">13<\/a>] = <a href=\"https:\/\/www.iplocation.net\/\">Where is my IP location? (Geolocation)<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/play.google.com\/store\/apps\/details?id=ua.com.streamsoft.pingtools\" target=\"_blank\">14<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/play.google.com\/store\/apps\/details?id=ua.com.streamsoft.pingtools\" target=\"_blank\">PingTools Network Utilities<\/a> [Google Play]<\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/programmerall.com\/article\/31282042528\/\" target=\"_blank\">15<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/programmerall.com\/article\/31282042528\/\" target=\"_blank\">Mangle table for iptables<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/www.netfilter.org\/\" target=\"_blank\">16<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/www.netfilter.org\/\" target=\"_blank\">The netfilter.org project<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Stateful_firewall#:~:text=In%20computing%2C%20a%20stateful%20firewall%20is%20a%20network-based,feature%20often%20used%20in%20non-commercial%20and%20business%20networks.\" target=\"_blank\">17<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Stateful_firewall#:~:text=In%20computing%2C%20a%20stateful%20firewall%20is%20a%20network-based,feature%20often%20used%20in%20non-commercial%20and%20business%20networks.\" target=\"_blank\">Stateful firewall<\/a> [Wikipedia]<\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/slacksite.com\/other\/ftp.html\" target=\"_blank\">18<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/slacksite.com\/other\/ftp.html\" target=\"_blank\">Active FTP vs. Passive FTP, a Definitive Explanation<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/conntrack-tools.netfilter.org\/manual.html\" target=\"_blank\">19<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/conntrack-tools.netfilter.org\/manual.html\" target=\"_blank\">The conntrack-tools user manual<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/manpages.debian.org\/jessie\/conntrack\/conntrack.8.en.html\" target=\"_blank\">20<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/manpages.debian.org\/jessie\/conntrack\/conntrack.8.en.html\" target=\"_blank\">man 8 conntrack<\/a><\/li>\n\n\n\n<li>[<a rel=\"noreferrer noopener\" href=\"https:\/\/www.rigacci.org\/wiki\/lib\/exe\/fetch.php\/doc\/appunti\/linux\/sa\/iptables\/conntrack.html\" target=\"_blank\">21<\/a>] = <a rel=\"noreferrer noopener\" href=\"https:\/\/www.rigacci.org\/wiki\/lib\/exe\/fetch.php\/doc\/appunti\/linux\/sa\/iptables\/conntrack.html\" target=\"_blank\">Connection tracking<\/a><\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post explains how I use client VPNs together with simple Policy Routing on my Linux server in order to relegate outgoing connections to various network interfaces and, ultimately, to different countries. The examples use IPv4 only.<\/p>\n","protected":false},"author":1,"featured_media":766,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35],"tags":[102,97,101,98],"class_list":["post-765","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-it","tag-iptables","tag-openvpn","tag-policyrouting","tag-vpn"],"_links":{"self":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts\/765","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=765"}],"version-history":[{"count":20,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts\/765\/revisions"}],"predecessor-version":[{"id":1416,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts\/765\/revisions\/1416"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/media\/766"}],"wp:attachment":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=765"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=765"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=765"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}