Files
Meshray-Manager/cmd/mr-wg/main.go
T
zkcoi 7cbe063ac1 fix: update core dependency path to git.zkcoi.com/zkcoi/meshray (drop /core)
- go.mod require/replace git.zkcoi.com/zkcoi/meshray/core -> git.zkcoi.com/zkcoi/meshray
- .go imports meshray/core/{engine,connect} -> meshray/{engine,connect} (5 files)
- sync CHANGELOG + adapter.go comments
2026-07-15 16:37:03 +08:00

266 lines
8.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 命令 meshray-coremeshray-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/engine"
"git.zkcoi.com/zkcoi/meshray/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. 创建 Bindenhanced=自定义接管;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 并 upbest-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
}