How to Trace TCP Socket TCP_INFO tcpi_snd_wnd Updates in Linux Using eBPF

High-performance network services frequently experience throughput slowdowns caused by receiving peers advertising tiny receive windows. The Linux kernel struct tcp_info records tcpi_snd_wnd, representing the right-edge window limit advertised by the remote peer. Tracing changes to this metric using eBPF provides direct observability into peer-induced transmission stalls.

Understanding Advertised Send Window Dynamics

The send window (snd_wnd) dictates the maximum volume of unacknowledged data the sender may push before receiving a window update ACK. When remote client sockets fail to drain receive buffers promptly, the advertised window collapses to zero (zero-window state), forcing the Linux sender into TCP persist probe mode.

eBPF BCC Program for Window Tracking

The following script hooks into tcp_ack to log changes in advertised window sizing in real time:

from bcc import BPF

bpf_text = """
#include <net/sock.h>
#include <linux/tcp.h>

struct wnd_event_t {
    u32 pid;
    u16 sport;
    u16 dport;
    u32 snd_wnd;
};

BPF_PERF_OUTPUT(wnd_events);

int trace_ack(struct pt_regs *ctx, struct sock *sk) {
    if (sk->sk_family != AF_INET)
        return 0;

    struct tcp_sock *tp = tcp_sk(sk);
    struct wnd_event_t evt = {};

    evt.pid = bpf_get_current_pid_tgid() >> 32;
    evt.sport = sk->sk_num;
    evt.dport = bpf_ntohs(sk->sk_dport);
    evt.snd_wnd = tp->snd_wnd;

    wnd_events.perf_submit(ctx, &evt, sizeof(evt));
    return 0;
}
"""

b = BPF(text=bpf_text)
b.attach_kprobe(event="tcp_ack", fn_name="trace_ack")

def print_wnd(cpu, data, size):
    e = b["wnd_events"].event(data)
    print(f"PID {e.pid:<6} | Port {e.sport} -> {e.dport} | snd_wnd: {e.snd_wnd} bytes")

b["wnd_events"].open_perf_buffer(print_wnd)
print("Monitoring TCP snd_wnd transitions... Press Ctrl+C to stop.")
while True:
    b.perf_buffer_poll()

Operational Diagnosis

Execute the tracing utility with superuser permissions:

sudo python3 trace_snd_wnd.py

When snd_wnd consistently drops near zero, the bottleneck resides entirely in downstream peer consumption rather than the local server’s transport queue.

Get the best tech tips delivered straight to your inbox.

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