{"id":49154053,"url":"https://github.com/unredacted/packetframe","last_synced_at":"2026-05-20T04:03:02.428Z","repository":{"id":352586114,"uuid":"1215000425","full_name":"unredacted/packetframe","owner":"unredacted","description":"PacketFrame is a modular eBPF-based packet-processing framework written in pure Rust","archived":false,"fork":false,"pushed_at":"2026-04-25T07:42:15.000Z","size":663,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-25T08:02:25.669Z","etag":null,"topics":["bpf","ebpf","networking","routing","rust","rust-lang","xdp"],"latest_commit_sha":null,"homepage":"https://unredacted.org","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unredacted.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-19T10:48:13.000Z","updated_at":"2026-04-24T02:55:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"9ad1d8f6-94f0-49a3-ad97-4cb69502eba2","html_url":"https://github.com/unredacted/packetframe","commit_stats":null,"previous_names":["unredacted/packetframe"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/unredacted/packetframe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unredacted%2Fpacketframe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unredacted%2Fpacketframe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unredacted%2Fpacketframe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unredacted%2Fpacketframe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unredacted","download_url":"https://codeload.github.com/unredacted/packetframe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unredacted%2Fpacketframe/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32291341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T08:29:33.829Z","status":"ssl_error","status_checked_at":"2026-04-26T08:29:18.366Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bpf","ebpf","networking","routing","rust","rust-lang","xdp"],"created_at":"2026-04-22T08:00:54.456Z","updated_at":"2026-05-20T04:03:02.421Z","avatar_url":"https://github.com/unredacted.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PacketFrame\n\n**eBPF/XDP fast-path for Linux packet forwarding.** Pure Rust, pluggable, attaches per-interface. Forwards allowlisted traffic directly between NICs at the driver level (bypassing iptables, conntrack, and the kernel routing stack), and falls back to normal kernel forwarding for everything else.\n\nProduction-tested on edge routers with full-table BGP feeds. **~98% of allowlisted flows fast-path** in measured deployments, with conntrack table size and customer-facing latency both reduced significantly versus stock kernel forwarding.\n\nGPL-3.0-or-later. Linux ≥ 5.15. Single static binary; no separate libbpf, bpftool, or runtime nightly toolchain.\n\n## What it does\n\nFor each interface you attach it to, PacketFrame runs an eBPF program at XDP ingress that:\n\n1. **Filters** by your declared `allow-prefix` / `allow-prefix6` lists. Non-matching packets fall through to the kernel unchanged.\n2. **Forwards** matched packets directly to the egress NIC via `bpf_redirect_map`: no `nf_hook_slow`, no conntrack, no iptables walk, no kernel skb allocation in native XDP mode.\n3. **Resolves the egress** via either the kernel FIB (`bpf_fib_lookup`) or PacketFrame's own LPM trie populated from a BGP feed (your choice via `forwarding-mode`).\n\nOptional layered features:\n\n- **VLAN push/pop/rewrite** for tagged forwarding\n- **Custom-FIB mode**: ingest BGP routes directly via iBGP (production today, with `bird`) or BMP (RFC 7854/9069). No netlink dependency, no race with other daemons subscribed to kernel routes.\n- **Per-host fast-path for connected destinations** via `local-prefix` directives + ARP scavenging\n- **XDP-time bogon block** (`block-prefix`) for dropping traffic to unrouteable destinations before kernel processing\n- **Default-route synthesis** in custom-FIB mode (`fallback-default`) for catching destinations the BGP feed doesn't cover\n\n## Benefits\n\n| Concern | Stock kernel forwarding | PacketFrame fast-path |\n|---|---|---|\n| Per-packet conntrack lookup | yes, every packet | bypassed for allowlisted flows |\n| iptables FORWARD chain walk | yes, every packet, every rule | bypassed |\n| skb allocation cost (native XDP) | yes | bypassed |\n| BGP route source | netlink from a routing daemon | direct iBGP/BMP, no netlink coupling |\n| Kernel features still work | yes | yes (slow path is unchanged) |\n| Fallback path | n/a | always: non-matching traffic uses kernel |\n\nRelative improvements measured after enabling custom-FIB on a production deployment:\n\n| Metric | Improvement |\n|---|---|\n| Allowlisted flows fast-pathed (bypass rate) | ~98% |\n| Active conntrack entries | ↓ ~85% |\n| Per-CPU softirq utilization (`%soft`) | ~18 percentage points lower |\n| Per-CPU idle headroom (`%idle`) | ~20 percentage points higher |\n| Customer-facing ping (avg) | ~57% lower |\n| Customer-facing ping (p99 tail) | ~55% lower |\n\nActual results depend on workload mix, NIC, kernel version, and deployment topology.\n\n## How it compares\n\n| | PacketFrame | DPDK / VPP | FRR / pure routing daemon | Plain kernel + iptables |\n|---|---|---|---|---|\n| Bypasses kernel | partially (XDP) | fully (userspace) | no | no |\n| Dedicated cores required | no | yes | no | no |\n| Kernel features still work | yes | no, replaces stack | yes | yes |\n| Has its own BGP daemon | no, pairs with bird | typically not | yes | n/a |\n| Memory model | kernel-managed BPF maps | hugepages | kernel | kernel |\n| Deploy disruption | per-iface attach, opt-in | replaces network stack | runs alongside | default |\n\nPacketFrame complements existing routing daemons rather than replacing them. The intended pairing is `bird` (BGP) + `pathvector` (config generator) + PacketFrame (fast-path). FRR works similarly via its BMP support.\n\n## Status\n\n| Component | State |\n|---|---|\n| `fast-path` module (XDP ingress, allowlist, redirect) | Production |\n| `kernel-fib` forwarding mode (default) | Production |\n| `custom-fib` forwarding mode (BGP-fed LPM) | Production (v0.2.0+) |\n| iBGP route source (`route-source bgp`) | Production (v0.2.0+) |\n| BMP station route source (`route-source bmp`) | Ready, untested in production (no current emitter) |\n| Connected-destination fast-path (`local-prefix`) | Production (v0.2.1+) |\n| `fallback-default` synthesis | Production (v0.2.1+) |\n| `block-prefix` XDP-time drop | Production (v0.2.1+) |\n| `mss-clamp` directive (fast-path) | Production (v0.2.4+; per-prefix loads on stricter kernels in v0.2.5+) |\n| `packetframe reconfigure` / `systemctl reload packetframe` | Production (v0.2.4+) |\n| Two-stage BPF datapath (`fast_path` + `finalize` via `bpf_tail_call`) | Production (v0.2.5+); see [docs/runbooks/tail-call-architecture.md](docs/runbooks/tail-call-architecture.md) |\n| `probe` module (diagnostic XDP) | Production |\n| `ddos` module (XDP-time SYN-flood + amplification filter) | Future; sketched in SPEC §5.2 (priority 0–999, security/admission) |\n| `sampler` module (per-flow ringbuf observability) | Future; sketched in SPEC §5.3 (priority 2000–2999, observation) |\n| `randomizer` module (TC egress jitter for NoiseNet anti-correlation) | Future; sketched in SPEC §5.1 (priority ~3000, egress) |\n| Multi-module dispatcher (prerequisite for any second module on the same hook) | Future; module trait already shaped for it (SPEC §3.2 / §3.4) |\n\n## Install\n\nReleases are published on the [GitHub releases page](https://github.com/unredacted/packetframe/releases) as both `.deb` packages (Debian / Ubuntu, `amd64` and `arm64`) and `.tar.gz` archives (any Linux, four target triples).\n\n### Debian / Ubuntu (.deb)\n\n```sh\nVERSION=v0.2.6\nARCH=$(dpkg --print-architecture)   # amd64 or arm64\n\ncurl -LO \"https://github.com/unredacted/packetframe/releases/download/${VERSION}/packetframe_${VERSION#v}_${ARCH}.deb\"\ncurl -LO \"https://github.com/unredacted/packetframe/releases/download/${VERSION}/SHA256SUMS\"\nsha256sum -c SHA256SUMS --ignore-missing\n\nsudo apt-get install ./packetframe_${VERSION#v}_${ARCH}.deb\n```\n\nInstalls `/usr/bin/packetframe`, the systemd unit at `/lib/systemd/system/packetframe.service`, and an example config at `/etc/packetframe/example.conf`. The service is **not** auto-started. Copy the example to `/etc/packetframe/packetframe.conf`, edit per the [Quickstart](#quickstart), then `sudo systemctl enable --now packetframe`. Requires glibc ≥ 2.31 (Debian 11+ / Ubuntu 20.04+).\n\n### Tarball (any Linux)\n\nFor musl-static deployments, non-Debian distros, or anything else:\n\n```sh\nVERSION=v0.2.6\nTARGET=aarch64-unknown-linux-gnu     # or: x86_64-unknown-linux-{gnu,musl}, aarch64-unknown-linux-musl\n\ncurl -LO \"https://github.com/unredacted/packetframe/releases/download/${VERSION}/packetframe-${VERSION}-${TARGET}.tar.gz\"\ncurl -LO \"https://github.com/unredacted/packetframe/releases/download/${VERSION}/SHA256SUMS\"\nsha256sum -c SHA256SUMS --ignore-missing\ntar xzf \"packetframe-${VERSION}-${TARGET}.tar.gz\"\n\nsudo install -m 0755 \"packetframe-${VERSION}-${TARGET}/packetframe\" /usr/local/bin/\nsudo install -m 0644 -D \"packetframe-${VERSION}-${TARGET}/conf/example.conf\" /etc/packetframe/example.conf\n```\n\nOptional GPG verification: download `SHA256SUMS.asc` and `gpg --verify SHA256SUMS.asc SHA256SUMS` (key ID in release notes).\n\n## Quickstart\n\nThe reference workflow is **probe → dry-run → live**. It deliberately makes you watch counters before flipping anything that affects production traffic.\n\n### 1. Verify the host\n\n```sh\nsudo packetframe feasibility --human\n```\n\nReports kernel capabilities (BPF syscalls, LPM trie, devmap-hash, ringbuf, etc.) and whether bpffs is mounted. Anything `FAIL` is a kernel/host prerequisite to fix before continuing.\n\n### 2. Write a minimal config\n\n`/etc/packetframe/packetframe.conf`:\n\n```\nglobal\n  bpffs-root /sys/fs/bpf/packetframe\n  state-dir /var/lib/packetframe/state\n  metrics-textfile /var/lib/node_exporter/textfile/packetframe.prom\n\nmodule fast-path\n  attach eth0 auto\n  allow-prefix 192.0.2.0/24       # your customer / forwarding scope\n  allow-prefix6 2001:db8::/48\n  dry-run on                       # observe-only, no redirects yet\n  circuit-breaker drop-ratio 0.01 of matched window 5s threshold 5\n  # mss-clamp via eth0 1360       # optional, clamp TCP MSS for fast-pathed\n                                   # traffic egressing eth0 (closes the\n                                   # iptables-bypass MSS gap; v0.2.4+)\n```\n\n`dry-run on` makes the program count matched packets but always return `XDP_PASS`. The kernel handles forwarding as if PacketFrame weren't there. Counters tell you whether your allowlist matches the right traffic before you flip the switch.\n\n### 3. Validate against the host\n\n```sh\nsudo packetframe feasibility --config /etc/packetframe/packetframe.conf --human\n```\n\nNow also runs a per-interface trial XDP attach to catch driver compatibility issues before live deploy.\n\n### 4. Run\n\n```sh\nsudo packetframe run                 # foreground; --config defaults to /etc/...\nsudo packetframe status              # in another shell, live counters\n```\n\n### 5. Flip dry-run off when match ratios look right\n\nEdit the config, change `dry-run on` to `dry-run off`, then trigger a reload (v0.2.4+):\n\n```sh\nsudo packetframe reconfigure                # synchronous; exits non-zero on parse error\nsudo systemctl reload packetframe           # equivalent under systemd; both end up sending SIGHUP\n```\n\nWhat's hot-reloadable: `allow-prefix*`, `block-prefix`, `dry-run`, `forwarding-mode`, `mss-clamp`, VLAN-subif resolution, and the redirect devmap. Attach-set changes (interfaces added/removed), `route-source` config, `circuit-breaker` thresholds, and `local-prefix` still require a full restart. See [docs/runbooks/reconfigure.md](docs/runbooks/reconfigure.md).\n\n### 6. Tear down\n\n```sh\nsudo packetframe detach --all        # removes pins, detaches XDP\n```\n\n## Forwarding modes\n\n`forwarding-mode` selects how PacketFrame resolves the egress for a matched packet:\n\n- **`kernel-fib`** (default): uses `bpf_fib_lookup()` against the kernel's routing table. Same routing decisions as plain Linux. The permanent rollback path.\n- **`custom-fib`**: uses PacketFrame's own LPM trie, populated from a BGP feed. Lets daemons that consume the kernel route table work in parallel without racing on BGP attribute updates from the routing daemon.\n- **`compare`**: runs both lookups, forwards via the kernel result, bumps a disagreement counter. Pre-cutover validation only.\n\nCustom-fib mode requires a `route-source` directive:\n\n```\nroute-source bgp 127.0.0.1:1179 local-as 65000 peer-as 65000\n```\n\nBird connects out to PacketFrame as an iBGP peer on this address. Bird's `protocol bgp` export filter runs *after* best-path selection, so PacketFrame receives one UPDATE per prefix.\n\nFor BMP emitters that ship RFC 9069 Loc-RIB (FRR; future bird):\n\n```\nroute-source bmp 127.0.0.1:6543 require-loc-rib\n```\n\n`require-loc-rib` rejects pre/post-policy frames at session-init so misconfigured emitters fail loudly rather than silently driving forwarding off the wrong RIB view.\n\nSee [`docs/runbooks/custom-fib.md`](docs/runbooks/custom-fib.md) for the full operational guide: cutover sequence, rollback, integrity checking, troubleshooting.\n\n## Attach modes\n\nEach `attach \u003ciface\u003e \u003cmode\u003e` directive picks how XDP binds to the interface:\n\n| Mode | Cost | Use when |\n|---|---|---|\n| `native` | Lowest; runs in NIC driver before skb alloc | Driver supports native XDP and delivers Ethernet-shaped frames |\n| `generic` | Higher; runs after skb alloc | Driver doesn't support native XDP, or has known native-mode bugs |\n| `auto` | tries native, falls back to generic | Most cases; downgraded automatically on drivers with known bugs |\n\n### Driver caveats\n\nPacketFrame refuses configurations it has empirical evidence are unsafe:\n\n**Marvell `rvu-nicpf` on kernels \u003c v6.8:** native XDP attach leaks a kernel resource counter (`non_qos_queues`) on every detach. After a handful of attach/detach cycles the kernel page allocator can corrupt. PacketFrame hard-refuses explicit `attach \u003ciface\u003e native` here and downgrades `auto` to `generic`. Fixed upstream in commit `04f647c8e456`; operators with the backport can opt out via `driver-workaround rvu-nicpf-head-shift off`.\n\n**Marvell `rvu-nicpf` on multi-member bridges:** XDP attach AND detach briefly bounce the link, which the bridge stack treats as a port-state change. Two ports flapping inside one STP/RSTP window has caused L2 loops and kernel panics. PacketFrame paces both attach and detach via `attach-settle-time` (default 2 s, raise on slow-converging bridges) when ≥ 2 attached ifaces share a `/sys/class/net/\u003ciface\u003e/master`.\n\n### Diagnosing driver-specific issues\n\nIf `packetframe status` shows `rx_total` climbing in lockstep with `pass_not_ip` while `matched_*` stays at zero, the program is running but not parsing frames it receives. That usually points to a driver-specific native-mode delivery quirk. Use `packetframe probe` to inspect what the driver actually hands to XDP:\n\n```sh\nsudo packetframe probe --iface eth0 --mode native --duration 2s\nsudo packetframe probe --iface eth0 --mode native --duration 2s --offset 128\nsudo packetframe probe --iface eth0 --mode generic --duration 2s   # what kernel sees\n```\n\nOutput dumps the first 16 bytes of a packet sample plus a one-line verdict.\n\n## Configuration reference\n\n`conf/example.conf` ships with the binary as the canonical reference, with every directive commented and explained inline. Read that for the full grammar.\n\nQuick directive index:\n\n**Global**\n- `bpffs-root`, `state-dir`, `metrics-textfile`, `log-level`, `attach-settle-time`\n\n**Module fast-path: attach + allowlist**\n- `attach \u003ciface\u003e {native|generic|auto}`\n- `allow-prefix \u003cipv4-cidr\u003e`, `allow-prefix6 \u003cipv6-cidr\u003e`: src-or-dst match\n- `dry-run {on|off}`\n- `circuit-breaker drop-ratio X of matched window Ys threshold N`\n\n**Module fast-path: forwarding mode**\n- `forwarding-mode {kernel-fib|custom-fib|compare}`\n- `route-source bgp \u003caddr\u003e:\u003cport\u003e local-as \u003casn\u003e peer-as \u003casn\u003e [router-id \u003cipv4\u003e]`\n- `route-source bmp \u003caddr\u003e:\u003cport\u003e [require-loc-rib]`\n- `local-prefix \u003ccidr\u003e via \u003ciface\u003e [arp-scavenge]`: per-host fast-path for connected destinations\n- `fallback-default via \u003ciface\u003e nexthop \u003cipv4\u003e`: synthetic 0.0.0.0/0 catch-all\n- `block-prefix \u003ccidr\u003e`: XDP-time drop for unrouteable destinations\n- `ecmp-default-hash-mode {3|4|5}`: tuple width for ECMP hashing\n\n**Module fast-path: TCP transforms (v0.2.4+)**\n- `mss-clamp \u003cmtu\u003e`: global clamp ceiling for matched TCP SYN/SYN-ACK\n- `mss-clamp via \u003ciface\u003e \u003cmtu\u003e`: per-egress-iface\n- `mss-clamp \u003ccidr\u003e \u003cmtu\u003e`: per-src-or-dst-prefix (any egress)\n- `mss-clamp \u003ccidr\u003e via \u003ciface\u003e \u003cmtu\u003e`: most specific (precedence: prefix+iface \u003e prefix \u003e iface \u003e global)\n\n**Module fast-path: driver opt-ins**\n- `driver-workaround rvu-nicpf-head-shift {auto|on|off}`\n\n`SIGHUP` (or `packetframe reconfigure` / `systemctl reload packetframe`) applies delta-only changes to allowlists, block-prefix, VLAN-resolve, devmap, mss-clamp, dry-run, and forwarding-mode bits. Adding or removing an `attach`, changing `route-source`, mutating `circuit-breaker` thresholds, or editing `local-prefix` requires a restart.\n\n## Operator tools\n\n```sh\nsudo packetframe status                # live counters from pinned STATS map\nsudo packetframe fib stats             # custom-FIB occupancy / hash mode\nsudo packetframe fib lookup \u003cip\u003e       # \"what would XDP do for this dst?\"\nsudo packetframe fib dump-v4           # walk FIB_V4 LPM trie\nsudo packetframe detach --all          # remove all pins, detach XDP\n```\n\nCounters export as Prometheus textfile every 15 s when `metrics-textfile` is set. Metrics include per-counter gauges, custom-FIB occupancy by nexthop state, and the active forwarding mode.\n\n## Documentation\n\n- [`conf/example.conf`](conf/example.conf): annotated reference config\n- [`docs/runbooks/custom-fib.md`](docs/runbooks/custom-fib.md): operational runbook for custom-FIB mode (cutover, rollback, integrity checks, triage by symptom)\n\n## Build from source\n\n```sh\nmake build        # debug, host target\nmake release      # release, host target\nmake release-all  # all four published targets (requires `cross`)\n\nmake test         # workspace tests\nmake lint         # cargo fmt --check + cargo clippy -D warnings\n```\n\nToolchain: stable Rust pinned in `rust-toolchain.toml`. The BPF crates (`crates/modules/*/bpf/`) each have their own pinned nightly toolchain + `bpf-linker`, installed automatically by CI; for local BPF rebuilds, install `rustup` and let it follow the toolchain files.\n\nCross-compiling to release targets uses [`cross`](https://github.com/cross-rs/cross): `cargo install --locked cross`.\n\n## Project layout\n\n```\npacketframe/\n├── crates/\n│   ├── common/                       # config parser, Module trait, capability probes\n│   ├── cli/                          # the `packetframe` binary\n│   └── modules/\n│       ├── fast-path/                # main forwarding module\n│       │   └── bpf/                  # XDP program (nightly toolchain)\n│       └── probe/                    # diagnostic XDP probe\n│           └── bpf/                  # probe BPF program\n├── conf/example.conf                 # annotated reference config\n├── docs/runbooks/                    # operational runbooks\n└── .github/workflows/                # CI (fmt/clippy/test, cross-build, qemu-verifier, release)\n```\n\n## License\n\nGPL-3.0-or-later. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funredacted%2Fpacketframe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funredacted%2Fpacketframe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funredacted%2Fpacketframe/lists"}