Stage 3 · Build
Networking Stack Deep Dive
Packet Flow Through the Kernel
RX/TX rings, NAPI, XDP, and the receive path — how packets traverse the Linux networking stack.
The Packet Receive Path
When a network packet arrives at the NIC, it travels through several layers before reaching your application: DMA to ring buffer, interrupt, softirq, netfilter, socket buffer, and finally the read() call. Understanding this path is essential for network performance tuning.
- 1. NIC DMA — Packet is DMA'd into a ring buffer in RAM
- 2. Interrupt — NIC raises an interrupt to notify the CPU
- 3. NAPI — Kernel switches to polling mode under load
- 4. GRO — Generic Receive Offload merges small packets
- 5. Netfilter — iptables/nftables rules are applied
- 6. Socket — Packet is delivered to the application socket buffer
- 7. Application — recv()/read() copies data to userspace
RX and TX Rings
The NIC uses DMA ring buffers to transfer packets between device memory and main memory. The ring is a fixed-size circular buffer: the NIC writes incoming packets, and the kernel reads them. The ring size determines how many packets can be buffered before drops occur.
# View ring buffer sizes
ethtool -g eth0
# Ring parameters for eth0:
# Pre-set maximums:
# RX: 4096
# TX: 4096
# Current hardware settings:
# RX: 256
# TX: 256
# Increase ring buffers for high-throughput
ethtool -G eth0 rx 4096 tx 4096
# Check multi-queue support
ethtool -l eth0
# Combined: 8Larger ring buffers reduce packet drops during burst traffic but use more memory. The default 256 is fine for most workloads; increase to 2048-4096 for 10G+ networks.
NAPI Polling
NAPI (New API) prevents interrupt storms under high packet rates. Instead of generating an interrupt per packet, the NIC generates one interrupt, then the kernel polls for packets in a loop. This dramatically reduces overhead at high packet rates.
# View current coalescing settings
ethtool -c eth0
# rx-usecs: 3
# rx-frames: 0
# tx-usecs: 3
# tx-frames: 0
# adaptive-rx: on
# Disable adaptive and set fixed coalescing
ethtool -C eth0 adaptive-rx off adaptive-tx off
ethtool -C eth0 rx-usecs 50 rx-frames 25
# For low-latency workloads
ethtool -C eth0 rx-usecs 0 rx-frames 1Adaptive coalescing lets the kernel adjust coalescing based on traffic patterns. Fixed values give more predictable behavior for tuned workloads.
XDP: eBPF at the NIC
XDP (eXpress Data Path) runs eBPF programs at the earliest point in the receive path — before the kernel even allocates an sk_buff. This enables wire-speed packet processing for load balancers, firewalls, and DDoS mitigation.
# Check if NIC supports XDP native mode
ethtool -i eth0 | grep driver
# ixgbe (supports XDP)
# List attached XDP programs
ip link show eth0
# ... xdp qdisc noqueue ...
# Check XDP drop statistics
bpftool prog list | grep xdp
# Simple XDP drop program (bpftrace)
sudo bpftrace -e 'xdp { printf("packet on %s\n", comm); }'XDP native mode (driver-level) processes packets at wire speed. XDP generic mode works on any NIC but goes through the normal network stack, reducing the benefit.
| XDP Mode | Speed | NIC Requirement |
|---|---|---|
| Native | Wire speed | Driver support required |
| Offloaded | Beyond wire speed | SmartNIC (Netronome, etc.) |
| Generic | ~2M pps | Any NIC |
The Transmit Path
The transmit path mirrors receive but in reverse. The application writes to a socket, the kernel builds an sk_buff, routes it, applies netfilter rules, queues it via the traffic control (qdisc) layer, and finally the NIC transmits via DMA.
# Check queueing discipline
tc qdisc show dev eth0
# qdisc fq 0: root refcnt 2 limit 10000p
# View transmission statistics
ip -s link show eth0
# TX: bytes packets errors dropped overrun mcast
# 1234567890 1234567 0 123 0 0
# Check for drops in the stack
cat /proc/net/softnet_stat
# Each line is a CPU: processed, dropped, time_squeezefq (Fair Queue) is the default qdisc for most NICs. It provides better fairness and lower latency than the older pfifo_fast.
Performance Bottlenecks
Network performance issues usually fall into a few categories: interrupt saturation, softirq overload, socket buffer limits, or application read/write overhead. Identifying which layer is the bottleneck determines your tuning approach.
# Check for packet drops at each layer
cat /proc/net/softnet_stat # Kernel drops
ethtool -S eth0 | grep drop # NIC drops
ss -s # Socket statistics
nstat -a | grep Drop # Stack drops
# Monitor interrupt distribution
cat /proc/interrupts | grep eth
# Check socket buffer limits
sysctl net.core.rmem_max net.core.wmem_max
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmemIf softnet_stat shows drops, increase the per-CPU backlog with sysctl net.core.netdev_max_backlog. If NIC shows drops, increase ring buffers.
Run mpstat -I SCPU 1 to see softirq time per CPU. If one CPU is at 100% softirq time, enable RPS (Receive Packet Steering) to distribute softirq processing across CPUs.
When the ring buffer fills, the NIC drops packets silently. There is no error message — only increasing drop counters in ethtool -S. Monitor these counters during peak traffic.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.