Map

eBPF sock_ops

Wiki conceptlinuxebpfkernelnetworkingtcp โ†ณ show in map Markdown
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:

  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.