Skip to content

zoklk/LeanGuard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lean-guard

eBPF (XDP + TC-BPF + sock_ops) L3/L4 flood defense for self-hosted game servers. On a single machine whose public IP is directly exposed, it passes UDP game packets only from source IPs that have completed the TCP login handshake, and drops or rate-limits the rest. The goal is not survival but preserving the latency and QoS of legitimate players even while a flood is in progress. The scope is L3/L4 defense at the booter/stresser level (L7 application vulnerabilities and upstream link saturation are out of scope).

Headline results (x86 VM, 500K pps peak, n=10)

Attack Defense RTT p99 (µs) Loss (%) core-0 CPU (%)
c3-spoof nftables 9988 ± 71 87.4 100.0
c3-spoof iptables-legacy 6217 ± 66 32.3 100.0
c3-spoof lean (native XDP) 1829 ± 131 0.00 1.6
c1-spoof nftables 6113 ± 182 33.1 100.0
c1-spoof lean (native XDP) 2808 ± 149 0.79 85.5
  • Firewall p99 degrades linearly with load (+8 to +16 µs/Kpps, p≪0.001), but lean stays statistically independent of load, with a slope near 0 and R² near 0.
  • All 16 metric×attack combinations have ICC=0 (no measurement variance between cycles, which means very high reproducibility).
  • Full numbers, regressions, and figures are in results/analysis-cycles-1-10/ (FINDINGS.md, CONNTRACK_BIMODALITY.md).

Layout

src/        eBPF dataplane (the product itself): guard.c, maps.h, Makefile
scripts/    operations + measurement scripts (flat layout, lib/common.sh is the node/IP/IF SSOT)
report/     final report PDF (EN) + figures/ (fig1-4, table CSVs)
results/    analysis-cycles-1-10/ (aggregation, regression, figures) + summaries/ (per-cycle CSV)

scripts/ depends on relative-path source and REPO_ROOT computation, so the flat layout must be kept as is.

  • Operations (deployment): attach_lean.sh, detach_all.sh, lean_guard.py (userspace control), setup_victim.sh, lib/common.sh
  • Measurement (reproduction): run_sweep.sh, run_final.sh, collect_metrics.sh, verify_results.sh, summarize_results.py, make_result_figures.py, attack generators (attack_pktgen.sh, syn_flood*, pktgen_mt.sh), firewall comparison (ipt_legacy.sh, nft_rules.sh), and so on
  • Raw run payloads (hundreds of MB per cycle) are not included in this distribution.

Build (as root on the victim machine)

Kernel 6.6+ is recommended (TC uses the tcx link), cgroup v2 with CONFIG_CGROUP_BPF (needed for sock_ops), and net.core.bpf_jit_enable=1. Dependencies: clang llvm libbpf-dev linux-headers-$(uname -r) bpftool.

sudo ./scripts/setup_victim.sh        # one-time: prepare the environment (governor/core isolation/headers/jit) and build guard.o
make -C src                           # auto-detects arch (x86_64/aarch64), normal profile (10k maps)
make -C src MAP_PROFILE=high          # 100k maps. min|normal|high|max = 1k|10k|100k|1M (fixed at build time)
make -C src verify                    # (optional) dry load check, does not attach

Deployment (attach, observe, detach)

Attach all three hooks (XDP, TC, sock_ops) together. $IF is the victim NIC, check it with ip link.

sudo ./scripts/attach_lean.sh --iface eno1 --xdp-mode native   # native|generic|auto
python3 ./scripts/lean_guard.py stats        # drop/pass/blacklist/conn_cap counters
python3 ./scripts/lean_guard.py events        # ringbuf tail
python3 ./scripts/lean_guard.py dump-all      # full snapshot (JSON)
sudo ./scripts/detach_all.sh --iface eno1     # reverse order, idempotent

Runtime tuning is limited to the 8 fields of config_map plus manual map entries (changing defense logic means a rebuild, map size is set at build time, mode and iface are set at attach time).

python3 ./scripts/lean_guard.py config set max_conn_per_ip=8   # concurrent-connection cap (0=off)
python3 ./scripts/lean_guard.py config get

The config_map fields are max_tokens (100), refill_rate (50/s), violation_threshold (10), max_conn_per_ip (0), block_duration_s (300), whitelist_ttl_s (300), and login_port/game_port (25565).

Reproducing the measurements

⚠️ Attacks must only be run on an isolated network that has no external uplink. Nodes, IPs, and interfaces are resolved from the --victim label in scripts/lib/common.sh.

sudo ./scripts/run_sweep.sh --victim victim1 --hirate   # attack×defense×pps sweep
sudo ./scripts/run_final.sh                             # repeated (cycle) batch run
./scripts/verify_results.sh                             # check output consistency
python3 ./scripts/summarize_results.py                  # headline summary
python3 ./scripts/make_result_figures.py                # generate result figures (fig3/fig4 etc.)

Outputs land in results/<victim>/<defense>/<pps>/<attack>/run<n>/ (victim ∈ {victim0, victim1, victim-demo}; defense ∈ {none, ipt-legacy, nft, lean}; attack ∈ {c1, c3} × {single, spoof}). The provenance of each run is guaranteed by the commit hash in meta.json.

License and origin

Because the eBPF dataplane declares SEC("license")="GPL", this repository is distributed under GPL-2.0.

This distribution repository was curated and derived from commit c6e5e5a (2026-06-15) of the development repository pi-guard. The ADRs (decision records), the full design documents (runbook, ARCHITECTURE, and test-plan, which is the measurement SSOT), and the raw measurement data are kept in the development repository.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors