In carrier-grade Segment Routing over IPv6 (SRv6) deployments, the End.OP (Endpoint with OAM packet processing) behaviour provides critical telemetry verification by punting SRv6 telemetry probes to control planes. Securing BGP sessions that peer with these OAM endpoints requires TCP Authentication Option (TCP-AO, RFC 5925). However, during key rollover sequences, asymmetric key states can cause MAC verification failures on incoming SYN-ACK packets, triggering retransmissions tracked via tcpi_retrans_synack in Linux struct tcp_info. Combined with receive window clamping under End.OP timestamp insertion, packets may be dropped unexpectedly in the kernel network stack. Diagnosing these subtle edge conditions requires low-overhead extended Berkeley Packet Filter (eBPF) tracing. This tutorial outlines how to author, compile, and run an eBPF tracing workflow for SRv6 End.OP in Linux.
Kernel Fundamentals: TCP-AO and SRv6 End.OP Forwarding
Under modern Linux kernels (version 6.x+), TCP-AO validates segment authenticators against key configurations maintained in struct tcp_ao_info. During an asymmetric key transition, if the remote peer transmits an updated Key ID before the local kernel updates its active keychain, SYN-ACK verification fails. The connection stalls and retransmits according to kernel exponential backoff timers, incrementing tcpi_retrans_synack.
Concurrently, when an SRv6 router executes an End.OP action (seg6_do_srh_op()), packet headers are inspected for OAM flags and punted to user-space daemons. If socket buffers are full or if packet size exceeds maximum transmission units, the TCP stack clamps the receive window (rcv_wnd) or drops segments inside seg6_lookup_nexthop(). Capturing both the socket state and segment routing actions in real time requires targeted eBPF kprobes.
Writing the eBPF Telemetry Program
Save the following source code as trace_srv6_op_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 = 901; // Key rollover mismatch
bpf_ringbuf_submit(e, 0);
return 0;
}
SEC("kprobe/seg6_do_srh_op")
int BPF_KPROBE(trace_seg6_srh_op, struct sk_buff *skb)
{
__u32 len = BPF_CORE_READ(skb, len);
if (len > 1460) {
struct drop_event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (e) {
e->drop_reason = 902; // SRv6 End.OP 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_op_ao.bpf.c -o trace_srv6_op_ao.bpf.o
# Load probe into kernel
bpftool prog load trace_srv6_op_ao.bpf.o /sys/fs/bpf/trace_srv6_op_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.OP Drops
| Observed Event | Probable Root Cause | Remediation Procedure |
|---|---|---|
| Reason 901 (SYN-ACK Retrans) | Asymmetric TCP-AO key negotiation discrepancy | Verify active keyrings in ip tcp_metrics and coordinate peer rollover |
| Reason 902 (OAM MTU Drop) | Punted OAM segment exceeds path MTU threshold | Clamp TCP MSS to 1380 bytes using iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu |
| Receive window collapse | Buffer queue saturation during continuous OAM probing | 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.