How to Trace TCP Socket TCP_INFO tcpi_data_segs_in in Linux Using eBPF

In high-throughput server environments, distinguishing between total received TCP packets (which include pure acknowledgement frames, window updates, and keep-alive probes) and packets that carry genuine application layer payload data is essential for accurate capacity planning. The standard tcpi_segs_in counter inside the Linux kernel increments for every valid segment received on a socket, including empty ACKs. To measure actual payload-bearing traffic, the Linux kernel maintains the dedicated tcpi_data_segs_in counter inside struct tcp_info.

By tracing tcpi_data_segs_in using extended Berkeley Packet Filter (eBPF) and bpftrace, systems engineers can audit payload ingestion velocity, detect empty ACK floods, and evaluate application data efficiency directly within kernel space.

Understanding tcpi_data_segs_in in Kernel Space

Defined in include/uapi/linux/tcp.h, modern Linux kernels track separate counters for total segments versus data-bearing segments:

struct tcp_info {
    /* ... state fields ... */
    __u32   tcpi_segs_in;        /* Total segments received (including pure ACKs) */
    __u32   tcpi_data_segs_in;   /* Segments received containing payload data */
    __u32   tcpi_segs_out;       /* Total segments sent */
    __u32   tcpi_data_segs_out;  /* Segments sent containing payload data */
};

By comparing tcpi_data_segs_in against tcpi_segs_in, engineers can compute the Data Segment Efficiency Ratio (DSER):

Data Segment Efficiency Ratio = (tcpi_data_segs_in / tcpi_segs_in) * 100%

On bulk ingest endpoints, a low DSER indicates that a peer is emitting high volumes of redundant micro-ACKs or window update packets rather than streaming full-sized data payloads.

Authoring the bpftrace Payload Ingestion Probe

The following bpftrace script probes tcp_close() to generate socket closure reports summarizing inbound payload efficiency:

#!/usr/bin/env bpftrace

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

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

    $daddr = ntop($inet->inet_daddr);
    $saddr = ntop($inet->inet_saddr);
    $lport = bswap($inet->inet_sport);
    $segs_in = $tp->segs_in;
    $data_segs_in = $tp->data_segs_in;

    if ($segs_in > 50) {
        $efficiency = $segs_in > 0 ? ($data_segs_in * 100) / $segs_in : 0;
        time("%H:%M:%S ");
        printf("CLOSED PORT %d <- %s | Total In: %d | Data In: %d | Data Ratio: %d%%\n",
            $lport, $daddr, $segs_in, $data_segs_in, $efficiency);
    }
}

Running the Trace and Diagnosing Traffic Patterns

Execute the script on your Linux ingestion cluster:

sudo bpftrace trace_data_segs.bt

During active traffic, bpftrace outputs structured connection summaries:

00:14:02 CLOSED PORT 9000 <- 10.0.2.45 | Total In: 42000 | Data In: 39500 | Data Ratio: 94%
00:14:15 CLOSED PORT 9000 <- 192.168.1.18 | Total In: 8500 | Data In: 850 | Data Ratio: 10%

The second log line flags a connection where only 10% of incoming packets contained application payload data, revealing excessive empty polling or an un-coalesced acknowledgement stream from a legacy client.

Production Applications and Optimization

Monitoring tcpi_data_segs_in with eBPF enables systems teams to:

  1. Audit whether incoming clients utilize TCP Cork or Nagle’s algorithm effectively to coalesce payload data into full MSS packets.
  2. Identify clients sending excessive keep-alive pings or heartbeats that consume kernel socket processing overhead without transferring business data.
  3. Tune socket receive buffer sizing and backlog thresholds based on true payload volume rather than raw packet counts.

Get the best tech tips delivered straight to your inbox.

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