Linux Network Packet Reception: From NIC to Kernel and Protocol Stack
This article explains how Linux receives network packets, detailing the NIC’s DMA transfer, hardware and software interrupt handling, NAPI scheduling, kernel processing, and the subsequent traversal through the IP and transport layers up to the user‑space application.
In Linux network programming, receiving a packet means the host obtains a data packet from another computer or from the localhost.
When a network packet is received it passes through several layers: the physical layer where the NIC detects the packet, the kernel’s protocol stack that parses and routes it, and finally the application that reads the data via a socket.
First, at the physical layer, the NIC detects the packet and passes it to the kernel.
Then the kernel parses the packet, checks the destination IP, port, and performs routing, filtering or forwarding.
Finally, after successful processing, the application reads the packet data from a socket, enabling functions such as real‑time communication, monitoring, and analysis.
1. NIC Reception
When a packet arrives at the NIC, it is stored in the receive buffer. The NIC typically uses DMA to copy the data directly into main memory, reducing CPU involvement.
The NIC has its own memory (usually >4 KB) for sending and receiving data. Received data is first queued in the NIC’s memory pages (256 B each) before an interrupt notifies the CPU to read it.
The page layout includes separate regions for sending and receiving; the driver writes new data into the next free page and updates pointers accordingly.
Common NIC registers include CR (command), TSR (transmit state), ISR (interrupt state), RSR (receive state), RCR (receive configure), TCR (transmit configure), DCR (data configure), IMR (interrupt mask), and many others that control packet handling.
1.1 Framework
The network subsystem focuses on the interaction between the driver and the kernel. The NIC is represented by a net_device structure, and packets are represented by sk_buff structures.
Two additional structures, softnet_data and napi_struct , support processing packets via soft interrupts (NAPI).
1.2 Initialization
During system boot, the network subsystem is initialized in start_kernel → net_dev_init . The function creates per‑CPU softnet_data structures, registers soft‑irqs NET_TX_SOFTIRQ and NET_RX_SOFTIRQ , and sets up the packet receive queues.
pid = kernel_thread(kernel_init, NULL, CLONE_FS); static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}The driver registers its net_device and the kernel can then interact with the hardware.
1.3 Driver Reception
The NIC generates a hardware interrupt when a packet arrives. The driver’s interrupt handler (e.g., e1000_intr ) disables further interrupts, prepares the NAPI structure, and schedules it:
static irqreturn_t e1000_intr(int irq, void *data)
{
struct net_device *netdev = data;
struct e1000_adapter *adapter = netdev_priv(netdev);
u32 icr = er32(ICR);
ew32(IMC, ~0);
E1000_WRITE_FLUSH();
if (likely(napi_schedule_prep(&adapter->napi))) {
__napi_schedule(&adapter->napi);
} else {
if (!test_bit(__E1000_DOWN, &adapter->flags))
e1000_irq_enable(adapter);
}
return IRQ_HANDLED;
}The NAPI schedule adds the napi_struct to the per‑CPU softnet_data poll list and raises the soft‑irq NET_RX_SOFTIRQ .
1.4 Kernel Processing
The soft‑irq handler net_rx_action runs in the ksoftirqd kernel thread, pulls NAPI entries from the poll list, and calls napi_poll which invokes the driver’s poll function (e.g., e1000_clean ).
static __latent_entropy void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
...
n = list_first_entry(&list, struct napi_struct, poll_list);
budget -= napi_poll(n, &repoll);
...
}The driver’s clean function processes TX completion and receives packets via clean_rx , ultimately delivering the packet to the network stack with napi_gro_receive and netif_receive_skb .
static int e1000_clean(struct napi_struct *napi, int budget)
{
struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter, napi);
int tx_clean_complete = 0, work_done = 0;
tx_clean_complete = e1000_clean_tx_irq(adapter, &adapter->tx_ring[0]);
adapter->clean_rx(adapter, &adapter->rx_ring[0], &work_done, budget);
...
return work_done;
}1.5 Receiving the Packet in the Stack
The packet is now in a sk_buff and traverses the protocol stack: netif_receive_skb → ip_rcv (IPv4) → ip_local_deliver → transport layer ( tcp_v4_rcv or udp_rcv ) → socket buffers → user‑space read calls.
At the IP layer, the packet is validated, routed, or forwarded. The TCP layer reassembles fragments, orders data, and places it in the socket’s receive queue.
2. Interrupt Handling
After the NIC signals an interrupt, the kernel’s hard‑interrupt handler runs, then schedules the NAPI soft‑irq. The soft‑irq processing ultimately calls the driver’s poll routine to pull packets from the ring buffer.
2.1 Registering the Hard‑Interrupt Handler
Drivers register their interrupt handler (e.g., igb_msix_ring ) during device open.
igb_open() → igb_request_irq → igb_request_msix → igb_msix_ring()2.2 Soft‑IRQ Processing
The ksoftirqd thread executes net_rx_action , which calls napi_poll and ultimately the driver’s igb_clean_rx_irq to move data from the NIC’s ring buffer into sk_buff structures.
2.3 Protocol Stack Handling (L3/L4)
After the packet reaches the IP layer, the registered handler ip_rcv processes it, then hands it to the appropriate transport‑layer handler ( tcp_v4_rcv , udp_rcv , etc.).
ip_rcv() → ip_local_deliver() → tcp_v4_rcv() / udp_rcv()3. Application Processing
Finally, the data is placed in the socket’s receive queue. User‑space programs read it via read → sock_read_iter → sock_recvmsg → tcp_recvmsg (or UDP equivalents).
When the application finishes processing, any response data is handed back to the stack, encapsulated, and transmitted by the NIC.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.