How to Trace TCP Early Retransmit Delays in Linux Using eBPF

Standard TCP Fast Retransmit requires an endpoint to receive three duplicate acknowledgments (dupacks) before concluding that a packet was lost. However, for short-lived HTTP connections, small request payloads, or small congestion windows (where fewer than four segments are in flight), generating three duplicate ACKs is mathematically impossible. In these scenarios, the sender previously stalled until the Retransmission Timeout (RTO) expired. TCP Early Retransmit (RFC 5827) resolves this by dynamically reducing the duplicate ACK threshold for small windows. Tracing this behaviour with eBPF reveals how effectively your kernel mitigates tail loss latency.

Understanding TCP Early Retransmit Mechanics

TCP Early Retransmit operates inside the kernel transport layer according to defined window thresholds:

  • Tail Loss Dilemma: If a connection transmits 3 segments and the middle segment drops, the receiver returns only 1 duplicate ACK—falling short of the standard 3-ACK trigger and causing a multi-hundred millisecond RTO stall.
  • Byte-Based Early Retransmit: When outstanding segments (packets_out) are fewer than 4 and no new data is ready to send, the kernel lowers the duplicate ACK threshold to packets_out - 1.
  • Tail Loss Probe (TLP) Interaction: Early Retransmit operates alongside TLP, which proactively schedules a probe segment when acknowledgments cease.

Auditing Linux Early Retransmit Kernel Parameters

Inspect existing sysctl configurations governing Early Retransmit:

# Check tcp_early_retrans value (0=disabled, 3=enabled with delay/loss recovery)
sysctl net.ipv4.tcp_early_retrans

# Inspect system-wide Early Retransmit counters
netstat -s | grep -i "early retrans"

The kernel metric TCPEarlyRetrans increments whenever Early Retransmit succeeds without relying on expensive RTO recovery.

Tracing tcp_enter_loss and tcp_xmit_retransmit_queue with bpftrace

To capture when Early Retransmit triggers versus standard Fast Retransmit, hook tcp_fastretrans_alert:

sudo bpftrace -e '
#include <net/sock.h>
#include <linux/tcp.h>

kprobe:tcp_fastretrans_alert
{
    $sk = (struct sock *)arg0;
    $tp = (struct tcp_sock *)arg0;

    $packets_out = $tp->packets_out;
    $sacked_out = $tp->sacked_out;
    $retrans_out = $tp->retrans_out;

    if ($packets_out < 4 && $packets_out > 0) {
        printf("Early Retransmit Condition Met: PID %-6d Comm %-12s Out: %-2u SACKed: %-2u\n",
               pid, comm, $packets_out, $sacked_out);
        @early_retrans_events = count();
    }
}'

This trace confirms that small-window transfers recover promptly without suffering complete RTO backoff penalties.

Measuring Recovery Latency Differences

Compare the elapsed duration between packet loss events and successful acknowledgment across loss recovery modes:

sudo bpftrace -e '
#include <net/sock.h>
#include <linux/tcp.h>

kprobe:tcp_retransmit_skb
{
    $sk = (struct sock *)arg0;
    @retrans_start[$sk] = nsecs;
}

kprobe:tcp_clean_rtx_queue
{
    $sk = (struct sock *)arg0;
    if (@retrans_start[$sk]) {
        $recovery_ms = (nsecs - @retrans_start[$sk]) / 1000000;
        @retransmit_recovery_lat_ms = hist($recovery_ms);
        delete(@retrans_start[$sk]);
    }
}

END
{
    printf("\nRetransmission Recovery Latency (ms):\n");
    print(@retransmit_recovery_lat_ms);
    clear(@retrans_start);
}'

Optimizing Transport Configuration

To maximize short-transfer responsiveness across web and API services:

  • Ensure net.ipv4.tcp_early_retrans=3 (enables delayed Early Retransmit, which prevents spurious retransmits caused by minor packet reordering).
  • Enable TCP SACK (net.ipv4.tcp_sack=1), which allows the kernel to map missing sequence holes accurately even with minimal feedback.

Get the best tech tips delivered straight to your inbox.

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