Question about design principles of network stack?
09:45 22 Apr 2026

Below are the network stack design principles I am currently considering under different scenarios. Could someone please help me evaluate if my thinking is correct? And are there any additional scenarios that I should consider?

1. General-purpose

First, it shows layering, because it performs a specific task within the stack while leaving other responsibilities to other layers. Second, it reflects separation of concerns and modularity, since protocol handling, buffering, forwarding, and hardware interaction are kept as distinct functions. Third, it shows extensibility and protocol independence, because the design can support multiple protocols or features through common interfaces rather than hard-coded behaviour. Fourth, it reflects portability, which is important in kernel networking code that must run on different architectures. Fifth, it shows performance-aware design, for example by keeping the main processing path short and avoiding unnecessary work. Sixth, it suggests careful buffer management, which is central to efficient packet handling. Finally, it demonstrates concurrency awareness, since Linux networking code must work correctly in interrupt, softirq, and multicore contexts. Overall, the code balances clarity, reuse, efficiency, and correctness, which are key goals in network stack implementation.

2. Driver / NAPI / softirq answer template

Use this for napi_poll(), receive paths, IRQ handling, netif_rx(), napi_schedule(), or polling-related code.

This code illustrates several principles of high-performance network stack design. Most importantly, it keeps the fast path short and avoids doing too much work in the most time-critical context. This is closely related to deferred processing, where urgent reception work is separated from heavier later-stage processing in order to improve throughput. The code also shows layering: the driver or polling logic handles packet movement from the NIC, while higher-level protocol interpretation is left to later parts of the stack. It reflects modularity, because reception, queue management, and higher-layer handling are separated into different components. Another clear principle is scalability, since modern Linux networking must operate efficiently at high line rates and across multiple CPUs. This also implies concurrency safety, because shared queues and state may be accessed in interrupt or softirq context. The code further shows performance awareness, especially in reducing interrupt overhead and avoiding unnecessary copying. Overall, the design is intended to support efficient packet reception under heavy load while preserving structure and correctness.

netif_rx() and NET_RX_SOFTIRQ is that the interrupt handler should do only the minimum amount of work and queue the remaining processing for later.

3. Protocol identification / L2 to L3

Use this for eth_type_trans(), protocol handler tables, and byte-order conversion.

This code demonstrates classic principles of protocol-stack design. First, it shows layering, because it acts as a bridge between Layer 2 framing and Layer 3 protocol handling rather than mixing responsibilities from the whole stack. Second, it reflects protocol independence, since it identifies the next-layer protocol through a generic mechanism instead of assuming one fixed protocol. Third, it illustrates modularity and extensibility, because protocol handlers can be registered and used through common structures and callbacks. Fourth, the code shows portability, especially where byte-order conversion is needed to support different processor architectures while preserving network byte order. Fifth, it reflects clean separation of concerns, because frame-format interpretation is isolated from the later protocol-specific receive functions. Sixth, it is also performance-conscious, as this classification step lies on the packet reception path and therefore must be efficient. Overall, the code shows how Linux networking combines flexible protocol support, portability, and efficient dispatch while maintaining a well-structured layered architecture.

eth_type_trans() explicitly mentions different Ethernet variants, the packet_type structure, dev_add_pack(), and the use of htons() / ntohs() for portability.

4. Encapsulation / tunnelling / compression

Use this for udp_compress(), vti6_xmit(), IPv6 tunnels, and 6LoWPAN.

This code reflects several principles of network stack design, particularly those related to encapsulation and adaptation between protocols. It shows layering, because the function works at a specific point in the stack, transforming packet representation while relying on other layers for transmission or higher-level semantics. It also demonstrates modularity, since tunnelling or compression is implemented as a separate function rather than being merged into the whole protocol stack. Another clear principle is extensibility, because mechanisms such as tunnelling and header compression allow the stack to support new environments without redesigning everything else. The code is also performance-aware, since compression or encapsulation is usually introduced either to reduce overhead or to support transmission more efficiently over constrained or specialised links. In addition, it reflects buffer-management awareness, because header insertion, removal, or modification must be done carefully and efficiently. Finally, it shows correctness and interoperability, because protocol adaptation must preserve enough information for the packet to be handled properly at later stages. Overall, the design balances flexibility, efficiency, and compatibility.

5. Security / filtering / validation

Use this for netfilter, WireGuard counter validation, and checking/filtering functions.

This code demonstrates network stack design principles with a stronger emphasis on robustness and controlled packet processing. First, it shows modularity, because filtering, validation, or policy enforcement is inserted as a distinct stage rather than being spread throughout the stack. Second, it reflects separation of concerns, since packet forwarding and security checking are treated as related but different responsibilities. Third, it shows layering, because the function operates at a defined point in the packet path while leaving unrelated tasks to other parts of the stack. Fourth, the code illustrates extensibility, because new rules, checks, or policies can usually be added without redesigning the whole stack. Fifth, it reflects performance-conscious design, since security mechanisms in a packet path must still minimise overhead. Finally, it demonstrates correctness and concurrency awareness, because validation code must behave safely under high traffic rates and concurrent execution contexts. Overall, the function balances flexibility, safety, and performance, which is essential for modern packet filtering and secure network processing.

Common examples:

eth_type_trans(): add byte order and protocol dispatch

napi_poll(): add deferred processing and interrupt mitigation

udp_compress(): add header compression and constrained links

vti6_xmit(): add encapsulation and tunnelling

validation/filtering code: add robustness and policy enforcement

network-programming