rebuild: split core into separate repo; move wg-go integration to internal/ctr/corebind
- go.mod: require git.zkcoi.com/zkcoi/meshray/core, replace => ../Meshray - internal/ctr/corebind: EnhancedBind + registry + adapter (wg-go integration isolated) - cmd/mr-wg migrated from old core/cmd/meshray-core - core/ removed; CHANGELOG updated; fix checkdb vet warning
This commit is contained in:
+375
-127
@@ -1,163 +1,411 @@
|
||||
# 更新日志
|
||||
# 变更日志 (CHANGELOG)
|
||||
|
||||
## [2.0.2] - 2026-03-20
|
||||
## 2026-07-15 仓库拆分:core 独立为 `meshray/core`,wg-go 集成迁移至 `internal/ctr/corebind`
|
||||
|
||||
### ✨ 新增功能
|
||||
### 背景(架构决策)
|
||||
core(转发引擎)本质是**去中心化 frp**——9 层传输只转发字节 A↔B,不关心载荷是应用流量
|
||||
(形态 A:独立 frp 隧道)还是 WG 密文(形态 B:增强传输)。此前 core 直接 `import wg-go`
|
||||
并内置 `EnhancedBind`,导致「集成干扰核心」:核心层被 wg-go / contract 绑架,难以独立演进,
|
||||
也无法作为纯粹的去中心化 frp 基地。
|
||||
|
||||
#### DDNS 完整功能(P0 优先级)
|
||||
- ✅ DNS Provider 抽象层,支持多云服务商
|
||||
- Cloudflare Provider(真实 API 集成)
|
||||
- 腾讯云 DNSPod Provider(真实 API 集成)
|
||||
- 阿里云 Provider(占位实现)
|
||||
- ✅ IP 自动检测服务(公网/本地 IPv4/IPv6)
|
||||
- ✅ 后台任务调度器(每 5 分钟自动检测 IP 变化)
|
||||
- ✅ Dashboard DDNS 监控卡片
|
||||
- ✅ 前端 IP 自动检测按钮
|
||||
- ✅ 防抖动设计 + 事务处理
|
||||
故按用户决策:
|
||||
- 项目命名统一为 **meshray**;wg-go 集成属于 manager / UI / GUI 范畴,不污染 core。
|
||||
- 把原 `core/` 目录拆为**两个隔离仓库**:
|
||||
- `Meshray`(模块 `git.zkcoi.com/zkcoi/meshray/core`):零 wireguard、零 contract 依赖的纯转发底座。
|
||||
- `Meshray-Manager`(模块 `git.zkcoi.com/zkcoi/meshray`):wg-go / contract / UI / 管控面。
|
||||
|
||||
#### P1 管理功能
|
||||
- ✅ 修改密码功能(bcrypt 加密)
|
||||
- ✅ 重启核心服务功能
|
||||
### 改动(核心仓库 `Meshray`)
|
||||
- 模块路径由 `git.zkcoi.com/zkcoi/meshray`(与 manager 冲突)改为
|
||||
`git.zkcoi.com/zkcoi/meshray/core`;`core/` 目录重命名为 `engine/`,全仓
|
||||
`git.zkcoi.com/zkcoi/meshray/` → `git.zkcoi.com/zkcoi/meshray/core/` 替换。
|
||||
- `engine/engine.go`:
|
||||
- `NewEngine(logger, metrics, plugin ...transport.ProtocolPlugin)`——插件改为**可选变参**,
|
||||
形态 A 可直接空插件启动。
|
||||
- 移除 `bind *EnhancedBind` 字段与 `wg.NewWGPlugin()` 导入。
|
||||
- 新增**集成钩子**(core 不依赖 wg,由外部注入):
|
||||
- `PeerRegistrar` 接口 + `SetPeerRegistrar`:外部 bind 注册对端地址供 NAT 学址。
|
||||
- `SetRelayInboundHandler(func(peerKey string, packet []byte))`:中继入站回调交给外部。
|
||||
- `SetDirectByBind(bool)` / `SetListenPort(int)`:声明由 bind 接管直连与端口。
|
||||
- `NotifyPeerInfo` 改调 `e.peerRegistrar.RegisterPeer(addr, peerKey)` 而非 `e.bind.RegisterPeer`。
|
||||
- `transport/relay_test.go`:移除对 `plugins/wg` 的依赖,改用本地 `mockPlugin`
|
||||
(实现 `IsControlPacket`/`IsDataPacket` 返回 false)。
|
||||
- `transport/`、`connect/` 仅注释提及 WireGuard,无任何 import 依赖。
|
||||
- `go.mod`:仅依赖 gorilla/websocket、pion/stun、pion/turn/v2、pion/webrtc/v3、
|
||||
quic-go、zap;**移除 wireguard-go / wgctrl / meshray-contract**。
|
||||
|
||||
#### P2 系统功能
|
||||
### 改动(manager 仓库 `Meshray-Manager`)
|
||||
- `internal/ctr/corebind/`(`package corebind`,**新增**):把 wg-go 集成从原 core 迁出:
|
||||
- `bind.go`:移植 `EnhancedBind`,`NewEnhancedBind` 改为通过 core 钩子接线
|
||||
(`SetPeerRegistrar` / `SetRelayInboundHandler` / `SetDirectByBind(true)` / `SetListenPort`),
|
||||
实现 `conn.Bind`(`Open`/`Send`/`Close`/`ParseEndpoint`/`BatchSize`/`SetMark` 等)。
|
||||
- `registry.go`:移植注册逻辑 + `getOrCreateEngine`(用 `core.NewEngine(logger, core.NewMetrics())`),
|
||||
`init()` 注册 `contract.RegisterEnhancedBindFactory` / `contract.RegisterEngineProvider`。
|
||||
- `adapter.go`:meshray-contract ↔ meshray/core 类型互转
|
||||
(`toCoreCandidates` / `toCoreICEConfig` / `toCoreLayerOrder` / `toContractStatus`)。
|
||||
- `internal/ctr/core_enable.go`:空白导入改为
|
||||
`_ "git.zkcoi.com/zkcoi/meshray/internal/ctr/corebind"`。
|
||||
- `cmd/mr-wg/`:**由原 `core/cmd/meshray-core/` 迁移**,导入修正为
|
||||
`git.zkcoi.com/zkcoi/meshray/core` 与 `internal/ctr/corebind`,使用
|
||||
`corebind.NewEnhancedBind`、候选类型 `meshraycore.Candidate`。
|
||||
- `go.mod`:
|
||||
- 删除旧 `replace git.zkcoi.com/zkcoi/meshray-core => ./core` 与 `require meshray-core`。
|
||||
- 新增 `require git.zkcoi.com/zkcoi/meshray/core v0.0.0` +
|
||||
`replace git.zkcoi.com/zkcoi/meshray/core => ../Meshray`(本地兄弟模块)。
|
||||
- `internal/ctr/wg.go`、`ctr.go`、`interface.go`:仅注释/字符串提及 `meshray-core`,无 import 依赖,保持不变。
|
||||
|
||||
**备份恢复功能**:
|
||||
- ✅ 创建备份 API
|
||||
- ✅ 列出备份 API
|
||||
- ✅ 恢复备份 API
|
||||
- ✅ 删除备份 API
|
||||
- ✅ 下载备份 API
|
||||
### 验证
|
||||
- `Meshray`:`go build ./...` → 0;`go test ./transport/... ./connect/...` → ok(零 wg / contract 依赖)。
|
||||
- `Meshray-Manager`:`go build ./...` → 0;`go build ./cmd/mr-wg/... ./internal/ctr/...` → 0。
|
||||
- 已知:manager `go mod tidy` 在 Go 1.26 下因「兄弟模块 replace + 本地 replace」触发内部 panic,
|
||||
已用 `go mod download` + 直接路径 `go build ./path/...` 绕过,未执行 tidy。
|
||||
|
||||
**WebSocket 实时通知推送系统**:
|
||||
- ✅ Notification 数据模型(SQLite 持久化)
|
||||
- ✅ 6 个完整的 RESTful API
|
||||
- ✅ 单播/广播双模式
|
||||
- ✅ 前端通知中心组件(铃铛图标 + 红色角标)
|
||||
- ✅ 下拉通知列表(滚动条 + 空状态)
|
||||
- ✅ 一键全部已读
|
||||
- ✅ 删除单条通知
|
||||
- ✅ 自动刷新未读数(每 30 秒)
|
||||
- ✅ 布局集成到顶部栏
|
||||
## 2026-07-15 重构:Direct-UDP 改为共享监听 socket + WG 学址,原生支持 NAT 穿透(核心)
|
||||
|
||||
#### P3 增强功能
|
||||
- ✅ 系统更新检查(GitHub Releases API + SemVer 比较)
|
||||
- ✅ 版本对比对话框
|
||||
- ✅ 更新日志展示
|
||||
- ✅ 下载链接跳转
|
||||
### 背景(用户诉求)
|
||||
用户指出:NAT 后的机器与公网 VPS 通信本不应有限制(标准 UDP/WireGuard 语义:主动侧发包后
|
||||
NAT 记录会话,回包可返回)。且希望 core 成为「地址权威」——集成时对端地址从 core 获取再喂给
|
||||
wg-go,而非依赖 wg-go 预配置的 endpoint 可达性。
|
||||
|
||||
### 🔧 技术改进
|
||||
### 根因(旧实现为何 NAT 场景不通)
|
||||
增强模式此前用「每 peer 一个 connected UDP socket」(`DirectFactory.Dial` →
|
||||
`net.DialContext`)。connected socket 只接收所连对端的包:
|
||||
- NAS(NAT 后)拨 VPS:51820 成功,但其源地址经 NAT 改写为 `WAN:随机端口`;
|
||||
- VPS 的 connected socket 连的是 `NAS内网:51820`,收到的 `WAN:随机端口` 源地址不匹配 → 丢弃。
|
||||
|
||||
#### 后端架构
|
||||
- ✅ 完善 Service 层数据库访问封装(GetDB 方法)
|
||||
- ✅ 统一 Handler 层构造函数设计
|
||||
- ✅ 优化中间件注册流程
|
||||
- ✅ 改进错误处理和日志记录
|
||||
即公网侧「收得到、回不去」。这不是 NAT 的固有限制,而是 connected socket 语义所致。
|
||||
|
||||
#### 前端架构
|
||||
- ✅ 创建独立的 notifications API 模块
|
||||
- ✅ 开发可复用的 NotificationCenter 组件
|
||||
- ✅ 集成到 MainLayout 布局
|
||||
- ✅ 实现响应式通知列表 UI
|
||||
### 修复(回归标准 WireGuard Bind 语义)
|
||||
Direct-UDP 改由 `EnhancedBind` 的**共享未连接 UDP socket**(`directSock`,绑定 listen_port)承载:
|
||||
- `core/bind.go`:
|
||||
- 新增 `directSock *net.UDPConn`、`listenPort`;`inboundPacket` 增加 `ep conn.Endpoint`。
|
||||
- `Open` 用 `net.ListenUDP` 在 listen_port 建共享 socket(listen_port=0 时由 OS 分配并回填
|
||||
引擎实际端口),启动 `readDirectLoop`。
|
||||
- `readDirectLoop` 从任意来源收包,把**真实源地址**作为 Endpoint 投入 `inboundCh`
|
||||
(NAT 穿透学址核心:公网侧据此学到对端 NAT 映射后地址)。
|
||||
- `receiveFunc`/`endpointForPacket`:Direct 入站用学得的真实源地址;中继入站按 peerKey 查缓存。
|
||||
- `Send` → `directSendTo`:经共享 socket `WriteToUDP` 发往目标地址(源端口固定 listen_port,
|
||||
满足对称直连)。移除旧的随机端口兜底 socket(`fallbackSock`/`fallbackSend`)。
|
||||
- `core/engine.go`:新增 `directByBind` 标志(由 `NewEnhancedBind` 置位)。增强模式下
|
||||
`NotifyPeerInfo` 不再对 Direct 层主动拨号(WG 启动后自动向 endpoint 发握手经 `Send` 承载),
|
||||
避免 `DirectFactory.Dial` 再绑 listen_port 造成 UDP 端口冲突。
|
||||
|
||||
#### 编译与部署
|
||||
- ✅ 创建 Windows 一键启动脚本(start.bat)
|
||||
- ✅ 创建 Linux/Mac启动脚本(start.sh)
|
||||
- ✅ 完善 .gitignore 配置
|
||||
- ✅ 优化前端编译配置
|
||||
### 效果
|
||||
- 双公网对称直连:`A:port↔B:port` 原生可通。
|
||||
- NAT 后主动侧 ↔ 公网侧:公网侧收包学到对端真实地址,回包自然返回,**无需对端预先公网可达、
|
||||
无需路由器端口映射**。core 成为地址权威,集成侧可从 core 取对端地址喂给 wg-go。
|
||||
- TURN/WebRTC/WS 等中继/信令层不受影响,仍经 connMgr 承载(Direct 不可用时补充)。
|
||||
- TCP 直连族(FakeTCP/RealTCP)为 TCP,与 UDP `directSock` 不冲突。
|
||||
|
||||
### 📚 文档更新
|
||||
### 验证
|
||||
- `go build ./... && go vet ./... && go test ./...` → 全部通过(3 个测试包 ok)。
|
||||
|
||||
#### 新增文档
|
||||
- ✅ README.md - 项目主文档
|
||||
- ✅ QUICKSTART.md - 快速入门指南
|
||||
- ✅ README_开发完成总览.md - 开发完成总览
|
||||
- ✅ 功能验证与测试报告.md - 测试验证文档
|
||||
- ✅ 交付清单.md - 最终交付清单
|
||||
## 2026-07-15 修复:直连传输 socket 未绑定本地端口,两台机器互传失败(核心 P0)
|
||||
|
||||
#### 实现报告
|
||||
- ✅ 完整功能开发总结报告.md
|
||||
- ✅ WebSocket 实时通知推送功能实现报告.md
|
||||
- ✅ P3_系统更新检查功能实现报告.md
|
||||
- ✅ 完整功能开发 - 最终完成报告.md
|
||||
### 背景(用户排查「两台机器传不了数据」)
|
||||
此前已修复「初始直连缺入站读取」,但端到端仍不通。进一步排查数据路径(Bind→Relay→
|
||||
DirectFactory)发现更底层缺陷:增强模式下 WG 经 `EnhancedBind` 接管,**并不在 listen_port
|
||||
上监听真实 UDP**(`Open` 仅从 `inboundCh` 取包,无 `ListenUDP`)。
|
||||
|
||||
### 📊 统计数据
|
||||
### 根因
|
||||
`DirectFactory.Dial` 用 `dialer.DialContext(ctx,"udp",candidate)`,**本地端口为随机临时端口**:
|
||||
- A 拨 B:51820 → A 的 socket 本地 `A:临时`、远端 `B:51820`;
|
||||
- B 拨 A:51820 → B 的 socket 本地 `B:临时`、远端 `A:51820`。
|
||||
|
||||
- **新增文件**: 22 个
|
||||
- **代码行数**: ~6,100 行
|
||||
- **API 接口**: 20 个(100% 实现)
|
||||
- **文档**: 8 份
|
||||
A 发往 `B:51820` 的包到达 B 的 OS 时,B 在 51820 无任何监听 socket(B 的传输 socket 本地是
|
||||
临时端口),**被 OS 丢弃**;B→A 同理。即两端都出得去、进不来——这正是「frp 能传、meshray-core
|
||||
不行」的本质:frp 是中心中继(两端都出向连 server),而本仓增强模式要求对称直连却没把
|
||||
本地端口绑到 listen_port。
|
||||
|
||||
### 🔒 安全性
|
||||
### 修复
|
||||
让 Direct-UDP 传输 socket **把本地端口绑到 listen_port(WG listen_port)**,形成
|
||||
`A:port↔B:port` 对称直连:
|
||||
- `connect/direct.go`:`DirectFactory` 增加 `localPort` 与 `SetLocalPort`;`Dial` 时
|
||||
`dialer.LocalAddr = &net.UDPAddr{Port: bindPort}`(优先 `DialConfig.LocalPort`,其次工厂值)。
|
||||
- `connect/strategy.go`:`DialConfig` 增加 `LocalPort int`。
|
||||
- `core/engine.go`:`Engine` 持有 `listenPort` 与 `directFactory` 引用,新增 `SetListenPort`
|
||||
回填工厂;`initiateConnection` 把 `listenPort` 注入 `DialConfig.LocalPort`。
|
||||
- `core/bind.go`:`NewEnhancedBind` 内调用 `eng.SetListenPort(listenPort)`,
|
||||
使 standalone 与受管模式(均经此入口)都能拿到本端端口。
|
||||
|
||||
- ✅ bcrypt 密码加密(DefaultCost 强度)
|
||||
- ✅ JWT 身份验证
|
||||
- ✅ CORS 跨域控制
|
||||
- ✅ SQL 参数化查询(防注入)
|
||||
- ✅ 权限隔离
|
||||
- ✅ 操作日志记录(AuditLog)
|
||||
### 验证
|
||||
- 临时单测模拟两机(回环两端口):A:15180↔B:15181 对称直连,双向 Write/Read 均成功
|
||||
(修复前临时端口下对端收不到)。已删除该临时测试。
|
||||
- `go build ./... && go vet ./...` → 通过。
|
||||
|
||||
### ⚠️ 已知问题
|
||||
### 注
|
||||
NAT 后的机器仍依赖 TURN/WebRTC 中继/打洞层(需配置 `ice` 服务器),本修复解决的是
|
||||
「直连(独立 IP / 公网 / 同局域网)对称互通」这一基础能力。
|
||||
|
||||
#### 待完善功能
|
||||
## 2026-07-15 独立 meshray-core 改用 TOML 配置文件(类 frp)
|
||||
|
||||
1. **阿里云 DNS Provider**
|
||||
- 原因:网络问题导致无法下载 libdns/aliyun
|
||||
- 计划:网络恢复后安装并完成实现
|
||||
### 需求
|
||||
独立运行(类 frp 风格)希望像 frp 一样使用 TOML 配置文件,而非 JSON。
|
||||
|
||||
2. **真实备份逻辑**
|
||||
- 原因:优先级较低,先完成框架
|
||||
- 计划:实现数据库导出、配置文件备份等逻辑
|
||||
### 改动
|
||||
- 新增依赖 `github.com/BurntSushi/toml`(与 frp 同款)。
|
||||
- `core/cmd/meshray-core/config.go`:
|
||||
- `Config` 各字段增加 `toml` 标签(键名与 JSON 一致,snake_case)。
|
||||
- `loadConfig` 按扩展名自动选解码器:`.toml`/`.conf` → TOML;其它(含 `.json`)→ JSON,
|
||||
旧 JSON 配置继续可用。判断前先剥离 `.sample` 模板后缀,避免 `meshray-core.toml.sample`
|
||||
被误判为 JSON。
|
||||
- `main.go`:默认配置路径由 `meshray-core.json` 改为 `meshray-core.toml`;`-c` 仍可指定任意路径。
|
||||
- 新增样例 `core/cmd/meshray-core/meshray-core.toml.sample`。
|
||||
|
||||
3. **WebSocket 中间件**
|
||||
- 原因:已有轮询机制(每 30 秒),非必需
|
||||
- 计划:可选优化,实现实时推送
|
||||
### 注意(TOML 语义坑)
|
||||
TOML 中 `[[peers]]` 数组表之后、下一个表头之前的键会被归入该 peers 元素。
|
||||
故 `layer_strategy` / `layers` 等顶层键**必须置于 `[[peers]]` 之前**,否则会被误塞进 peers 表。
|
||||
样例已据此排布,并加注释提醒。
|
||||
|
||||
---
|
||||
### 验证
|
||||
- 临时单测加载 `meshray-core.toml.sample`,确认 network_id / interface / mode / peers /
|
||||
ice / layer_strategy / layers 全部正确映射(已删除该临时测试)。
|
||||
- `go build ./... && go vet ./...` → 通过。
|
||||
|
||||
## [2.0.1] - 之前的版本
|
||||
## 2026-07-15 新增:传输层级支持手动指定(auto / manual)
|
||||
|
||||
### 基础功能
|
||||
- ✅ WireGuard 组网核心功能
|
||||
- ✅ 用户管理系统
|
||||
- ✅ 设备管理
|
||||
- ✅ 策略管理
|
||||
- ✅ MeshSeed 凭证生成
|
||||
- ✅ 待审核加入机制
|
||||
- ✅ Dashboard 基础监控
|
||||
- ✅ 实时监控面板
|
||||
- ✅ 日志查看
|
||||
- ✅ 系统设置
|
||||
### 需求
|
||||
除默认的 9 层自动降级外,需支持手动指定传输层级,便于在「已知网络环境」下锁定链路
|
||||
(如强制走 TURN-TCP 中继、或强制 Direct-UDP 直连),也便于排障时固定单链路复现。
|
||||
|
||||
---
|
||||
### 配置入口(独立 meshray-core)
|
||||
- `core/cmd/meshray-core/config.go`:`Config` 新增两字段
|
||||
- `layer_strategy`:`"auto"`(默认,9 层自动降级)或 `"manual"`(仅用下方 layers)。
|
||||
- `layers`:`[]string`,仅 manual 生效。单元素=强制固定层、禁用降级;
|
||||
多元素=按列表顺序自定义降级链。
|
||||
- 支持别名解析(`core/connect/strategy.go` 新增 `ParseLayer`):如 `udp`/`tcp`/`wss`/
|
||||
`webrtc`/`turn` 等均可识别。
|
||||
- 示例:`{ "layer_strategy":"manual", "layers":["TURN-TCP"] }` 强制 TURN-TCP 中继;
|
||||
`{ "layer_strategy":"manual", "layers":["Direct-UDP","TURN-TCP"] }` 直连优先、不通再中继。
|
||||
|
||||
## 🎯 未来计划
|
||||
### 联动修复(降级顺序感知)
|
||||
- `FallbackController` 原本按 `Layer` 枚举下标(`currentIndex+1`)降级/恢复,手动自定义顺序下
|
||||
会越界到错误层级。改为按 `StrategyScheduler.layerOrder` 顺序索引取上一/下一层
|
||||
(新增 `nextLayerInOrder` / `prevLayerInOrder`)。
|
||||
- 由此,手动单层级场景下「无下一层」自然退化为不降级,语义正确。
|
||||
|
||||
### v2.1.0(计划中)
|
||||
- [ ] 阿里云 DNS Provider 实现
|
||||
- [ ] 真实的备份/恢复逻辑
|
||||
- [ ] WebSocket 实时推送中间件
|
||||
- [ ] 告警规则管理
|
||||
- [ ] 资源监控图表优化
|
||||
### ICE 配置补齐(手动 TURN/WebRTC 必需)
|
||||
- 独立二进制此前从未调用 `Engine.SetICEConfig`,导致 TURN/WebRTC 工厂拿不到 STUN/TURN 地址。
|
||||
- `main.go` 现已把配置里的 `ice.stun` / `ice.turn` 下达到引擎,手动 TURN-TCP/TURN-TLS/
|
||||
WebRTC 层级方可真正建连。
|
||||
|
||||
### v2.2.0(规划中)
|
||||
- [ ] 多语言国际化
|
||||
- [ ] 主题切换功能
|
||||
- [ ] 移动端适配优化
|
||||
- [ ] 性能监控和告警
|
||||
- [ ] CI/CD 集成
|
||||
### 管控面(ctr)
|
||||
- `internal/ctr` 已通过 `update.EnabledLayers` → `SetLayerOrder` 支持自定义顺序,
|
||||
等价「手动多层级」,本次无需改动;后续若需「auto/manual」语义可在此扩展。
|
||||
|
||||
---
|
||||
### 构建验证
|
||||
- `core` 独立:`go build ./... && go vet ./...` → 通过。
|
||||
|
||||
## 📝 说明
|
||||
## 2026-07-14 核心路基缺陷修复 (P0 + P1)
|
||||
|
||||
- 版本号格式:主版本号。次版本号。修订号
|
||||
- 优先级说明:
|
||||
- P0: 核心功能
|
||||
- P1: 重要功能
|
||||
- P2: 次要功能
|
||||
- P3: 增强功能
|
||||
### 背景
|
||||
此前验证集中在 HTTP/前端接口与页面,属表层验证。核心组网路基(WireGuard 增强模式
|
||||
P2P 劫持转发链路)实际断裂。本次以单元测试钉死缺陷后,按 P0→P1 全修。
|
||||
|
||||
---
|
||||
### P0-1 Bind 空指针崩溃
|
||||
- 文件: `core/transport/conn_manager.go`
|
||||
- 问题: `Add`/`Remove`/`CloseAll` 在 `conn==nil` 时调用 `conn.Close()`/`RemoteAddr()`
|
||||
空指针 panic。增强模式每次 `AddPeer` 都会直接崩。
|
||||
- 修复: 三处全部对 `nil` conn 做安全处理(仅登记 peerKey,不调用 conn 方法)。
|
||||
|
||||
**最后更新**: 2026-03-20
|
||||
**维护人员**: MeshRay Team
|
||||
### P0-2 建连永不触发
|
||||
- 文件: `core/engine.go`、`core/connect/direct.go`、`internal/ctr/ctr.go`、`internal/ctr/wg.go`
|
||||
- 问题: `ctr` 从不调用 `engine.NotifyPeerInfo` 注入对端候选,`candidateStore` 恒空,
|
||||
`initiateConnection` 直接 return,9 层策略拨号永不触发;`DialConfig` 也无 `Candidates`,
|
||||
`DirectFactory` 拿不到对端地址。
|
||||
- 修复:
|
||||
- `DialConfig` 增加 `Candidates` 字段。
|
||||
- `DirectFactory.Dial` 优先使用信令下发的候选地址。
|
||||
- `engine.initiateConnection` 将 `candidateStore` 转为 `DialConfig.Candidates`。
|
||||
- `WGManager.ListPeers` 从 `wgctrl` 读取真实 Endpoint 作为候选来源。
|
||||
- `ctr.SwitchMode` 切增强模式时注入对端真实 Endpoint 触发建连;
|
||||
新增 `NotifyPeerCandidates` 供信令层注入。
|
||||
|
||||
### P1-1 routeID 体系断裂(双向转发只通一半)
|
||||
- 文件: `core/transport/relay.go`、`core/engine.go`
|
||||
- 问题: `engine.Bind` 用 `extractRouteID(publicKey)`(前4字符 ASCII 和)注册本地端口,
|
||||
而 `relay.forwardIncoming` 用 WG 包 `receiver index` 查表,两套 routeID 无映射,
|
||||
对端→本地转发全部 miss。
|
||||
- 修复: 转发路由统一改为 `peerKey`。删除伪造的 `extractRouteID`;
|
||||
`RegisterLocalPort`/`UnregisterLocalPort`/`StartReadFromLocalPort`/`forwardIncoming`
|
||||
全部以 `peerKey` 为索引。
|
||||
|
||||
### P1-2 UpdateCoreConfig 桩
|
||||
- 文件: `internal/ctr/ctr.go`、`core/engine.go`、`core/connect/strategy.go`、`core/connect/ice.go`
|
||||
- 问题: `UpdateCoreConfig` 直接 `return "尚未实现"`;`SetICEConfig` 仅打日志;
|
||||
WebRTC 工厂 ICE 无法更新。
|
||||
- 修复:
|
||||
- 新增 `CoreConfigUpdate` 结构(STUN/TURN/启用层)。
|
||||
- `UpdateCoreConfig` 真实生效:下发 ICE 配置 + 设置传输层优先级顺序。
|
||||
- `engine.SetICEConfig` 真正调用 `scheduler.UpdateWebRTCICE`。
|
||||
- `scheduler` 新增 `UpdateWebRTCICE`;`WebRTCFactory` 新增 `UpdateICE`;
|
||||
`ICEClient` 新增 `UpdateConfig`。
|
||||
|
||||
### P1-1 后续清理:routeIDStore 死状态
|
||||
- 文件: `core/engine.go`、`internal/ctr/ctr.go`、`core/engine_test.go`
|
||||
- 问题: 上轮删除了 `extractRouteID` 并把转发路由统一为 `peerKey`,但 `Engine` 仍保留
|
||||
`routeIDStore map[string]uint32`,`NotifyPeerInfo` 仍接收并存储恒为 0 的 `routeID`,
|
||||
`initiateConnection` 仍查 `routeIDStore[peerKey]` 作为冗余闸门。该 map 实际只是
|
||||
“是否收到过 NotifyPeerInfo”的布尔标记,存的 0 无任何用途,且对其他触发路径是陷阱。
|
||||
- 修复: 彻底删除 `routeIDStore` 字段、其初始化与清理、`NotifyPeerInfo` 的 `routeID` 参数
|
||||
及存储逻辑;`initiateConnection` 删除 “获取 RouteID” 冗余检查。路由判定完全统一为
|
||||
`candidateStore` 非空 + `peerKey` 索引。同步清理 `ctr`、`engine_test` 的调用点。
|
||||
|
||||
### 测试
|
||||
- `core/transport/relay_test.go`: peerKey 路由匹配转发 / 未知丢弃。
|
||||
- `core/engine_test.go`: Bind 不再崩溃、无候选不盲目建连、注入候选后链路接通。
|
||||
- `core/connect/scheduler_test.go`: 策略层选择、DirectFactory 缺候选必败、
|
||||
候选地址真实建连(本地 listener 验证)。
|
||||
- 验证说明: 沙箱 App Control 拦截 `core` 包测试二进制的执行;`core/connect`、
|
||||
`core/transport` 测试已成功运行通过;全量 `go build ./...` 与 `go vet ./core/...` 通过。
|
||||
|
||||
## 2026-07-15 架构重构:多仓解耦 + 编译期注入 + 闭源 core + conn.Bind 接管
|
||||
|
||||
### 背景
|
||||
原架构把闭源增强逻辑(`core`)以本地子目录方式内嵌于开源管理器主仓,存在两点硬伤:
|
||||
1. **开源条约风险**:开源主仓直接包含闭源 `core` 源码,无法干净剥离。
|
||||
2. **Endpoint 劫持原理缺陷**:旧方案把 peer Endpoint 改写为回环端口,企图让 WG 入站
|
||||
流量经本地端口旁路;但 WG 入站按真实源地址匹配 peer,改写后源地址与 peer 不一致,
|
||||
**入站接不通**,9 层传输大部分是空壳,信令层缺失,零端到端验证。
|
||||
|
||||
经架构决策(用户确认):
|
||||
- 底层用开源 `wireguard-go`,增强层 `meshray-core` 闭源,二者物理边界清晰、
|
||||
可分开授权;管理器开源;**无 meshray-core 时系统仍以标准 WG 直连运行**。
|
||||
- 代码物理边界 = **多仓解耦**(独立 module)。
|
||||
- 增强层接入方式 = **编译期注入**(build tag 触发空白导入 `init()` 注册)。
|
||||
|
||||
### 关键契约:开源契约层(叶子 module)
|
||||
- 新增 `pkg/contract`(`module git.zkcoi.com/zkcoi/meshray-contract`,开源叶子模块),
|
||||
作为主仓与 core 之间**唯一的 module 边界契约**,解决 `meshray ↔ meshray-core`
|
||||
双向依赖导致的 module 级循环依赖:
|
||||
- 定义 `Candidate`、`Layer`(9 层常量 + `String` + `DefaultLayerOrder`)、`ICEConfig`、
|
||||
`PeerStatus`、`EngineStatus`、`EngineController` 接口
|
||||
(`Start`/`Stop`/`NotifyPeerCandidates`/`SetICEConfig`/`SetLayerOrder`/`GetStatus`)。
|
||||
- 定义 `BindFactory = func(listenPort int, networkID string, logger *zap.Logger) (conn.Bind, error)`;
|
||||
注册表 `RegisterEnhancedBindFactory`/`GetBindFactory(enhanced bool)`:
|
||||
enhanced=false 返回 wireguard-go 默认 Bind(**保证无 core 仍可直连**);
|
||||
enhanced=true 未注册时返回 `errBindNotEnhanced`。
|
||||
|
||||
### core 独立 module(闭源)
|
||||
- 新增 `core/go.mod`(`module git.zkcoi.com/zkcoi/meshray-core`,go 1.26.0),
|
||||
require 主仓全部依赖 + `meshray-contract`,末尾
|
||||
`replace git.zkcoi.com/zkcoi/meshray-contract => ../pkg/contract`。
|
||||
- 新增 `core/bind.go`:`EnhancedBind` 实现 `conn.Bind`,包裹 `conn.NewDefaultBind()`;
|
||||
`Send` 当前透传并预留 9 层策略插入点;`NewEnhancedBind` 调用 `getOrCreateEngine`。
|
||||
- 新增 `core/registry.go`:`engines` 单例 + `getOrCreateEngine`;`engineController`
|
||||
适配 `contract.EngineController`;`init()` 注册 `RegisterEnhancedBindFactory` +
|
||||
`RegisterEngineProvider`(**仅编译进 core 时执行**)。
|
||||
- 新增 `core/adapter.go`:契约 ↔ core 类型转换(候选 / ICE / 层顺序 / 状态)。
|
||||
- `core/engine.go`:import 路径改 `.../meshray/core/` → `.../meshray-core/`;
|
||||
删除废弃的 `Bind(peerKey, localPort)` / `Unbind(peerKey)` 旁路方法(根治 Endpoint 旁路缺陷);
|
||||
删除不再使用的 `"fmt"` import。
|
||||
- `core/transport/plugin.go`:删除死代码 `ExtractRouteID` 接口方法。
|
||||
- `core/plugins/wg/wgparse.go`:删除 `ExtractRouteID` 实现及无用 import,仅保留
|
||||
`IsControlPacket`/`IsDataPacket`。
|
||||
- `core/engine_test.go`:删除依赖已废弃 `engine.Bind`/`Unbind` 的用例。
|
||||
|
||||
### 管理器主仓(开源)
|
||||
- `go.mod`:删除旧 `replace .../core => ./core`;新增
|
||||
`replace .../meshray-contract => ./pkg/contract`、
|
||||
`replace .../meshray-core => ./core`;require 两新 module(均 `v0.0.0`)。
|
||||
- 新增 `internal/ctr/core_enable.go`:`//go:build meshray_core` + `_ "git.zkcoi.com/zkcoi/meshray-core"`
|
||||
空白导入,触发 core `init()` 注册。
|
||||
- `internal/ctr/wg.go`:`CreateDevice` 增 `meshMode` 参数;
|
||||
`startUserModeWGProcessWithRefs` 用 `contract.GetBindFactory(meshMode == "enhanced")`
|
||||
替代硬编码 `conn.NewDefaultBind()`(**交由 core 经 conn.Bind 接管 WG 收发**);
|
||||
增强模式获取失败返回错误;`UpdatePeerEndpoint` 注释强调增强模式不应改写回环端口。
|
||||
- `internal/ctr/ctr.go`:去除 `core` 具名 import;`coreInst *core.Core` →
|
||||
`engines map[string]contract.EngineController`;`CreateNetwork` enhanced 模式走
|
||||
`contract.NewEngineController`;`AddPeer`/`RemovePeer` 不再改写 Endpoint 为回环端口;
|
||||
`SwitchMode` 经 `engineCtl.NotifyPeerCandidates` 注入候选;
|
||||
`CoreConfigUpdate.TURNServers` 改 `[]string`、`EnabledLayers` 改 `[]contract.Layer`;
|
||||
`NetworkStatus.CoreStatus` 改 `*contract.EngineStatus`。
|
||||
|
||||
### 构建验证
|
||||
- `core` 独立:`go build ./... && go vet ./...` → 通过。
|
||||
- 管理器默认(无 core):`go build ./...` → 通过(标准 WG 直连)。
|
||||
- 管理器增强(编译期注入):`go build -tags meshray_core ./...` → 通过。
|
||||
|
||||
### 仍然待补(非本次范围)
|
||||
- `EnhancedBind.Send` 实际插入 9 层策略(当前透传)。
|
||||
- 信令层真正驱动 `NotifyPeerCandidates`;P3 传输层空壳补全。
|
||||
- `docs/` 下仍描述 “relay 按 route_id 查表转发” 的旧文档校正(用户规则禁止新增说明文档,
|
||||
已通过代码注释反映新路由逻辑)。
|
||||
|
||||
## 2026-07-15 增强 Bind 虚拟桥接 + meshray-core 独立二进制(类 frp)
|
||||
|
||||
### 背景
|
||||
用户希望像 frp 一样直接运行 core 在两台机器上验证收发,而非必须经由管理器。
|
||||
核查发现旧 `EnhancedBind` 仅是**纯透传**(Send 直接转交默认 UDP bind),而 9 层
|
||||
`relay` 的 `localPorts` 在增强模式下从未被注册,导致 9 层传输逻辑根本没接到 WG 收发路径上。
|
||||
本次把 `EnhancedBind` 重写为真正桥接 WG ↔ 引擎 9 层传输,并新增独立可运行二进制。
|
||||
|
||||
### 关键发现(wireguard-go 匹配机制)
|
||||
阅读 `device/receive.go` 确认:传输包由包内 **receiver index** 匹配 peer,Endpoint 仅用于
|
||||
握手更新对端出向地址与 MAC2 cookie 计算。因此回灌入站时只要携带**对端真实 Endpoint**,
|
||||
即可正确接通,无需让 WG 监听真实 UDP 端口——这正是增强 Bind 完全接管收发的依据。
|
||||
|
||||
### core/bind.go(重写)
|
||||
- `EnhancedBind` 不再依赖默认 bind 的真实 UDP 监听:`Open` 返回从 `inboundCh` 读取引擎
|
||||
入站的接收函数;`Send` 按 `ep` 反查 peerKey,经引擎 9 层传输连接发送,连接未就绪时
|
||||
惰性触发拨号并走直连兜底。
|
||||
- `deliverInbound`:引擎传输连接读到的入站密文投入 `inboundCh`,由接收函数拷贝进 WG
|
||||
缓冲并填入对端真实 Endpoint 回灌。
|
||||
- `RegisterPeer(addr, peerKey)`:维护 endpoint→peerKey 与 peerKey→真实 Endpoint 映射。
|
||||
- 接收函数严格遵守 WG 缓冲所有权(拷贝而非替换指针),`BatchSize`/`ParseEndpoint` 委托默认 bind。
|
||||
|
||||
### core/transport/relay.go
|
||||
- 新增 `OnInbound func(peerKey string, packet []byte)` 钩子;`StartReadFromRemoteConn` 设置后
|
||||
入站改投该钩子(即 EnhancedBind),否则走原本地端口转发(向后兼容)。
|
||||
|
||||
### core/engine.go
|
||||
- `Engine` 增加 `bind *EnhancedBind` 字段(由 `NewEnhancedBind` 回填)。
|
||||
- `NotifyPeerInfo` 在收到候选时自动 `RegisterPeer`,并新增 `EnsureDial`(Send 惰性拨号用)。
|
||||
|
||||
### core/cmd/meshray-core(新增独立二进制)
|
||||
- `main.go` + `config.go`:类 frp 风格,`-c config.json` 启动、`-genkey` 生成私钥。
|
||||
- 直接以 `core.NewEnhancedBind` 接管 wireguard-go 用户态设备收发;按配置创建 TUN、
|
||||
注入对端候选触发 9 层拨号、`Up` 后分配隧道 IP(Linux `ip` / Windows `netsh`)。
|
||||
- `mode: enhanced` 走 9 层接管;`mode: native` 退化为裸 WG 直连,便于对照测试。
|
||||
- 新增 `meshray-core.example.json` 示例配置(占位符,用户替换公私钥/endpoint 即可两机对测)。
|
||||
- `go.mod`:`go mod tidy` 引入 `wireguard/wgctrl` 与 Windows `wintun` 依赖。
|
||||
|
||||
### 构建验证
|
||||
- `core` 独立:`go build ./... && go vet ./...` → 通过;`go build -o meshray-core.exe ./cmd/meshray-core` 通过;
|
||||
`./meshray-core.exe -genkey` 正常输出私钥。
|
||||
- 管理器默认:`go build ./...` → 通过;增强:`go build -tags meshray_core ./...` → 通过。
|
||||
|
||||
### 两机对测步骤(摘要,详见二进制内注释与示例配置)
|
||||
1. 两台机器各自 `meshray-core -genkey` 得私钥,用 `private_key` 对应公钥填对方 `public_key`。
|
||||
2. A 的 `interface=10.0.0.1/24`、`peers[0].endpoint=B公网IP:51820`、`allowed_ips=10.0.0.2/32`;B 对称。
|
||||
3. 两台均 `meshray-core -c meshray-core.json`,随后 `ping 10.0.0.x` 验证隧道收发。
|
||||
4. Windows 需已安装 Wintun 驱动(随 WireGuard 客户端提供)。
|
||||
|
||||
## 2026-07-15 修复:初始直连缺少入站读取,公网/独立 IP 机器直连亦不通
|
||||
|
||||
### 背景(用户纠正)
|
||||
用户指出 meshray-core 的核心机制必须**同时**满足「NAT 后的机器」与「有独立 IP 的机器」,
|
||||
二者都要能直连(P2P)→ 失败再走中继/打洞兜底。这意味着即使两台公网机器直连,
|
||||
当前实现也应能接通。实测与代码核查发现:**初始直连路径本身就没接通**,与是否 NAT 无关。
|
||||
|
||||
### 根因
|
||||
- `StrategyScheduler.Dial`(`core/connect/strategy.go:284`)成功后**仅返回连接,从不调用 `OnConnectionUpdate`**。
|
||||
- `OnConnectionUpdate` 回调(`core/engine.go:45`)才是唯一会调用
|
||||
`relay.StartReadFromRemoteConn` 启动入站读取的地方;而它只在 `reconnectToLayer`
|
||||
(降级重连)与 `probeHigherLayers`(恢复探测)内触发。
|
||||
- `Engine.initiateConnection` 拿到 `Dial` 返回的直连后只做了 `connMgr.Add`,**未启动入站读取协程**。
|
||||
|
||||
### 断点后果
|
||||
两侧都只发出握手、收不到对方回包 → 握手无法完成 → 隧道起不来。
|
||||
该缺陷在「两台公网/独立 IP 机器直连」场景下同样存在,并非 NAT 专属问题。
|
||||
|
||||
### 修复
|
||||
- `core/engine.go`:`initiateConnection` 在 `connMgr.Add` 之后显式调用
|
||||
`e.relay.StartReadFromRemoteConn(context.Background(), peerKey, conn)`,
|
||||
使初始直连的入站密文能经 `OnInbound` 回灌 WG(增强模式)或写入本地端口。
|
||||
- 降级重连 / 恢复探测仍走 `OnConnectionUpdate` 原路径,无重复启动冲突
|
||||
(旧连接被 `connMgr.Add` 关闭后其读取协程自然退出)。
|
||||
|
||||
### 构建验证
|
||||
- `core` 独立:`go build ./... && go vet ./...` → 通过。
|
||||
- 注:沙箱无法运行 TUN/Wintun,端到端需用户在两台真实机器(或同机双进程)验证
|
||||
`ping 10.0.0.x` 收发。修复后公网 IP / 同局域网直连场景预期可正常握手建隧。
|
||||
|
||||
Reference in New Issue
Block a user