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:
2026-07-15 16:08:13 +08:00
parent 15dab96872
commit 59e3059246
39 changed files with 2350 additions and 4361 deletions
+8
View File
@@ -0,0 +1,8 @@
//go:build meshray_core
// Package ctr 管理器在增强构建下空白导入 corebind,
// 触发其 init() 向开源契约注册表注册自定义 conn.Bind 工厂与引擎提供方。
// 默认构建不编译本文件,因此管理器可独立编译、以标准 WireGuard 直连运行。
package ctr
import _ "git.zkcoi.com/zkcoi/meshray/internal/ctr/corebind"
+67
View File
@@ -0,0 +1,67 @@
// Package corebind 的契约类型转换:在 meshray-contract 与 meshray/core 内部类型间双向转换。
package corebind
import (
"git.zkcoi.com/zkcoi/meshray-contract"
"git.zkcoi.com/zkcoi/meshray/core/connect"
"git.zkcoi.com/zkcoi/meshray/core/engine"
)
// 以下函数完成开源契约类型(contract 包)与 meshray/core 内部类型之间的双向转换。
// 数值型 Layer 在 contract 与 connect 中定义一致,可直接转型。
func toCoreCandidates(in []contract.Candidate) []core.Candidate {
out := make([]core.Candidate, 0, len(in))
for _, c := range in {
out = append(out, core.Candidate{
Addr: c.Addr,
Type: c.Type,
Priority: c.Priority,
Protocol: c.Protocol,
})
}
return out
}
func toCoreICEConfig(in contract.ICEConfig) connect.ICEConfig {
turn := make([]connect.TURNServerConfig, 0, len(in.TURNServers))
for _, s := range in.TURNServers {
turn = append(turn, connect.TURNServerConfig{URLs: []string{s}})
}
return connect.ICEConfig{
STUNServers: in.STUNServers,
TURNServers: turn,
}
}
func toCoreLayerOrder(in []contract.Layer) []connect.Layer {
out := make([]connect.Layer, 0, len(in))
for _, l := range in {
out = append(out, connect.Layer(l))
}
return out
}
func toContractStatus(in *core.EngineStatus) *contract.EngineStatus {
if in == nil {
return nil
}
peers := make(map[string]*contract.PeerStatus, len(in.Peers))
for k, v := range in.Peers {
peers[k] = &contract.PeerStatus{
PeerKey: v.PeerKey,
Connected: v.Connected,
Layer: v.Layer,
}
}
return &contract.EngineStatus{
PeerCount: in.PeerCount,
Peers: peers,
ActiveConnections: in.ActiveConnections,
TotalConnections: in.TotalConnections,
BytesSent: in.BytesSent,
BytesReceived: in.BytesReceived,
StrategyFallbacks: in.StrategyFallbacks,
LastSwitchTime: in.LastSwitchTime,
}
}
+309
View File
@@ -0,0 +1,309 @@
// Package corebind 是 Meshray-Manager 侧的 wireguard-go 集成层(形态 B:增强传输)。
//
// 它实现 wireguard-go 的 conn.Bind 接口(EnhancedBind),完全接管 WG 的 UDP 收发,
// 把密文流量导向 meshray 核心(git.zkcoi.com/zkcoi/meshray)的 9 层传输。
// 该层属于「管理器 / UI」范畴,而非核心;核心本身零 wireguard 依赖。
package corebind
import (
"net"
"sync"
"git.zkcoi.com/zkcoi/meshray/core/engine"
"golang.zx2c4.com/wireguard/conn"
"go.uber.org/zap"
)
// EnhancedBind 是 corebind 提供的 conn.Bind 实现,完全接管 WireGuard 的 UDP 收发,
// 使所有 WG 密文流量都经由 meshray 核心的 9 层传输(Direct/TURN/WebRTC/...)而非裸 UDP。
//
// 设计要点:
// - Direct-UDP 由一个绑定在 listen_port 的*共享未连接 UDP socket*directSock)承载,
// 从任意来源收包,把「真实源地址」作为 Endpoint 交给 WGWG 用密文内的 receiver index
// 匹配 peer 并据此更新对端出向地址(学址)。由此对称直连与 NAT 穿透学址原生可通。
// - TURN/WebRTC/WS 等中继层经核心传输连接(connMgr)承载,Direct 不可用时补充。
// - 集成通过核心暴露的钩子接入:SetPeerRegistrar / SetRelayInboundHandler /
// SetDirectByBind,核心永不反向依赖本包。
type EnhancedBind struct {
inner conn.Bind
networkID string
engine *core.Engine
logger *zap.Logger
// listenPort 本端 WG 监听端口;Direct-UDP 共享监听 socket 绑定于此,形成对称直连。
listenPort int
mu sync.RWMutex
peerByAddr map[string]string // endpoint addr(str) -> peerKeySend 反查中继连接)
peerEndpoint map[string]conn.Endpoint // peerKey -> 对端真实 Endpoint(中继入站回灌用)
// directSock 承载 Direct-UDP 的共享未连接 UDP socket(绑定 listenPort)。
// 从任意来源收包(学习对端经 NAT 后的真实源地址),并按目标地址发包。
directSock *net.UDPConn
inboundCh chan inboundPacket
closed bool
}
// inboundPacket 入站密文;携带来源信息以便回灌 WG 时定位对端 Endpoint。
type inboundPacket struct {
peerKey string // 中继入站时按 peerKey 查 Endpoint
ep conn.Endpoint // Direct 共享 socket 学得的真实源地址;非空时优先使用
data []byte
}
// NewEnhancedBind 创建增强 Bind,并关联指定组网的 Engine 单例;
// 同时经核心钩子把入站回灌接到本 Bind,使 9 层传输连接读到的包能回到 WG。
func NewEnhancedBind(listenPort int, networkID string, logger *zap.Logger) (*EnhancedBind, error) {
eng := getOrCreateEngine(networkID, logger)
inner := conn.NewDefaultBind()
b := &EnhancedBind{
inner: inner,
networkID: networkID,
engine: eng,
logger: logger,
listenPort: listenPort,
peerByAddr: make(map[string]string),
peerEndpoint: make(map[string]conn.Endpoint),
inboundCh: make(chan inboundPacket, 1024),
}
// 通过核心钩子接入,避免核心反向依赖本包:
eng.SetPeerRegistrar(b) // 收到对端候选时注册 endpoint→peerKey
eng.SetRelayInboundHandler(b.deliverInbound) // 传输连接入站回灌 WG
eng.SetDirectByBind(true) // Direct 由本 Bind 共享 socket 承载
eng.SetListenPort(listenPort)
return b, nil
}
// Engine 返回关联的引擎实例(standalone 二进制用于注入候选触发拨号)
func (b *EnhancedBind) Engine() *core.Engine { return b.engine }
// RegisterPeer 注册 endpoint 地址到 peerKey 的映射,并缓存对端真实 Endpoint 供回灌使用。
// 由 Engine.NotifyPeerInfo 在收到候选时自动调用;standalone 也可直接调用。
func (b *EnhancedBind) RegisterPeer(addr string, peerKey string) {
ep, err := b.inner.ParseEndpoint(addr)
if err != nil {
b.logger.Warn("解析对端 Endpoint 失败,回灌将使用 0.0.0.0:0",
zap.String("addr", addr), zap.Error(err))
ep = nil
}
b.mu.Lock()
b.peerByAddr[addr] = peerKey
if ep != nil {
b.peerEndpoint[peerKey] = ep
}
b.mu.Unlock()
}
// Open 创建 Direct-UDP 共享监听 socket(绑定 listen_port),并启动接收协程。
// 返回从 inboundCh 读取入站密文的接收函数:Direct 入站与中继入站统一经此回灌 WG。
func (b *EnhancedBind) Open(port uint16) ([]conn.ReceiveFunc, uint16, error) {
b.mu.Lock()
b.closed = false
// 端口优先用 WG 传入的 port,其次用构造时的 listenPort;0 表示由 OS 分配。
bindPort := int(port)
if bindPort == 0 {
bindPort = b.listenPort
}
// 关闭可能残留的旧 socket(重开场景)
if b.directSock != nil {
b.directSock.Close()
b.directSock = nil
}
b.mu.Unlock()
sock, err := net.ListenUDP("udp", &net.UDPAddr{Port: bindPort})
if err != nil {
return nil, 0, err
}
actual := sock.LocalAddr().(*net.UDPAddr).Port
b.mu.Lock()
b.directSock = sock
b.listenPort = actual
b.mu.Unlock()
// 回填引擎实际监听端口(listen_port=0 自动分配时同步)
if b.engine != nil {
b.engine.SetListenPort(actual)
}
go b.readDirectLoop(sock)
b.logger.Info("Direct-UDP 共享监听 socket 已就绪",
zap.Int("listen_port", actual))
// v4/v6 各一个接收函数,均从同一个 inboundCh 读取
return []conn.ReceiveFunc{b.receiveFunc, b.receiveFunc}, uint16(actual), nil
}
// readDirectLoop 从共享监听 socket 收包(任意来源),把「真实源地址」作为 Endpoint 投入 inboundCh。
// 这是 NAT 穿透学址的核心:公网侧据此学到对端 NAT 映射后的真实地址,回包得以正确返回。
func (b *EnhancedBind) readDirectLoop(sock *net.UDPConn) {
buf := make([]byte, 65535)
for {
n, src, err := sock.ReadFromUDP(buf)
if err != nil {
b.mu.RLock()
closed := b.closed
b.mu.RUnlock()
if closed {
return
}
b.logger.Debug("Direct 共享 socket 读取失败,停止接收", zap.Error(err))
return
}
data := make([]byte, n)
copy(data, buf[:n])
ep, perr := b.inner.ParseEndpoint(src.String())
if perr != nil {
b.logger.Debug("解析入站源地址失败,丢弃", zap.String("src", src.String()), zap.Error(perr))
continue
}
select {
case b.inboundCh <- inboundPacket{ep: ep, data: data}:
default:
b.logger.Warn("入站队列满,丢弃包", zap.String("src", src.String()))
}
}
}
// receiveFunc 从 inboundCh 取出入站密文,拷贝进 WG 提供的缓冲(保持 WG 缓冲所有权),
// 并填入来源 Endpoint 供 WG 握手更新出向地址:
// - Direct 入站:ep 为共享 socket 学得的真实源地址(优先);
// - 中继入站:ep 为空,按 peerKey 查缓存的对端 Endpoint。
func (b *EnhancedBind) receiveFunc(packets [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
first, ok := <-b.inboundCh
if !ok {
return 0, net.ErrClosed
}
n := 0
copy(packets[n], first.data)
sizes[n] = len(first.data)
eps[n] = b.endpointForPacket(first)
n++
for n < len(packets) {
select {
case pkt := <-b.inboundCh:
copy(packets[n], pkt.data)
sizes[n] = len(pkt.data)
eps[n] = b.endpointForPacket(pkt)
n++
default:
return n, nil
}
}
return n, nil
}
// endpointForPacket 返回入站包的来源 Endpoint:Direct 入站直接用其学得的 ep;
// 中继入站按 peerKey 查缓存,未知时退化为 0.0.0.0:0。
func (b *EnhancedBind) endpointForPacket(pkt inboundPacket) conn.Endpoint {
if pkt.ep != nil {
return pkt.ep
}
b.mu.RLock()
defer b.mu.RUnlock()
if ep, ok := b.peerEndpoint[pkt.peerKey]; ok {
return ep
}
ep, _ := b.inner.ParseEndpoint("0.0.0.0:0")
return ep
}
// Send 出站路径:
// - 若该对端已建立中继/信令层连接(TURN/WebRTC/WS,位于 connMgr),经其发送;
// - 否则(Direct-UDP)经共享监听 socket 直接发往目标地址(WG 维护的对端 Endpoint
// 初始为配置 endpoint,学址后为对端真实地址)。
func (b *EnhancedBind) Send(bufs [][]byte, ep conn.Endpoint) error {
addr := ep.DstToString()
b.mu.RLock()
peerKey, mapped := b.peerByAddr[addr]
b.mu.RUnlock()
if mapped {
if c, ok := b.engine.GetConnMgr().Get(peerKey); ok && c != nil {
for _, buf := range bufs {
if len(buf) == 0 {
continue
}
if _, err := c.Write(buf); err != nil {
b.logger.Debug("经中继连接发送失败,转 Direct 共享 socket",
zap.String("peer", peerKey), zap.Error(err))
return b.directSendTo(addr, bufs)
}
}
return nil
}
}
// Direct-UDP:共享监听 socket 直发(NAT 穿透原生支持)
return b.directSendTo(addr, bufs)
}
// directSendTo 经共享监听 socket 把数据发往目标地址。源端口固定为 listen_port
// 满足对称直连;NAT 后主动侧发包后,NAT 记录会话,公网侧回包得以返回。
func (b *EnhancedBind) directSendTo(addr string, bufs [][]byte) error {
b.mu.RLock()
sock := b.directSock
b.mu.RUnlock()
if sock == nil {
return net.ErrClosed
}
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return err
}
for _, buf := range bufs {
if len(buf) == 0 {
continue
}
if _, err := sock.WriteToUDP(buf, udpAddr); err != nil {
return err
}
}
return nil
}
// deliverInbound 由引擎中继传输连接读取到入站时调用,把密文投入 inboundCh 回灌 WG。
// 中继入站不携带来源地址,按 peerKey 在回灌时查对端 Endpoint。
func (b *EnhancedBind) deliverInbound(peerKey string, packet []byte) {
cp := make([]byte, len(packet))
copy(cp, packet)
select {
case b.inboundCh <- inboundPacket{peerKey: peerKey, data: cp}:
default:
b.logger.Warn("入站队列满,丢弃包", zap.String("peer", peerKey))
}
}
// Close 关闭共享监听 socket 与入站通道(使接收函数返回 net.ErrClosed)。
func (b *EnhancedBind) Close() error {
b.mu.Lock()
if !b.closed {
b.closed = true
close(b.inboundCh)
}
if b.directSock != nil {
b.directSock.Close()
b.directSock = nil
}
b.mu.Unlock()
return b.inner.Close()
}
// SetMark 本实现不涉及内核 socket,置空标记为 no-op
func (b *EnhancedBind) SetMark(mark uint32) error { return nil }
// ParseEndpoint 委托默认 Bind 解析
func (b *EnhancedBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return b.inner.ParseEndpoint(s)
}
// BatchSize 返回默认 Bind 的批量大小
func (b *EnhancedBind) BatchSize() int {
return b.inner.BatchSize()
}
+80
View File
@@ -0,0 +1,80 @@
// Package corebind 的契约注册:向开源契约(meshray-contract)注册增强 Bind 工厂与引擎提供方。
package corebind
import (
"sync"
"git.zkcoi.com/zkcoi/meshray-contract"
"git.zkcoi.com/zkcoi/meshray/core/engine"
"go.uber.org/zap"
"golang.zx2c4.com/wireguard/conn"
)
// engines 维护 networkID -> Engine 单例映射。
// EnhancedBind(收发接管)与 engineController(控制面)均通过 networkID 关联同一个 Engine
// 使信令下发的候选地址、ICE 配置、层顺序都能落到接管该组网收发的 Engine 上。
var (
enginesMu sync.Mutex
engines = make(map[string]*core.Engine)
)
// getOrCreateEngine 获取或创建指定组网的 Engine 单例(线程安全)
func getOrCreateEngine(networkID string, logger *zap.Logger) *core.Engine {
enginesMu.Lock()
defer enginesMu.Unlock()
if e, ok := engines[networkID]; ok {
return e
}
e := core.NewEngine(logger, core.NewMetrics())
engines[networkID] = e
return e
}
// engineController 适配 contract.EngineController,以 networkID 关联 Engine 单例,
// 供管理器在 enhanced 模式调用,无需直接依赖本包具体类型。
type engineController struct {
networkID string
engine *core.Engine
}
func (c *engineController) Start() error { return c.engine.Start() }
func (c *engineController) Stop() error { return c.engine.Stop() }
// NotifyPeerCandidates 由信令层调用:注入对端候选地址并触发 P2P 建连
func (c *engineController) NotifyPeerCandidates(peerKey string, candidates []contract.Candidate) error {
return c.engine.NotifyPeerInfo(peerKey, toCoreCandidates(candidates))
}
// SetICEConfig 更新 ICE/STUN/TURN 配置
func (c *engineController) SetICEConfig(cfg contract.ICEConfig) error {
return c.engine.SetICEConfig(toCoreICEConfig(cfg))
}
// SetLayerOrder 设置传输层优先级顺序(策略核心配置)
func (c *engineController) SetLayerOrder(order []contract.Layer) error {
c.engine.SetLayerOrder(toCoreLayerOrder(order))
return nil
}
// GetStatus 查询引擎状态
func (c *engineController) GetStatus() (*contract.EngineStatus, error) {
st, err := c.engine.GetStatus()
if err != nil {
return nil, err
}
return toContractStatus(st), nil
}
// init 向开源契约注册表注册增强 Bind 工厂与引擎提供方。
// 由管理器侧 core_enable.go 在 -tags meshray_core 构建下空白导入本包触发,
// 此时管理器侧 contract.GetBindFactory(true) / NewEngineController 才能取到实现;
// 默认构建不引入本包,原生直连(默认 Bind)始终可用。
func init() {
contract.RegisterEnhancedBindFactory(func(listenPort int, networkID string, logger *zap.Logger) (conn.Bind, error) {
return NewEnhancedBind(listenPort, networkID, logger)
})
contract.RegisterEngineProvider(func(networkID string, logger *zap.Logger) (contract.EngineController, error) {
return &engineController{networkID: networkID, engine: getOrCreateEngine(networkID, logger)}, nil
})
}
+135 -131
View File
@@ -5,8 +5,7 @@ import (
"strconv"
"sync"
"git.zkcoi.com/zkcoi/meshray/core"
"git.zkcoi.com/zkcoi/meshray/core/connect"
contract "git.zkcoi.com/zkcoi/meshray-contract"
"go.uber.org/zap"
)
@@ -20,8 +19,8 @@ type Ctr struct {
// WireGuard 管理器
wgManager *WGManager
// Core 实例(直接集成,无需 gRPC)
coreInst *core.Core
// 增强引擎实例(由 meshray-core 提供,按 networkID 索引);原生模式为空
engines map[string]contract.EngineController
mu sync.RWMutex
}
@@ -38,14 +37,12 @@ func NewCtr(name string, networkID uint64, config *CtrConfig, logger *zap.Logger
networkID: networkID,
config: config,
logger: logger,
engines: make(map[string]contract.EngineController),
}
// 初始化 WireGuard 管理器
// 初始化 WireGuard 管理器(两种模式都需要)
ctr.wgManager = NewWGManager(logger)
// 初始化 Core 实例(直接集成,无需 gRPC)
ctr.coreInst = core.NewCore(logger)
return ctr, nil
}
@@ -58,9 +55,6 @@ func (c *Ctr) Start() error {
zap.String("name", c.name),
zap.Uint64("network_id", c.networkID))
// ✅ Watchdog 监控暂不实现(P3 阶段)
// go c.watchdog.Start()
return nil
}
@@ -72,10 +66,13 @@ func (c *Ctr) Stop() error {
c.logger.Info("停止 meshray-ctr",
zap.Uint64("network_id", c.networkID))
// 停止 Core 实例(直接调用方法)
if c.coreInst != nil {
// ✅ Close 方法已实现(会停止所有 Engine)
c.coreInst.Close()
// 停止所有增强引擎
for networkID, engineCtl := range c.engines {
if err := engineCtl.Stop(); err != nil {
c.logger.Warn("停止增强引擎失败",
zap.String("network_id", networkID), zap.Error(err))
}
delete(c.engines, networkID)
}
// 停止 WireGuard 管理
@@ -87,8 +84,8 @@ func (c *Ctr) Stop() error {
}
// CreateNetwork 创建网络(根据组网模式决定行为)
// - 原生模式:仅创建 WG 设备
// - 增强模式:创建 WG 设备 + 启动 Core 实例
// - 原生模式:仅创建 WG 设备wireguard-go 默认 Bind 直连)
// - 增强模式:WG 设备使用 meshray-core 的自定义 conn.Bind 接管收发;并启动增强引擎控制面
func (c *Ctr) CreateNetwork(networkID uint64, subnet string, listenPort int, meshMode string) error {
c.mu.Lock()
defer c.mu.Unlock()
@@ -99,41 +96,30 @@ func (c *Ctr) CreateNetwork(networkID uint64, subnet string, listenPort int, mes
zap.Int("listen_port", listenPort),
zap.String("mesh_mode", meshMode))
// 1. 创建 WireGuard 设备(两种模式都需要)
// 1. 创建 WireGuard 设备(两种模式都需要;增强模式使用 core 注册的自定义 Bind
networkIDStr := strconv.FormatUint(networkID, 10)
if err := c.wgManager.CreateDevice(networkIDStr, subnet, listenPort); err != nil {
if err := c.wgManager.CreateDevice(networkIDStr, subnet, listenPort, meshMode); err != nil {
return fmt.Errorf("创建 WireGuard 设备失败:%w", err)
}
c.logger.Debug("WireGuard 设备创建成功",
zap.Uint64("network_id", networkID))
// 2. 仅增强模式需要创建并启动 Core 实例
// 2. 仅增强模式需要启动增强引擎(控制面)
if meshMode == "enhanced" {
// 直接调用 Core 实例的方法(无需 gRPC)
metrics := core.NewMetrics()
engine, err := c.coreInst.CreateEngine(networkIDStr, metrics)
engineCtl, err := contract.NewEngineController(networkIDStr, c.logger)
if err != nil {
c.wgManager.DeleteDevice(networkIDStr) // 回滚 WG设备
c.logger.Error("创建 Engine 失败",
zap.Uint64("network_id", networkID),
zap.Error(err))
return fmt.Errorf("创建 Engine 失败:%w", err)
}
if err := engine.Start(); err != nil {
// ✅ 调用 Stop 方法停止(会清理所有资源)
engine.Stop()
c.wgManager.DeleteDevice(networkIDStr) // 回滚 WG 设备
c.logger.Error("启动 Engine 失败",
zap.Uint64("network_id", networkID),
zap.Error(err))
return fmt.Errorf("启动 Engine 失败:%w", err)
return fmt.Errorf("增强模式需 meshray-core%w", err)
}
c.logger.Info("增强模式:Engine 实例已启动",
if err := engineCtl.Start(); err != nil {
c.wgManager.DeleteDevice(networkIDStr) // 回滚 WG 设备
return fmt.Errorf("启动增强引擎失败:%w", err)
}
c.engines[networkIDStr] = engineCtl
c.logger.Info("增强模式:引擎已启动(收发由 meshray-core conn.Bind 接管)",
zap.Uint64("network_id", networkID))
} else {
c.logger.Info("原生模式:仅创建 WG 设备,不启动 Core",
c.logger.Info("原生模式:仅创建 WG 设备,标准直连",
zap.Uint64("network_id", networkID))
}
@@ -152,11 +138,17 @@ func (c *Ctr) DeleteNetwork(networkID uint64) error {
c.logger.Info("开始删除网络",
zap.Uint64("network_id", networkID))
// 1. Core 实例不需要显式删除(它是内存中的对象)
// 如果需要清理,可以在未来实现 Engine.Stop() 方法
networkIDStr := strconv.FormatUint(networkID, 10)
// 2. 删除 WireGuard 设备
// 停止并清理增强引擎(若存在)
if engineCtl, ok := c.engines[networkIDStr]; ok {
if err := engineCtl.Stop(); err != nil {
c.logger.Warn("停止增强引擎失败", zap.String("network_id", networkIDStr), zap.Error(err))
}
delete(c.engines, networkIDStr)
}
// 删除 WireGuard 设备
if err := c.wgManager.DeleteDevice(networkIDStr); err != nil {
return fmt.Errorf("删除 WireGuard 设备失败:%w", err)
}
@@ -182,29 +174,13 @@ func (c *Ctr) AddPeer(networkID uint64, publicKey, allowedIP string) error {
return fmt.Errorf("添加 Peer 失败:%w", err)
}
// 2. 通知 Core
// 如果已经在 Enhanced 模式,需要接管刚刚加入的这个 Peer
engine, err := c.coreInst.GetEngine(networkIDStr)
if err == nil && engine != nil {
// 引擎存在,说明是 enhanced 模式
// 由 Core 分配一个本地端口
localPort, err := engine.Bind(publicKey, 0)
if err != nil {
c.logger.Error("增强模式动态 AddPeer Bind 失败", zap.Error(err))
} else {
// 通知 WG 更新为本地代理 Endpoint
newEndpoint := fmt.Sprintf("127.0.0.1:%d", localPort)
if err := c.wgManager.UpdatePeerEndpoint(networkIDStr, publicKey, newEndpoint); err != nil {
c.logger.Warn("增强模式自动重写 Endpoint 失败", zap.Error(err))
engine.Unbind(publicKey)
} else {
c.logger.Info("增强模式:动态 Peer 添加并成功代理给 Core",
zap.String("public_key", publicKey[:8]+"..."),
zap.String("new_endpoint", newEndpoint))
}
}
// 2. 增强模式:meshray-core 的 conn.Bind 已接管该组网收发,peer Endpoint 保持真实地址,
// 无需改写回环端口;候选地址后续由信令层经 NotifyPeerCandidates 注入触发建连。
if _, ok := c.engines[networkIDStr]; ok {
c.logger.Info("增强模式:动态 Peer 已交由 meshray-core 接管",
zap.String("public_key", publicKey[:8]+"..."))
} else {
c.logger.Debug("Core 未启动,普通模式无需代理 Peer", zap.Uint64("network_id", networkID))
c.logger.Debug("原生模式无需代理 Peer", zap.Uint64("network_id", networkID))
}
return nil
@@ -225,36 +201,38 @@ func (c *Ctr) RemovePeer(networkID uint64, publicKey string) error {
return fmt.Errorf("移除 Peer 失败:%w", err)
}
// 通知 Core 卸载这台 Peer 的监听
engine, err := c.coreInst.GetEngine(networkIDStr)
if err == nil && engine != nil {
engine.Unbind(publicKey)
c.logger.Info("增强模式:Core 已解除对该 Peer 的挂载", zap.String("public_key", publicKey[:8]+"..."))
// 增强模式:meshray-core 的 conn.Bind 按 WG peer 状态自动管理收发,
// 移除 WG peer 后不再有该 peer 的包,无需显式解绑
if _, ok := c.engines[networkIDStr]; ok {
c.logger.Info("增强模式:Core 已随 WG Peer 移除停止对该 Peer 的转发",
zap.String("public_key", publicKey[:8]+"..."))
}
return nil
}
// SetSTUNTURNConfig 为指定网络设置 STUN/TURN 配置
func (c *Ctr) SetSTUNTURNConfig(networkID uint64, stunServers []string, turnServers []connect.TURNServerConfig) error {
// SetSTUNTURNConfig 为指定网络设置 STUN/TURN 配置(仅增强模式生效)
func (c *Ctr) SetSTUNTURNConfig(networkID uint64, stunServers []string, turnServers []string) error {
c.mu.RLock()
defer c.mu.RUnlock()
networkIDStr := strconv.FormatUint(networkID, 10)
// 获取 Engine 实例
engine, err := c.coreInst.GetEngine(networkIDStr)
if err != nil {
// 获取增强引擎
engineCtl, ok := c.engines[networkIDStr]
if !ok {
c.logger.Debug("网络未启动增强模式,跳过 STUN/TURN 配置",
zap.Uint64("network_id", networkID))
return nil // 无需错误,因为原生模式不需要
}
// 更新 WebRTC 工厂的 ICE 配置
engine.SetICEConfig(connect.ICEConfig{
if err := engineCtl.SetICEConfig(contract.ICEConfig{
STUNServers: stunServers,
TURNServers: turnServers,
})
}); err != nil {
return fmt.Errorf("设置 ICE 配置失败:%w", err)
}
c.logger.Info("STUN/TURN 配置已设置",
zap.Uint64("network_id", networkID),
@@ -263,6 +241,7 @@ func (c *Ctr) SetSTUNTURNConfig(networkID uint64, stunServers []string, turnServ
return nil
}
func (c *Ctr) GetStatus(networkID uint64) (*NetworkStatus, error) {
c.mu.RLock()
defer c.mu.RUnlock()
@@ -279,11 +258,9 @@ func (c *Ctr) GetStatus(networkID uint64) (*NetworkStatus, error) {
}
status.WGStatus = wgStatus
// 获取 Core 状态(直接调用 Engine 的方法)
engine, err := c.coreInst.GetEngine(networkIDStr)
if err == nil && engine != nil {
coreStatus, err := engine.GetStatus()
if err == nil {
// 获取增强引擎状态
if engineCtl, ok := c.engines[networkIDStr]; ok {
if coreStatus, err := engineCtl.GetStatus(); err == nil {
status.CoreStatus = coreStatus
}
}
@@ -292,7 +269,8 @@ func (c *Ctr) GetStatus(networkID uint64) (*NetworkStatus, error) {
}
// SwitchMode 切换传输模式(原生→增强)
// 仅支持 "native" → "enhanced" 单向切换
// 仅支持 "native" → "enhanced" 单向切换。增强模式由 meshray-core 的 conn.Bind 接管收发,
// 不再改写 peer Endpoint 为回环端口(根治入站接不通的缺陷)。
func (c *Ctr) SwitchMode(networkID uint64, mode string) error {
c.mu.Lock()
defer c.mu.Unlock()
@@ -317,18 +295,17 @@ func (c *Ctr) SwitchMode(networkID uint64, mode string) error {
c.logger.Info("获取到 WG 状态",
zap.Int("peer_count", wgStatus.PeerCount))
// 3. 创建并启动 Core Engine(增强模式核心
metrics := core.NewMetrics()
engine, err := c.coreInst.CreateEngine(networkIDStr, metrics)
// 3. 创建并启动增强引擎(控制面
engineCtl, err := contract.NewEngineController(networkIDStr, c.logger)
if err != nil {
return fmt.Errorf("创建 Core Engine 失败%w", err)
return fmt.Errorf("创建增强引擎失败(需 meshray-core%w", err)
}
if err := engine.Start(); err != nil {
return fmt.Errorf("启动 Core Engine 失败:%w", err)
if err := engineCtl.Start(); err != nil {
return fmt.Errorf("启动增强引擎失败:%w", err)
}
c.engines[networkIDStr] = engineCtl
c.logger.Info("Core Engine 已启动",
c.logger.Info("增强引擎已启动(收发由 meshray-core conn.Bind 接管)",
zap.Uint64("network_id", networkID))
// 4. 获取 Peer 列表
@@ -337,60 +314,87 @@ func (c *Ctr) SwitchMode(networkID uint64, mode string) error {
return fmt.Errorf("获取 Peer 列表失败:%w", err)
}
// 5. 修改 Peer Endpoint → 127.0.0.1:{port},并且让 Core 绑定
// 增强模式下,所有 Peer Endpoint 都指向本地回环地址
// 5. 把已知真实 Endpoint 作为候选注入,触发 P2P 建连
// (不再改写 peer Endpoint 为回环端口;peer Endpoint 保持真实地址,WG 入站匹配成功)
for _, peer := range peers {
// 1. Core 引擎动态接管,传 0 意为让操作系统挑个未占用的随意回环端口
localPort, err := engine.Bind(peer.PublicKey, 0)
if err != nil {
c.logger.Error("Engine Bind 失败",
zap.String("public_key", peer.PublicKey[:8]+"..."),
zap.Error(err))
continue
if peer.Endpoint != "" {
candidates := []contract.Candidate{
{Addr: peer.Endpoint, Type: "host", Priority: 1, Protocol: "udp"},
}
if err := engineCtl.NotifyPeerCandidates(peer.PublicKey, candidates); err != nil {
c.logger.Warn("注入对端候选地址失败",
zap.String("public_key", peer.PublicKey[:8]+"..."),
zap.Error(err))
}
}
newEndpoint := fmt.Sprintf("127.0.0.1:%d", localPort)
// 2. 将此端口告知 WG 更新 Endpoint
if err := c.wgManager.UpdatePeerEndpoint(networkIDStr, peer.PublicKey, newEndpoint); err != nil {
c.logger.Warn("更新 Peer Endpoint 失败",
zap.String("public_key", peer.PublicKey[:8]+"..."),
zap.Error(err))
// 如果更新失败,解绑 Core
engine.Unbind(peer.PublicKey)
continue
}
c.logger.Info("Peer Endpoint 已成功切换并接入 Core",
zap.String("public_key", peer.PublicKey[:8]+"..."),
zap.String("original_endpoint", peer.Endpoint),
zap.String("new_endpoint", newEndpoint))
}
c.logger.Info("传输模式切换成功(基础功能已实现,P3 阶段待完善",
c.logger.Info("传输模式切换成功(增强模式:meshray-core 接管收发",
zap.Uint64("network_id", networkID),
zap.String("mode", mode))
return nil
}
// CoreConfigUpdate 核心配置更新请求(由 server 层在策略变更时构造并传入)
type CoreConfigUpdate struct {
STUNServers []string // STUN 服务器列表
TURNServers []string // TURN 服务器列表(URL
EnabledLayers []contract.Layer // 启用的传输层顺序(为空保持默认 9 层)
}
// NotifyPeerCandidates 由信令层调用:注入对端候选地址并触发 P2P 建连
func (c *Ctr) NotifyPeerCandidates(networkID uint64, publicKey string, candidates []contract.Candidate) error {
c.mu.RLock()
defer c.mu.RUnlock()
networkIDStr := strconv.FormatUint(networkID, 10)
engineCtl, ok := c.engines[networkIDStr]
if !ok {
return fmt.Errorf("网络未启用增强模式")
}
return engineCtl.NotifyPeerCandidates(publicKey, candidates)
}
// UpdateCoreConfig 更新 Core 配置(策略修改后同步)
// ✅ P3-1 阶段实现
// 真实生效:更新 ICE/STUN/TURN 配置到 WebRTC 工厂,并按需调整传输层优先级顺序
func (c *Ctr) UpdateCoreConfig(networkID uint64, config interface{}) error {
c.mu.RLock()
defer c.mu.RUnlock()
c.logger.Info("更新 Core 配置",
zap.Uint64("network_id", networkID))
update, ok := config.(*CoreConfigUpdate)
if !ok {
return fmt.Errorf("不支持的配置类型:%T", config)
}
// ✅ P3-1 阶段 - 暂不实现
// 未来实现:
// 1. 根据策略类型解析配置
// 2. 调用 Core 的 UpdateConfig 方法
// 3. 持久化到数据库
networkIDStr := strconv.FormatUint(networkID, 10)
engineCtl, ok := c.engines[networkIDStr]
if !ok {
return fmt.Errorf("网络未启用增强模式")
}
return fmt.Errorf("UpdateCoreConfig 尚未实现(P3-1 阶段)")
// 1. 更新 ICE/STUN/TURN 配置(下发到 WebRTC 工厂)
if err := engineCtl.SetICEConfig(contract.ICEConfig{
STUNServers: update.STUNServers,
TURNServers: update.TURNServers,
}); err != nil {
return fmt.Errorf("设置 ICE 配置失败:%w", err)
}
// 2. 更新传输层优先级顺序(策略核心配置)
if len(update.EnabledLayers) > 0 {
if err := engineCtl.SetLayerOrder(update.EnabledLayers); err != nil {
return fmt.Errorf("设置传输层顺序失败:%w", err)
}
}
c.logger.Info("Core 配置已更新",
zap.Uint64("network_id", networkID),
zap.Int("stun_count", len(update.STUNServers)),
zap.Int("turn_count", len(update.TURNServers)),
zap.Int("enabled_layers", len(update.EnabledLayers)))
return nil
}
// GetWGMode 获取系统 WG 模式
@@ -400,7 +404,7 @@ func (c *Ctr) GetWGMode() string {
// NetworkStatus 网络状态
type NetworkStatus struct {
NetworkID string `json:"network_id"`
WGStatus *WGStatus `json:"wg_status"`
CoreStatus *core.EngineStatus `json:"core_status,omitempty"`
NetworkID string `json:"network_id"`
WGStatus *WGStatus `json:"wg_status"`
CoreStatus *contract.EngineStatus `json:"core_status,omitempty"`
}
+52 -12
View File
@@ -8,8 +8,8 @@ import (
"strings"
"sync"
contract "git.zkcoi.com/zkcoi/meshray-contract"
"go.uber.org/zap"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/wgctrl"
@@ -48,7 +48,8 @@ func NewWGManager(logger *zap.Logger) *WGManager {
}
// CreateDevice 创建 WireGuard 设备
func (m *WGManager) CreateDevice(networkID string, subnet string, listenPort int) error {
// meshMode: "native" 使用 wireguard-go 默认 Bind 直连;"enhanced" 使用 meshray-core 注册的自定义 Bind 接管收发
func (m *WGManager) CreateDevice(networkID string, subnet string, listenPort int, meshMode string) error {
m.mu.Lock()
defer m.mu.Unlock()
@@ -63,7 +64,8 @@ func (m *WGManager) CreateDevice(networkID string, subnet string, listenPort int
zap.String("network_id", networkID),
zap.String("device", deviceName),
zap.String("subnet", subnet),
zap.Int("listen_port", listenPort))
zap.Int("listen_port", listenPort),
zap.String("mesh_mode", meshMode))
// 生成密钥对
privateKey, err := wgtypes.GeneratePrivateKey()
@@ -76,7 +78,7 @@ func (m *WGManager) CreateDevice(networkID string, subnet string, listenPort int
zap.String("public_key", publicKey.String()))
// 使用用户态模式启动 wireguard-go 进程,并获取资源引用
tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort)
tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort, networkID, meshMode)
if err != nil {
return err
}
@@ -356,19 +358,43 @@ type WGStatus struct {
}
// ListPeers 获取设备下所有 Peer 信息
// 除内存记录外,还会从 wgctrl 读取对端真实 Endpoint,作为增强模式 P2P 建连的候选地址来源
func (m *WGManager) ListPeers(networkID string) ([]PeerInfo, error) {
m.mu.RLock()
defer m.mu.RUnlock()
device, ok := m.devices[networkID]
if !ok {
m.mu.RUnlock()
return nil, fmt.Errorf("网络 %s 的设备不存在", networkID)
}
// 复制内存中的 peer 列表,避免返回内部共享引用
peers := make([]PeerInfo, len(device.Peers))
copy(peers, device.Peers)
m.mu.RUnlock()
return device.Peers, nil
// 从 wgctrl 读取真实 Endpoint(若已配置)
client, err := wgctrl.New()
if err == nil {
defer client.Close()
if dev, err := client.Device(device.Name); err == nil {
for i := range peers {
for _, wp := range dev.Peers {
if wp.PublicKey.String() == peers[i].PublicKey {
if wp.Endpoint != nil {
peers[i].Endpoint = wp.Endpoint.String()
}
break
}
}
}
}
}
return peers, nil
}
// UpdatePeerEndpoint 更新 Peer 的 Endpoint
// 注意:增强模式(meshray-core 接管收发)下不应再改写 peer Endpoint 为回环端口,
// 否则 WG 入站源地址匹配失败;此方法仅用于设置对端真实地址或原生模式。
func (m *WGManager) UpdatePeerEndpoint(networkID string, publicKey string, newEndpoint string) error {
m.mu.Lock()
defer m.mu.Unlock()
@@ -434,7 +460,7 @@ func (m *WGManager) UpdatePeerEndpoint(networkID string, publicKey string, newEn
// startUserModeWGProcess 用户态模式启动 wireguard-go(保存资源引用)
func (m *WGManager) startUserModeWGProcess(deviceName string, privateKey wgtypes.Key, listenPort int) error {
tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort)
tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort, "", "native")
if err != nil {
return err
}
@@ -445,10 +471,12 @@ func (m *WGManager) startUserModeWGProcess(deviceName string, privateKey wgtypes
}
// startUserModeWGProcessWithRefs 用户态模式启动 wireguard-go,返回资源引用
func (m *WGManager) startUserModeWGProcessWithRefs(deviceName string, privateKey wgtypes.Key, listenPort int) (tun.Device, *device.Device, error) {
// meshMode 决定 Bind 来源:native 用 wireguard-go 默认 Bindenhanced 用 meshray-core 注册的自定义 Bind
func (m *WGManager) startUserModeWGProcessWithRefs(deviceName string, privateKey wgtypes.Key, listenPort int, networkID string, meshMode string) (tun.Device, *device.Device, error) {
m.logger.Info("使用用户态模式启动 WireGuard",
zap.String("device", deviceName),
zap.Int("port", listenPort))
zap.Int("port", listenPort),
zap.String("mesh_mode", meshMode))
// 1. 创建 TUN 设备并保存引用
tunDevice, err := tun.CreateTUN(deviceName, 1420)
@@ -456,8 +484,20 @@ func (m *WGManager) startUserModeWGProcessWithRefs(deviceName string, privateKey
return nil, nil, fmt.Errorf("创建 TUN 设备失败:%w", err)
}
// 2. 创建 UDP bind
bind := conn.NewDefaultBind()
// 2. 创建 UDP bind:根据组网模式选择
// - 原生模式:wireguard-go 默认 Bind(标准直连,无需 meshray-core
// - 增强模式:优先使用 meshray-core 注册的自定义 Bind(接管收发,根治 Endpoint 旁路缺陷);
// 若默认构建未引入 meshray-core,则返回错误,增强模式不可用。
bindFactory, err := contract.GetBindFactory(meshMode == "enhanced")
if err != nil {
tunDevice.Close()
return nil, nil, fmt.Errorf("获取 Bind 工厂失败(增强模式需 meshray-core):%w", err)
}
bind, err := bindFactory(listenPort, networkID, m.logger)
if err != nil {
tunDevice.Close()
return nil, nil, fmt.Errorf("创建 Bind 失败:%w", err)
}
// 3. 创建 WireGuard device 并保存引用
logger := &device.Logger{