1. Introduction

In computer networking, the availability of virtual devices that aren’t backed by a hardware adapter opens up new possibilities. One way of leveraging this concept is through TUN, a network interface within the kernel enabling us to form virtual connections in the operating system.

In this tutorial, we’ll first explore what TUN is. Then, we’ll see how it works with a simple practical example using the Linux command line. Finally, we’ll explore real-world use cases.

2. What Is TUN?

TUN, short for network TUNnel, is a virtual interface that implements a software-based abstraction of a network by emulating the behavior of physical devices like Ethernet or Wi-Fi interface cards. It operates on layer 3 of the OSI model, handling the transmission and reception of IP packets. Therefore, TUN has no relation with Ethernet frames and MAC addresses unlike TAP, another interface operating on layer 2.

TUN lacks the capability of bridging different LANs as opposed to TAP. However, TUN can be used to route traffic through a tunnel, making it suitable for VPN services.

At its core, TUN provides an interface for user-space applications to deal with the raw network traffic. Programs can attach to this network interface. Consequently, they can read data from and write data to the interface in the same manner as with the physical interfaces.

3. Creating a TUN Interface

Several tools are available to create a TUN interface including tunctl and OpenVPN. In this case, we’ll use the ip command-line utility along with its tuntap interface type.

Let’s begin by creating a TUN device named tun0, configuring its IP address along with the subnet mask, and bringing it up with sudo privileges:

$ sudo ip tuntap add dev tun0 mode tun
$ sudo ip addr add 10.0.0.1/24 dev tun0
$ sudo ip link set up dev tun0

The aforementioned commands are sufficient to activate the TUN interface.

Next, let’s verify the existence of our new interface via ip:

$ ip addr show
...
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:37:cd:ab:f8 brd ff:ff:ff:ff:ff:ff
    inet 10.0.4.11/24 brd 10.0.4.255 scope global dynamic noprefixroute enp0s3
       valid_lft 86263sec preferred_lft 86263sec
    inet6 fe82::3597:47e3:1e77:5a3c/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: tun0: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 500
    link/none 
    inet 10.0.0.1/24 scope global tun0
       valid_lft forever preferred_lft forever

As we can see, the network interface is up and alive with an IP address 10.0.0.1.

Before moving to traffic capture, we need to attach a process to the interface so that the kernel makes it available. For this purpose, we’ll create a simple C program. This program attaches to the tun0 interface, sets some necessary configuration which is outside the scope of this article, and reads the data.

Let’s create a file named tun_program.c, and copy the provided code into it:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>

#define IFNAMSIZ 16

int main() {
  int tun_fd = open("/dev/net/tun", O_RDWR);
  struct ifreq ifr;
  memset(&ifr, 0, sizeof(ifr));
  ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
  strcpy(ifr.ifr_name, "tun0");
  ioctl(tun_fd, TUNSETIFF, (void *)&ifr);

  while (1) {
    char buffer[1500];
    int nread = read(tun_fd, buffer, sizeof(buffer));
    printf("Read %d bytes from device %s\n", nread, ifr.ifr_name);
  }

  close(tun_fd);
  return 0;
}

After preparing the file, let’s compile the code with gcc:

$ gcc -o tun_program tun_program.c

Now, that we have our program ready to attach to the TUN interface, we can proceed with capturing the network traffic with tcpdump. To accomplish this, let’s open three terminals, run our program, ping the IP address 10.0.0.2 which is routed from TUN, and capture the traffic with tcpdump:

[Terminal 1]
$ ./tun_program 
Read 84 bytes from device tun0
Read 84 bytes from device tun0
Read 84 bytes from device tun0
Read 84 bytes from device tun0
Read 84 bytes from device tun0
Read 84 bytes from device tun0

[Terminal 2]
$ sudo tcpdump -i tun0 -v
tcpdump: listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
23:36:25.137290 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 8) baeldung > ip6-allrouters: [icmp6 sum ok] ICMP6, router solicitation, length 8
23:36:26.064591 IP (tos 0x0, ttl 64, id 3637, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 4, seq 1, length 64
23:36:27.088521 IP (tos 0x0, ttl 64, id 3843, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 4, seq 2, length 64
23:36:28.115681 IP (tos 0x0, ttl 64, id 3851, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 4, seq 3, length 64
23:36:29.138589 IP (tos 0x0, ttl 64, id 3924, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 4, seq 4, length 64
23:36:30.163580 IP (tos 0x0, ttl 64, id 4006, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.1 > 10.0.0.2: ICMP echo request, id 4, seq 5, length 64
6 packets captured
6 packets received by filter
0 packets dropped by kernel

[Terminal 3]
$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
--- 10.0.0.2 ping statistics ---
6 packets transmitted, 0 received, 100% packet loss, time 4705ms

As we can see above, we’ve successfully captured the traffic using tcpdump as the packets are transmitted through the TUN interface and were routed to the pinged IP address.

4. Use Cases

Various domains harness the power of software-based abstractions over a network interface. In this section, we’ll delve into some of these domains one by one.

4.1. Virtual Private Networks (VPNs)

One of the primary use cases for the TUN interface is the implementation of VPNs. VPNs leverage this interface to pass encrypted data through the VPN tunnel. Accordingly, they can encapsulate the network traffic in a secure way, protecting the privacy of the users.

Notably, certain VPN protocols such as OpenVPN use TUN devices to route IP packets through tunnels. This creates an intermediate layer that precedes the transmission of data to a physical network device.

4.2. Virtualization

Network virtualization is another prominent area where TUN devices find wide use. Virtual machines, containers, and emulators utilize the TUN interface to create virtual networks that are isolated from underlying physical adapters. Hence, it gives the power to modify network configurations and routing policies, optimizing them to suit specific applications.

4.3. Network Simulation

On the other hand, network simulators can leverage the TUN interface for the testing of specific network configurations. Furthermore, simulators can capture the real traffic flowing through the interface, enabling further analysis. This functionality empowers engineers to test an extensive range of network scenarios.

5. Conclusion

In conclusion, the TUN interface plays a crucial role in computer networking, providing a virtual network device for encapsulating and transmitting data packets.

In this article, we gained a decent understanding of TUN. Thereafter, we successfully created our own TUN interface and tested it by capturing the traffic flowing through it. Finally, we explored the application areas of TUNs in real-world scenarios.