How to Trace TCP Socket TCP_INFO tcpi_retrans_synack TCP TCP-AO Asymmetric Segment Hardware Key Rollover Window Clamp SRv6 End.B6-Insert Drops in Linux Using eBPF

Service provider backbones running Segment Routing over IPv6 (SRv6) increasingly deploy End.B6-Insert behaviours to insert an SRH (Segment Routing Header) directly into an existing IPv6 packet header without full outer IPv6 header encapsulation. When combining this insertion mechanism with TCP Authentication Option (TCP-AO, RFC 5925) for core BGP sessions, asymmetrical cryptographic key rollovers can introduce packet validation discrepancies. Combined with subtle TCP receive window clamping under MTU inflation, these discrepancies manifest as repeated SYN-ACK retransmissions recorded in tcpi_retrans_synack within Linux struct tcp_info. Isolating these issues without degrading carrier-grade throughput necessitates extended Berkeley Packet Filter (eBPF) tracing. This tutorial outlines how to author, compile, and run an eBPF tracing workflow for SRv6 End.B6-Insert operations in Linux.

Kernel Fundamentals: TCP-AO and SRv6 End.B6-Insert

In modern Linux networking (kernel 6.x+), TCP-AO evaluates per-packet MAC signatures against cryptographic keychains stored in struct tcp_ao_info. During an asynchronous key rollover, if one node adopts a new key ID before its peer registers the update, SYN-ACK verification fails. The sender kernel retransmits the segment according to exponential backoff rules while logging increments to tcpi_retrans_synack.

Simultaneously, the SRv6 End.B6-Insert action modifies the packet in-place by calling seg6_do_srh_insert(). This extends the IPv6 header extension chain. If the expanded header causes the total packet length to breach interface MTU limits or if receive socket buffers lack headroom, the kernel throttles the receive window (rcv_wnd) or drops the segment inside seg6_input(). Capturing both the socket state and netfilter processing in real time requires targeted eBPF kprobes.

Writing the eBPF Telemetry Program

Save the following source code as trace_srv6_b6_insert_ao.bpf.c:

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>

char LICENSE[] SEC("license") = "Dual BSD/GPL";

struct drop_event {
    __u32 saddr[4];
    __u32 daddr[4];
    __u16 sport;
    __u16 dport;
    __u8  retrans_synack;
    __u32 snd_wnd;
    __u32 rcv_wnd;
    __u32 drop_reason;
};

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 1 << 24);
} events SEC(".maps");

SEC("kprobe/tcp_rtx_synack")
int BPF_KPROBE(trace_tcp_rtx_synack, struct sock *sk, struct request_sock *req)
{
    struct tcp_sock *tp = (struct tcp_sock *)sk;
    struct drop_event *e;

    e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (!e)
        return 0;

    e->sport = BPF_CORE_READ(sk, __sk_common.skc_num);
    e->dport = BPF_CORE_READ(sk, __sk_common.skc_dport);
    e->retrans_synack = BPF_CORE_READ(tp, total_retrans);
    e->snd_wnd = BPF_CORE_READ(tp, snd_wnd);
    e->rcv_wnd = BPF_CORE_READ(tp, rcv_wnd);
    e->drop_reason = 501; // Key rollover mismatch

    bpf_ringbuf_submit(e, 0);
    return 0;
}

SEC("kprobe/seg6_do_srh_insert")
int BPF_KPROBE(trace_seg6_srh_insert, struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
{
    __u32 len = BPF_CORE_READ(skb, len);
    if (len > 1480) {
        struct drop_event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
        if (e) {
            e->drop_reason = 602; // SRv6 End.B6-Insert MTU breach
            bpf_ringbuf_submit(e, 0);
        }
    }
    return 0;
}

Compiling and Loading the eBPF Program

Compile the BPF object file using Clang and load it into the kernel via bpftool:

# Install build dependencies
sudo apt-get install -y clang llvm libbpf-dev linux-tools-common linux-tools-$(uname -r)

# Compile bytecode
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -c trace_srv6_b6_insert_ao.bpf.c -o trace_srv6_b6_insert_ao.bpf.o

# Load probe into kernel
bpftool prog load trace_srv6_b6_insert_ao.bpf.o /sys/fs/bpf/trace_srv6_b6_insert_ao type kprobe

Observing Kernel Ring Buffer Events

Stream decoded JSON events directly from the eBPF ring buffer:

# Stream decoded ringbuffer events
sudo bpftool map dump name events -j | jq '.[] | {sport: .value.sport, dport: .value.dport, retrans: .value.retrans_synack, reason: .value.drop_reason}'

Diagnostic Reference for SRv6 End.B6-Insert Drops

Observed Event Probable Root Cause Remediation Procedure
Reason 501 (SYN-ACK Retrans) Asymmetric TCP-AO key negotiation discrepancy Verify active keyrings in ip tcp_metrics and coordinate peer rollover
Reason 602 (Insert MTU Drop) Inserted SRH header pushes packet beyond transit MTU Clamp TCP MSS to 1400 bytes using iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
Receive window collapse Kernel socket backlog saturation during high packet insertion rate Tune net.core.netdev_max_backlog and net.ipv4.tcp_rmem

Using this eBPF tracing workflow provides clear operational visibility into cryptographic session maintenance and segment-routed packet processing on modern Linux distributions.

Get the best tech tips delivered straight to your inbox.

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