N.04 / SYSTEMS

Sodama datapath engineering note

The full engineering record of rewriting a production datapath in Rust: kernel boundaries, map contracts, performance remediation, long-running stability and evidence scope.

READING TIME 13 min · 2026-10-25

Engineering notes on rewriting a production datapath in Rust: kernel boundaries, map contracts, reproducible evidence, and the unglamorous work of making a system stay healthy.

ENGINEERING NOTE 001READING TIME · 13 MIN

01 / The problem is the boundary, not the language.

A rewrite becomes meaningful only when it changes how a system can be reasoned about. Sodama begins with three constraints: the datapath must be Rust end to end; rule compilation must be comparable byte for byte with the Go reference; and every performance or stability claim must have a reproducible experiment.

The result is both a production-tested network system and a research record of Linux kernel paths, eBPF lifecycle, protocol behavior and resource governance.

02 / One datapath, eight programs, fourteen maps.

The kernel side contains seven TC classifiers and one sk_skb offload-verdict program. Fourteen BTF maps carry shared state. Rust / aya-ebpf defines the programs; Rust + aya loads and coordinates them from userspace.

The design keeps packet decisions close to the kernel while moving configuration, protocol implementations and operations into a testable control plane. The map layouts and single-writer discipline are treated as architectural contracts rather than implementation details.

CONFIG→RULE COMPILER→BTF MAPS→TC + SK_SKB
FIG. 01 — Control and state converge on explicit kernel contracts.

03 / The control plane carries the complexity.

Userspace parses the complete .dae configuration and compiles rules. It implements DNS caching, TCP, regex and EDNS behavior; TCP relay, UDP NAT and TCP offload; health probes, group policy, failure switching, runtime state and metrics export.

Protocol work covers Shadowsocks, Trojan, VLESS, VMess, TUIC, Hysteria2 and AnyTLS, including UDP and session behavior. TLS SNI and HTTP Host sniffing preserve domain information for remote resolution. Each feature is evaluated as part of the datapath, not as an isolated checklist item.

04 / Performance is a measured wait, not an adjective.

A reload should not inherit the latency of a slow subscription fetch. Decoupling fetch from reload reduced panel acknowledgement from an observed 19–34 seconds to 0.74 seconds, while preserving validation, persistence and acknowledgement.

The regression laboratory still combines netns topologies, fault injection, cross-implementation clients, soak tests and byte-exact parity checks. A faster path counts only when the same contracts continue to pass.

FIG. 02 / PERFORMANCERELOAD ACKNOWLEDGEMENT · SECONDS
Less waiting, same operational contract. Observed before/after range on a linear seconds axis 061218243036 s BEFORE / 19–34 s AFTER / 0.74 s ÷ 25.7–45.9WAIT TIME Decouple subscription fetching from reload; retain validation, persistence and acknowledgement.
Source: production remediation §10–§11. The range is observed before remediation; 0.74 s is the acknowledged reload after decoupling.
TABLE 01 — Core regression baseline
Unit tests268 / 2687 ignored
Standard / one-arm topology9 / 9 · 9 / 9netns verification
DNS / forwarding / operations6 / 6 · 6 / 6 · 5 / 5domain-specific suites
Protocol ladder17 / 1715 sessions
Go rule-compiler parity5 / 5byte-exact
Kernel matrix9 / 9 · 9 / 9host + QEMU

05 / Stability is the absence of drift over a long window.

Real workloads exposed failures that feature tests could not: blocking health probes, unbounded handshake threads, sticky IPs that were never re-resolved, and reloads coupled to slow network reads.

After hard deadlines, bounded probe concurrency, a dial watchdog, resolve-on-dial and per-source failure isolation, a three-month long-running production test kept RSS resident at 68 MB, with no observed memory growth or memory step.

FIG. 03 / STABILITY3 MONTHS · LONG-RUNNING
Stability is a long window, not a single point. RSS resident at 68 MB over a three-month production window RSS / MB 020406080100 68 MB 3 monthsOBSERVATION WINDOW 0OBSERVED MEMORY STEPS 0.74 sRELOAD ACKNOWLEDGEMENT
Source: three-month long-running production test. RSS remained resident at 68 MB. The window is not extrapolated into a bounded-memory claim; hardware, kernel, commit and traffic profile are not published here.
TABLE 02 — Observed failure → engineering action → measured result
553 blocked receive threadsSocket timeouts + probe cap 96 + dial watchdogNo recurrence observed
Relay threads 36Return group dialing to the reactorRelay threads 0; slope +2.39 → −0.11
Sticky address driftResolve on dial, preserve healthy source membersSelf-recovery without source loss
TABLE 03 — Evidence scope
Reload acknowledgement19–34 s → 0.74 sPanel acknowledgement after validation, persistence and reload; not full request latency.
RSS68 MBThree-month long-running test, one production workload.
Unit tests268 / 268 · 7 ignoredA specific revision; passing tests are not proof of all paths.
Not published hereHardware · kernel · commit SHA · traffic mix · full sample logRequired before treating these numbers as a general benchmark.

03′ / A console without engine changes.

The operations panel uses an Axum backend and a Leptos → WASM frontend, shipped as a single musl binary. It reads existing Unix-socket state and metrics contracts; configuration changes pass through validation, persistence and reload acknowledgement.

The binary is approximately 2.6 MB with 940 KB of static assets. Without an active browser session, it idles at roughly 3 MB RSS and three threads. Runtime cards expose RSS, threads, file descriptors, CPU and connections; data-age indicators distinguish a stalled panel from a genuinely stable engine.

06 / Decisions need an exit condition.

Sixty-three accepted ADRs record context, alternatives, rationale, consequences and—most importantly—the measurement that would justify revision. The archive extends from language migration and map layouts to the M4 reactor and zero-thread handshakes.

The remaining backlog is explicit: packaging and release CI, fuzzing, additional eBPF attachment modes, supply-chain pinning and broader protocol coverage. Stability is not a claim that the system is finished; it is a description of behavior that continues to be measured.

Memory safety is the starting line. Operational safety is the discipline of knowing every thread, descriptor, deadline and failure path.
RELATED PROJECT: Sodama→ ← Back to notes→