| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package validator
- import (
- "fmt"
- "net"
- "strings"
- "networktool/internal/model"
- )
- type Service struct{}
- func New() *Service { return &Service{} }
- func (s *Service) Validate(input model.InterfaceConfig) model.ValidateResponse {
- resp := model.ValidateResponse{Valid: false, Warnings: []string{}, Errors: []string{}}
- addresses := normalizedAddresses(input)
- routes := normalizedRoutes(input)
- if input.Interface == "" {
- resp.Errors = append(resp.Errors, "目标接口不能为空。")
- }
- for _, dns := range input.DNS {
- if dns == "" {
- continue
- }
- parsed := net.ParseIP(dns)
- if parsed == nil || parsed.To4() == nil {
- resp.Errors = append(resp.Errors, fmt.Sprintf("DNS 格式不正确:%s", dns))
- }
- }
- if input.Dhcp4 {
- resp.Valid = len(resp.Errors) == 0
- return resp
- }
- if len(addresses) == 0 {
- resp.Errors = append(resp.Errors, "至少需要填写一个 IP 地址。")
- }
- seenAddresses := make(map[string]struct{})
- validNetworks := make([]*net.IPNet, 0, len(addresses))
- for _, address := range addresses {
- ip := net.ParseIP(address.IP)
- if ip == nil || ip.To4() == nil {
- resp.Errors = append(resp.Errors, fmt.Sprintf("IP 地址格式不正确:%s", address.IP))
- continue
- }
- if address.Prefix < 0 || address.Prefix > 32 {
- resp.Errors = append(resp.Errors, fmt.Sprintf("前缀长度不正确:%s/%d", address.IP, address.Prefix))
- continue
- }
- if isNetworkOrBroadcastAddress(ip, address.Prefix) {
- resp.Errors = append(resp.Errors, fmt.Sprintf("IP 地址不能是网络地址或广播地址:%s/%d", address.IP, address.Prefix))
- continue
- }
- key := fmt.Sprintf("%s/%d", ip.String(), address.Prefix)
- if _, ok := seenAddresses[key]; ok {
- resp.Errors = append(resp.Errors, fmt.Sprintf("IP 地址重复:%s", key))
- continue
- }
- seenAddresses[key] = struct{}{}
- mask := net.CIDRMask(address.Prefix, 32)
- 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)
- if to == "" {
- resp.Errors = append(resp.Errors, "路由目标不能为空。")
- continue
- }
- if via == "" {
- resp.Errors = append(resp.Errors, fmt.Sprintf("路由 %s 的下一跳不能为空。", to))
- continue
- }
- 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))
- }
- }
- gateway := net.ParseIP(via)
- if gateway == nil || gateway.To4() == nil {
- resp.Errors = append(resp.Errors, fmt.Sprintf("路由下一跳格式不正确:%s", via))
- continue
- }
- key := to + " via " + gateway.String()
- if _, ok := seenRoutes[key]; ok {
- resp.Errors = append(resp.Errors, fmt.Sprintf("路由重复:%s", key))
- continue
- }
- seenRoutes[key] = struct{}{}
- if len(validNetworks) > 0 && !containsGateway(validNetworks, gateway) {
- resp.Errors = append(resp.Errors, fmt.Sprintf("路由下一跳与任一目标接口 IP 都不在同一子网:%s", via))
- }
- }
- resp.Valid = len(resp.Errors) == 0
- return resp
- }
- func normalizedAddresses(input model.InterfaceConfig) []model.InterfaceAddressConfig {
- if len(input.Addresses) > 0 {
- return input.Addresses
- }
- if strings.TrimSpace(input.IP) == "" {
- return nil
- }
- return []model.InterfaceAddressConfig{{IP: strings.TrimSpace(input.IP), Prefix: input.Prefix}}
- }
- func normalizedRoutes(input model.InterfaceConfig) []model.InterfaceRouteConfig {
- if len(input.Routes) > 0 {
- return input.Routes
- }
- if strings.TrimSpace(input.Gateway) == "" {
- return nil
- }
- return []model.InterfaceRouteConfig{{To: "default", Via: strings.TrimSpace(input.Gateway)}}
- }
- func containsGateway(networks []*net.IPNet, gateway net.IP) bool {
- for _, network := range networks {
- if network.Contains(gateway) {
- return true
- }
- }
- return false
- }
- func isNetworkOrBroadcastAddress(ip net.IP, prefix int) bool {
- if prefix > 30 {
- return false
- }
- ipv4 := ip.To4()
- if ipv4 == nil {
- return false
- }
- mask := net.CIDRMask(prefix, 32)
- network := ipv4.Mask(mask)
- broadcast := make(net.IP, len(network))
- for i := range network {
- broadcast[i] = network[i] | ^mask[i]
- }
- return ipv4.Equal(network) || ipv4.Equal(broadcast)
- }
|