跳转至

基本原理

QRE 的目标是让用户侧只管理一个 profile 和一个 qre-tool 进程,而把握手、会话派生、内核状态和共享端口分流封装在工具与 kernel module 中。

组件

组件 作用
qre-tool 用户态控制工具。负责配置解析、生命周期、daemon 模式、诊断、会话安装和调试命令。
qred@.service systemd 模板。实际执行 qre-tool daemon %i
qre.ko Linux kernel module。负责 QRE 设备、封装/解封装、会话表、CID 控制和统计。
qre-ebpf 共享 UDP 端口场景下的 SO_REUSEPORT 分流程序。
REALITY/QUIC 控制路径 建立伪装握手,派生 QUIC 数据通道 key,把内核可用的 session 安装到 qre.ko

控制面和数据面

启动 profile 时,qre-tool 依次完成:

  1. 读取全局配置和接口 profile。
  2. 加载 qre.ko,除非通过环境变量禁用。
  3. 通过 Generic Netlink 把 profile 同步到 qre.ko
  4. 通过 rtnetlink 设置 QRE 设备地址和 link 状态。
  5. 进入 server 或 client 的持久控制面。
  6. 握手成功后,把会话 key、CID、packet number 和 underlay tuple 安装到内核。

安装会话后,数据包进入内核快速路径。用户态继续负责重连、重载、ready file、共享端口 loader 生命周期和退出清理。

REALITY 关系

服务端配置:

Dest = www.example.com:443
ServerName = www.example.com
RealityPrivateKey = ...
RealityShortIds = 0123456789abcdef
RealityMaxTimeDiff = 120

客户端配置:

Endpoint = 203.0.113.10:443
ServerName = www.example.com
RealityPublicKey = ...
RealityShortId = 0123456789abcdef

客户端连接 Endpoint,但握手中使用 ServerName 和服务端公钥。服务端用 RealityPrivateKey 验证客户端,检查 short id、时间窗口和 SNI。通过验证后,双方派生 QRE 数据通道需要的 QUIC key。

Dest 不等于 QRE 服务端地址。它是服务端用于伪装行为和 Dest profile 的目标源站。

CID 分流

QRE 使用连接 ID 作为内核和 eBPF 分流的稳定键:

  • install-sessionupdate-session 成功后会自动 adopt-cid
  • adopt-cidConnectionId 加入内核 CID 控制表。
  • 注册了 QRE_CID_ROUTES BPF map 时,内核会把已采用和后续采用的 CID 同步到 map。
  • 共享端口 eBPF 程序根据短头包中的 routed CID,把包送到用户态 fallback socket 或内核 socket。

routed CID 长度默认来自库常量,调试命令允许通过 RoutedCidLen=17..20 覆盖。

共享端口

服务端常见需求是让普通 TLS passthrough、REALITY 握手和已安装 QRE 数据流共享一个公网端口。QRE 的共享端口路径包含三层:

  1. 用户态握手/fallback socket,负责接收新 REALITY/QUIC 握手和非 QRE fallback。
  2. 内核 slot UDP socket,供 eBPF 把已采用 CID 的数据流导向 qre.ko
  3. QRE_SOCKETSQRE_CID_ROUTES BPF map,供 qre_reuseport_demux 查询。

生产路径由 qre-tool daemon <server-profile> 持有。shared-port-loadershared-port-loader-checkshared-port-session-demo 是调试或验收命令。

SSLPreRead fallback

[SSLPreRead "..."] 是全局配置中的低优先级 TCP passthrough 规则。它只处理不是有效 QRE REALITY 握手的普通 TLS 流量:

[SSLPreRead "web-a"]
Listen = 0.0.0.0:443
SNI = cdn-a.example.com
Upstream = 127.0.0.1:8443

[SSLPreRead "default"]
Listen = 0.0.0.0:443
SNI = default
Upstream = 127.0.0.1:4430

它读取 ClientHello SNI,按 Listen + SNIListen + default 选择 Upstream,然后透明转发字节。它不终止 TLS,不安装 QRE 会话,也不参与 REALITY 验证。

同端口多租户

同一个 Listen 可以服务多个 profile。规则是:

  • 每个租户仍然有自己的 /etc/qre/dev/<profile>.conf
  • sibling server profile 可以共享 ListenServerNameDest
  • 每个 profile 保留自己的 DeviceAddress、hooks、routes 和 RealityShortIds
  • 多个 server profile 也可以共享 Listen,但使用不同 ServerName
  • 分流键是 ListenServerName 和认证后的 short id。

这让一个公网端口可以同时承载多个 QRE 设备或租户。