# eBPF sock_ops

`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 [[bypassing-dpi-with-ebpf|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:

1. **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_ops` is the only hook that fires *exactly* when a connection becomes usable.
2. **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.
3. **In-kernel state mutation.** Setting MSS via `bpf_setsockopt` from 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_ops` for SNI-bypass
- [[little-snitch-linux]] — different eBPF-based networking tool (per-app filtering / monitoring), uses different hooks
