Nov 19

Opening a TUN Device on UNIX

Category: C, Networking, Programming

The TUN/TAP interface under Linux provides user space access to Transport (Ethernet) or Network Layer (IP) traffic by allowing a developer to create a “virtual” interface that can be openend in user space as a file descriptor.

What is it Used For?

The TUN/TAP interface is most often used by tunneling applications like openVPN. In that scenario, each end of the VPN creates a TUN interface and adds a route to the routing table that forwards all traffic destined for the other host to that interface. The tunneling program uses the device to capture the IP traffic and forward it to the other host, where the traffic is retrieved and placed on opposing TUN interface.

How do I create one?

This code works on CentOS 5, Unbuntu Linux, and RHEL 5.:

#include <sys/socket.h>
#include <asm/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <unistd.h>
 
int tun_alloc(char *dev)
{
    struct ifreq ifr;
    int fd, err;
 
    if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
       return fd;
 
    memset(&ifr, 0, sizeof(ifr));
 
    /* Flags: IFF_TUN   - TUN device (no Ethernet headers) 
     *        IFF_TAP   - TAP device  
     *
     *        IFF_NO_PI - Do not provide packet information  
     */ 
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI; 
    if( *dev )
       strncpy(ifr.ifr_name, dev, IFNAMSIZ);
 
    if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
       close(fd);
       return err;
    }
    strcpy(dev, ifr.ifr_name);
    return fd;
}            
 
int main(){
	int fd;
	char dev[IFNAMSIZ];
	memset(dev, 0, sizeof(dev));
	if( (fd = tun_alloc(dev)) < 0){
		return 1;
	}
	//read/write raw packets to/from the FD
	...
}

How do I configure it?

Tun devices can be viewed and configured in the same way as any other network devices.

Viewing the device

#/sbin/ifconfig -a
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          POINTOPOINT NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

Setting the IP address

#ip addr add 10.8.0.1/24 dev tun0
#ifconfig -a
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          inet addr:10.1.0.1  P-t-P:10.1.0.1  Mask:255.255.255.0
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

Bringing up the link

At this point the address is configured, but the virtual link is not considered active

#ip link set tun0 up

Reading and writing to/from the device

Something that is not clear from the minimal tun documentation is that you have to write a single IP packet per write and have to read a full IP packet per read. If you try to write a partial packet it will be interpreted as erroneous by the interface. if your read doesn’t ask for enough bytes it will continue to return -1 until you read the full packet in one go.

1 comment

Comments are closed.