Bits and Bytes Security

VPN Routing and Security

I was working abroad temporarily and needed to use a shopping mall’s Internet service. This mall offered open Wi-Fi that anyone could use after going through a captive portal.

After filling out the necessary form, I gained access to the Internet through the network. Then, I activated the VPN based on the OpenVPN client. All traffic will go through the VPN, so I should be safe, right?

Well, not exactly!

I was using this shopping mall’s Internet without any issues until I had to connect to an internal IP through SSH. Strangely, it was timing out.

$ ssh my-host
ssh: connect to host 10.2.1.30 port 22: Operation timed out

I started to try to understand what was happening. At first, I thought it was a problem with the server itself or the internal network. But it sounded more like my packets were not reaching the internal network.

So, what’s happening to the packets on my laptop? I’ve used tcpdump to watch the movement of packets in the network interface.

$ tcpdump -lvvnp port 22
tcpdump: data link type PKTAP
tcpdump: listening on pktap, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes
15:17:39.392720 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64)
    10.199.125.146.59021 > 10.2.1.30.22: Flags [SEW], cksum 0x7d95 (correct), seq 1625334601, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 719456825 ecr 0,sackOK,eol], length 0
15:17:40.379066 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64)
    10.199.125.146.59021 > 10.2.1.30.22: Flags [S], cksum 0x7a6d (correct), seq 1625334601, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 719457825 ecr 0,sackOK,eol], length 0
15:17:41.382076 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64)
    10.199.125.146.59021 > 10.2.1.30.22: Flags [S], cksum 0x7685 (correct), seq 1625334601, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 719458825 ecr 0,sackOK,eol], length 0
15:17:42.384117 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64)
    10.199.125.146.59021 > 10.2.1.30.22: Flags [S], cksum 0x729d (correct), seq 1625334601, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 719459825 ecr 0,sackOK,eol], length 0

The source IP of the packets is set to the gateway of the Wifi AP and not from the VPN tunnel interface. Why? I’d expect that if I have the VPN active and with adequate VPN configuration, all packets would go through the VPN tunnel…

The next step was to look at the route tables. In macOS we can use netstat -rn command. An excerpt of the output is:

:::plaintext
$ netstat -rn
Routing tables

Internet:
Destination        Gateway            Flags        Netif Expire
0/1                x.x.x.x            UGSc         utun2
default            10.128.128.128     UGSc           en0
10                 link#4             UCS            en0      !
10.128.128.128/32  link#4             UCS            en0      !
10.128.128.128     x.x.x.x            UHLWIir        en0   1145
10.199.125.146/32  link#4             UCS            en0      !
10.248.5.109       x.x.x.x.           UHLWI          en0   1194
10.255.255.255     ff:ff:ff:ff:ff:ff  UHLWbI         en0      !
80.1.1.1/29        x.x.x.x            UGSc         utun2
80.1.1.1/32        10.128.128.128     UGSc           en0
127                127.0.0.1          UCS            lo0
127.0.0.1          127.0.0.1          UH             lo0
...

The 80.1.1.1 is the public IP of the VPN gateway, and the 10.128.128.128 is the internal IP of the Wifi AP gateway. So, the VPN configuration adds a default destination, 0/1, which should have more priority than the “default” gateway of the Wifi AP.

Why is the packet routed from the en0 interface if the routing table looks good? 🤔

If we look more carefully, we see an additional entry for the destination 10.0.0.0/8:

:::plaintext
...
default            10.128.128.128     UGSc           en0
10                 link#4             UCS            en0      <---
10.128.128.128/32  link#4             UCS            en0
...

Since the network stack prioritizes routes with a longer prefix, instead of forwarding the packet through the default gateway, it will use this route with a longer prefix: 10.0.0.0/8 (instead of 0/1) if the destination IP is within the range 10.0.0.0/8, which is the case!

So even though the VPN configuration sets a default gateway, network-provided routes for specific IP ranges can’t override it!

Off-topic, but if it happens to you also, we’ve to remove the conflicting route to fix the connectivity issue. In macOS, that can be done with:

# route delete -host 10.0.0.0/8 -link 4

Comments? Feel free to send me an email or reach out through Twitter.