- C 91%
- CMake 5.5%
- Makefile 3.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- config: drop Kconfig; all runtime settings now live in SPIFFS config.yaml (renamed from server.yaml: wifi/realm/tls/auth/masquerade/led); ship a sanitized config.yaml.example and gitignore the real config.yaml/certs - tls: remove compiled-in embedded_certs.h; TLS cert/key are provisioned via SPIFFS only (self-signed via upstream `hysteria cert`), documented in README - udp: Hysteria2 UDP relay over QUIC unreliable datagrams (UDPMessage codec + fragmentation/reassembly, per-session upstream port, pre-auth silent discard, idle-session release); also start the QUIC server in direct (non-realm) mode - realm: robust registration (drop futile DELETE on 409, wait out TTL), heartbeat sequence counter, graceful DELETE deregister on Linux SIGINT/SIGTERM - sse: stream events via esp_tls directly; IPv6 STUN via esp_netif_get_all_ip6 - led: WS2812 status indicator (red/green/blue states + yellow/white pulses), low brightness, fully runtime-configurable (no recompile) - build: externalize picoquic ESP32+ECDSA-TLS changes to patches/picoquic-esp32.patch with idempotent auto-apply (CMake+Makefile) and .gitmodules ignore=dirty; compile-time debug switch via idf.py -DCHYSTERIA_DEBUG_LOG=1 / make DEBUG=1 - cleanup: remove dead code (cert_pem_data/key_pem_data, realm_bytes_to_hex, hy2_quic_server_stop, in-function includes, deprecated esp_netif_next), redact secrets in config dump - tests: UDPMessage codec/fragmentation + config led-parse cases - docs: README versions/deps table, Hysteria-compatibility section, cert generation + provisioning, LED and UDP usage |
||
| deps | ||
| examples/esp32_server | ||
| include | ||
| patches | ||
| src | ||
| test | ||
| .gitignore | ||
| .gitmodules | ||
| CMakeLists.txt | ||
| Makefile | ||
| README.md | ||
chysteria
chysteria 是用 C 语言实现的轻量级 Hysteria 2 Server,专为资源受限的嵌入式设备(如 ESP32、OpenWrt 路由器)及 Linux 边缘服务器设计。
支持与官方 Hysteria 2 客户端正常通信,支持 Realm NAT 穿透 / Rendezvous 打洞 与 TCP / UDP 代理流量双向转发。
🌟 特性
- 轻量紧凑:经过
-Os、LTO 及链接期死代码消除优化,Linux 二进制约 460KB;ESP32-C6(无 PSRAM)应用固件约 1.3MB,常驻堆余量 ~220KB。 - Realm NAT 穿透:
- 支持
realm://token@host/id格式的监听地址。 - RFC 5389 STUN 公网地址探测(IPv4 / IPv6)。
- 双栈本地地址枚举:Linux 走
getifaddrs,ESP32 走esp_netif_get_all_ip6,过滤 link-local / loopback。 HYRLMv1打洞握手(SHA256 混淆掩码 + 对称 NAT 端口预测)。- SSE 长连接接收
punch事件后异步打洞。
- 支持
- 标准 Hysteria 2 协议:
- 基于
picoquic实现 TLS 1.3 + QUIC 传输(ESP32 使用内置mbedtls后端)。 - HTTP/3 认证握手(
233 HyOK/ QPACK)。 0x401TCP Request 解析与非阻塞双向转发。- UDP 中继:基于 QUIC 不可靠数据报(DATAGRAM),按规范封装
UDPMessage([u32 SessionID][u16 PacketID][u8 FragID][u8 FragCount][varint addrLen][addr][payload]), 含分片/重组、按 SessionID 分配独立上游 UDP 端口、鉴权前静默丢弃、空闲超时释放。
- 基于
- Uber Zap 风格结构化日志:对齐官方 Go 版本格式(
ISO8601\tLEVEL\tmsg\t{json})。 - 运行期配置(无需重编译):全部经 SPIFFS 的
config.yaml+ 证书文件加载;无 Kconfig、无内嵌证书,改配置或换证书只需storage-flash+ 重启。证书请用上游hysteria cert自行生成(见下)。 - 双模跨平台构建:
Makefile/CMake(Linux)与 ESP-IDF 组件(idf.py)。
🧩 环境与依赖版本
当前开发与验证所用的版本(构建产物与行为以这些版本为准):
| 组件 | 版本 / commit | 固定方式 | 说明 |
|---|---|---|---|
| ESP-IDF | v5.5.2(target esp32c6) |
工具链 eim/activate_idf_v5.5.2.sh |
components/ + sdkconfig 均基于此版本 |
| Hysteria 协议 | Hysteria 2(内部 "v4",QUIC+HTTP/3) | 依 PROTOCOL.md 实现 |
兼容官方 hysteria 客户端 |
| Hysteria 参考实现 | HyNetworks/hysteria v2.12.2(619a6f8) |
验证基准 | 协议线格式 / realm(extras/realm) 逐项比对对象 |
| picoquic | private-octopus/picoquic 534b2953(draft-16-final-5544,2026-08-29) |
git submodule(gitlink) | 加本地补丁 patches/picoquic-esp32.patch(同基线) |
| picotls | h2o/picotls f07f1c8(2026-07-15) |
git submodule(gitlink) | 未改动 |
| led_strip | espressif/led_strip 2.5.5 |
main/idf_component.yml 约束 ~2.5 + dependencies.lock |
ESP32 状态指示灯驱动(RMT/WS2812) |
复现要点:
- 子模块 commit 由 gitlink 固定;
deps/picoquic的改动以补丁形式维护、构建时自动 apply(见「picoquic 补丁工作流」)。 - 托管组件精确版本由
examples/esp32_server/dependencies.lock锁定。 - Linux 端用系统
gcc/cmake/libssl-dev(本仓库验证于 gcc 14.2 / OpenSSL 3.5 / cmake 3.30)。
📁 目录结构
chysteria/
├── CMakeLists.txt # 双模构建:Linux 可执行 + ESP-IDF 组件(并自动 apply picoquic 补丁)
├── Makefile # 原生 Linux 构建(自动初始化子模块、编译 QUIC/TLS 依赖、apply 补丁)
├── .gitmodules # picoquic / picotls 子模块(picoquic 设 ignore=dirty)
├── patches/
│ └── picoquic-esp32.patch# picoquic 的 ESP32/mbedtls 移植 + ECDSA TLS 签名修复(构建时自动 apply)
├── include/ # 头文件 (STUN / 打洞 / 协议 / QUIC / 配置 / 日志)
├── src/ # 核心源码
│ ├── main.c # Linux CLI 入口、信号处理、优雅注销
│ ├── hysteria_quic.c # QUIC/HTTP3 事件循环与 TCP 中继
│ ├── realm_manager.c # STUN 探测、Rendezvous 注册/心跳/注销、SSE、双栈打洞
│ ├── realm_http.c # Rendezvous REST / SSE 客户端(ESP 用 esp_tls,Linux 用 OpenSSL)
│ ├── realm_punch.c # HYRLMv1 打洞包加解密
│ ├── hysteria_proto.c # QUIC Varint 与 0x401 编解码
│ ├── hysteria_config.c # config.yaml 解析
│ └── hysteria_log.c # Zap 风格日志
├── examples/
│ └── esp32_server/ # 完整 ESP-IDF 示例工程(app_main、SPIFFS 配置、分区表、config.yaml.example)
├── deps/ # 子模块依赖 (picoquic, picotls)
└── test/ # 单元与集成测试
🛠️ Linux 构建
依赖
gcc、make、cmake、libssl-dev、pkg-config、git。
sudo apt-get install -y build-essential cmake libssl-dev pkg-config git
使用 Makefile(推荐)
自动初始化子模块、编译 picotls/picoquic、并给 picoquic 打 patches/picoquic-esp32.patch:
make -j$(nproc) # 产出 build/chysteria_server + 测试
make test # 运行单元 + 集成测试
make DEBUG=1 # 调试构建:启用 HY_VERBOSE_DEBUG 详细日志
make clean # 清理产物
make clean-deps # 额外清理 picotls/picoquic 构建
使用 CMake
cmake -S . -B build && cmake --build build -j
ctest --test-dir build --output-on-failure # UnitTests / IntegrationTests
picoquic 补丁工作流
本仓库对 deps/picoquic 的改动以补丁形式维护,源码树保持干净:
- 补丁:
patches/picoquic-esp32.patch(基线 commit 见文件头)。 - 构建时自动、幂等地 apply(CMake configure 阶段与
make均检测实际 git 状态)。 - 手动:
make patches(应用) /make unpatches(回退)。 - 与上游同步:
make unpatches && git -C deps/picoquic checkout -- . && git submodule update后重新构建即自动再 apply。 - 因此
git status在 apply 后可能显示deps/picoquic修改;子模块已配置ignore = dirty,主仓git status不会因此被污染。
🚀 使用指南(Linux)
配置示例 config.yaml
listen: realm://your_token@realm.example.com/your_realm_id # 或 host:port 直连模式
tls:
cert: /path/to/server.crt
key: /path/to/server.key
auth:
type: password
password: your_password
masquerade:
type: proxy
proxy:
url: https://www.example.com
rewriteHost: true
led: # 可选,仅 ESP32 状态指示灯用到
enable: true
gpio: 8
brightness: 2
证书建议为 ECDSA P-256(客户端按 TLS 要求校验证书签名,需 DER 编码的 ECDSA 签名,本仓库已正确处理)。
启动 / 停止
./build/chysteria_server -c config.yaml # 标准启动
./build/chysteria_server -c config.yaml -l debug # 详细日志
# Ctrl-C / SIGTERM:会向 Rendezvous 发送 DELETE 主动注销,立即释放 realm 槽位
🔌 ESP32 / ESP-IDF
完整示例工程位于 examples/esp32_server/(默认目标 esp32c6,USB-Serial/JTAG)。本工程可直接构建烧录;亦可将仓库根目录作为 components/chysteria 引入你自己的 ESP-IDF 工程。
构建与烧录
source $IDF_PATH/export # 或使用 eim activate <ver>
cd examples/esp32_server
idf.py set-target esp32c6
idf.py build
idf.py -p /dev/ttyACM0 flash monitor # 首次或配置变更时可加:
idf.py -p /dev/ttyACM0 storage-flash # 仅烧写 SPIFFS(config.yaml / 证书)
证书与配置准备(首次使用,仓库不含真实凭据/私钥)
config_data/ 里的 config.yaml、server.crt、server.key 均被 gitignore,需要你自备:
cd examples/esp32_server/config_data
# 1) 用上游 hysteria(v2.12.2)生成自签证书;--host 要与客户端 tls.sni 一致
# (客户端用 tls.insecure 时 SAN 主要满足握手即可)
hysteria cert --host <client-sni> --cert server.crt --key server.key
# 2) 由模板生成本地配置并填写 WiFi/Realm/认证
cp config.yaml.example config.yaml
# 编辑 config.yaml:wifi.ssid/password、listen 的 token/realm id、auth.password
# 3) 一次性把固件 + config.yaml + 证书烧进 SPIFFS(storage 分区打包整个 config_data/)
idf.py -p /dev/ttyACM0 flash
运行期配置(免重编译,唯一配置入口)
启动时挂载 SPIFFS → 读 /spiffs/config.yaml(无 Kconfig,配置全部来自此文件);TLS 证书固定从 /spiffs/server.crt|key 读取(不再内嵌进固件)。改配置或换证书:编辑 config_data/ 下文件 → idf.py -p PORT storage-flash → 重启即生效,无需重新编译。若证书文件缺失,设备会在日志中报错并停止启动 QUIC。
examples/esp32_server/config_data/config.yaml 会被打包进 SPIFFS(分区见 partitions.csv 的 storage)。示例:
wifi:
ssid: "YOUR_SSID"
password: "YOUR_PASS"
listen: realm://<token>@<rendezvous.example.com>/<realm_id> # 或 host:port 直连
# 自定义 STUN / 本地端口用查询参数:realm://<token>@<host>/<id>?stun=<stun.example.com:3478>&lport=4433
tls:
cert: /spiffs/server.crt
key: /spiffs/server.key
auth:
type: password
password: "your_password"
led: # 板载 WS2812 状态指示灯(可选)
enable: true
gpio: 8
brightness: 2 # 百分比 1-100,越小越暗
状态指示灯(WS2812 RGB)
config.yaml 的 led: 段配置(全运行期,改完 idf.py -p PORT storage-flash + 重启即生效,不重编译):
| 表现 | 含义 |
|---|---|
| 红(常亮暗) | realm 未连接 |
| 绿(常亮暗) | realm 已连接/注册 |
| 蓝(常亮暗) | 有代理客户端已连接 |
| 黄(短脉冲) | realm 心跳 |
| 白(短脉冲) | 客户端请求(TCP/UDP) |
enable: false 可整体关闭;gpio 默认 8(ESP32-C6-DevKitC-1 板载 RGB),brightness 默认 2%。无 led: 段时用内置默认(enable、GPIO8、2%)。
构建/sdkconfig 相关项
- 调试构建(编译期详细日志:IPv6 ping 自检、原始 SSE dump、周期 free-heap 监视):已无 Kconfig 项,用
idf.py -DCHYSTERIA_DEBUG_LOG=1 build开启(默认 release)。 - 网络/系统(
sdkconfig.defaults固定):CONFIG_LWIP_IPV6=y+IPV6_AUTOCONFIG+IPV6_DHCP6(Realm 需 Global IPv6,app_main会等待 SLAAC 分配);CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384、CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=24576(打洞/TLS 握手栈需求)。 - 运行期可变的 WiFi/Realm/认证/证书/LED 全部走
config.yaml,不再是编译期配置。
低内存说明
ESP32-C6(单核 + LP core、无 PSRAM)下按 low-profile 运行:QUIC 连接数与流数、中继缓冲均已收敛;TLS 走 mbedtls,证书链走系统 esp_crt_bundle。
🩺 Realm 运维行为(常见疑问)
- 启动时反复
409 realm_taken:Rendezvous 端同一realm_id为单一槽位。若上一个实例是崩溃/重刷断电退出的(未能注销),其 session 会在服务端存活至 TTL(默认 60s) 自然过期;期间新实例每几秒重试注册 → 连续 409,直到旧 session 过期后成功。属正常自愈。 DELETE返回 401:注销需携带当前 session token(而非配置里的 auth token)。因此启动早于旧 session 过期时无法主动抢占,只能等 TTL。- Linux 正常
Ctrl-C/SIGTERM会主动注销,立即释放槽位,避免下一次启动吃 409。ESP32 的重启多来自 reflash/掉电(无优雅钩子),仍走 TTL 等待路径。 - 多实例:并发运行的服务端应使用不同
realm_id(或不同 token),否则会互相抢占同一槽位而彼此 409。
🧭 与原版 Hysteria 的兼容性 / 差异
chysteria 是独立的 C 实现,协议线格式与官方一致(已逐项比对 HyNetworks/hysteria 的 PROTOCOL.md 与 extras/realm):
协议层(一致)
- 认证:
POST /auth+Hysteria-Auth,成功响应 HTTP 状态233 HyOK。 - TCP:
0x401TCPRequest / TCPResponse。 - UDP:QUIC DATAGRAM 上的
UDPMessage,分片FragID/FragCount与官方internal/frag语义一致。 - Realm 打洞:
HYRLMv1\0魔数 +SHA256(obfsKey+salt)XOR 掩码 + 对称 NAT 端口候选,与extras/realm一致。 - Realm HTTP API:
POST /v1/{id}(注册,用 auth token)、/v1/{id}/heartbeat、/v1/{id}/connects/{nonce}、GET /v1/{id}/events(SSE)、DELETE /v1/{id}(注销);除注册外均用Authorization: Bearer <session_id>。 - Realm URL scheme:
realm://=TLS、realm+http://=明文,与官方一致。
配置层(子集,命名有差异)
listen支持realm://token@host/id或host:port(直连)。- chysteria 读:
wifi:、tls:(cert/key/sniGuard)、auth.type=password、masquerade.proxy、顶层标量stun:。 - 不支持原版的:
realm:结构块(stunServers列表 /stunTimeout/punchTimeout/heartbeatInterval/ipMode/insecure/portMapping)、obfs(salamander/gecko)、acme、bandwidth、resolver、sniff、acl、trafficStats、masquerade.file/string、auth fast-pattern。原版配置迁移时需按上述字段名调整。
UDP 大数据报说明
- 单个数据报(≤~MTU)与 ≤2 分片的中继已在官方
hysteria client端到端验证通过;更大的多分片(>2 datagram)消息在本机回环测试中连原版 server↔client 亦无法送达(quic-go 在回环/该 MTU 下的发送行为),属环境/客户端特性,非服务端逻辑差异。
✅ 测试
make test # Linux:单元(STUN / 打洞编解码 / 协议)+ 集成(配置解析 / STUN / 回环打洞 / TCP 转发 / UDPMessage 编解码与分片)
ESP 侧以真机 + 官方 hysteria client 做端到端联通验证(含 TCP 与 UDP 中继)。
📄 开源许可
MIT License