validator.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package validator
  2. import (
  3. "fmt"
  4. "net"
  5. "strings"
  6. "nettool/internal/model"
  7. )
  8. type Service struct{}
  9. func New() *Service { return &Service{} }
  10. func (s *Service) Validate(input model.InterfaceConfig) model.ValidateResponse {
  11. resp := model.ValidateResponse{Valid: false, Warnings: []string{}, Errors: []string{}}
  12. addresses := normalizedAddresses(input)
  13. routes := normalizedRoutes(input)
  14. if input.Interface == "" {
  15. resp.Errors = append(resp.Errors, "目标接口不能为空。")
  16. }
  17. for _, dns := range input.DNS {
  18. if dns == "" {
  19. continue
  20. }
  21. parsed := net.ParseIP(dns)
  22. if parsed == nil || parsed.To4() == nil {
  23. resp.Errors = append(resp.Errors, fmt.Sprintf("DNS 格式不正确:%s", dns))
  24. }
  25. }
  26. if input.Dhcp4 {
  27. resp.Valid = len(resp.Errors) == 0
  28. return resp
  29. }
  30. seenAddresses := make(map[string]struct{})
  31. validNetworks := make([]*net.IPNet, 0, len(addresses))
  32. for _, address := range addresses {
  33. ip := net.ParseIP(address.IP)
  34. if ip == nil || ip.To4() == nil {
  35. resp.Errors = append(resp.Errors, fmt.Sprintf("IP 地址格式不正确:%s", address.IP))
  36. continue
  37. }
  38. if address.Prefix < 0 || address.Prefix > 32 {
  39. resp.Errors = append(resp.Errors, fmt.Sprintf("前缀长度不正确:%s/%d", address.IP, address.Prefix))
  40. continue
  41. }
  42. if isNetworkOrBroadcastAddress(ip, address.Prefix) {
  43. resp.Errors = append(resp.Errors, fmt.Sprintf("接口 IP 不能填写网段地址或广播地址:%s/%d", address.IP, address.Prefix))
  44. continue
  45. }
  46. key := fmt.Sprintf("%s/%d", ip.String(), address.Prefix)
  47. if _, ok := seenAddresses[key]; ok {
  48. resp.Errors = append(resp.Errors, fmt.Sprintf("IP 地址重复:%s", key))
  49. continue
  50. }
  51. seenAddresses[key] = struct{}{}
  52. mask := net.CIDRMask(address.Prefix, 32)
  53. validNetworks = append(validNetworks, &net.IPNet{IP: ip.Mask(mask), Mask: mask})
  54. }
  55. seenRoutes := make(map[string]struct{})
  56. defaultRouteCount := 0
  57. for _, route := range routes {
  58. to := strings.TrimSpace(route.To)
  59. via := strings.TrimSpace(route.Via)
  60. if to == "" {
  61. resp.Errors = append(resp.Errors, "路由目标不能为空。")
  62. continue
  63. }
  64. if via == "" {
  65. resp.Errors = append(resp.Errors, fmt.Sprintf("路由 %s 的下一跳不能为空。", to))
  66. continue
  67. }
  68. if to == "default" {
  69. defaultRouteCount++
  70. if defaultRouteCount > 1 {
  71. resp.Errors = append(resp.Errors, "默认网关只能配置一个。")
  72. }
  73. } else {
  74. ip, ipNet, err := net.ParseCIDR(to)
  75. if err != nil || ip == nil || ip.To4() == nil || ipNet == nil {
  76. resp.Errors = append(resp.Errors, fmt.Sprintf("路由目标格式不正确:%s", to))
  77. } else {
  78. ipv4 := ip.To4()
  79. networkIP := ipv4.Mask(ipNet.Mask)
  80. prefix, _ := ipNet.Mask.Size()
  81. if !ipv4.Equal(networkIP) {
  82. resp.Errors = append(resp.Errors, fmt.Sprintf("路由目标必须填写目标网段地址(网络号):当前为 %s,应为 %s/%d", to, networkIP.String(), prefix))
  83. }
  84. }
  85. }
  86. gateway := net.ParseIP(via)
  87. if gateway == nil || gateway.To4() == nil {
  88. resp.Errors = append(resp.Errors, fmt.Sprintf("路由下一跳格式不正确:%s", via))
  89. continue
  90. }
  91. key := to + " via " + gateway.String()
  92. if _, ok := seenRoutes[key]; ok {
  93. resp.Errors = append(resp.Errors, fmt.Sprintf("路由重复:%s", key))
  94. continue
  95. }
  96. seenRoutes[key] = struct{}{}
  97. if len(validNetworks) > 0 && !containsGateway(validNetworks, gateway) {
  98. resp.Errors = append(resp.Errors, fmt.Sprintf("路由下一跳与任一目标接口 IP 都不在同一子网:%s", via))
  99. }
  100. }
  101. resp.Valid = len(resp.Errors) == 0
  102. return resp
  103. }
  104. func normalizedAddresses(input model.InterfaceConfig) []model.InterfaceAddressConfig {
  105. if len(input.Addresses) > 0 {
  106. return input.Addresses
  107. }
  108. if strings.TrimSpace(input.IP) == "" {
  109. return nil
  110. }
  111. return []model.InterfaceAddressConfig{{IP: strings.TrimSpace(input.IP), Prefix: input.Prefix}}
  112. }
  113. func normalizedRoutes(input model.InterfaceConfig) []model.InterfaceRouteConfig {
  114. if len(input.Routes) > 0 {
  115. return input.Routes
  116. }
  117. if strings.TrimSpace(input.Gateway) == "" {
  118. return nil
  119. }
  120. return []model.InterfaceRouteConfig{{To: "default", Via: strings.TrimSpace(input.Gateway)}}
  121. }
  122. func containsGateway(networks []*net.IPNet, gateway net.IP) bool {
  123. for _, network := range networks {
  124. if network.Contains(gateway) {
  125. return true
  126. }
  127. }
  128. return false
  129. }
  130. func isNetworkOrBroadcastAddress(ip net.IP, prefix int) bool {
  131. if prefix > 30 {
  132. return false
  133. }
  134. ipv4 := ip.To4()
  135. if ipv4 == nil {
  136. return false
  137. }
  138. mask := net.CIDRMask(prefix, 32)
  139. network := ipv4.Mask(mask)
  140. broadcast := make(net.IP, len(network))
  141. for i := range network {
  142. broadcast[i] = network[i] | ^mask[i]
  143. }
  144. return ipv4.Equal(network) || ipv4.Equal(broadcast)
  145. }