How to Trace TCP Socket TCP_INFO tcpi_segs_out Burst Rates in Linux Using eBPF

In high-throughput Linux network infrastructure, aggregate bandwidth metrics often hide micro-bursting: short millisecond spikes where a network socket emits hundreds of packets simultaneously. While average utilization over a 1-second window might appear modest (e.g., 200 Mbps), micro-bursts can momentarily saturate top-of-rack switch buffers, causing tail-drop packet loss and triggering latency spikes across co-located tenants. Inside the Linux kernel, the cumulative number of transmitted segments is tracked in struct tcp_info as tcpi_segs_out.

Using extended Berkeley Packet Filter (eBPF) and bpftrace, systems engineers can sample tcpi_segs_out across millisecond intervals to detect micro-bursting behavior and audit TCP pacing efficiency in real time.

Understanding tcpi_segs_out in Kernel Space

Defined in include/uapi/linux/tcp.h, struct tcp_info exposes segment transmission counters:

struct tcp_info {
    /* ... state fields ... */
    __u32   tcpi_segs_out;       /* Total outbound segments sent */
    __u32   tcpi_data_segs_out;  /* Outbound segments containing payload */
    __u32   tcpi_pacing_rate;    /* Current TCP pacing rate (bytes/sec) */
    __u32   tcpi_max_pacing_rate;
};

When TCP pacing is disabled or misconfigured, the kernel releases an entire congestion window’s worth of segments upon receiving an ACK. With a large CWND (e.g., 100 packets), this creates an instant 150KB burst onto the wire. TCP pacing spreads packet transmission evenly over the round-trip time.

Authoring the bpftrace Micro-Burst Detection Probe

The following bpftrace script samples tcp_write_xmit() to detect burst transmissions exceeding 40 packets in a single transmit burst:

#!/usr/bin/env bpftrace

#include <net/sock.h>
#include <linux/tcp.h>

kprobe:tcp_write_xmit
{
    $sk = (struct sock *)arg0;
    $inet = (struct inet_sock *)$sk;
    $tp = (struct tcp_sock *)$sk;

    $daddr = ntop($inet->inet_daddr);
    $saddr = ntop($inet->inet_saddr);
    $dport = bswap($inet->inet_dport);
    $segs = $tp->segs_out;
    $cwnd = $tp->snd_cwnd;

    if ($cwnd > 50) {
        time("%H:%M:%S ");
        printf("MICRO-BURST POTENTIAL | %s -> %s:%d | CWND: %d pkts | Pacing: %d KB/s\n",
            $saddr, $daddr, $dport, $cwnd, $tp->pacing_rate / 1024);
    }
}

Running the Trace and Diagnosing Burst Patterns

Execute the bpftrace script with root privileges:

sudo bpftrace trace_bursts.bt

When high-CWND sockets transmit without pacing, bpftrace outputs structured alerts:

10:14:02 MICRO-BURST POTENTIAL | 10.0.1.20 -> 198.51.100.44:443 | CWND: 120 pkts | Pacing: 0 KB/s
10:14:08 MICRO-BURST POTENTIAL | 10.0.1.20 -> 203.0.113.88:443 | CWND: 85 pkts | Pacing: 42000 KB/s

The first log line flags an unpaced socket: with a CWND of 120 packets and zero pacing rate, it bursts 120 segments simultaneously, risking switch buffer drops.

Production Remediation and TCP Pacing Configuration

To eliminate micro-burst packet loss across your fleet:

  1. Deploy the FQ (Fair Queue) packet scheduler on egress network interfaces:
    sudo tc qdisc replace dev eth0 root fq
    FQ enforces kernel-level TCP pacing, converting bursty transfers into smooth, continuous packet streams.
  2. Transition to rate-based congestion control algorithms like BBR:
    sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
  3. Verify that socket send buffers are not configured with excessively large burst thresholds.

Get the best tech tips delivered straight to your inbox.

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