Skip to content
TUN/TAP Devices
step 1/5

Reading — step 1 of 5

Read

~2 min readFrames & IP

TUN and TAP Devices

To build a network stack you need packets to build it from. A TUN device is the kernel's gift: a virtual network interface whose "wire" is your process. Whatever the kernel routes to it, you read; whatever you write, the kernel treats as if it had arrived from the network.

There are two flavours:

  • TUN — a layer-3 device. You read and write raw IP packets. No Ethernet header, no MAC addresses.
  • TAP — a layer-2 device. You read and write full Ethernet frames, MACs and all.

For a userspace IP stack, TUN is the right choice: we care about IP and up, and we would rather not simulate ARP and Ethernet framing just to get started. (TAP is what you reach for when you do need L2 — software switches, VM bridges.)

Wiring one up

On Linux a TUN interface is created and addressed like any other NIC:

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

Now anything the kernel routes to 10.0.0.0/24 is handed to whoever holds the device. Your process claims it by opening the clone device and issuing one ioctl:

python

Each read returns one whole IP packet; each write injects one. There is no stream to reframe and no length prefix to parse — the packet boundary is the syscall boundary.

The trap: IFF_NO_PI

Leave IFF_NO_PI off and the kernel prepends a 4-byte packet information header (flags + protocol) to every read and expects it on every write. Forget it and byte 0 of what you think is the IP version/IHL is actually a flag byte — every field is shifted by four and nothing parses. Setting IFF_NO_PI makes the device hand you the IP packet and nothing else, which is what a from-scratch stack wants. This single flag is the most common reason a first TUN program "reads garbage."

Why it is worth it

Kernel-bypass buys total control: you can implement any protocol, log every byte, and inject faults a real NIC never would. The cost is speed — every packet crosses the user/kernel boundary with a syscall and a copy, so a userspace stack trades throughput for flexibility. WireGuard's userspace build, gVisor, slirp4netns, and QEMU's user-mode networking all make exactly that trade.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…