Writing VPN with Go — 2. Client

Ömer Burak Demirpolat
5 min readOct 20, 2022

--

According to the previous article, we already have a VPN Server waiting for connection by clients.

So what will VPN Client do?

  • A TUN device must be created. If you remember from the first article, VPN Client should catch requests from my notebook to 192.168.1.12, that is, Kubernetes Node. We will create a TUN device and the subnet of the IP address we will give to the TUN device will be 255.255.255.0, and accordingly, requests to the address 192.168.1.12 will be sent over the TUN device. TUN device will deliver these requests to the process of our VPN Client application.
  • It should create a TCP connection to the 8990 port of the VPN server because we will forward the IP packets we receive from the network interface to the VPN server over this connection. You need to write a TCP client.
  • We need a loop that will listen for the TCP connection, in line with this loop we will forward the packets from the VPN server to the interface.
  • We need another loop listening for the interface, which will forward the packets from the TUN interface to the VPN server over the TCP connection.

Let’s start by creating the TUN Interface.

To summarize the code above:

The TUN Interface was created and an IP was assigned to the TUN Interface with the IP address from the parameter named ip, and the subnet of this IP is 255.255.255.0.

We made the TUN Interface “up” with the 2nd command.
Now let’s create the TCP client that will connect to the 8990 port of the VPN server and make it read the data coming from the connection we created.

The function called creteConn returned a connection and a connection to port 8990 of the server with IP address 89.252.131.88.

We listen to this connection with the listen function and assign every message forwarded to this connection from the VPN server to the variable named “message”.

Finally, we write this message we get to the interface and forward it to the kernel.

Finally, we need something to listen to the data coming to the TUN Interface we created.

This code is also quite simple, we assign each packet we read by the interface to a variable named “packet” and we write the packet to the connection via “conn”, which carries our TCP connection, to send each packet we obtain to our VPN server.

Now we can try the VPN we made.

If we remember our initial goal, our goal is to access the Kubernetes node on the company’s private network via the notebook.

I will send an HTTP request to 192.168.1.12:8080/hi from my home notebook and try to get a response from there.

First of all, I have to get the VPN Server up, because when VPN Client wants to establish TCP connection, VPN Server must be ready.

root@ip-89-14-22-20:~/Go/src/vpntest# go run server/main.go
2022/10/04 08:46:44 Interface Name: tun0
interface listening
tcp server ready

When I run my application, a TUN interface named tun0 is created and the packets sent to this interface are listened, when the packet arrives, it will be written to the relevant TCP connection and as it can be understood from the last line, my TCP Server is ready to listen to the packets coming from the 8990 port by the Client.

Then I run my VPN Client application with root user or sudo.

[root@ip-172-15-42-63 vpntest]# go run client/main.go
2022/10/04 08:48:04 Interface Name: tun0
tcp connection listening
interface listening

As can be seen here, tun0 is created, a TCP connection is created on the VPN Server side and it is listening, when packets are received from here, it will be written to the interface and also the packets leaving the interface are listened, when a packet arrives at the interface, it will be written to the TCP connection.

Now VPN Server and VPN Client are ready to do their job.
To test this work, I need to send a request to the address 192.168.1.12 from the device where the VPN Client is installed.

The terminal session on which I run the application on the VPN Client device continues and I run the following command to make a request in a new terminal.

[root@ip-172-15-42-63]# curl 192.168.1.12:8080/hi

While I was making the request, a few things printing on the terminal screen. (on Client)

[root@ip-172-15-42-63 vpntest]# go run client/main.go
2022/10/04 08:48:04 Interface Name: tun0
tcp connection listening
interface listening
--------------------------------------------------
START - incoming packet from INTERFACE
SRC: 192.168.9.1
DST: 192.168.9.12
ID: 41135
CHECKSUM: 18351
DONE - incoming packet from INTERFACE
--------------------------------------------------
START - incoming packet from TUNNEL
SRC: 192.168.9.12
DST: 192.168.9.1
ID: 16868
CHECKSUM: 25979
DONE - incoming packet from TUNNEL
---------------------------------------------------

Here we see a request from 192.168.9.1 to 192.168.9.12 over the interface named tun0.

So why does the request from my notebook device seem to go from the source 192.168.9.1?

While creating TUN Interfaces, an IP is given and we gave this IP address to the interface, of course, being in the same subnet allows us to do routing.

In short, the TUN Interface of the notebook has been given an IP address of 192.168.9.1 and now all requests to the address 192.168.9.xxx on this notebook will be sent over the TUN Interface.

Now let’s look at the terminal’s messages in the second and last part. Something was written to the TCP connection by the VPN Server, and I logged it as “START — incoming packet from TUNNEL”.

Here, we got the response of the device with the IP address 192.168.9.11 and the destination address is 192.168.9.9.

Now let’s take a look at the logs on the VPN Server side.

root@ip-89-14-22-20:~/Go/src/vpntest# go run server/main.go
2022/10/04 08:46:44 Interface Name: tun0
interface listening
tcp server ready
---------------------------------------------------
START - incoming packet from TUNNEL
SRC: 192.168.9.1
DST: 192.168.9.12
ID: 41135
CHECKSUM: 18351
---------------------------------------------------
START - incoming packet from INTERFACE
SRC: 192.168.9.12
DST: 192.168.9.1
ID: 16868
CHECKSUM: 25979
DONE - incoming packet from INTERFACE
---------------------------------------------------

Here, too, we actually see the opposite of VPN Client.

A packet came from 192.168.9.1 to 192.168.9.12 over a TCP connection, and then a packet came from 192.168.9.12 to 192.168.9.1 over the TUN Interface and was written back to the TCP connection.

As a result, I can see my response on my second terminal screen on the VPN Client machine.

[root@ip-172-15-42-63] $ curl 192.168.9.12:8080/hihi 192.168.9.1:3510

You can find full code from here: https://github.com/bdemirpolat/vpntest

Thanks for reading this.

Please feel free to contact me.

burakdemirpolat.com

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Responses (1)

Write a response