← Home

Building a userspace VPN in Go

I was watching a video on travel routers wanted to understand IP spoofing, so I made a simple VPN client/server in Golang.

What we’re building

VPN client

VPN server

Prerequisites: Basic understanding of TCP/IP

Quick glossary

Following one packet

Here is the whole journey of a single packet. Step through it, and click any box to jump to it.

Click Next, press ← →, or click any box.
Local Linux clientPublic InternetRemote Linux serverraw IP packetread() on tun fdUDPUDPwrite() to tun fdforward + NATEncryptedUDP tunnelUser appBrowser / curltun0Layer 3 virtual device · kernelGo VPN clientUser-space programDestinationPublic web servertun0Layer 3 virtual device · kernelGo VPN serverUser-space program

Packet inspector
In code

Details the walkthrough skips

Why two /1 routes

The default route of 0.0.0.0/0 goes to the physical NIC. Having 0.0.0.0/1 (which points to tun0) essentially creates a new default route. I found this to be better because when a user exits the program, the 0.0.0.0/1 gets destroyed and the routing table is restored to what it normally was.

How the server finds the client

The server listens for UDP traffic on a specific port. I personally used Oracle Cloud, so I didn’t have to expose my home network. If you want to test this at home, you will have to do port forwarding.

Limitations

This project is a learning tool, not something you should trust with your real traffic. I cut a lot of corners on purpose to keep the code small enough to follow in one sitting. Here are the biggest ones.

Wrapping up

Before I built this, a VPN felt like magic. It turns out to be a virtual network card, a routing table, some encryption, and a UDP socket. If you made it this far, I hope the walkthrough made that clearer. The full source is available at https://github.com/pacyluu/vpn.