How to Benchmark Network Performance in Linux Using the In-Kernel pktgen Packet Generator

When benchmarking high-bandwidth 10GbE, 25GbE, or 100GbE network interfaces, traditional user-space traffic generators (such as iperf3 or netperf) often bottleneck on CPU interrupt handling, user-to-kernel context switches, and socket buffer copies long before saturating physical wire capacity. To evaluate raw network interface card (NIC) throughput, packet-per-second (PPS) limits, and kernel packet scheduler performance, the Linux kernel includes a built-in packet generator module: pktgen. Operating directly within kernel thread space, pktgen can saturate multi-gigabit links with millions of packets per second.

Understanding the In-Kernel pktgen Architecture

The pktgen framework runs as an in-kernel module (net/core/pktgen.c). Rather than relying on traditional Berkeley sockets, it communicates directly with kernel device drivers (such as ixgbe, i40e, or mlx5_core):

  • Zero Context Switching: Packets are generated and submitted to device transmission ring buffers directly inside kernel space.
  • Multi-Threaded Scaling: Kernel threads (kpktgend_X) can be pinned to individual CPU cores, allowing linear throughput scaling across modern multi-core processors.
  • Control via procfs: All runtime configuration (packet size, target MAC/IP, rate throttling, and packet counts) is managed through standard shell interaction with the /proc/net/pktgen/ virtual directory.

Loading the pktgen Kernel Module

Before configuring traffic streams, load the module into the active kernel:

sudo modprobe pktgen

Verify that the procfs control interface is mounted:

ls -l /proc/net/pktgen/

You will observe:

  • kpktgend_X: One thread control entry for each available logical CPU core (e.g., kpktgend_0, kpktgend_1).
  • pgctrl: The master control interface used to start, pause, and stop packet generation.

Configuring an In-Kernel Packet Generation Thread

To configure a packet generation run on interface eth0 pinned to CPU core 0:

First, associate the physical device with the kernel thread:

echo "rem_device_all" | sudo tee /proc/net/pktgen/kpktgend_0
echo "add_device eth0" | sudo tee /proc/net/pktgen/kpktgend_0

This creates a dedicated device control handle at /proc/net/pktgen/eth0.

Next, configure the packet parameters (destination IP, destination MAC, packet size, and packet count):

# Set packet count (0 indicates continuous transmission until manually stopped)
echo "count 10000000" | sudo tee /proc/net/pktgen/eth0

# Set packet payload size (64 bytes for maximum packet-per-second stress testing)
echo "pkt_size 64" | sudo tee /proc/net/pktgen/eth0

# Configure destination IP and MAC addresses
echo "dst 192.168.10.50" | sudo tee /proc/net/pktgen/eth0
echo "dst_mac 00:1B:21:BA:81:42" | sudo tee /proc/net/pktgen/eth0

# Set source IP and delay
echo "src_min 192.168.10.10" | sudo tee /proc/net/pktgen/eth0
echo "delay 0" | sudo tee /proc/net/pktgen/eth0

Starting and Monitoring the Packet Stream

Once parameters are defined, trigger packet generation by sending start to the master controller:

echo "start" | sudo tee /proc/net/pktgen/pgctrl

While the test is executing, inspect live transmission telemetry and wire statistics by reading the interface status file:

cat /proc/net/pktgen/eth0

The output reports granular kernel statistics:

Params: count 10000000  min_pkt_size: 64  max_pkt_size: 64
     frags: 0  delay: 0  clone_skb: 0  ifname: eth0
     flows: 0 flowlen: 0
     dst_min: 192.168.10.50  dst_max:
     src_min: 192.168.10.10  src_max:
     dst_mac: 00:1B:21:BA:81:42
Result: OK: 10000000(c9999820+d180) usec, 10000000 (64byte,0frags)
  14285714pps 7314Mb/sec (7314285568bps) errors: 0

The summary provides:

  • PPS (Packets Per Second): Here, 14.28 million packets per second.
  • Mb/sec: Instantaneous wire throughput achieved without dropping packets.
  • Errors: Driver ring buffer drops or transmit queue overruns.

Stopping the Benchmark and Unloading the Driver

To halt an active packet generation test:

echo "stop" | sudo tee /proc/net/pktgen/pgctrl

To remove the interface binding and reset the kernel threads:

echo "rem_device_all" | sudo tee /proc/net/pktgen/kpktgend_0

When testing is concluded, unload the kernel module to restore standard system state:

sudo modprobe -r pktgen

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.