eBPF sock_ops
- title
- eBPF sock_ops
- type
- concept
- summary
- Linux's per-cgroup BPF hook into the kernel TCP socket lifecycle โ fires at SYN-sent, established, retransmit, and other transitions
- tags
- linux, ebpf, kernel, networking, tcp
- created
- 2026-05-06
- updated
- 2026-05-06
sock_ops is the BPF program type that hooks into the Linux kernel TCP socket state machine. You attach a BPF program to a cgroup, and the kernel calls into it at well-defined points in every TCP socket's lifecycle. The program runs in the kernel; userspace gets notified via perf events or BPF maps when it wants to know something happened.
This is the hook gecit uses to inject its fake ClientHello before the application sends any data โ and the post explicitly argues that this hook is the reason the Linux implementation is a few hundred lines and the macOS/Windows implementations are essentially tiny VPNs.
The lifecycle callbacks
The kernel calls the attached sock_ops program with a struct bpf_sock_ops whose op field tells you which event fired. The ones that matter for circumvention and connection-shaping work:
BPF_SOCK_OPS_TCP_CONNECT_CBโ outgoing SYN about to be sent.BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CBโ three-way handshake just completed on an outgoing connection. This is the one gecit hooks โ connection is up, application has not sent any data yet.BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CBโ same, but for accepted connections.BPF_SOCK_OPS_HDR_OPT_LEN_CB/BPF_SOCK_OPS_WRITE_HDR_OPT_CBโ let the program add custom TCP options to outgoing packets.BPF_SOCK_OPS_RTO_CB,BPF_SOCK_OPS_RETRANS_CB,BPF_SOCK_OPS_STATE_CBโ TCP misbehavior callbacks (used for in-kernel telemetry, congestion-control tuning, etc.).
What the program can do
From inside the callback, the program has direct access to socket fields that are otherwise expensive or impossible to read from userspace:
skops->snd_nxt,skops->rcv_nxtโ sequence and ack numbers, exactly as the kernel sees them. gecit reads these and forwards them to userspace via a perf event so the spoofed packet carries valid TCP state.skops->local_ip4,skops->remote_ip4,skops->local_port,skops->remote_portโ the 5-tuple.bpf_setsockopt(skops, IPPROTO_TCP, TCP_MAXSEG, ...)โ modify socket options for that specific connection from inside the kernel. gecit clamps MSS to 88 to force ClientHello fragmentation; other programs use it to set ECN, congestion control, or initial window per-flow.
Why this hook is uniquely useful
Three properties combine in a way no other Linux network hook gets you all at once:
- Per-connection synchronous callback. XDP and TC fire on packets, not connections. cgroup BPF hooks for
connect(2)fire too early (the socket isn't established yet, no ack number to use).sock_opsis the only hook that fires exactly when a connection becomes usable. - Cgroup attachment. The program runs for any process inside the attached cgroup, no matter what its userspace networking does. gecit doesn't need apps to honor proxy settings โ Discord, Steam, anything in the cgroup gets the same treatment.
- In-kernel state mutation. Setting MSS via
bpf_setsockoptfrom the callback affects the kernel's segmentation behavior for that single socket. Userspace would have to wrap the syscall, which is fragile and per-app.
Platform comparison
The post's framing line: "the gap isn't a slope, it's a step." On macOS and Windows, gecit needs a TUN device, gVisor's userspace TCP stack, raw socket injection (or pcap on Windows), and ARP table parsing โ all userspace, all per-platform. Every packet now crosses the kernel-user boundary. On Linux, only the fake injection itself touches userspace; bulk traffic stays in-kernel.
This is a recurring theme in low-level networking work: BSD has dtrace + pf + pcap, macOS has Network Extensions (entitlement-gated), Windows has WFP / NDIS / WinDivert. None of them give you a synchronous in-kernel TCP-state callback that's safe to attach to a userland cgroup. eBPF is the differentiator.
Related
- tls-desync-fake-clienthello โ the bypass technique gecit uses sock_ops to implement
- bypassing-dpi-with-ebpf โ the post that motivated this page
- gecit โ concrete user of
sock_opsfor SNI-bypass - little-snitch-linux โ different eBPF-based networking tool (per-app filtering / monitoring), uses different hooks