Securityvpntunnelencapsulationwireguardipsec

VPNs and Tunnels

A VPN wraps whole IP packets inside encrypted packets, adds a virtual interface and a route that sends chosen destinations through it — and every byte of wrapper is a byte of MTU your packets no longer have.

ConceptualLinux
▶ InteractiveInterview question
Progress

The problem

Your laptop is on café Wi-Fi with address 192.168.4.23. The database is on the office network at 10.20.0.5, which is not routable from anywhere on the internet. You need packets addressed to 10.20.0.5 to reach it, encrypted, over a network that only carries public addresses. How do you send a private packet across a public network?

A packet inside a packet

The trick is Encapsulation: Data, Segment, Packet, Frame applied one more time. Your application sends a normal IP packet to 10.20.0.5. Instead of handing it to the Wi-Fi interface, the kernel hands it to a virtual interfacetun0, wg0 — whose driver is the VPN software. The VPN encrypts the entire IP packet, headers and all, and puts the ciphertext in the payload of a new UDP (sometimes TCP) packet addressed to the VPN endpoint’s public IP. That outer packet crosses the café network and the internet like any other; every router sees only 192.168.4.23 → 203.0.113.50 UDP 51820 and an opaque payload.

At the endpoint, the VPN software decrypts the payload, recovers the original → 10.20.0.5 packet, and injects it into the office network as if it had arrived on a local cable. Replies take the same path in reverse. Neither the application nor the office database knows the tunnel exists; the office sees a packet from the address the VPN assigned you (10.99.0.7), the café sees UDP to one address.

Outbound: what wraps what
  1. Applicationconnect() to 10.20.0.5:5432
  2. Routing table10.20.0.0/16 via wg0 — the VPN’s route wins by longest prefix
  3. Virtual interface wg0 / tun0the kernel hands the whole inner IP packet to the VPN process or module
  4. Encrypt + encapsulateinner packet becomes the payload of an outer UDP packet to 203.0.113.50:51820
  5. Physical interface wlan0the outer packet leaves like any other; the café sees one UDP flow
  6. VPN endpointdecrypts, drops the wrapper, forwards the inner packet onto 10.20.0.0/16

The routing-table change: split vs full tunnel

Linux

Connecting a VPN does two visible things to the host: a new interface appears, and the routing table gains routes through it. In a split tunnel only the private prefixes go through the VPN (10.20.0.0/16 via wg0); everything else keeps using the default route out of wlan0. The café still sees your ordinary browsing; the office sees only office-bound traffic. In a full tunnel the default route itself is redirected, so all traffic — including public internet — goes to the VPN endpoint first and out from there; the café sees one encrypted flow, and web sites see the office’s public IP.

A full tunnel cannot simply replace the default route, because the outer packets to the endpoint would then be routed into the tunnel too. The usual trick is to add 0.0.0.0/1 and 128.0.0.0/1 via the tunnel — two routes that together cover everything and are both more specific than 0.0.0.0/0, so they win longest-prefix match — plus a single host route for the endpoint’s public IP via the physical interface. WireGuard’s wg-quick does it with a firewall mark and a policy-routing rule instead, to the same effect.

Before and after a split-tunnel WireGuard connection (Linux)
$ ip route
default via 192.168.4.1 dev wlan0
192.168.4.0/24 dev wlan0 proto kernel scope link src 192.168.4.23

$ sudo wg-quick up office
$ ip -brief link | grep wg
wg0   UNKNOWN   <POINTOPOINT,NOARP,UP,LOWER_UP>      # mtu 1420

$ ip route
default via 192.168.4.1 dev wlan0
10.20.0.0/16 dev wg0 scope link                     # ← new: office prefix via the tunnel
10.99.0.0/24 dev wg0 proto kernel scope link src 10.99.0.7
192.168.4.0/24 dev wlan0 proto kernel scope link src 192.168.4.23

$ ip route get 10.20.0.5
10.20.0.5 dev wg0 src 10.99.0.7

# full tunnel instead: two /1 routes beat the /0 default, endpoint pinned to the physical path
0.0.0.0/1 dev wg0
128.0.0.0/1 dev wg0
203.0.113.50 via 192.168.4.1 dev wlan0

MTU: the wrapper is not free

Conceptual

The physical link carries at most 1500 bytes per frame (IP: Best-Effort Delivery Between Machines). The outer packet is a full IP + UDP header plus the VPN’s own header and authentication tag — WireGuard adds 60 bytes over IPv4 (80 over IPv6), IPsec ESP around 50–75, OpenVPN more. So the inner packet can be at most about 1420–1440 bytes, and the virtual interface advertises that smaller MTU: WireGuard defaults wg0 to 1420. Local sockets see the smaller MTU and size their segments accordingly; the local side is fine.

Trouble starts with hosts that do not know about the tunnel. A server on the office network sends 1500-byte packets toward you; the endpoint must either fragment the inner packet before wrapping it, or send back ICMP "fragmentation needed" so the server’s path MTU discovery lowers its size. If that ICMP is filtered anywhere (Firewalls), large packets are silently dropped: SSH logs in but hangs on ls of a big directory, small web pages load and large ones stall, the database connects and the first big result set never arrives. This is the MTU black hole (the mtu-blackhole challenge) with the VPN as the narrowest link. The blunt fixes are TCP MSS clamping at the endpoint (iptables -t mangle ... TCPMSS --clamp-mss-to-pmtu) or a lower MTU on the tunnel.

