package verify import ( "bufio" "fmt" "net" "os" "os/exec" "strings" "quickip/internal/model" ) type Service struct{} func New() *Service { return &Service{} } func (s *Service) Verify(input model.InterfaceConfig, managementInterface string, maintenanceCIDR string) error { iface, err := net.InterfaceByName(input.Interface) if err != nil { return err } addrs, err := iface.Addrs() if err != nil { return err } matchedAddress := false for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) if !ok || ipNet.IP.To4() == nil { continue } prefix, _ := ipNet.Mask.Size() if ipNet.IP.String() == input.IP && prefix == input.Prefix { matchedAddress = true break } } if !matchedAddress { return fmt.Errorf("目标接口地址未按预期生效") } actualGateway := readGateway(input.Interface) if strings.TrimSpace(input.Gateway) != strings.TrimSpace(actualGateway) { return fmt.Errorf("目标接口网关未按预期生效") } actualDNS := readDNS() if !containsAll(actualDNS, input.DNS) { return fmt.Errorf("DNS 未按预期生效") } if managementInterface != "" && maintenanceCIDR != "" { ok, err := hasInterfaceCIDR(managementInterface, maintenanceCIDR) if err != nil { return err } if !ok { return fmt.Errorf("管理接口维护地址未按预期保留") } } return nil } func readGateway(interfaceName string) string { cmd := exec.Command("ip", "route", "show", "dev", interfaceName) output, err := cmd.Output() if err != nil { return "" } for _, line := range strings.Split(string(output), "\n") { line = strings.TrimSpace(line) if !strings.HasPrefix(line, "default via ") { continue } parts := strings.Fields(line) if len(parts) >= 3 { return parts[2] } } return "" } func readDNS() []string { file, err := os.Open("/etc/resolv.conf") if err != nil { return []string{} } defer file.Close() result := make([]string, 0, 2) scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if !strings.HasPrefix(line, "nameserver ") { continue } parts := strings.Fields(line) if len(parts) >= 2 { result = append(result, parts[1]) } } return result } func hasInterfaceCIDR(interfaceName string, expectedCIDR string) (bool, error) { iface, err := net.InterfaceByName(interfaceName) if err != nil { return false, err } addrs, err := iface.Addrs() if err != nil { return false, err } for _, addr := range addrs { if addr.String() == expectedCIDR { return true, nil } } return false, nil } func containsAll(actual []string, expected []string) bool { if len(expected) == 0 { return true } set := make(map[string]struct{}, len(actual)) for _, item := range actual { set[strings.TrimSpace(item)] = struct{}{} } for _, item := range expected { if _, ok := set[strings.TrimSpace(item)]; !ok { return false } } return true }