package firewall import ( "fmt" "net" "strings" ) // SyncAddressLists updates Linux ipsets to match the configured AddressList entries and referenced lists func SyncAddressLists(entries []AddressListEntry, referencedLists ...string) error { // Group addresses by list name listMap := make(map[string][]string) for _, e := range entries { if e.Disabled || e.Name == "" || e.Address == "" { continue } listMap[e.Name] = append(listMap[e.Name], e.Address) } // Ensure any custom list explicitly referenced in rules exists even if empty for _, ref := range referencedLists { ref = strings.TrimPrefix(ref, "!") if ref != "" && !strings.HasPrefix(ref, "geo:") && !strings.HasPrefix(ref, "geo_") { if _, ok := listMap[ref]; !ok { listMap[ref] = []string{} } } } for name, addrs := range listMap { var v4, v6 []string for _, address := range addrs { ip := net.ParseIP(address) if ip == nil { ip, _, _ = net.ParseCIDR(address) } if ip == nil { return fmt.Errorf("invalid address: %s", address) } if ip.To4() != nil { v4 = append(v4, address) } else { v6 = append(v6, address) } } if err := replaceSet(GetIPSetName(name), "inet", v4); err != nil { return err } if err := replaceSet(GetIPSetName(name)+"_v6", "inet6", v6); err != nil { return err } } return nil } // GetIPSetName returns the internal ipset name for an address list func GetIPSetName(listName string) string { if listName == "" { return "" } clean := strings.TrimPrefix(listName, "!") if strings.HasPrefix(clean, "geo:") { code := strings.ToUpper(strings.TrimPrefix(clean, "geo:")) return "mk_geo_" + code } if strings.HasPrefix(clean, "geo_") { return "mk_" + clean } return "mk_" + strings.ReplaceAll(clean, " ", "_") } // GetGeoIPSetName returns the internal ipset name for a Geo-Location country or preset func GetGeoIPSetName(code string) string { if code == "" { return "" } clean := strings.TrimPrefix(code, "!") clean = strings.TrimPrefix(clean, "geo:") clean = strings.TrimPrefix(clean, "geo_") return "mk_geo_" + strings.ToUpper(clean) }