9/23/2026
Per-process routing without TUN
Most transparent proxies on Linux work the same way: create a TUN device, point the default route at it, and run a user-space TCP/IP stack that turns raw packets back into connections. It works everywhere, but it has costs that matter on a developer machine. Specola’s Linux backend takes a different route: it attaches eBPF programs to the cgroup v2 hierarchy and redirects sockets, not packets.
What a TUN device does
With TUN, the kernel hands every routed IP packet to a user-space process. That process has to:
- reassemble TCP streams and track UDP flows with its own network stack;
- work out which process sent each packet after the fact, usually by scanning socket tables;
- change the routing table and system DNS so traffic reaches the device, and restore them later.
Every packet of every connection crosses into user space — including the ones you only want to send direct.
What Specola’s eBPF backend does
Specola attaches small programs to cgroup socket hooks. They run inside the kernel at the moment a program creates a socket, connects, or sends a datagram:
| Hook | Job |
|---|---|
cgroup/sock_create, sock_release |
remember which process owns each socket |
cgroup/connect4 |
decide per TCP connection whether to redirect it to Core |
cgroup/sendmsg4, recvmsg4 |
the same for UDP, and restore the original peer on replies |
cgroup/getpeername4 |
make redirected sockets still report the original destination |
sockops |
mark accepted connections so Core can match them to their metadata |
The decision in connect4 follows a fixed order:
- Core’s own sockets and the Specola UI are always bypassed.
- DNS queries are sent to Core’s DNS module, so domain rules keep working.
- Loopback, local addresses, private and link-local ranges, multicast and broadcast are bypassed.
- Everything else is checked against a candidate snapshot that Core pushes into BPF maps:
process selectors from your rules, IPv4 ranges from IP and GeoIP rules, addresses that DNS
resolved for domains with rules, and the current
FINALtarget.
A connection that is not a candidate follows FINAL right there in the kernel. With
FINAL,DIRECT its socket is left completely untouched: it uses the kernel’s own TCP stack, the
normal route, and never reaches user space. A candidate is redirected to Core’s local listener.
Core looks up the original destination, socket cookie and process, runs the full ordered rule list,
and then goes direct, rejects, or dials the chosen proxy or group.
Why that matters for developers
- Process is known at connect time. Rules like
PROCESS-NAME,cargo,workare evaluated with the real owner of the socket, not a guess made later from packet headers. - Direct traffic stays in the kernel. Your browser, video calls and large local transfers do not pay for a user-space hop when no rule claims them.
- No default route or TUN interface. Specola does not rewrite your routing table to capture traffic. Stopping the service detaches the programs.
- One rule list. Traffic from the local HTTP/SOCKS5 port and transparently captured traffic are
matched by the same
[rule].list.
Current limits
- The backend captures IPv4 only; IPv6 traffic bypasses it.
- It needs root to load and attach programs, and cgroup v2 at
/sys/fs/cgroup. - Existing TCP connections keep the decision made when they connected; rule changes apply to new connections.
- With
FINAL,DIRECT, an application that resolves names through its own encrypted DNS never shows Specola a domain. Match it with a process or IP rule instead. - It is Linux-only. On macOS and Windows, Specola uses TUN.
When to still use TUN
TUN is the portable choice and captures everything on the machine the same way, regardless of
cgroup placement. If you need IPv6 capture today, or you route a whole network namespace rather than
individual tools, type = "tun" is the right backend. Both backends share the same rules, so
switching is a one-line change in [enhanced-mode].
We are preparing reproducible benchmarks comparing both backends on the same machine. They will be published here with the scripts to run them yourself.