Initial commit
This commit is contained in:
@@ -0,0 +1,406 @@
|
||||
package ctr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"git.zkcoi.com/zkcoi/meshray/core"
|
||||
"git.zkcoi.com/zkcoi/meshray/core/connect"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Ctr meshray-ctr 调度中心
|
||||
type Ctr struct {
|
||||
name string // 组网名称(用于日志)
|
||||
networkID uint64 // 组网 ID(雪花算法 ID)
|
||||
config *CtrConfig
|
||||
logger *zap.Logger
|
||||
|
||||
// WireGuard 管理器
|
||||
wgManager *WGManager
|
||||
|
||||
// Core 实例(直接集成,无需 gRPC)
|
||||
coreInst *core.Core
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// CtrConfig 配置
|
||||
type CtrConfig struct {
|
||||
// 空配置,保留结构体以备未来扩展
|
||||
}
|
||||
|
||||
// NewCtr 创建调度中心
|
||||
func NewCtr(name string, networkID uint64, config *CtrConfig, logger *zap.Logger) (*Ctr, error) {
|
||||
ctr := &Ctr{
|
||||
name: name,
|
||||
networkID: networkID,
|
||||
config: config,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 初始化 WireGuard 管理器
|
||||
ctr.wgManager = NewWGManager(logger)
|
||||
|
||||
// 初始化 Core 实例(直接集成,无需 gRPC)
|
||||
ctr.coreInst = core.NewCore(logger)
|
||||
|
||||
return ctr, nil
|
||||
}
|
||||
|
||||
// Start 启动调度中心
|
||||
func (c *Ctr) Start() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.logger.Info("启动 meshray-ctr",
|
||||
zap.String("name", c.name),
|
||||
zap.Uint64("network_id", c.networkID))
|
||||
|
||||
// ✅ Watchdog 监控暂不实现(P3 阶段)
|
||||
// go c.watchdog.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop 停止调度中心
|
||||
func (c *Ctr) Stop() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.logger.Info("停止 meshray-ctr",
|
||||
zap.Uint64("network_id", c.networkID))
|
||||
|
||||
// 停止 Core 实例(直接调用方法)
|
||||
if c.coreInst != nil {
|
||||
// ✅ Close 方法已实现(会停止所有 Engine)
|
||||
c.coreInst.Close()
|
||||
}
|
||||
|
||||
// 停止 WireGuard 管理
|
||||
if err := c.wgManager.Stop(); err != nil {
|
||||
c.logger.Error("停止 WireGuard 管理失败", zap.Error(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNetwork 创建网络(根据组网模式决定行为)
|
||||
// - 原生模式:仅创建 WG 设备
|
||||
// - 增强模式:创建 WG 设备 + 启动 Core 实例
|
||||
func (c *Ctr) CreateNetwork(networkID uint64, subnet string, listenPort int, meshMode string) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.logger.Info("开始创建网络",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.String("subnet", subnet),
|
||||
zap.Int("listen_port", listenPort),
|
||||
zap.String("mesh_mode", meshMode))
|
||||
|
||||
// 1. 创建 WireGuard 设备(两种模式都需要)
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
if err := c.wgManager.CreateDevice(networkIDStr, subnet, listenPort); err != nil {
|
||||
return fmt.Errorf("创建 WireGuard 设备失败:%w", err)
|
||||
}
|
||||
c.logger.Debug("WireGuard 设备创建成功",
|
||||
zap.Uint64("network_id", networkID))
|
||||
|
||||
// 2. 仅增强模式需要创建并启动 Core 实例
|
||||
if meshMode == "enhanced" {
|
||||
// 直接调用 Core 实例的方法(无需 gRPC)
|
||||
metrics := core.NewMetrics()
|
||||
engine, err := c.coreInst.CreateEngine(networkIDStr, metrics)
|
||||
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)
|
||||
}
|
||||
|
||||
c.logger.Info("增强模式:Engine 实例已启动",
|
||||
zap.Uint64("network_id", networkID))
|
||||
} else {
|
||||
c.logger.Info("原生模式:仅创建 WG 设备,不启动 Core",
|
||||
zap.Uint64("network_id", networkID))
|
||||
}
|
||||
|
||||
c.logger.Info("网络创建成功",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.String("mesh_mode", meshMode))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNetwork 删除网络
|
||||
func (c *Ctr) DeleteNetwork(networkID uint64) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.logger.Info("开始删除网络",
|
||||
zap.Uint64("network_id", networkID))
|
||||
|
||||
// 1. Core 实例不需要显式删除(它是内存中的对象)
|
||||
// 如果需要清理,可以在未来实现 Engine.Stop() 方法
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
|
||||
// 2. 删除 WireGuard 设备
|
||||
if err := c.wgManager.DeleteDevice(networkIDStr); err != nil {
|
||||
return fmt.Errorf("删除 WireGuard 设备失败:%w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("网络删除成功",
|
||||
zap.Uint64("network_id", networkID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPeer 添加 Peer
|
||||
func (c *Ctr) AddPeer(networkID uint64, publicKey, allowedIP string) error {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
c.logger.Info("添加 Peer",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.String("public_key", publicKey[:8]+"..."))
|
||||
|
||||
// 1. 添加到 WireGuard
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
if err := c.wgManager.AddPeer(networkIDStr, publicKey, allowedIP); err != nil {
|
||||
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))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.logger.Debug("Core 未启动,普通模式无需代理 Peer", zap.Uint64("network_id", networkID))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePeer 移除 Peer
|
||||
func (c *Ctr) RemovePeer(networkID uint64, publicKey string) error {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
c.logger.Info("移除 Peer",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.String("public_key", publicKey[:8]+"..."))
|
||||
|
||||
// 从 WireGuard 移除
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
if err := c.wgManager.RemovePeer(networkIDStr, publicKey); err != nil {
|
||||
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]+"..."))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSTUNTURNConfig 为指定网络设置 STUN/TURN 配置
|
||||
func (c *Ctr) SetSTUNTURNConfig(networkID uint64, stunServers []string, turnServers []connect.TURNServerConfig) error {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
|
||||
// 获取 Engine 实例
|
||||
engine, err := c.coreInst.GetEngine(networkIDStr)
|
||||
if err != nil {
|
||||
c.logger.Debug("网络未启动增强模式,跳过 STUN/TURN 配置",
|
||||
zap.Uint64("network_id", networkID))
|
||||
return nil // 无需错误,因为原生模式不需要
|
||||
}
|
||||
|
||||
// 更新 WebRTC 工厂的 ICE 配置
|
||||
engine.SetICEConfig(connect.ICEConfig{
|
||||
STUNServers: stunServers,
|
||||
TURNServers: turnServers,
|
||||
})
|
||||
|
||||
c.logger.Info("STUN/TURN 配置已设置",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.Int("stun_count", len(stunServers)),
|
||||
zap.Int("turn_count", len(turnServers)))
|
||||
|
||||
return nil
|
||||
}
|
||||
func (c *Ctr) GetStatus(networkID uint64) (*NetworkStatus, error) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
status := &NetworkStatus{
|
||||
NetworkID: networkIDStr,
|
||||
}
|
||||
|
||||
// 获取 WireGuard 状态
|
||||
wgStatus, err := c.wgManager.GetStatus(networkIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status.WGStatus = wgStatus
|
||||
|
||||
// 获取 Core 状态(直接调用 Engine 的方法)
|
||||
engine, err := c.coreInst.GetEngine(networkIDStr)
|
||||
if err == nil && engine != nil {
|
||||
coreStatus, err := engine.GetStatus()
|
||||
if err == nil {
|
||||
status.CoreStatus = coreStatus
|
||||
}
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// SwitchMode 切换传输模式(原生→增强)
|
||||
// 仅支持 "native" → "enhanced" 单向切换
|
||||
func (c *Ctr) SwitchMode(networkID uint64, mode string) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.logger.Info("开始切换传输模式",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.String("target_mode", mode))
|
||||
|
||||
// 1. 验证目标模式
|
||||
if mode != "enhanced" {
|
||||
return fmt.Errorf("仅支持切换到 enhanced 模式,当前请求:%s", mode)
|
||||
}
|
||||
|
||||
networkIDStr := strconv.FormatUint(networkID, 10)
|
||||
|
||||
// 2. 获取当前 WG 状态(获取 Peer 数量)
|
||||
wgStatus, err := c.wgManager.GetStatus(networkIDStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 WG 状态失败:%w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("获取到 WG 状态",
|
||||
zap.Int("peer_count", wgStatus.PeerCount))
|
||||
|
||||
// 3. 创建并启动 Core Engine(增强模式核心)
|
||||
metrics := core.NewMetrics()
|
||||
engine, err := c.coreInst.CreateEngine(networkIDStr, metrics)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建 Core Engine 失败:%w", err)
|
||||
}
|
||||
|
||||
if err := engine.Start(); err != nil {
|
||||
return fmt.Errorf("启动 Core Engine 失败:%w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("Core Engine 已启动",
|
||||
zap.Uint64("network_id", networkID))
|
||||
|
||||
// 4. 获取 Peer 列表
|
||||
peers, err := c.wgManager.ListPeers(networkIDStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 Peer 列表失败:%w", err)
|
||||
}
|
||||
|
||||
// 5. 修改 Peer Endpoint → 127.0.0.1:{port},并且让 Core 绑定
|
||||
// 增强模式下,所有 Peer 的 Endpoint 都指向本地回环地址
|
||||
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
|
||||
}
|
||||
|
||||
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 阶段待完善)",
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.String("mode", mode))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateCoreConfig 更新 Core 配置(策略修改后同步)
|
||||
// ✅ P3-1 阶段实现
|
||||
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))
|
||||
|
||||
// ✅ P3-1 阶段 - 暂不实现
|
||||
// 未来实现:
|
||||
// 1. 根据策略类型解析配置
|
||||
// 2. 调用 Core 的 UpdateConfig 方法
|
||||
// 3. 持久化到数据库
|
||||
|
||||
return fmt.Errorf("UpdateCoreConfig 尚未实现(P3-1 阶段)")
|
||||
}
|
||||
|
||||
// GetWGMode 获取系统 WG 模式
|
||||
func (c *Ctr) GetWGMode() string {
|
||||
return c.wgManager.GetWGMode()
|
||||
}
|
||||
|
||||
// NetworkStatus 网络状态
|
||||
type NetworkStatus struct {
|
||||
NetworkID string `json:"network_id"`
|
||||
WGStatus *WGStatus `json:"wg_status"`
|
||||
CoreStatus *core.EngineStatus `json:"core_status,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ctr
|
||||
|
||||
// Client Ctr 客户端接口(Service 层调用)
|
||||
//
|
||||
// 职责:
|
||||
// 1. 定义 Service 层可调用的所有方法
|
||||
// 2. 解耦 Service 和 Ctr 实现
|
||||
// 3. 便于单元测试 Mock
|
||||
//
|
||||
// 使用示例:
|
||||
//
|
||||
// type NetworkService struct {
|
||||
// ctrClient ctr.Client
|
||||
// }
|
||||
//
|
||||
// func (s *NetworkService) CreateNetwork(req *model.Network) error {
|
||||
// return s.ctrClient.CreateNetwork(req.ID, req.Subnet, listenPort)
|
||||
// }
|
||||
type Client interface {
|
||||
// ========== 网络生命周期管理 ==========
|
||||
|
||||
// CreateNetwork 创建网络
|
||||
// networkID: 组网 ID(雪花算法 uint64)
|
||||
// subnet: 子网段(例如:"10.0.0.0/24")
|
||||
// listenPort: WireGuard 监听端口(例如:51820 + hash(networkID))
|
||||
// meshMode: 组网模式("native" | "enhanced")
|
||||
CreateNetwork(networkID uint64, subnet string, listenPort int, meshMode string) error
|
||||
|
||||
// DeleteNetwork 删除网络
|
||||
DeleteNetwork(networkID uint64) error
|
||||
|
||||
// ========== Peer 管理 ==========
|
||||
|
||||
// AddPeer 添加 Peer
|
||||
// publicKey: Peer 的公钥
|
||||
// allowedIP: Peer 的允许 IP(例如:"10.0.0.2/32")
|
||||
AddPeer(networkID uint64, publicKey, allowedIP string) error
|
||||
|
||||
// RemovePeer 移除 Peer
|
||||
RemovePeer(networkID uint64, publicKey string) error
|
||||
|
||||
// ========== 状态查询 ==========
|
||||
|
||||
// GetStatus 获取网络状态
|
||||
// 返回 WGStatus 和 CoreStatus(如果 Core 存在)
|
||||
GetStatus(networkID uint64) (*NetworkStatus, error)
|
||||
|
||||
// ========== 模式切换(P3 阶段) ==========
|
||||
|
||||
// SwitchMode 切换传输模式(原生→增强)
|
||||
// mode: "native" | "enhanced"
|
||||
// ✅ P3-1 阶段实现
|
||||
SwitchMode(networkID uint64, mode string) error
|
||||
|
||||
// ========== 配置同步(P3 阶段) ==========
|
||||
|
||||
// UpdateCoreConfig 更新 Core 配置(策略修改后同步)
|
||||
// config: 新的配置对象(根据策略类型决定)
|
||||
// ✅ P3-1 阶段实现
|
||||
UpdateCoreConfig(networkID uint64, config interface{}) error
|
||||
}
|
||||
|
||||
// 确保 Ctr 实现了 Client 接口
|
||||
var _ Client = (*Ctr)(nil)
|
||||
@@ -0,0 +1,581 @@
|
||||
package ctr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
// WGManager WireGuard 管理器(P2 阶段骨架)
|
||||
type WGManager struct {
|
||||
devices map[string]*WGDevice // network_id -> device
|
||||
mu sync.RWMutex
|
||||
logger *zap.Logger
|
||||
wgMode string // "kernel" | "userspace"(当前运行模式)
|
||||
}
|
||||
|
||||
// WGDevice WireGuard 设备
|
||||
type WGDevice struct {
|
||||
NetworkID string
|
||||
Name string // wg0, wg1, ...
|
||||
Config *DeviceConfig
|
||||
Peers []PeerInfo
|
||||
Running bool
|
||||
// 用户态模式下使用的资源引用
|
||||
tunDevice tun.Device // TUN 设备引用(用户态模式)
|
||||
wgDevice *device.Device // WireGuard 设备引用(用户态模式)
|
||||
}
|
||||
|
||||
// NewWGManager 创建 WireGuard 管理器(固定使用用户态)
|
||||
func NewWGManager(logger *zap.Logger) *WGManager {
|
||||
logger.Info("使用 wireguard-go 用户态模式(跨平台统一)")
|
||||
|
||||
return &WGManager{
|
||||
devices: make(map[string]*WGDevice),
|
||||
logger: logger,
|
||||
wgMode: "userspace", // 固定为用户态
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDevice 创建 WireGuard 设备
|
||||
func (m *WGManager) CreateDevice(networkID string, subnet string, listenPort int) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// 检查是否已存在
|
||||
if _, ok := m.devices[networkID]; ok {
|
||||
return fmt.Errorf("网络 %s 的设备已存在", networkID)
|
||||
}
|
||||
|
||||
deviceName := fmt.Sprintf("wg%s", networkID)
|
||||
|
||||
m.logger.Info("开始创建 WireGuard 设备",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("device", deviceName),
|
||||
zap.String("subnet", subnet),
|
||||
zap.Int("listen_port", listenPort))
|
||||
|
||||
// 生成密钥对
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成私钥失败:%w", err)
|
||||
}
|
||||
publicKey := privateKey.PublicKey()
|
||||
|
||||
m.logger.Debug("生成 WG 密钥对",
|
||||
zap.String("public_key", publicKey.String()))
|
||||
|
||||
// 使用用户态模式启动 wireguard-go 进程,并获取资源引用
|
||||
tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. ✅ Core 模块使用回环地址拦截,无需配置 TUN 设备 IP
|
||||
// WireGuard 用户态设备会自动管理自己的网络接口
|
||||
m.logger.Info("WireGuard 用户态设备已创建,使用回环地址拦截模式",
|
||||
zap.String("device", deviceName))
|
||||
|
||||
// 5. 启动设备(带回滚)
|
||||
if err := m.bringUpDevice(deviceName); err != nil {
|
||||
m.cleanupDevice(deviceName) // 回滚
|
||||
return fmt.Errorf("启动设备失败:%w", err)
|
||||
}
|
||||
|
||||
// 7. 记录到内存
|
||||
device := &WGDevice{
|
||||
NetworkID: networkID,
|
||||
Name: deviceName,
|
||||
Config: &DeviceConfig{
|
||||
PrivateKey: privateKey.String(),
|
||||
PublicKey: publicKey.String(),
|
||||
ListenPort: listenPort,
|
||||
Subnet: subnet,
|
||||
},
|
||||
Running: true,
|
||||
}
|
||||
|
||||
// ⚠️ 关键:保存用户态模式资源引用
|
||||
device.tunDevice = tunDev
|
||||
device.wgDevice = wgDev
|
||||
m.logger.Debug("已保存用户态模式资源引用",
|
||||
zap.String("device", deviceName))
|
||||
|
||||
m.devices[networkID] = device
|
||||
|
||||
m.logger.Info("WireGuard 设备创建成功(用户态模式)",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("public_key", publicKey.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPeer 添加 Peer
|
||||
func (m *WGManager) AddPeer(networkID string, publicKey, allowedIP string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
device, ok := m.devices[networkID]
|
||||
if !ok {
|
||||
return fmt.Errorf("网络 %s 的设备不存在", networkID)
|
||||
}
|
||||
|
||||
m.logger.Info("添加 Peer",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("public_key", truncatePublicKey(publicKey)+"..."),
|
||||
zap.String("allowed_ip", allowedIP))
|
||||
|
||||
// 1. 解析公钥
|
||||
peerKey, err := wgtypes.ParseKey(publicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析公钥失败:%w", err)
|
||||
}
|
||||
|
||||
// 2. 解析 AllowedIP
|
||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析 AllowedIP 失败:%w", err)
|
||||
}
|
||||
|
||||
// 3. 连接 wgctrl 并配置
|
||||
client, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf("wgctrl 连接失败:%w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 4. 配置 Peer
|
||||
config := wgtypes.Config{
|
||||
Peers: []wgtypes.PeerConfig{
|
||||
{
|
||||
PublicKey: peerKey,
|
||||
ReplaceAllowedIPs: true,
|
||||
AllowedIPs: []net.IPNet{*ipNet},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := client.ConfigureDevice(device.Name, config); err != nil {
|
||||
return fmt.Errorf("配置 Peer 失败:%w", err)
|
||||
}
|
||||
|
||||
m.logger.Info("Peer 配置成功",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("public_key", truncatePublicKey(publicKey)+"..."),
|
||||
zap.String("allowed_ip", allowedIP))
|
||||
|
||||
// 5. 同时更新内存(保持向后兼容)
|
||||
peer := PeerInfo{
|
||||
PublicKey: publicKey,
|
||||
AllowedIPs: []string{allowedIP},
|
||||
}
|
||||
device.Peers = append(device.Peers, peer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePeer 移除 Peer
|
||||
func (m *WGManager) RemovePeer(networkID string, publicKey string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
device, ok := m.devices[networkID]
|
||||
if !ok {
|
||||
return fmt.Errorf("网络 %s 的设备不存在", networkID)
|
||||
}
|
||||
|
||||
m.logger.Info("移除 Peer",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("public_key", truncatePublicKey(publicKey)+"..."))
|
||||
|
||||
// 1. 解析公钥
|
||||
peerKey, err := wgtypes.ParseKey(publicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析公钥失败:%w", err)
|
||||
}
|
||||
|
||||
// 2. 连接 wgctrl 并移除
|
||||
client, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf("wgctrl 连接失败:%w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 3. 配置移除 Peer(使用 Remove: true)
|
||||
config := wgtypes.Config{
|
||||
Peers: []wgtypes.PeerConfig{
|
||||
{
|
||||
PublicKey: peerKey,
|
||||
Remove: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := client.ConfigureDevice(device.Name, config); err != nil {
|
||||
return fmt.Errorf("移除 Peer 失败:%w", err)
|
||||
}
|
||||
|
||||
m.logger.Info("Peer 移除成功",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("public_key", truncatePublicKey(publicKey)+"..."))
|
||||
|
||||
// 4. 同时从内存删除(保持向后兼容)
|
||||
newPeers := []PeerInfo{}
|
||||
for _, p := range device.Peers {
|
||||
if p.PublicKey != publicKey {
|
||||
newPeers = append(newPeers, p)
|
||||
}
|
||||
}
|
||||
device.Peers = newPeers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDevice 删除 WireGuard 设备
|
||||
func (m *WGManager) DeleteDevice(networkID string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
device, ok := m.devices[networkID]
|
||||
if !ok {
|
||||
return fmt.Errorf("网络 %s 的设备不存在", networkID)
|
||||
}
|
||||
|
||||
m.logger.Info("删除 WireGuard 设备",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("device", device.Name))
|
||||
|
||||
// 1. 清理真实设备(内核态或用户态)
|
||||
m.cleanupDevice(device.Name)
|
||||
|
||||
// 2. 从内存删除
|
||||
delete(m.devices, networkID)
|
||||
|
||||
m.logger.Info("WireGuard 设备已删除",
|
||||
zap.String("network_id", networkID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStatus 获取设备状态
|
||||
func (m *WGManager) GetStatus(networkID string) (*WGStatus, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
device, ok := m.devices[networkID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("网络 %s 的设备不存在", networkID)
|
||||
}
|
||||
|
||||
status := &WGStatus{
|
||||
DeviceName: device.Name,
|
||||
Running: device.Running,
|
||||
PeerCount: len(device.Peers),
|
||||
Subnet: device.Config.Subnet,
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// GetWGMode 获取当前 WG 模式
|
||||
func (m *WGManager) GetWGMode() string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.wgMode
|
||||
}
|
||||
|
||||
// Stop 停止管理器并释放所有资源
|
||||
func (m *WGManager) Stop() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.logger.Info("正在停止 WireGuard 管理器...")
|
||||
|
||||
// 关闭所有设备
|
||||
for networkID, device := range m.devices {
|
||||
m.logger.Info("正在关闭设备",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("device_name", device.Name))
|
||||
|
||||
// 如果是用户态模式,关闭相关资源
|
||||
if device.wgDevice != nil {
|
||||
m.logger.Debug("关闭用户态 WireGuard 设备",
|
||||
zap.String("device", device.Name))
|
||||
device.wgDevice.Close()
|
||||
}
|
||||
|
||||
if device.tunDevice != nil {
|
||||
m.logger.Debug("关闭 TUN 设备",
|
||||
zap.String("device", device.Name))
|
||||
device.tunDevice.Close()
|
||||
}
|
||||
|
||||
// 清理内核态设备(忽略返回值)
|
||||
m.cleanupDevice(device.Name)
|
||||
|
||||
delete(m.devices, networkID)
|
||||
}
|
||||
|
||||
m.logger.Info("WireGuard 管理器已停止")
|
||||
return nil
|
||||
}
|
||||
|
||||
// WGDevice WireGuard 设备(结构已在上面定义)
|
||||
|
||||
// DeviceConfig 设备配置
|
||||
type DeviceConfig struct {
|
||||
PrivateKey string
|
||||
PublicKey string
|
||||
ListenPort int
|
||||
Subnet string
|
||||
}
|
||||
|
||||
// PeerInfo Peer 信息
|
||||
type PeerInfo struct {
|
||||
PublicKey string
|
||||
Endpoint string
|
||||
AllowedIPs []string
|
||||
}
|
||||
|
||||
// WGStatus WireGuard 状态
|
||||
type WGStatus struct {
|
||||
DeviceName string `json:"device_name"`
|
||||
Running bool `json:"running"`
|
||||
PeerCount int `json:"peer_count"`
|
||||
Subnet string `json:"subnet"`
|
||||
}
|
||||
|
||||
// ListPeers 获取设备下所有 Peer 信息
|
||||
func (m *WGManager) ListPeers(networkID string) ([]PeerInfo, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
device, ok := m.devices[networkID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("网络 %s 的设备不存在", networkID)
|
||||
}
|
||||
|
||||
return device.Peers, nil
|
||||
}
|
||||
|
||||
// UpdatePeerEndpoint 更新 Peer 的 Endpoint
|
||||
func (m *WGManager) UpdatePeerEndpoint(networkID string, publicKey string, newEndpoint string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
device, ok := m.devices[networkID]
|
||||
if !ok {
|
||||
return fmt.Errorf("网络 %s 的设备不存在", networkID)
|
||||
}
|
||||
|
||||
m.logger.Info("更新 Peer Endpoint",
|
||||
zap.String("network_id", networkID),
|
||||
zap.String("public_key", truncatePublicKey(publicKey)+"..."),
|
||||
zap.String("new_endpoint", newEndpoint))
|
||||
|
||||
// 解析公钥
|
||||
peerKey, err := wgtypes.ParseKey(publicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析公钥失败:%w", err)
|
||||
}
|
||||
|
||||
// 解析 Endpoint 地址
|
||||
var udpAddr *net.UDPAddr
|
||||
if newEndpoint != "" {
|
||||
addr, err := net.ResolveUDPAddr("udp", newEndpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析 Endpoint 地址 %s 失败:%w", newEndpoint, err)
|
||||
}
|
||||
udpAddr = addr
|
||||
}
|
||||
|
||||
// 连接 wgctrl 并配置
|
||||
client, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf("wgctrl 连接失败:%w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 配置更新 Peer
|
||||
config := wgtypes.Config{
|
||||
Peers: []wgtypes.PeerConfig{
|
||||
{
|
||||
PublicKey: peerKey,
|
||||
UpdateOnly: true,
|
||||
Endpoint: udpAddr,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := client.ConfigureDevice(device.Name, config); err != nil {
|
||||
return fmt.Errorf("更新 Peer 失败:%w", err)
|
||||
}
|
||||
|
||||
// 同时更新内存
|
||||
for i, p := range device.Peers {
|
||||
if p.PublicKey == publicKey {
|
||||
device.Peers[i].Endpoint = newEndpoint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startUserModeWGProcess 用户态模式启动 wireguard-go(保存资源引用)
|
||||
func (m *WGManager) startUserModeWGProcess(deviceName string, privateKey wgtypes.Key, listenPort int) error {
|
||||
tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 兼容旧接口,忽略返回值
|
||||
_ = tunDev
|
||||
_ = wgDev
|
||||
return nil
|
||||
}
|
||||
|
||||
// startUserModeWGProcessWithRefs 用户态模式启动 wireguard-go,返回资源引用
|
||||
func (m *WGManager) startUserModeWGProcessWithRefs(deviceName string, privateKey wgtypes.Key, listenPort int) (tun.Device, *device.Device, error) {
|
||||
m.logger.Info("使用用户态模式启动 WireGuard",
|
||||
zap.String("device", deviceName),
|
||||
zap.Int("port", listenPort))
|
||||
|
||||
// 1. 创建 TUN 设备并保存引用
|
||||
tunDevice, err := tun.CreateTUN(deviceName, 1420)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("创建 TUN 设备失败:%w", err)
|
||||
}
|
||||
|
||||
// 2. 创建 UDP bind
|
||||
bind := conn.NewDefaultBind()
|
||||
|
||||
// 3. 创建 WireGuard device 并保存引用
|
||||
logger := &device.Logger{
|
||||
Verbosef: func(format string, args ...interface{}) {
|
||||
m.logger.Debug(fmt.Sprintf(format, args...))
|
||||
},
|
||||
Errorf: func(format string, args ...interface{}) {
|
||||
m.logger.Error(fmt.Sprintf(format, args...))
|
||||
},
|
||||
}
|
||||
|
||||
wgDevice := device.NewDevice(tunDevice, bind, logger)
|
||||
|
||||
// 4. 配置设备
|
||||
config := fmt.Sprintf("private_key=%s\nlisten_port=%d\n",
|
||||
privateKey.String(), listenPort)
|
||||
|
||||
if err := wgDevice.IpcSet(config); err != nil {
|
||||
wgDevice.Close()
|
||||
return nil, nil, fmt.Errorf("配置 WireGuard 设备失败:%w", err)
|
||||
}
|
||||
|
||||
// 5. 启动设备
|
||||
if err := wgDevice.Up(); err != nil {
|
||||
wgDevice.Close()
|
||||
return nil, nil, fmt.Errorf("启动 WireGuard 设备失败:%w", err)
|
||||
}
|
||||
|
||||
m.logger.Info("用户态 WireGuard 启动成功",
|
||||
zap.String("device", deviceName),
|
||||
zap.Int("port", listenPort))
|
||||
|
||||
// 6. 返回资源引用
|
||||
return tunDevice, wgDevice, nil
|
||||
}
|
||||
|
||||
// truncatePublicKey 截断公钥用于日志显示(避免数组越界)
|
||||
func truncatePublicKey(publicKey string) string {
|
||||
if len(publicKey) <= 8 {
|
||||
return publicKey
|
||||
}
|
||||
return publicKey[:8]
|
||||
}
|
||||
|
||||
// bringUpDevice 启动设备(跨平台实现)
|
||||
func (m *WGManager) bringUpDevice(deviceName string) error {
|
||||
m.logger.Info("启动设备",
|
||||
zap.String("device", deviceName))
|
||||
|
||||
var cmd *exec.Cmd
|
||||
var output []byte
|
||||
var err error
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// Linux: ip link set up device
|
||||
cmd = exec.Command("ip", "link", "set", "up", deviceName)
|
||||
output, err = cmd.CombinedOutput()
|
||||
case "windows":
|
||||
// Windows: 用户态模式下 wireguard-go 会自动管理设备状态
|
||||
// 这里不需要额外操作
|
||||
m.logger.Debug("Windows 平台用户态模式无需手动启动设备")
|
||||
return nil
|
||||
case "darwin":
|
||||
// macOS: ifconfig device up
|
||||
cmd = exec.Command("ifconfig", deviceName, "up")
|
||||
output, err = cmd.CombinedOutput()
|
||||
default:
|
||||
return fmt.Errorf("不支持的操作系统:%s", runtime.GOOS)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("启动设备失败 (%s): %w", string(output), err)
|
||||
}
|
||||
|
||||
m.logger.Info("设备已启动",
|
||||
zap.String("device", deviceName))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupDevice 清理残留设备(回滚用,跨平台实现)
|
||||
func (m *WGManager) cleanupDevice(deviceName string) {
|
||||
m.logger.Warn("清理残留设备",
|
||||
zap.String("device", deviceName))
|
||||
|
||||
var cmd *exec.Cmd
|
||||
var output []byte
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// Linux: ip link delete device
|
||||
cmd = exec.Command("ip", "link", "delete", deviceName)
|
||||
output, _ = cmd.CombinedOutput()
|
||||
case "windows":
|
||||
// Windows: 无法通过命令行直接删除,需要用户手动操作
|
||||
m.logger.Warn("Windows 平台需要通过 WireGuard 客户端删除设备",
|
||||
zap.String("device", deviceName))
|
||||
return
|
||||
case "darwin":
|
||||
// macOS: ifconfig device down delete
|
||||
cmd = exec.Command("ifconfig", deviceName, "down")
|
||||
_, _ = cmd.CombinedOutput()
|
||||
// macOS 可能需要额外步骤,这里简化处理
|
||||
default:
|
||||
m.logger.Warn("不支持的操作系统,跳过清理",
|
||||
zap.String("os", runtime.GOOS))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否是"设备不存在"错误
|
||||
if strings.Contains(string(output), "does not exist") ||
|
||||
strings.Contains(string(output), "cannot find device") {
|
||||
m.logger.Debug("设备不存在,无需清理",
|
||||
zap.String("device", deviceName))
|
||||
return
|
||||
}
|
||||
|
||||
m.logger.Debug("设备清理完成",
|
||||
zap.String("device", deviceName))
|
||||
}
|
||||
Reference in New Issue
Block a user