How to read packet by packet from linux tun/tap

linux create tun device
linux tap interface bridge
create a host to host tunnel using tun/tap
linux tuntap tutorial
create tap interface ubuntu
tunctl example
tun vs tap
tap device

I have connected to an existing Tap device using

fd = open(...)

Now i want to read from it packet by packet. if i use


I wont read exactly 1 packet.

How can i read exactly 1 packet? Is there some kind of header that says the packet length or in the worst case i will have to parse the packet and figure the length myself?

I was wrong about the way read() reads bytes from a tap device. It turns out when I use read() it reads exactly 1 frame, (or n bytes if n is smaller then frame size)

TUN/TAP, Description TUN/TAP provides packet reception and transmission for user space program has to read/write IP packets (with tun) or ethernet frames (with tap). It is known to work with Linux kernel 4.15. The intent of this program is to explore how packets can be read and dissected from the tap interface. TUN/TAP frame format (for tun devices) The TUN/TAP interface basically spits out raw frames to you. You have the option of having the kernel pre-pend a "packet information" header to this.

This is how I parse packets from a raw layer3 network stream.

#include "bigendian.h"

#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>

int pb_packet_read(PacketBuffer *b, int fd, int count, PacketCallback cb) {
    int i = 0;
    int result;
    Packet *p;
    unsigned char *start;
    size_t remaining;

    while (i < count) {
        if (b->packet == NULL) {
            if (b->level < 20 ) {
                // Read up to MTU bytes to determine packet header.
                result = read(fd, b->blob + b->level, MTU - b->level);
                if (result <= 0) {
                    return i;
                b->level += result;

            if (b->level < 20 ) {
                return i;

            // Now, can read the packet total length
            start = b->blob;
            p = (Packet*) malloc(sizeof(Packet));
            p->start = start;
            p->version = start[0] >> 4;
            p->total_length = bigendian_deserialize_uint16(start + 2);
            memcpy(&(p->src.s_addr), start + 12, 4);
            memcpy(&(p->dst.s_addr), start + 16, 4);

            b->packet = p;

        else {
            L_DEBUG("Using prev stored complete packet.");
            p = b->packet;

        // Read the rest of the packet
        if (p->total_length > b->level) {
            remaining = p->total_length - b->level;
            L_DEBUG("Packet not completed, trying read more.");
            result = read(fd, b->blob + b->level, remaining);

            if(result <= 0) {
                if (result == EAGAIN) {
                perror("READ BODY");
                return i;
            b->level += result;
            if (result < remaining) {
                L_DEBUG("Not enough data");
                return i;

        if (b->level > p->total_length) {
            remaining = b->level - p->total_length;
            memcpy(b->blob, b->blob + p->total_length, remaining);
            b->level = remaining;
            L_DEBUG("Remaining data: %lu", remaining);

        // Packet is ready to pass to callback.
        if(cb) {

        // Cleanup for the next packet
        b->level = 0;
        b->packet = NULL;
    return i;

Tun/Tap interface tutorial « \1, Tun/tap interfaces are a feature offered by Linux (and probably by that attaches to the interface and reads packets sent out by the kernel. When data comes in from the network, thanks to the aforementioned trick, we can know how long the next packet is going to be by reading the two-bytes length that precedes it in the stream. When we've read the packet, we write it to the tun/tap interface descriptor, where it will be received by the kernel as coming "from the wire".


You can read packets coming in through whatever interface you specify, in this example its wlan1.

int main(){
    char *device = NULL;
    pcap_t* descr;

     if(argc > 0)
       device = "wlan1";

    if(device == NULL)
    descr = pcap_open_live(device,BUFSIZ,0,-1,errbuf);

    if(descr == NULL){ printf("pcap_open_live(): %s\n",errbuf); exit(1); }

    errbuf[0] = 0;
    handle = pcap_open_live(device, BUFSIZ,1,0,errbuf);

    pcap_loop(handle,-1, process_packet, NULL);
    return 0;

Where in pcap_loop the process_packet is the callback of the packets coming in.

Let me know if there is anything you are unsure of.

P.S. Here is some links that should help you with parsing 802.11/ethernet headers.

How to read data from tap interface?, -ESUDO. Seriously, you should set the owner of the tap interface. Try to following: ip tuntap add tap0 mode tap user USER. Where USER is the  I have an existing tap device (tap0) that I created on command line. # ip tuntap add dev tap0 mode tap I want to read any data coming in on that interface using a C program. I checked other SO questions, but found code that create an interface by opening /dev/net/tun. Can anyone provide some direction on how to open and read existing interface ?

simpletun.c, Handles (badly) IPv4 for tun, ARP and IPv4 for * * tap. #include <sys/socket.h> #include <linux/if.h> #include <linux/if_tun.h> #include <sys/types.h> end */ break; } net2tap++; /* read packet */ nread = read_n(net_fd, buffer, ntohs(plength​));  Assuming you've created a TAP interface already (using ip tuntap add), you can use a Packet Socket to write data into it (try man 7 packet for more info). To start with, use int sockfd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) to create a packet socket. The second step is to find the interface index (ifindex) of the TAP interface.

TUN/TAP Demystified – Page 2 –, Open /dev/net/tun . Call the TUNSETIFF ioctl to select the device mode and options. Read packets from, and write packets to, the file descriptor. The interface a packet is forwarded to will be based on the routing table as usual which you can view with ip route or netstat -r. This will apply to packets addressed for the localhost also. Additionally, TUN/TAP interface can also be used as the interface for a route which appears in the routing table.

TUN/TAP Interface, From the Linux kernel documentation: TUN/TAP provides packet reception and transmission for user space programs. That means the user program can only read/write IP packets from/to a TUN interface and Ethernet  On receiving the packets on the server side, the tunneling software decrypts the packets and pushes it to the tun device on the server side. The routing tables will forward the packets to the 192

  • By "packet" do you mean "Ethernet frame"? A normal user doesn't have access to the protocol metadata, so if you're consuming Ethernet, you don't see the frame data, just the payload. You need elevated privileges, like libpcap does, to access the raw data.
  • yes i need to read the whole frame with all headers, anyway if i just read the payload, how do i know its size so that i know how much to read? is there a header before each payload?
  • The "read" call will block when there's no more data, or set error EAGAIN in non-blocking mode.
  • yes but it can read much more then one frame at once, so how do i read exactly 1 frame?
  • @yonigo did you ever find an answer for this? If you did could you post the answer. Thanks.
  • Me too, the exact experience. but I prefer to take care of any remaining or extra bytes, because this is network stream. I cannot find any manual and evidence which proves the Linux reads packets one-by-one from the tun/tap devices.
  • thanks! but it seems i was wrong, when i read() from a tap device i get exactly 1 frame (or less if bytes_to_read is less). so i didnt realy have to do anything just use read and it gives my a frame..