Kernel Networking Stack
The networking subsystem is one of the most complex and performance-critical parts of the Linux kernel. Understanding how packets flow from the Network Interface Card (NIC) through kernel layers to application sockets is essential for building high-performance networked systems.1. The Network Stack Architecture
1.1 Layer Overview
The Linux network stack follows the OSI model but implements it in a Linux-specific way:2. The Core Data Structure: sk_buff
Thestruct sk_buff (socket buffer) is the heart of the Linux networking stack. It represents a network packet as it travels through the kernel.
Think of an sk_buff like a shipping envelope with adjustable flaps. As a packet moves down the stack (application to wire), each layer adds a header by pulling the front flap forward. As it moves up (wire to application), each layer strips a header by pushing the flap back. The clever part: the actual data never moves in memory — only the pointers change. This is what makes Linux networking fast even at millions of packets per second.
2.1 sk_buff Structure
2.2 sk_buff Memory Layout
Understanding the memory layout is crucial for understanding zero-copy optimizations:2.3 Zero-Copy Mechanisms
Problem: Copying large packets is expensive (memory bandwidth limited). On a 100 Gbps NIC, the CPU would spend all its time inmemcpy() if every packet required a full copy. Zero-copy techniques are what make high-speed networking possible on commodity hardware.
Solution 1: skb_clone() - Clone sk_buff structure, share data
2.4 sk_buff Operations
- Header Manipulation
- Memory Management
- Data Access
3. Packet Reception: From Wire to Socket
3.1 The Legacy Interrupt-Driven Model (Pre-NAPI)
Old Approach (before 2.5 kernel):3.2 NAPI: New API (Polling + Interrupts)
Solution: Hybrid polling/interrupt model. The analogy: imagine a doorbell that rings every time a letter arrives. If you get one letter per hour, the doorbell is helpful. If you get 1,000 letters per second, you would never leave the door. NAPI’s approach: after the first ring, disable the doorbell and check the mailbox in batches until it is empty, then re-enable the doorbell.- Low latency under low load: Interrupts still used
- High throughput under high load: Polling avoids interrupt overhead
- Fairness: Budget limits per-device processing
- CPU efficiency: No interrupt storm
3.3 Receive Packet Steering (RPS/RFS)
Problem: Single NIC queue means all packets processed on one CPU core. On a 10 Gbps link pushing small packets, a single core can become 100% saturated while the other 31 cores sit idle. Solution: Distribute packet processing across multiple CPUs. There are three levels of this, each solving a different part of the problem:RSS (Hardware)
- NIC has multiple RX queues
- NIC hashes packet (IP + port)
- Distributes to different queues
- Each queue has own IRQ → CPU core
RPS (Software)
- Software-based RSS
- CPU that receives IRQ hashes packet
- Enqueues to target CPU’s backlog
- Target CPU processes packet
4. XDP: eXpress Data Path
XDP allows running eBPF programs before sk_buff allocation, at the earliest possible point in packet processing.4.1 XDP Architecture
4.2 XDP Program Example
4.3 XDP Actions
- XDP_DROP
- XDP_TX
- XDP_REDIRECT
- XDP_PASS
- DDoS mitigation (drop attack traffic before stack)
- Invalid packet filtering
- Rate limiting at wire speed
4.4 AF_XDP: Zero-Copy to User Space
AF_XDP allows user-space programs to receive packets directly from NIC DMA buffer (bypassing kernel stack entirely).5. The TCP/IP Stack
5.1 IP Layer Processing
5.2 TCP Layer: The Fast Path
TCP processing has two paths:Fast Path
- In-order segment
- No flags (except ACK)
- Window not full
- No urgent data
- Checksum OK
Slow Path
- Out-of-order segment
- Retransmission
- Window probing
- Options (SACK, timestamps)
- Connection management (SYN, FIN)
5.3 TCP Congestion Control
Linux supports pluggable congestion control algorithms:- CUBIC (Default)
- BBR (Modern)
- Configuration
6. Socket Layer & System Calls
6.1 Socket Creation
6.2 send() and recv() Internals
- send()
- recv()
6.3 Zero-Copy Techniques
sendfile()
MSG_ZEROCOPY
7. Netfilter & Packet Filtering
7.1 Netfilter Hook Points
7.2 Connection Tracking (conntrack)
7.3 iptables Performance
8. Network Buffers & Memory Management
8.1 Socket Buffers
8.2 TCP Autotuning
8.5 Production Caveats and Common Pitfalls
The kernel network stack ships with reasonable defaults for a generic workload. The moment your traffic profile diverges from “generic,” those defaults become silent throughput killers. Below are the four traps that bite senior engineers most often in production, paired with the patterns that fix them.9. Performance Monitoring & Debugging
9.1 Essential Tools
- ss (socket statistics)
- netstat
- ethtool
- /proc & /sys
9.2 Tracing with BPF
10. Interview Questions & Answers
Q1: Explain the sk_buff structure and why headroom/tailroom matter.
Q1: Explain the sk_buff structure and why headroom/tailroom matter.
- Application data
- +20 bytes TCP header (skb_push)
- +20 bytes IP header (skb_push)
- +14 bytes Ethernet header (skb_push)
data pointer backwards.Why Tailroom Matters:- For adding trailers (less common)
- For TSO/GSO (TCP Segmentation Offload): Kernel builds large packets, NIC splits them
Q2: How does NAPI improve packet processing performance?
Q2: How does NAPI improve packet processing performance?
- Each packet → hardware interrupt
- At 1 Gbps (1.5M packets/sec), CPU spends 100% time handling interrupts
- This is “interrupt storm” or “receive livelock”
- Packet arrives → IRQ
- Driver disables NIC interrupts
- Schedules NAPI poll
- Returns immediately from IRQ
budget packets (default 64)
7. If more packets remain, stay in polling mode
8. If ring buffer empty, re-enable interruptsBenefits:- Low latency (low load): Interrupts still used
- High throughput (high load): Polling avoids interrupt overhead
- Fairness: Budget prevents one NIC from starving others
- Adaptive: Automatically switches modes
Q3: What is XDP and how does it achieve such high performance?
Q3: What is XDP and how does it achieve such high performance?
- No sk_buff allocation: Operating directly on DMA buffer
- No cache misses: Data still in L1 cache from DMA
- No context switches: Runs in softirq context
- Early drop: Can discard packets before any processing
- JIT compiled: eBPF → native machine code
XDP_DROP: Discard (DDoS mitigation at 10M+ pps)XDP_TX: Bounce back same interface (L2 load balancer)XDP_REDIRECT: Send to different NIC or CPUXDP_PASS: Continue to normal stack
- DDoS mitigation
- Load balancing
- Packet filtering
- Network monitoring
Q4: Explain TCP Fast Path vs Slow Path.
Q4: Explain TCP Fast Path vs Slow Path.
- TCP connection is ESTABLISHED
- Packet arrives in-order (seq == rcv_nxt)
- No flags except ACK
- Receive window not full
- No urgent data
- Checksum valid
- Out-of-order segment (requires reassembly)
- Retransmission (update RTO, congestion window)
- Connection management (SYN, FIN, RST)
- Options processing (SACK, timestamps, window scaling)
- Zero window probing
- Avoiding packet loss (good network)
- Using large enough buffers (avoid window full)
- Minimizing out-of-order delivery (good QoS)
Q5: How does RSS/RPS/RFS distribute packet processing across CPU cores?
Q5: How does RSS/RPS/RFS distribute packet processing across CPU cores?
- NIC has multiple RX queues (e.g., 8 queues)
- NIC computes hash:
hash(src_ip, dst_ip, src_port, dst_port) % num_queues - Each queue has dedicated IRQ mapped to specific CPU
- Result: Packets distributed across CPUs in hardware
- Single queue NIC
- CPU receiving IRQ computes hash
- Enqueues packet to target CPU’s backlog
- Target CPU processes packet
- Extension of RPS
- Tracks which CPU application is running on
- Steers packets to that specific CPU
- Result: Packet data in cache when application reads it
Q6: What is connection tracking (conntrack) and why can it be a bottleneck?
Q6: What is connection tracking (conntrack) and why can it be a bottleneck?
- Enable stateful firewall rules
- NAT (must remember translations)
- Connection-based filtering
- Hash table lookup: O(1) but still overhead on every packet
- Global lock: (Older kernels) serializes all conntrack operations
- Memory: Each connection consumes memory (~300 bytes)
- Hash collisions: Degrade to O(n) lookup
Q7: Explain zero-copy networking techniques (sendfile, MSG_ZEROCOPY, splice).
Q7: Explain zero-copy networking techniques (sendfile, MSG_ZEROCOPY, splice).
- sendfile(): Web server serving files
- splice(): Proxy/gateway (socket → socket)
- MSG_ZEROCOPY: Bulk data transfer, streaming
Q8: How does TCP congestion control work? Compare CUBIC vs BBR.
Q8: How does TCP congestion control work? Compare CUBIC vs BBR.
CUBIC (Linux default):Algorithm:
- Maintains congestion window (cwnd) = max packets in flight
- On loss: cwnd = cwnd × β (reduce by 30%)
- Recovery: Grow cwnd using cubic function
- Aggressive growth after loss (good for high-bandwidth links)
- Fair to other CUBIC flows
- Simple, well-tested
- Treats loss as congestion signal (wrong for wireless)
- Can cause bufferbloat (fills queues)
- Slow convergence on very high BDP links
BBR (Bottleneck Bandwidth and RTT):Philosophy: Model the network, don’t react to loss.Measures:
- BtlBw (bottleneck bandwidth): Max delivery rate observed
- RTprop (round-trip propagation time): Min RTT observed
- STARTUP: Exponential growth to find BtlBw (like slow start)
- DRAIN: Drain queues created during startup
- PROBE_BW: Oscillate pacing rate around BtlBw (main phase)
- PROBE_RTT: Periodically reduce cwnd to re-measure RTprop
- Wireless networks drop packets due to RF interference
- BBR ignores loss, focuses on measured bandwidth
- Higher throughput on lossy links (wireless, satellite)
- Lower latency (doesn’t fill buffers)
- Better on bufferbloat-prone networks
- Can be unfair to CUBIC flows (more aggressive)
- Requires accurate RTT measurement
- More complex
When to Use:
Summary
Key Takeaways:- sk_buff: Central data structure. Understanding headroom/tailroom is key to zero-copy optimizations.
- NAPI: Hybrid interrupt/polling model solves interrupt storm problem at high packet rates.
- XDP: Fastest packet processing path. Process/drop packets before sk_buff allocation using eBPF.
- RSS/RPS/RFS: Distribute packet processing across CPUs for scalability. RFS optimizes for cache locality.
- TCP Fast Path: Handles common case (in-order delivery) with minimal overhead. Slow path handles edge cases.
- Congestion Control: CUBIC (default) vs BBR (better on lossy/bufferbloat links). Understand trade-offs.
- Zero-Copy: sendfile(), splice(), MSG_ZEROCOPY eliminate expensive memory copies for large transfers.
- Conntrack: Essential for stateful firewalls but can be bottleneck. Bypass for high-traffic stateless services.
- Enable multi-queue NIC and RSS
- Tune socket buffers for high BDP
- Use XDP for packet filtering
- Enable BBR for internet traffic
- Increase conntrack table for high connection count
- Use zero-copy for large data transfers
Interview Deep-Dive
A production service is dropping packets under high load. You suspect the kernel network stack is the bottleneck, not the application. Walk me through your debugging methodology from NIC to socket.
A production service is dropping packets under high load. You suspect the kernel network stack is the bottleneck, not the application. Walk me through your debugging methodology from NIC to socket.
- NIC level: Start with
ethtool -S eth0 | grep -i dropandethtool -S eth0 | grep -i error. Look forrx_dropped,rx_missed_errors, andrx_fifo_errors. If the NIC is dropping, the ring buffer is full — packets arrive faster than the CPU drains them. Fix: increase ring buffer size withethtool -G eth0 rx 4096, enable RSS (multiple hardware queues), or move to NAPI polling with a higher budget. - Softirq/NAPI level: Check
/proc/net/softnet_stat. Each line represents a CPU. Column 1 is total packets processed, column 2 is drops (backlog overflow), column 3 is time_squeeze (NAPI budget exhausted before all packets processed). If column 2 is non-zero, increasenet.core.netdev_max_backlog. If column 3 is non-zero, increasenet.core.netdev_budgetor enable RPS to spread load across more CPUs. - Socket buffer level: Check
ss -sfor socket memory statistics. If the TCP receive buffer is full because the application is not callingrecv()fast enough, the kernel drops incoming segments. Checknetstat -s | grep "pruned"for TCP pruning events. Fix: increase socket buffers (net.core.rmem_max,net.ipv4.tcp_rmem) or fix the slow application. - Conntrack table: If using iptables/nftables, check
conntrack -Cfor the current count and compare withnet.netfilter.nf_conntrack_max. A full conntrack table silently drops new connections. This is a classic production issue for high-connection-count services (100K+ concurrent connections). Fix: increase the max or bypass conntrack for stateless services with-j NOTRACK. - Application level: If none of the above show drops, the application is the bottleneck. Check with
ss -tlnpif the listen backlog is overflowing (Recv-Qexceeding the backlog value). Increasenet.core.somaxconnand the application’s listen backlog.
Explain the difference between TCP BBR and CUBIC congestion control. When would you switch a production service from CUBIC to BBR, and what could go wrong?
Explain the difference between TCP BBR and CUBIC congestion control. When would you switch a production service from CUBIC to BBR, and what could go wrong?
- CUBIC (default on Linux): Loss-based. CUBIC increases the congestion window following a cubic function until it detects packet loss, then backs off. The key insight is that packet loss signals network congestion. CUBIC is aggressive at probing for bandwidth (the cubic growth curve approaches the previous maximum quickly after a loss event) and conservative after loss.
- BBR (Bottleneck Bandwidth and RTT): Model-based. BBR continuously estimates two parameters: bottleneck bandwidth (max throughput the path can sustain) and minimum RTT. It then paces sending to match bottleneck bandwidth while keeping in-flight data to roughly bandwidth x RTT. BBR does not treat loss as a congestion signal — it treats it as noise.
- Bufferbloat networks: On paths with large buffers (common in ISPs, cellular networks), CUBIC fills the buffers until loss occurs, adding hundreds of milliseconds of queuing delay. BBR avoids filling buffers by targeting the bottleneck rate, resulting in dramatically lower latency (often 10-50x reduction in queuing delay).
- Lossy links (wireless, satellite): CUBIC interprets random packet loss (wireless interference) as congestion and backs off unnecessarily. BBR ignores occasional loss and maintains throughput.
- Long-distance, high-BDP paths: BBR converges to fair share faster than CUBIC on high bandwidth-delay product links.
- Fairness with CUBIC flows: BBR can be unfair when competing with CUBIC flows on the same bottleneck link. BBR tends to grab more bandwidth because it does not back off on loss. BBRv2 and BBRv3 address this somewhat, but fairness remains a concern in shared environments.
- RTT measurement sensitivity: BBR’s performance depends on accurate minimum RTT estimation. If your service runs behind a load balancer that adds variable latency, BBR may over-estimate min_RTT and underperform.
- Retransmission behavior: BBR can sometimes cause higher retransmission rates than CUBIC because it probes aggressively and does not immediately reduce sending on loss. Monitor
netstat -s | grep retransafter switching.
setsockopt(TCP_CONGESTION, "bbr"), so you do not need a system-wide switch.Follow-up: How would you measure whether switching to BBR actually improved your service’s performance?Before/after A/B test measuring three metrics: p50/p95/p99 TCP RTT (from ss -ti or TCP tracepoints), retransmission rate (nstat TcpRetransSegs), and application-level latency. Run both simultaneously on different server pools serving the same traffic. If BBR shows lower RTT and equal or lower retransmit rate, it is a win. If retransmits spike, investigate whether the path has a strict policer (some ISPs police by drop rate, which confuses BBR).What is the sk_buff structure and why is it designed the way it is? What would happen if you needed to redesign it for modern 100Gbps NICs?
What is the sk_buff structure and why is it designed the way it is? What would happen if you needed to redesign it for modern 100Gbps NICs?
sk_buff (socket buffer) is the central data structure representing a network packet as it traverses the Linux kernel network stack. Every packet — whether incoming or outgoing — is wrapped in an sk_buff from the moment it enters the kernel until it leaves.- Key design elements: An sk_buff contains a
headpointer (start of allocated buffer),datapointer (start of current layer’s header),tailpointer (end of data), andendpointer (end of allocated buffer). The space betweenheadanddatais “headroom” — reserved for prepending headers as the packet moves down the stack (e.g., adding an IP header, then an Ethernet header). The space betweentailandendis “tailroom” for appending data. This design means that adding or removing headers is a pointer adjustment, not a memory copy. - Metadata: sk_buff also carries extensive metadata: timestamp, hash value (for RSS), mark (for iptables), VLAN tag, checksum offload status, GRO/GSO information, and pointers to the associated socket and network device. This metadata is what makes protocol processing efficient — each layer annotates the sk_buff rather than parsing from scratch.
- Cloning: When a packet needs to go to multiple destinations (multicast, tapping), the kernel clones the sk_buff — creating a new metadata structure that points to the same data buffer. This avoids copying the packet payload.
- The problem: At 100Gbps with 64-byte packets, you need to process 148 million packets per second. Allocating and freeing an sk_buff per packet is impossibly expensive — each allocation involves slab allocator calls, cache-line bouncing, and metadata initialization.
- Batch processing: Modern approaches (GRO — Generic Receive Offload) coalesce multiple packets into a single sk_buff before handing to upper layers, reducing per-packet overhead by 10-60x.
- XDP’s approach: Bypass sk_buff entirely. XDP uses a minimal
xdp_mdstructure that is just pointers into the DMA buffer. No allocation, no metadata bloat. This is why XDP can process packets at line rate on 100Gbps NICs. - AF_XDP: Provides a zero-copy path from NIC DMA buffers directly to user-space ring buffers, completely bypassing the sk_buff-based stack. Used by high-frequency trading firms and DPDK-like workloads.
Walk me through the TCP three-way handshake from the kernel's perspective. What happens at each step, what data structures change, and where can it go wrong?
Walk me through the TCP three-way handshake from the kernel's perspective. What happens at each step, what data structures change, and where can it go wrong?
- SYN arrives at the listener. The packet hits the NIC, goes through softirq, IP, and lands in
tcp_v4_rcv. The kernel does an__inet_lookup_listenerto find aLISTEN-state socket bound to the destination IP and port. If found, it allocates a request socket (struct request_sock) — a lightweight half-open structure — and adds it to the SYN queue (accept_queue->syn_queue). The full socket is not allocated yet. - SYN-ACK is sent back. The kernel constructs a SYN-ACK with its initial sequence number and the negotiated TCP options (MSS, window scale, SACK permitted, timestamps). It also chooses a starting
sk_rcv_saddrif the listener was bound toINADDR_ANY. - ACK arrives, completing the handshake. The kernel matches the ACK to the request socket, allocates a full
struct sockviatcp_v4_syn_recv_sock, transitions it toESTABLISHED, and moves it from the SYN queue to the accept queue (accept_queue->rskq_accept_head). Only at this point does the application’saccept()syscall return a new file descriptor. - The listening socket has two queues.
tcp_max_syn_backlogcaps the SYN queue (half-open). Theaccept()queue (full-open, waiting to be picked up by the application) is capped atmin(somaxconn, application_backlog). If the accept queue overflows because the app is slow to callaccept(), the kernel drops the third ACK and the connection silently fails — the client thinks it succeeded, but the server has no socket.
netstat -s | grep -i "listen" showed thousands of ListenOverflows and ListenDrops per second. The cause: their accept queue was sized at the historical default of 128 (SOMAXCONN), but they were getting bursts of 5K connections in 200 ms. The fix was raising net.core.somaxconn to 16384 and updating the application’s listen() backlog to match. They also enabled tcp_abort_on_overflow=1 so that overflows became visible RSTs instead of silent drops, restoring deterministic error behavior.tcp_syncookies=1, default), so normal traffic is unaffected.accept() — it must be large enough to hide application stalls (GC pauses, slow startup). Sizing them together would force a tradeoff: raise the limit to handle floods, you also raise the time the app can stall before connections fail. Splitting them lets you size each for its purpose. Linux merged the two limits historically (pre-2.2) and split them precisely because operators kept hitting one cap or the other.- “Three packets, that is the handshake.” Correct in the abstract, but it dodges every implementation detail an interviewer cares about: the two queues, the request socket, the cookie path, where the backlog parameters apply.
- “The connection is fully open after the SYN-ACK.” No. The server moves to
SYN_RECEIVEDafter sending SYN-ACK and only toESTABLISHEDon the third ACK. Treating SYN-ACK as completion misses where overflow drops happen. - “
accept()blocks until a SYN arrives.”accept()blocks until a fully-established socket lands in the accept queue. SYNs alone never wakeaccept().
- Linux source:
net/ipv4/tcp_input.c(tcp_conn_request),net/ipv4/inet_connection_sock.c(inet_csk_accept) - Cloudflare blog: “SYN packet handling in the wild” (2018) — production breakdown of every queue and counter
- “TCP/IP Illustrated, Volume 2: The Implementation” by Wright and Stevens, chapters 28-29
A production service is showing 10x its normal TCP retransmission rate. What tools do you reach for, what knobs do you tune, and how do you tell if it is the network's fault or yours?
A production service is showing 10x its normal TCP retransmission rate. What tools do you reach for, what knobs do you tune, and how do you tell if it is the network's fault or yours?
- Establish baseline and current rate. Pull
nstat -az TcpRetransSegs TcpOutSegsover a 10-second window. The retransmit ratio isTcpRetransSegs / TcpOutSegs. Healthy data centers see under 0.01 percent; over the public internet, 0.1-0.5 percent is normal; above 1 percent is a real problem. Compare against your historical baseline — “10x normal” matters a lot more than the absolute number. - Slice by connection.
ss -tinshows per-socket retransmit counts (retrans:field) and the inferred congestion state. Sort by retransmits to find the worst offenders. Are retransmits concentrated on one peer, one subnet, one congestion control variant? That tells you whether it is a path problem or a host problem. - Use tracepoints to see why.
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[args->skaddr] = count(); }'counts retransmits per socket in real time. Even better, attach totcp_retransmit_skband dump the cause — RTO timeout vs fast retransmit vs tail loss probe. The mix matters: lots of fast retransmits suggest reordering or random loss; lots of RTO-driven retransmits suggest sustained congestion or routing flaps. - Capture and read.
tcpdump -i any -w trace.pcap 'tcp and host suspicious_peer', then load it in Wireshark with the “Expert” view. The cleanest signals are duplicate ACKs (loss in the forward direction), SACK blocks (out-of-order arrival, often from ECMP rehashing), and zero-window updates (receiver overloaded). - Tune with intent, not by spraying knobs. If the path is lossy and you are stuck on CUBIC, evaluate BBR. If retransmissions cluster on RTO timeouts of exactly 200ms, tune
tcp_rto_min— but only after confirming the path RTT is well below it. If you see persistent reordering due to multipath, enabletcp_recovery(RACK) which uses time-based loss detection rather than dup-ACK counting.
net.ipv4.tcp_reordering to tolerate more out-of-order segments before declaring loss, and enabling RACK for time-based recovery. The longer-term fix came from the cloud provider’s side, but the host-level tuning carried them through.cwnd shrinking despite TcpRetransSegs not increasing — the sender retransmits because RTO fired, but the data actually arrived; the ACKs were just lost. Counter signature: nstat TcpFastRetrans increases for forward loss, TcpTimeouts increases (without much fast retransmit) for ACK loss. ACK loss is rarer but worth recognizing because the cure (turn on selective ACK with tcp_sack=1, which is default but worth verifying) is different from forward-loss tuning.tcp_low_latency actually hurt latency on some workloads?tcp_low_latency (deprecated in modern kernels, but still relevant for older systems) disables prequeue processing — packets are processed in softirq context immediately rather than queued for the receiving process to drain. For a single connection on a single CPU it sounds like a win. The catch: under high softirq load, tying processing to the receive interrupt can starve other work and increase tail latency systemwide. Modern kernels removed the prequeue entirely, so the knob is a no-op. The lesson: latency knobs that look local often have systemic effects, which is why most “tcp_low_latency”-style flags get deprecated once the kernel team has data.tcp_retransmit_skb to capture the socket and the kernel stack. Then use bpf_get_current_pid_tgid and bpf_get_current_comm to capture which userspace thread owns the socket. For request-attribution, correlate the socket’s source port with a userspace ringbuffer that the application writes (request_id, source_port) tuples into when it opens a connection. This lets you say “retransmissions are concentrated on the search service’s outbound connections to the recommendations service from 14:02-14:07,” which is what you actually need to fix the problem. Tools like tcpretrans from BCC and retsnoop automate variants of this.- “Bump the TCP window.” A bigger window does not help retransmits — it makes them worse if the path is lossy because more in-flight segments mean more losses per RTO event.
- “Switch to UDP.” UDP does not have retransmits, but it also does not have ordering, congestion control, or reliability — you have just moved the problem out of the kernel into your application code, which is rarely simpler.
- “Enable jumbo frames.” Larger MSS reduces per-packet overhead but increases the cost of each lost packet (you retransmit 9000 bytes instead of 1500). It is a throughput optimization, not a retransmit fix.
- Brendan Gregg, “TCP Retransmits” tools and methodology — the BCC
tcpretranswriteup - Linux source:
net/ipv4/tcp_input.c(tcp_fastretrans_alert),net/ipv4/tcp_recovery.c(RACK) - “Making Linux TCP Fast” (Cardwell et al., NSDI 2017) — the BBR paper, which doubles as a clear tour of TCP loss detection
Next: File Systems →