PacketFrame
面向 Linux 边缘路由器的转发数据平面。 PacketFrame 将您允许列表中的流量从内核的 conntrack 和 netfilter 路径中剥离,直接在网卡之间转发,其余流量则交由常规的内核转发处理。它可以从 BGP 馈送中构建自己的路由表,而不是读取内核的路由表,因此不会与路由守护进程争夺该表。使用 Rust 编写,并按接口选择性启用:它仅处理您指定的接口。
已在配备全表 BGP 馈送的边缘路由器上投入生产运行。在那里,约 98% 的允许列表流量走快速路径;conntrack 条目和面向客户的延迟均大幅下降,具体数字见下文。
GPL-3.0-or-later。Linux ≥ 5.15。单一静态二进制文件;无需单独的 libbpf、bpftool 或运行时 nightly 工具链。
它做什么
一个守护进程,一个配置文件,以及一组模块。共有三个部分。
快速路径。 对于您挂载的每个接口,PacketFrame 在 XDP 入口运行一个 eBPF 程序,该程序:
- 过滤 基于您声明的
allow-prefix/allow-prefix6列表。不匹配的包原样落入内核。 - 转发 匹配的包直接通过
bpf_redirect_map发送到出口网卡:没有nf_hook_slow,没有 conntrack,没有 iptables 遍历,在原生 XDP 模式下没有内核 skb 分配。 - 解析出口 通过内核 FIB(
bpf_fib_lookup)或 PacketFrame 自己的路由表(通过forwarding-mode由您选择)。
路由侧。 在自定义 FIB 模式下,PacketFrame 自行读取 BGP —— 目前来自 bird,或 BMP (RFC 7854/9069) —— 并据此构建 LPM 树,通过 netlink 解析下一跳 MAC 地址。内核路由表不参与,因此读取该表的守护进程不受影响,且不存在针对 BGP 属性更新的竞态条件。
另一种转发方式。 vpp-offload 模块可以将白名单流量交给 SR-IOV 虚拟功能上的 VPP 进程,使用相同的路由。该模块已编写完成,但尚未在真实硬件上运行;参见 下文。
快速路径可执行的其他操作:
- 用于标记转发的 VLAN 推送/弹出/重写
- 通过
local-prefix指令 + ARP 扫描实现针对直连目的地的每主机快速路径 - 用于在内核处理前丢弃发往不可路由目的地的流量的 XDP 阶段 bogon 拦截(
block-prefix) - 自定义 FIB 模式下的默认路由合成(
fallback-default),用于捕获 BGP 馈送未覆盖的目的地 - 针对快速路径 TCP 的 MSS 钳制,iptables 不再看到这些流量
变更内容
| 关注点 | 标准内核转发 | PacketFrame 快速路径 |
|---|---|---|
| 每包 conntrack 查找 | 是,每个数据包 | 针对白名单流量被绕过 |
| iptables FORWARD 链遍历 | 是,每个数据包,每条规则 | 被绕过 |
| skb 分配开销(原生 XDP) | 是 | 被绕过 |
| BGP 路由源 | 来自路由守护进程的 netlink | 直接 iBGP/BMP;netlink 仅用于下一跳 MAC |
| 内核功能仍有效 | 是 | 是(慢速路径未改变) |
| 回退路径 | 不适用 | 始终存在:不匹配的流量使用内核 |
在生产部署中启用自定义 FIB 后测得的相对改进:
| 指标 | 改进 |
|---|---|
| 白名单流量走快速路径(绕过率) | ~98% |
| 活跃 conntrack 条目 | ↓ ~85% |
每 CPU softirq 利用率(%soft) | 降低约 18 个百分点 |
每 CPU 空闲余量(%idle) | 提高约 20 个百分点 |
| 面向客户的 ping(平均值) | 降低约 57% |
| 面向客户的 ping(p99 尾部) | 降低约 55% |
实际结果取决于工作负载组合、网卡、内核版本和部署拓扑。
对比
| PacketFrame | DPDK / VPP | FRR / 纯路由守护进程 | 纯内核 + iptables | |
|---|---|---|---|---|
| 绕过内核 | 部分 (XDP) | 完全 (用户空间) | 否 | 否 |
| 需要专用核心 | 否 | 是 | 否 | 否 |
| 内核功能仍可用 | 是 | 否,替换协议栈 | 是 | 是 |
| 拥有自己的 BGP 守护进程 | 否,与 bird 配对 | 通常没有 | 是 | 不适用 |
| 内存模型 | 内核管理的 BPF 映射 | 大页内存 | 内核 | 内核 |
| 部署干扰 | 按接口附加,可选 | 替换网络协议栈 | 并行运行 | 默认 |
PacketFrame 列描述的是快速路径,即在生产环境中运行的部分。vpp-offload 位于前两列之间:它在 PacketFrame 的监督下将 VPP 置于虚拟功能之上,因此对于被引导的流量,它确实完全绕过内核,并且确实需要专用核心。快速路径作为回退机制保留在下方。
PacketFrame 并不替换路由守护进程。预期的配对方式是 bird (BGP) + pathvector (配置生成器) + PacketFrame。FRR 也可以通过其 BMP 支持正常工作。
状态
| 组件 | 状态 |
|---|---|
fast-path 模块(XDP 入站、允许列表、重定向) | 生产环境 |
kernel-fib 转发模式(默认) | 生产环境 |
custom-fib 转发模式(BGP 驱动的 LPM) | 生产环境(v0.2.0+) |
iBGP 路由源(route-source bgp) | 生产环境(v0.2.0+) |
BMP 站点路由源(route-source bmp) | 就绪,未在生产环境测试(当前无发射器) |
直连目的地快速路径(local-prefix) | 生产环境(v0.2.1+) |
fallback-default 综合 | 生产环境(v0.2.1+) |
block-prefix XDP 时间丢弃 | 生产环境(v0.2.1+) |
mss-clamp 指令(快速路径) | 生产环境(v0.2.4+;在更严格的内核上,v0.2.5+ 支持按前缀加载) |
packetframe reconfigure / systemctl reload packetframe | 生产环境(v0.2.4+) |
两阶段 BPF 数据路径(fast_path + finalize 通过 bpf_tail_call) | 生产环境(v0.2.5+);参见 docs/runbooks/tail-call-architecture.md |
probe 模块(诊断 XDP) | 生产环境 |
tc-ingress 数据路径(attach <iface> tc,仅限自定义 FIB) | 已构建并测量出更慢:在参考硬件上每包 CPU 增加 70%。保留供参考,不推荐;参见 docs/runbooks/tc-datapath.md |
每模块健康状态 + 指标,由 packetframe status 和 Prometheus 文本文件读取 | 生产环境(v0.2.7+) |
vpp-offload 模块(VPP-on-VF 转发向量) | 代码完整,硬件未验证 — 从未针对真实 VPP 运行;参见 docs/runbooks/vpp-offload.md |
ddos 模块(XDP 时间 SYN 泛洪 + 放大攻击过滤器) | 未来;在 SPEC §5.2 中草图(优先级 0–999,安全/准入) |
sampler 模块(每流 ringbuf 可观测性) | 未来;已在 SPEC §5.3 中概述(优先级 2000–2999,观测) |
randomizer 模块(NoiseNet 反相关的 TC 出口抖动) | 未来;已在 SPEC §5.1 中概述(优先级 ~3000,出口) |
| 多模块分发器(同一 hook 上任何第二个模块的先决条件) | 未来;模块 trait 已为其塑形(SPEC §3.2 / §3.4) |
安装
发布版本已发布在 GitHub 发布页面,同时提供 .deb 软件包(Debian / Ubuntu,amd64 和 arm64)以及 .tar.gz 归档文件(任意 Linux,四个目标三元组)。
Debian / Ubuntu (.deb)
VERSION=v0.2.7
ARCH=$(dpkg --print-architecture) # amd64 or arm64
curl -LO "https://github.com/unredacted/packetframe/releases/download/${VERSION}/packetframe_${VERSION#v}_${ARCH}.deb"
curl -LO "https://github.com/unredacted/packetframe/releases/download/${VERSION}/SHA256SUMS"
sha256sum -c SHA256SUMS --ignore-missing
sudo apt-get install ./packetframe_${VERSION#v}_${ARCH}.deb
安装 /usr/bin/packetframe,位于 /lib/systemd/system/packetframe.service 的 systemd 单元,以及位于 /etc/packetframe/example.conf 的示例配置。该服务不会自动启动。将示例复制到 /etc/packetframe/packetframe.conf,按照 Quickstart 进行编辑,然后执行 sudo systemctl enable --now packetframe。需要 glibc ≥ 2.31(Debian 11+ / Ubuntu 20.04+)。
Tarball (any Linux)
适用于 musl-static 部署、非 Debian 发行版或其他情况:
VERSION=v0.2.7
TARGET=aarch64-unknown-linux-gnu # or: x86_64-unknown-linux-{gnu,musl}, aarch64-unknown-linux-musl
curl -LO "https://github.com/unredacted/packetframe/releases/download/${VERSION}/packetframe-${VERSION}-${TARGET}.tar.gz"
curl -LO "https://github.com/unredacted/packetframe/releases/download/${VERSION}/SHA256SUMS"
sha256sum -c SHA256SUMS --ignore-missing
tar xzf "packetframe-${VERSION}-${TARGET}.tar.gz"
sudo install -m 0755 "packetframe-${VERSION}-${TARGET}/packetframe" /usr/local/bin/
sudo install -m 0644 -D "packetframe-${VERSION}-${TARGET}/conf/example.conf" /etc/packetframe/example.conf
可选的 GPG 验证:下载 SHA256SUMS.asc 和 gpg --verify SHA256SUMS.asc SHA256SUMS(密钥 ID 见发布说明)。
快速入门
参考工作流为 probe → dry-run → live。它刻意要求你在切换任何影响生产流量的设置之前,先观察计数器。
1. 验证主机
sudo packetframe feasibility --human
报告内核能力(BPF 系统调用、LPM trie、devmap-hash、ringbuf 等)以及 bpffs 是否已挂载。任何 FAIL 都是继续之前需要修复的内核/主机先决条件。
2. 编写一个最小配置
/etc/packetframe/packetframe.conf:
global
bpffs-root /sys/fs/bpf/packetframe
state-dir /var/lib/packetframe/state
metrics-textfile /var/lib/node_exporter/textfile/packetframe.prom
module fast-path
attach eth0 auto
allow-prefix 192.0.2.0/24 # your customer / forwarding scope
allow-prefix6 2001:db8::/48
dry-run on # observe-only, no redirects yet
circuit-breaker drop-ratio 0.01 of matched window 5s threshold 5
# mss-clamp via eth0 1360 # optional, clamp TCP MSS for fast-pathed
# traffic egressing eth0 (closes the
# iptables-bypass MSS gap; v0.2.4+)
dry-run on 使程序统计匹配的报文,但始终返回 XDP_PASS。内核处理转发时,就好像 PacketFrame 不存在一样。计数器能让你在切换开关之前,确认你的允许列表是否匹配了正确的流量。
3. 针对主机进行验证
sudo packetframe feasibility --config /etc/packetframe/packetframe.conf --human
现在还会执行针对每个接口的试验性 XDP 挂载,以便在正式部署前捕获驱动程序兼容性问题。
4. 运行
sudo packetframe run # foreground; --config defaults to /etc/...
sudo packetframe status # in another shell, live counters
5. 当匹配比率看起来正确时,关闭 dry-run
编辑配置文件,将 dry-run on 更改为 dry-run off,然后触发重新加载(v0.2.4+):
sudo packetframe reconfigure # synchronous; exits non-zero on parse error
sudo systemctl reload packetframe # equivalent under systemd; both end up sending SIGHUP
可热重载的内容:allow-prefix*、block-prefix、dry-run、forwarding-mode、mss-clamp、VLAN 子接口解析以及重定向 devmap。Attach 集变更(接口添加/移除)、route-source 配置、circuit-breaker 阈值以及 local-prefix/local-prefix6 仍需要完全重启。参见 docs/runbooks/reconfigure.md。
6. 拆除
sudo packetframe detach --all # removes pins, detaches XDP
转发模式
forwarding-mode 选择 PacketFrame 如何为匹配的报文解析出口:
kernel-fib(默认):使用bpf_fib_lookup()针对内核路由表。与纯 Linux 相同的路由决策。永久回滚路径。custom-fib:使用 PacketFrame 自身的 LPM 树,由 BGP 馈送填充。允许消费内核路由表的守护进程并行工作,而不会与路由守护进程的 BGP 属性更新发生竞争。compare:执行两种查找,通过内核结果转发,增加不一致计数器。仅用于切换前验证。
Custom-fib 模式需要一个 route-source 指令:
route-source bgp 127.0.0.1:1179 local-as 65000 peer-as 65000
Bird 通过该地址作为 iBGP 对等体连接到 PacketFrame。Bird 的 protocol bgp 导出过滤器在最优路径选择之后运行,因此 PacketFrame 每个前缀仅接收一条 UPDATE。
对于发送 RFC 9069 Loc-RIB 的 BMP 发射器(FRR;未来的 bird):
route-source bmp 127.0.0.1:6543 require-loc-rib
require-loc-rib 在会话初始化时拒绝 pre/post-policy 帧,因此配置错误的发送器会显式失败,而不是静默地基于错误的 RIB 视图进行转发。
请参阅 docs/runbooks/custom-fib.md 获取完整的操作指南:切换序列、回滚、完整性检查、故障排除。
第二条转发路径 (vpp-offload)
XDP 快速路径是 PacketFrame 当前转发数据包的方式。
vpp-offload 模块为同一流量增加了第二个选项:NIC 的
硬件分类器将白名单中的数据包直接发送到 SR-IOV
虚拟功能,由 PacketFrame 启动并监督的 VPP 进程进行转发。VPP 从快速路径使用的同一 BGP 源获取路由,因此两条路径做出相同的转发决策。
未被引导的流量仍保留在 XDP 路径上,如果 VPP 停止运行,所有流量也是如此。
引导是按接口进行的,默认关闭。您可以一次在一个接口上开启,并通过配置重新加载将其关闭,而无需重启 VPP。此仓库中不包含 VPP 源代码:PacketFrame 构建未修改的上游 VPP 并在其自己的发布标签下发布。
代码已完成,但从未针对真实的 VPP 进程或真实的 NIC 运行过。在
docs/runbooks/vpp-offload.md 之前
在任何承载流量的地方开启它。
附加模式
每个 attach <iface> <mode> 指令选择 XDP 如何绑定到接口:
| 模式 | 成本 | 适用场景 |
|---|---|---|
native | 最低;在 NIC 驱动中于 skb 分配前运行 | 驱动支持原生 XDP 并交付以太网形状的帧 |
generic | 较高;在 skb 分配后运行 | 驱动不支持原生 XDP,或存在已知的原生模式 bug |
auto | 尝试原生,回退到通用 | 大多数情况;在存在已知 bug 的驱动上自动降级 |
驱动注意事项
PacketFrame 拒绝有实证证据表明不安全的配置:
Marvell rvu-nicpf 在内核 < v6.8 上: 原生 XDP 附加在每次分离时泄漏一个内核资源计数器(non_qos_queues)。经过几次附加/分离循环后,内核页分配器可能会损坏。PacketFrame 在此处硬性拒绝显式的 attach <iface> native 并将 auto 降级为 generic。已在上游提交 04f647c8e456 中修复;拥有该回移补丁的运维人员可通过 driver-workaround rvu-nicpf-head-shift off 选择退出。
Marvell rvu-nicpf 在多成员网桥上: XDP 附加和分离都会短暂地使链路抖动,网桥堆栈将其视为端口状态变化。在一个 STP/RSTP 窗口内两个端口抖动曾导致 L2 环路和内核恐慌。当 ≥ 2 个已附加接口共享一个 /sys/class/net/<iface>/master 时,PacketFrame 通过 attach-settle-time 对附加和分离进行节奏控制(默认 2 秒,在慢收敛网桥上提高)。
诊断特定驱动问题
如果 packetframe status 显示 rx_total 与 pass_not_ip 同步攀升,而 matched_* 保持为零,则程序正在运行但未解析其接收到的帧。这通常指向驱动特定的原生模式交付怪癖。使用 packetframe probe 检查驱动实际传递给 XDP 的内容:
sudo packetframe probe --iface eth0 --mode native --duration 2s
sudo packetframe probe --iface eth0 --mode native --duration 2s --offset 128
sudo packetframe probe --iface eth0 --mode generic --duration 2s # what kernel sees
输出数据包样本的前 16 个字节以及一行判定结果。
配置参考
conf/example.conf 随二进制文件附带作为标准参考,其中每条指令均附有注释和行内说明。请阅读该文件以获取完整语法。
快速指令索引:
全局
bpffs-root,state-dir,metrics-textfile,log-level,attach-settle-time
模块快速路径:附加 + 允许列表
attach <iface> {native|generic|auto}allow-prefix <ipv4-cidr>,allow-prefix6 <ipv6-cidr>: 源或目的匹配dry-run {on|off}circuit-breaker drop-ratio X of matched window Ys threshold N
模块快速路径:转发模式
forwarding-mode {kernel-fib|custom-fib|compare}route-source bgp <addr>:<port> local-as <asn> peer-as <asn> [router-id <ipv4>]route-source bmp <addr>:<port> [require-loc-rib]local-prefix <cidr> via <iface> [arp-scavenge]: 针对已连接 IPv4 目的地的每主机 /32 快速路径local-prefix6 <cidr> via <iface>: 每主机 /128 等效项,通过 NDP 解析(无arp-scavenge;/64 不可枚举)fallback-default via <iface> nexthop <ipv4>: 合成的 0.0.0.0/0 全匹配block-prefix <cidr>: 针对不可路由目的地的 XDP 时丢弃ecmp-default-hash-mode {3|4|5}: 用于 ECMP 哈希的元组宽度
模块快速路径:TCP 转换 (v0.2.4+)
mss-clamp <mtu>: 匹配 TCP SYN/SYN-ACK 的全局钳制上限mss-clamp via <iface> <mtu>: 每出口接口mss-clamp <cidr> <mtu>: 每源或目的前缀(任意出口)mss-clamp <cidr> via <iface> <mtu>: 最具体(优先级:前缀+接口 > 前缀 > 接口 > 全局)
模块快速路径:驱动程序选项
driver-workaround rvu-nicpf-head-shift {auto|on|off}
模块 vpp-offload(使用前请参阅 runbook)
port <iface> cores <n> steer {on|off}: VPP 参与的每个接口一行;steer是每接口开关expected-routes <n>: 设定 VPP 的内存大小,启动时固定hugepages <n>,vpp-binary <path>require-table-complete {on|off}: 在引导前等待路由表加载完成(默认开启)
SIGHUP(或 packetframe reconfigure / systemctl reload packetframe)应用仅增量变更,涉及允许列表、块前缀、VLAN 解析、devmap、mss-clamp、dry-run、转发模式位以及 vpp-offload 的 steer 开关。添加或移除 attach、更改 route-source、修改 circuit-breaker 阈值、编辑 local-prefix/local-prefix6,或更改除 steer 以外的任何 vpp-offload 指令,都需要重启。
运维工具
sudo packetframe status # live counters, plus each module's health
sudo packetframe fib stats # custom-FIB occupancy / hash mode
sudo packetframe fib lookup <ip> # "what would XDP do for this dst?"
sudo packetframe fib dump-v4 # walk FIB_V4 LPM trie
sudo packetframe detach --all # remove all pins, detach XDP
当设置 metrics-textfile 时,计数器每 15 秒导出为 Prometheus textfile:包括每个计数器的 gauge、按下一跳状态划分的自定义 FIB 占用率、当前活动转发模式,以及每个已加载模块发布的内容。
文档
conf/example.conf: 带注释的参考配置
Runbooks,位于 docs/runbooks/:
| 文件 | 涵盖内容 |
|---|---|
custom-fib.md | 自定义 FIB 模式:切换、回滚、完整性检查、按症状分类的故障排查 |
reconfigure.md | SIGHUP 应用的内容以及需要重启的内容 |
mss-clamp.md | MSS 钳制及其解决的 iptables 绕过漏洞 |
tail-call-architecture.md | 两阶段 BPF 数据路径及其拆分原因 |
generic-mode-performance.md | 通用 XDP 与原生 XDP 的实测成本,以及主机调优 |
tc-datapath.md | tc-ingress 变体,以及其实测较慢的原因 |
vpp-offload.md | 运行 vpp-offload:部署、回滚、故障排查,哪些数值是实测的 |
vpp-offload-spike.md | vpp-offload 在测试硬件上的启动过程,以及失败之处 |
从源码构建
make build # debug, host target
make release # release, host target
make release-all # all four published targets (requires `cross`)
make test # workspace tests
make lint # cargo fmt --check + cargo clippy -D warnings
工具链:在 rust-toolchain.toml 中固定的 stable Rust。BPF 相关 crate(crates/modules/*/bpf/)各自拥有固定的 nightly 工具链 + bpf-linker,由 CI 自动安装;对于本地 BPF 重新构建,请安装 rustup 并让其遵循工具链文件。
交叉编译到 release 目标使用 cross:cargo install --locked cross。
项目布局
packetframe/
├── crates/
│ ├── common/ # config parser, Module trait, capability probes
│ ├── cli/ # the `packetframe` binary
│ ├── modules/
│ │ ├── fast-path/ # main forwarding module
│ │ │ └── bpf/ # XDP program (nightly toolchain)
│ │ ├── probe/ # diagnostic XDP probe
│ │ │ └── bpf/ # probe BPF program
│ │ └── vpp-offload/ # second forwarding path (supervises VPP)
│ └── tools/vpp-api-codegen/ # generates VPP binary-API structs from its .api.json
├── conf/example.conf # annotated reference config
├── docs/runbooks/ # operational runbooks
├── vpp/pin.toml # which upstream VPP we build and ship
└── .github/workflows/ # CI (fmt/clippy/test, cross-build, qemu-verifier, release)
许可证
GPL-3.0-or-later。参见 LICENSE。