As you may already know, VPN stands for Virtual Private Network, namely it is a private network, with many components being virtualized. From a user’s perspective, all we need is a virtual network interface (e.g. /dev/tun0) on my device and configure my routing table to route all or part of traffic going through that interface, but what happens then? This post provides a demo revealing some implementation details after the virtual network interface - tunneling, the code is here.
In a nutshell, the process for client side tunneling is:
Open an UDP socket whose other side is the server.
Create the tun device, configure it and bring it up.
Configure routing table.
Read packets from tun device, encrypt, send to server via socket created in 1st step; And read from the socket, decrypt, write back to tun device. This step goes on and on.
The following code snippet creates the UDP socket, which is basic UNIX network programming.
The following code “clone” a new virtual interface named “tun0” from “/dev/net/tun”. IFF_TUN illustrates this virtual interface works on network layer while IFF_TAP will make the interface work on data link layer.
The following Linux commands route all traffic to the virtual interface, but with exception of packets with destination to VPN server, since tunneling data needs to go straight to VPN server through normal network interfaces. So the above illustration is inaccurate, it should be like this:
The following code snippet is the core packet switch algorithm. It reads packets from tun device, encrypt, send UDP socket; And read from UDP socket, decrypt, write to tun device. I used select multiplexing to monitor on these 2 fds.
As a simple Point-to-Point VPN tunnel, server side code is almost identical to the client side since the core packet switch logic is the same. The complete code for both server side and client side is here. And iOS demo written in Swift 3 is here.