Initial commit

This commit is contained in:
2026-06-30 15:14:37 +08:00
commit 15dab96872
311 changed files with 95639 additions and 0 deletions
+581
View File
@@ -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))
}