package firewall import ( "os/exec" "strings" ) // SyncAddressLists updates Linux ipsets to match the configured AddressList entries func SyncAddressLists(entries []AddressListEntry) 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) } // Clean up any mk_ sets that no longer exist or are now empty (ignoring mk_geo_ sets managed by GeoIP) if out, err := exec.Command("ipset", "list", "-name").Output(); err == nil { lines := strings.Split(string(out), "\n") for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "mk_") && !strings.HasPrefix(line, "mk_geo_") { name := strings.TrimPrefix(line, "mk_") if _, ok := listMap[name]; !ok { _ = exec.Command("ipset", "flush", line).Run() _ = exec.Command("ipset", "destroy", line).Run() } } } } for name, addrs := range listMap { setName := "mk_" + strings.ReplaceAll(name, " ", "_") // Create set if not exists _ = exec.Command("ipset", "create", setName, "hash:net", "-exist").Run() _ = exec.Command("ipset", "flush", setName).Run() for _, a := range addrs { _ = exec.Command("ipset", "add", setName, a, "-exist").Run() } } 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, " ", "_") }