package firewall import ( "encoding/base64" "encoding/json" "fmt" "net" "os/exec" "strconv" "strings" ) func dynamicEntryID(name, address string) string { b, _ := json.Marshal([]string{name, address}) return "dynamic-" + base64.RawURLEncoding.EncodeToString(b) } func deleteDynamicEntry(id string) error { b, err := base64.RawURLEncoding.DecodeString(strings.TrimPrefix(id, "dynamic-")) if err != nil { return fmt.Errorf("invalid dynamic entry ID") } var fields []string if json.Unmarshal(b, &fields) != nil || len(fields) != 2 || net.ParseIP(fields[1]) == nil { return fmt.Errorf("invalid dynamic entry ID") } if err := validateListName(fields[0]); err != nil { return err } name := GetDynamicIPSetName(fields[0]) if net.ParseIP(fields[1]).To4() == nil { name += "_v6" } return runIPSet("del", name, fields[1], "-exist") } func (m *FirewallManager) addressListReferences() []string { var refs []string for _, r := range m.filterRules { if !r.Disabled { refs = append(refs, r.SrcAddressList, r.DstAddressList, r.AddressList) } } for _, r := range m.natRules { if !r.Disabled { refs = append(refs, r.SrcAddressList, r.DstAddressList, r.AddressList) } } for _, r := range m.mangleRules { if !r.Disabled { refs = append(refs, r.SrcAddressList, r.DstAddressList, r.AddressList) } } return refs } func (m *FirewallManager) dynamicEntries() []AddressListEntry { names := map[string]bool{} for _, e := range m.addressLists { names[e.Name] = true } for _, name := range m.addressListReferences() { name = strings.TrimPrefix(name, "!") if name != "" && !strings.HasPrefix(name, "geo:") && !strings.HasPrefix(name, "geo_") { names[name] = true } } var result []AddressListEntry for name := range names { for _, suffix := range []string{"", "_v6"} { set := GetDynamicIPSetName(name) + suffix out, err := exec.Command("ipset", "save", set).Output() if err != nil { continue } for _, line := range strings.Split(string(out), "\n") { parts := strings.Fields(line) if len(parts) < 3 || parts[0] != "add" { continue } ip := net.ParseIP(parts[2]) if ip == nil { continue } remaining := 0 for i := 3; i+1 < len(parts); i++ { if parts[i] == "timeout" { remaining, _ = strconv.Atoi(parts[i+1]) } } result = append(result, AddressListEntry{ID: dynamicEntryID(name, ip.String()), Name: name, Address: ip.String(), Dynamic: true, Timeout: remaining}) if len(result) >= 2000 { return result } } } } return result }