package service import ( "crypto/rand" "crypto/sha256" "encoding/hex" "errors" "fmt" "net" "git.zkcoi.com/zkcoi/meshray/internal/ctr" "git.zkcoi.com/zkcoi/meshray/internal/model" "git.zkcoi.com/zkcoi/meshray/internal/store/sqlite" "git.zkcoi.com/zkcoi/meshray/pkg/idutil" "go.uber.org/zap" "gorm.io/gorm" ) // NetworkService 网络管理服务 type NetworkService struct { store *sqlite.Store ctrClient ctr.Client // meshray-ctr 客户端 logger *zap.Logger } // NewNetworkService 创建网络服务实例 func NewNetworkService(store *sqlite.Store, ctrClient ctr.Client, logger *zap.Logger) *NetworkService { return &NetworkService{ store: store, ctrClient: ctrClient, logger: logger, } } // ListNetworks 获取网络列表(Service 层方法) func (s *NetworkService) ListNetworks() ([]model.Network, error) { var networks []model.Network err := s.store.DB().Preload("Devices").Find(&networks).Error return networks, err } // GetNetworkByID 根据 ID 获取网络 func (s *NetworkService) GetNetworkByID(id uint64) (*model.Network, error) { var network model.Network err := s.store.DB().Preload("Devices").First(&network, id).Error if err != nil { return nil, err } return &network, nil } // CreateNetwork 创建网络 func (s *NetworkService) CreateNetwork(req *model.Network) (*model.Network, error) { // 验证子网格式 if err := s.validateSubnet(req.SubnetIPv4); err != nil { return nil, err } // 检查网络名称是否重复 var existing model.Network if err := s.store.DB().Where("name = ?", req.Name).First(&existing).Error; err == nil { return nil, errors.New("网络名称已存在") } // 生成雪花算法 ID if req.ID == 0 { gen, err := idutil.GetGenerator() if err != nil { return nil, fmt.Errorf("failed to get snowflake generator: %w", err) } req.ID, err = gen.NextID() if err != nil { return nil, fmt.Errorf("failed to generate network id: %w", err) } } // 计算 listenPort:51820 + hash(networkID)(先不保存,用于 ctr 调用) listenPort := 51820 + hashUint64(req.ID)%1000 // 根据组网模式决定是否启动 Core // 原生模式:仅创建 WG 设备 // 增强模式:创建 WG 设备 + 启动 Core 实例 if s.ctrClient != nil { // P2 阶段 - 先调用 meshray-ctr 创建 WireGuard 设备(失败则不回滚数据库) // 策略:宽松模式 - ctr 失败只记录警告,不影响数据库操作 if err := s.ctrClient.CreateNetwork(req.ID, req.SubnetIPv4, listenPort, req.Mode); err != nil { // 记录错误但不影响数据库操作(允许降级) s.logger.Warn("调用 ctr 创建网络失败,将手动启动", zap.Uint64("network_id", req.ID), zap.Error(err)) } } // 保存到数据库 if err := s.store.DB().Create(req).Error; err != nil { return nil, err } s.logger.Info("网络创建成功", zap.Uint64("network_id", req.ID), zap.String("mode", req.Mode)) return req, nil } // GetNetwork 获取网络详情 func (s *NetworkService) GetNetwork(id uint64) (*model.Network, error) { var network model.Network if err := s.store.DB().First(&network, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("网络不存在") } return nil, err } return &network, nil } // UpdateNetwork 更新网络 func (s *NetworkService) UpdateNetwork(id uint64, updates map[string]interface{}) (*model.Network, error) { var network model.Network if err := s.store.DB().First(&network, id).Error; err != nil { return nil, errors.New("网络不存在") } if err := s.store.DB().Model(&network).Updates(updates).Error; err != nil { return nil, err } return &network, nil } // DeleteNetwork 删除网络 func (s *NetworkService) DeleteNetwork(id uint64, force bool) error { // ✅ 使用事务保证数据一致性 tx := s.store.DB().Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() var network model.Network if err := tx.First(&network, id).Error; err != nil { tx.Rollback() return errors.New("网络不存在") } // 检查是否有关联的设备 var deviceCount int64 tx.Model(&model.Device{}).Where("network_id = ?", id).Count(&deviceCount) if deviceCount > 0 { if !force { tx.Rollback() return errors.New("该网络下仍有设备,为避免误操作,请确认后强制删除") } // 级联删除所有关联设备 if err := tx.Where("network_id = ?", id).Delete(&model.Device{}).Error; err != nil { tx.Rollback() return fmt.Errorf("级联删除设备失败:%w", err) } s.logger.Info("级联删除了关联设备", zap.Uint64("network_id", id), zap.Int64("device_count", deviceCount)) } // 先调用 ctr 删除 WG 设备(如果存在) if s.ctrClient != nil { if err := s.ctrClient.DeleteNetwork(id); err != nil { // 记录错误但不中断删除流程(设备可能已经不存在) s.logger.Warn("删除 WG 设备失败(可能已不存在)", zap.Uint64("network_id", id), zap.Error(err)) // 继续删除数据库记录 } } // 再删除数据库记录 if err := tx.Delete(&network, id).Error; err != nil { tx.Rollback() return err } // 提交事务 if err := tx.Commit().Error; err != nil { return err } return nil } // validateSubnet 验证子网格式 func (s *NetworkService) validateSubnet(subnet string) error { // 只调用一次 ParseCIDR ip, ipNet, err := net.ParseCIDR(subnet) if err != nil { return fmt.Errorf("无效的子网格式:%s", subnet) } // 检查是否是有效的 WireGuard 子网(至少 /24 或更小) ones, bits := ipNet.Mask.Size() if bits == 32 && ones > 24 { return errors.New("IPv4 子网掩码不能大于 /24") } if bits == 128 && ones > 64 { return errors.New("IPv6 子网掩码不能大于 /64") } // 检查是否是私有地址段 if !isPrivateSubnet(ip, ipNet) { return errors.New("请使用私有地址段(如 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)") } return nil } // isPrivateSubnet 检查是否是私有地址段 func isPrivateSubnet(ip net.IP, ipNet *net.IPNet) bool { // IPv4 私有地址段 privateRanges := []string{ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", // CGNAT } for _, privRange := range privateRanges { _, privNet, _ := net.ParseCIDR(privRange) if privNet.Contains(ip) { return true } } return false } // generateNetworkSecret 生成 Network Secret func generateNetworkSecret() string { // 使用加密安全的随机数生成器 bytes := make([]byte, 32) if _, err := rand.Read(bytes); err != nil { // ✅ 随机数生成失败时 panic,而不是返回弱密码 panic(fmt.Sprintf("生成安全随机数失败:%v", err)) } return hex.EncodeToString(bytes) } // hashUint64 计算 uint64 哈希值 func hashUint64(id uint64) int { h := sha256.Sum256([]byte(fmt.Sprintf("%d", id))) // 取前 4 字节转换为 int return int(h[0])<<24 | int(h[1])<<16 | int(h[2])<<8 | int(h[3]) } // StartNetwork 启动网络 func (s *NetworkService) StartNetwork(id uint64) error { network, err := s.GetNetworkByID(id) if err != nil { return err } // 如果已经有 ctr,直接调用 CreateNetwork if s.ctrClient != nil { listenPort := 51820 + hashUint64(network.ID)%1000 if err := s.ctrClient.CreateNetwork(network.ID, network.SubnetIPv4, listenPort, network.Mode); err != nil { s.logger.Error("启动网络失败", zap.Uint64("network_id", network.ID), zap.Error(err)) return fmt.Errorf("启动网络失败:%w", err) } s.logger.Info("网络已启动", zap.Uint64("network_id", network.ID), zap.Int("listen_port", listenPort)) } return nil } // StopNetwork 停止网络 func (s *NetworkService) StopNetwork(id uint64) error { network, err := s.GetNetworkByID(id) if err != nil { return err } // 调用 ctr 删除网络(清理 WG 设备) if s.ctrClient != nil { if err := s.ctrClient.DeleteNetwork(network.ID); err != nil { s.logger.Error("停止网络失败", zap.Uint64("network_id", network.ID), zap.Error(err)) return fmt.Errorf("停止网络失败:%w", err) } s.logger.Info("网络已停止", zap.Uint64("network_id", network.ID)) } return nil } // SwitchMode 切换组网模式 func (s *NetworkService) SwitchMode(id uint64, meshMode string) error { network, err := s.GetNetworkByID(id) if err != nil { return err } // 更新数据库 if err := s.store.DB().Model(network).Update("mode", meshMode).Error; err != nil { return err } s.logger.Info("组网模式已切换", zap.Uint64("network_id", network.ID), zap.String("mode", meshMode)) return nil }