package app import ( "fmt" "strings" "time" ) func readCurrentRoutesTableLines() ([]string, error) { out, _, code, err := runCommandTimeout(5*time.Second, "ip", "-4", "route", "show", "table", routesTableName()) if err != nil && code != 0 { return nil, err } lines := make([]string, 0, 32) for _, raw := range strings.Split(out, "\n") { ln := strings.TrimSpace(raw) if ln == "" { continue } lines = append(lines, ln) } return lines, nil } func detectIfaceFromRoutes(lines []string) string { for _, ln := range lines { fields := strings.Fields(ln) for i := 0; i+1 < len(fields); i++ { if fields[i] == "dev" { return strings.TrimSpace(fields[i+1]) } } } return "" } func restoreRouteLine(line string) error { fields := strings.Fields(strings.TrimSpace(line)) if len(fields) == 0 { return nil } args := []string{"-4", "route", "replace"} args = append(args, fields...) hasTable := false for i := 0; i+1 < len(fields); i++ { if fields[i] == "table" { hasTable = true break } } if !hasTable { args = append(args, "table", routesTableName()) } _, _, code, err := runCommandTimeout(5*time.Second, "ip", args...) if err != nil || code != 0 { if err == nil { err = fmt.Errorf("exit code %d", code) } return err } return nil } func shouldIgnoreRestoreRouteError(line string, err error) bool { ln := strings.ToLower(strings.TrimSpace(line)) if strings.Contains(ln, " linkdown") { return true } dev := routeLineDevice(ln) if dev != "" && !strings.HasPrefix(ln, "default ") && !ifaceExists(dev) { return true } msg := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", err))) if strings.HasPrefix(ln, "default ") { return false } if strings.Contains(msg, "cannot find device") || strings.Contains(msg, "no such device") || strings.Contains(msg, "network is down") { return true } return false } func routeLineDevice(line string) string { fields := strings.Fields(strings.TrimSpace(line)) for i := 0; i+1 < len(fields); i++ { if fields[i] == "dev" { return strings.TrimSpace(fields[i+1]) } } return "" }