package firewall import ( "crypto/sha256" "fmt" "net" "os/exec" "sync" ) var setApplyMu sync.Mutex func replaceSet(name, family string, addresses []string) error { setApplyMu.Lock() defer setApplyMu.Unlock() if len(name) > 31 { return fmt.Errorf("address list name too long: %s", name) } for _, address := range addresses { ip := net.ParseIP(address) if ip == nil { ip, _, _ = net.ParseCIDR(address) } if ip == nil || (ip.To4() != nil) != (family == "inet") { return fmt.Errorf("invalid %s address: %s", family, address) } } hash := sha256.Sum256([]byte(name)) temp := fmt.Sprintf("dt_tmp_%x", hash[:8]) run := func(args ...string) error { out, err := exec.Command("ipset", args...).CombinedOutput() if err != nil { return fmt.Errorf("ipset %v: %s: %w", args, out, err) } return nil } if err := run("create", temp, "hash:net", "family", family, "maxelem", "262144", "-exist"); err != nil { return err } defer exec.Command("ipset", "destroy", temp).Run() if err := run("flush", temp); err != nil { return err } for _, address := range addresses { if err := run("add", temp, address, "-exist"); err != nil { return err } } if err := run("create", name, "hash:net", "family", family, "maxelem", "262144", "-exist"); err != nil { return err } return run("swap", temp, name) }