// 命令 meshray-core:meshray-core 增强层的独立运行入口(类 frp 风格)。 // // 直接以自定义 conn.Bind 接管 WireGuard 收发,使所有隧道密文流量经由本仓 9 层传输 // (Direct/TURN/WebRTC/...)而非裸 UDP。无需管理器即可在两台机器间建立加密隧道, // 用于端到端验证 core 的收发能力。 // // 用法: // // meshray-core -genkey # 生成私钥 // meshray-core -c meshray-core.toml # 按配置启动(默认 TOML,类 frp;兼容 .json) package main import ( "flag" "fmt" "net" "os" "os/exec" "os/signal" "runtime" "strings" "syscall" meshraycore "git.zkcoi.com/zkcoi/meshray/core/engine" "git.zkcoi.com/zkcoi/meshray/core/connect" corebind "git.zkcoi.com/zkcoi/Meshray-Manager/internal/ctr/corebind" "go.uber.org/zap" "golang.zx2c4.com/wireguard/conn" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) func main() { cfgPath := flag.String("c", "meshray-core.toml", "配置文件路径(默认 TOML,类 frp;也兼容 .json)") genKey := flag.Bool("genkey", false, "仅生成私钥并打印后退出") flag.Parse() if *genKey { key, err := wgtypes.GeneratePrivateKey() if err != nil { fmt.Fprintln(os.Stderr, "生成私钥失败:", err) os.Exit(1) } fmt.Println(key.String()) return } cfg, err := loadConfig(*cfgPath) if err != nil { fmt.Fprintln(os.Stderr, "加载配置失败:", err) os.Exit(1) } logger := newLogger() defer func() { _ = logger.Sync() }() // 1. 准备私钥:配置中给出则解析,否则生成并落盘 priv, err := resolvePrivateKey(cfg, logger) if err != nil { logger.Fatal("私钥处理失败", zap.Error(err)) } // 2. 创建 TUN 设备(用户态)。Windows 需已安装 Wintun 驱动。 devName := "mrc0" tunDev, err := tun.CreateTUN(devName, cfg.MTU) if err != nil { logger.Fatal("创建 TUN 设备失败(Windows 需 Wintun 驱动)", zap.Error(err)) } defer tunDev.Close() // 3. 创建 Bind:enhanced=自定义接管;native=裸 WG 默认 Bind var wgBind conn.Bind if cfg.Mode == "native" { logger.Info("以原生直连模式运行(裸 WG UDP)") wgBind = conn.NewDefaultBind() } else { logger.Info("以增强模式运行(9 层传输接管收发)") eb, eerr := corebind.NewEnhancedBind(cfg.ListenPort, cfg.NetworkID, logger) if eerr != nil { logger.Fatal("创建增强 Bind 失败", zap.Error(eerr)) } wgBind = eb } wgLogger := &device.Logger{ Verbosef: func(format string, args ...interface{}) { logger.Debug(fmt.Sprintf(format, args...)) }, Errorf: func(format string, args ...interface{}) { logger.Error(fmt.Sprintf(format, args...)) }, } wgDev := device.NewDevice(tunDev, wgBind, wgLogger) defer wgDev.Close() // 4. 组装 WG 配置(私钥 / 监听端口 / 各 peer) var sb strings.Builder fmt.Fprintf(&sb, "private_key=%s\nlisten_port=%d\n", priv.String(), cfg.ListenPort) for _, p := range cfg.Peers { pub, perr := wgtypes.ParseKey(p.PublicKey) if perr != nil { logger.Fatal("对端公钥解析失败", zap.String("public_key", p.PublicKey), zap.Error(perr)) } fmt.Fprintf(&sb, "public_key=%s\n", pub.String()) if p.Endpoint != "" { fmt.Fprintf(&sb, "endpoint=%s\n", p.Endpoint) } for _, a := range p.AllowedIPs { fmt.Fprintf(&sb, "allowed_ip=%s\n", a) } fmt.Fprintf(&sb, "persistent_keepalive=25\n") } if err := wgDev.IpcSet(sb.String()); err != nil { logger.Fatal("配置 WireGuard 设备失败", zap.Error(err)) } // 5. 增强模式:把对端候选(endpoint)注入引擎触发 9 层拨号 if eb, ok := wgBind.(*corebind.EnhancedBind); ok { eng := eb.Engine() // 5.0 下达 ICE 配置(STUN/TURN),手动 TURN/WebRTC 层级必需 if len(cfg.ICE.STUN) > 0 || len(cfg.ICE.TURN) > 0 { turnCfgs := make([]connect.TURNServerConfig, 0, len(cfg.ICE.TURN)) for _, t := range cfg.ICE.TURN { turnCfgs = append(turnCfgs, connect.TURNServerConfig{URLs: []string{t}}) } ice := connect.ICEConfig{ STUNServers: cfg.ICE.STUN, TURNServers: turnCfgs, } if err := eng.SetICEConfig(ice); err != nil { logger.Error("下达 ICE 配置失败", zap.Error(err)) } } // 5.1 应用传输层级策略:auto=默认 9 层自动降级;manual=仅用配置的 layers if cfg.LayerStrategy == "manual" { if len(cfg.Layers) == 0 { logger.Fatal("layer_strategy=manual 但 layers 为空") } layers, perr := parseLayers(cfg.Layers) if perr != nil { logger.Fatal("解析手动层级失败", zap.Error(perr)) } eng.SetLayerOrder(layers) if len(layers) == 1 { logger.Info("手动层级模式:强制固定层(禁用降级)", zap.String("layer", layers[0].String())) } else { logger.Info("手动层级模式:自定义降级顺序", zap.Strings("layers", cfg.Layers)) } } else { logger.Info("自动层级模式:9 层自动降级", zap.Int("default_layers", len(connect.DefaultLayerOrder))) } // 5.2 注入对端候选,触发拨号 for _, p := range cfg.Peers { if p.Endpoint == "" { continue } if err := eng.NotifyPeerInfo(p.PublicKey, []meshraycore.Candidate{{Addr: p.Endpoint}}); err != nil { logger.Error("注入对端候选失败", zap.String("peer", p.PublicKey), zap.Error(err)) } } } // 6. 启动设备并配置隧道 IP if err := wgDev.Up(); err != nil { logger.Fatal("启动 WireGuard 设备失败", zap.Error(err)) } devName, nameErr := tunDev.Name() if nameErr != nil { logger.Warn("获取 TUN 设备名失败", zap.Error(nameErr)) devName = "mrc0" } assignInterfaceIP(devName, cfg.InterfaceIP, logger) logger.Info("meshray-core 已启动", zap.String("network_id", cfg.NetworkID), zap.String("mode", cfg.Mode), zap.String("device", devName), zap.String("interface", cfg.InterfaceIP)) // 7. 阻塞等待退出信号 sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig logger.Info("正在退出...") } // resolvePrivateKey 返回私钥:配置给出则解析,否则生成并写入 private_key_file func resolvePrivateKey(cfg *Config, logger *zap.Logger) (wgtypes.Key, error) { if cfg.PrivateKey != "" { return wgtypes.ParseKey(cfg.PrivateKey) } key, err := wgtypes.GeneratePrivateKey() if err != nil { return wgtypes.Key{}, err } if cfg.PrivateKeyFile != "" { if werr := os.WriteFile(cfg.PrivateKeyFile, []byte(key.String()), 0600); werr != nil { logger.Warn("写入私钥文件失败", zap.Error(werr)) } else { logger.Info("已生成并写入私钥", zap.String("file", cfg.PrivateKeyFile)) } } return key, nil } // assignInterfaceIP 给隧道接口分配 IP 并 up(best-effort,失败仅告警) func assignInterfaceIP(name, cidr string, logger *zap.Logger) { if cidr == "" { return } ip, ipNet, err := net.ParseCIDR(cidr) if err != nil { logger.Error("解析隧道 IP 失败", zap.String("cidr", cidr), zap.Error(err)) return } switch runtime.GOOS { case "linux": run(logger, "ip", "addr", "add", cidr, "dev", name) run(logger, "ip", "link", "set", name, "up") case "windows": // ipNet.Mask 已是网络掩码字节,转点分十进制 m := ipNet.Mask maskStr := fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) run(logger, "netsh", "interface", "ip", "set", "address", "name="+name, "static", ip.String(), maskStr) default: logger.Warn("不支持的平台,请手动配置隧道 IP", zap.String("os", runtime.GOOS)) } } // run 执行外部命令(best-effort,输出仅调试) func run(logger *zap.Logger, name string, args ...string) { cmd := exec.Command(name, args...) out, err := cmd.CombinedOutput() if err != nil { logger.Warn("执行命令失败(可忽略,手动配置即可)", zap.String("cmd", name), zap.String("args", strings.Join(args, " ")), zap.Error(err), zap.String("output", string(out))) } } // newLogger 创建生产级 logger func newLogger() *zap.Logger { l, err := zap.NewProduction() if err != nil { l = zap.NewNop() } return l } // parseLayers 将配置中的层级名列表解析为 connect.Layer 列表,任一无法解析则返回错误 func parseLayers(names []string) ([]connect.Layer, error) { layers := make([]connect.Layer, 0, len(names)) for _, n := range names { l, ok := connect.ParseLayer(n) if !ok { return nil, fmt.Errorf("未知传输层: %q", n) } layers = append(layers, l) } return layers, nil }