Преглед изворни кода

fix(network): 限制单接口及批量配置仅允许一个默认网关

防止多网口同时配置默认路由导致网络冲突,增强校验逻辑。
yangkaixiang пре 1 месец
родитељ
комит
abe8eae811

+ 2 - 1
docs/03-通信与HTTP_API.md

@@ -305,7 +305,8 @@ X-Admin-Password: Dt123$
 6. `routes[].to` 必须为 `default` 或 IPv4 CIDR
 7. `routes[].via` 必须为 IPv4 地址,且必须与任一 `addresses` 在同一子网
 8. 若任一 `addresses[].ip` 为 `169.254.x.x`,返回中文警告,不直接报错
-9. 为兼容旧客户端,Server 仍接受旧字段 `ip`、`prefix`、`gateway`,并转换为 `addresses` 与默认路由
+9. 单个接口最多只能配置 1 条 `default` 路由
+10. 批量配置中最多只能有 1 个网口配置默认网关;多个网口同时配置默认网关时校验失败
 
 ### 3.6 应用指定接口配置
 

+ 1 - 1
server/internal/config/config.go

@@ -6,7 +6,7 @@ import (
 	"net"
 )
 
-const ServerVersion = "20260511180458"
+const ServerVersion = "20260512120500"
 
 type Config struct {
 	HTTPHost         string

+ 20 - 0
server/internal/httpserver/server.go

@@ -505,6 +505,7 @@ func (s *Server) validateConfigs(inputs []model.InterfaceConfig) model.ValidateR
 		return result
 	}
 	seen := make(map[string]struct{})
+	defaultRouteInterfaces := make([]string, 0, 1)
 	managementInterface := s.currentManagementInterface()
 	for _, input := range inputs {
 		name := strings.TrimSpace(input.Interface)
@@ -524,6 +525,9 @@ func (s *Server) validateConfigs(inputs []model.InterfaceConfig) model.ValidateR
 			result.Errors = append(result.Errors, fmt.Sprintf("目标接口不存在:%s", name))
 			continue
 		}
+		if hasDefaultRouteConfig(input) {
+			defaultRouteInterfaces = append(defaultRouteInterfaces, name)
+		}
 		item := s.validatorSvc.Validate(input)
 		if name == managementInterface {
 			addManagementAddressWarning(&item, input)
@@ -538,9 +542,25 @@ func (s *Server) validateConfigs(inputs []model.InterfaceConfig) model.ValidateR
 			result.Warnings = append(result.Warnings, fmt.Sprintf("%s:%s", name, warning))
 		}
 	}
+	if len(defaultRouteInterfaces) > 1 {
+		result.Valid = false
+		result.Errors = append(result.Errors, fmt.Sprintf("只能有一个网口配置默认网关,当前检测到多个默认网关:%s。", strings.Join(defaultRouteInterfaces, "、")))
+	}
 	return result
 }
 
+func hasDefaultRouteConfig(input model.InterfaceConfig) bool {
+	if input.Dhcp4 {
+		return false
+	}
+	for _, route := range input.Routes {
+		if strings.TrimSpace(route.To) == "default" && strings.TrimSpace(route.Via) != "" {
+			return true
+		}
+	}
+	return false
+}
+
 func (s *Server) addManagementAddressWarning(result *model.ValidateResponse, input model.InterfaceConfig) {
 	managementInterface := s.currentManagementInterface()
 	if managementInterface == "" || strings.TrimSpace(input.Interface) != managementInterface {

+ 7 - 1
server/internal/network/validator/validator.go

@@ -58,6 +58,7 @@ func (s *Service) Validate(input model.InterfaceConfig) model.ValidateResponse {
 		validNetworks = append(validNetworks, &net.IPNet{IP: ip.Mask(mask), Mask: mask})
 	}
 	seenRoutes := make(map[string]struct{})
+	defaultRouteCount := 0
 	for _, route := range routes {
 		to := strings.TrimSpace(route.To)
 		via := strings.TrimSpace(route.Via)
@@ -69,7 +70,12 @@ func (s *Service) Validate(input model.InterfaceConfig) model.ValidateResponse {
 			resp.Errors = append(resp.Errors, fmt.Sprintf("路由 %s 的下一跳不能为空。", to))
 			continue
 		}
-		if to != "default" {
+		if to == "default" {
+			defaultRouteCount++
+			if defaultRouteCount > 1 {
+				resp.Errors = append(resp.Errors, "默认网关只能配置一个。")
+			}
+		} else {
 			ip, ipNet, err := net.ParseCIDR(to)
 			if err != nil || ip == nil || ip.To4() == nil || ipNet == nil {
 				resp.Errors = append(resp.Errors, fmt.Sprintf("路由目标格式不正确:%s", to))