Shapes of VPN, and the protocols in one paragraph

Remote access VPNs connect one device to a network: the laptop-to-office case above, one tunnel per user, an address assigned from a pool, usually split-tunnelled. Site-to-site VPNs connect two networks through two gateways: every host in 10.20.0.0/16 can reach every host in 10.30.0.0/16 and none of them run VPN software — the gateways hold one tunnel and the routers on each side have a route for the other prefix via their gateway. A cloud VPC connected to an office, or two VPCs in different regions, is site-to-site.

Three protocol families are in real use, and for this lesson the differences are secondary to the shared mechanism. WireGuard is a small, modern kernel module with fixed cryptography and a UDP transport; IPsec is the long-standing standard built into every OS and router, typically with IKEv2 for key exchange; OpenVPN is a user-space TLS-based tunnel that can run over TCP where UDP is blocked. All three do the same thing: authenticate the peer, agree keys, encrypt whole IP packets, and present a virtual interface. Tunnelling over TCP has a specific pathology — TCP inside TCP, where the inner and outer retransmission timers fight — which is why UDP is the default wherever it is allowed.

Remote access vs site-to-site
Remote accessSite-to-site
Tunnelsone per deviceone per pair of gateways
Who runs VPN softwareeach clienttwo gateways only
Addressingclient gets an address from a pooleach side keeps its own prefixes; routes point at the gateway
Typical useemployees, admins, contractorsoffice ↔ cloud VPC, VPC ↔ VPC, branch ↔ HQ
Failure symptom"the VPN dropped" on one laptopwhole prefix unreachable from the other site

Key points

  • A VPN encrypts a whole IP packet and carries it as the payload of another packet: encapsulation, one layer deeper.
  • Connecting adds a virtual interface (tun0, wg0) and routes through it; longest-prefix match decides which traffic enters the tunnel.
  • Split tunnel: only private prefixes via the VPN. Full tunnel: everything, via two /1 routes or policy routing, with the endpoint pinned to the physical path.
  • Every wrapper byte lowers the usable MTU (WireGuard: 1420); if ICMP fragmentation-needed is blocked, large transfers black-hole while small ones work.
  • Remote access: one tunnel per device. Site-to-site: one tunnel per gateway pair, hosts unaware.
  • WireGuard, IPsec and OpenVPN differ in cryptography and transport, not in the mechanism; prefer UDP transport to avoid TCP-in-TCP.

Why does this exist?

Mechanisms are answers to constraints. Open each question before reading the answer.

Why encapsulate a whole packet rather than just encrypt the payload?

Because the inner destination is a private address the internet will not route. Hiding the whole packet inside one addressed to a public endpoint is the only way to carry it across a network that cannot deliver it directly.

Why does the VPN need a virtual interface?

Routing decides by interface. A virtual interface gives the routing table something to point at, so ordinary connect() calls are steered into the tunnel without applications knowing.

Why is the tunnel MTU smaller?

The physical link still carries at most 1500 bytes and the wrapper occupies some of them. The inner packet must fit in what is left, or be fragmented, or be dropped.

Why UDP rather than TCP as the outer transport?

Two reliable layers stacked retransmit each other’s losses on independent timers and collapse under loss. UDP outside lets the inner TCP be the only one doing recovery.

VPN tunnel

VPN tunnel: a packet inside a packet
Connect, watch the routing table change, then follow one packet through tun0 and out over hostile Wi-Fi.
Tunnel mode
Destination
LinuxConceptual
ip route (on the laptop)
default via 192.168.1.1 dev wlan0 proto dhcp metric 600
192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.23
ip addr
wlan0: 192.168.1.23/24  mtu 1500
tun0:  (absent until connected)
App sends to 10.0.5.20:443. The application writes to a socket as usual. It has no idea whether a VPN is up; the routing table decides.
Inner packet (what the app produced)
IP192.168.1.23 → 10.0.5.20
TCP51514 → 443
TLS data1380 B
Café Wi-Fi sees
192.168.1.23 → 10.0.5.20 tcp/443 (+ SNI hostname)
VPN endpoint sees
nothing — packet never reached it
MTU consequence
1/3 · App sends to 10.0.5.20:443

How it fails

What the failure looks like from inside real software.

  • Connected, ping 10.20.0.5 works, ssh logs in and then hangs on any large output: MTU black hole; ICMP fragmentation-needed dropped or MSS not clamped.
  • Connected, office resources unreachable, ip route get 10.20.0.5 shows dev wlan0: the route for the office prefix was not installed, or a more specific local route shadows it.
  • Full tunnel connects and immediately loses all connectivity: the endpoint’s own address is routed into the tunnel; the host route via the physical interface is missing.
  • Internal DNS names do not resolve on the VPN: split tunnel routes the packets but the resolver is still the café’s; a split-DNS rule for the office domain is missing.
  • VPN over café Wi-Fi is slow and stalls under any loss: outer transport is TCP (UDP blocked); TCP-in-TCP retransmission fighting.
  • Two site-to-site networks both use 10.0.0.0/16: overlapping prefixes cannot be routed; one side must be renumbered or NATed at the gateway.