func (r *runner) parseIPSetSave(result string) {
	sets := make(map[string]*Set)
	lines := strings.Split(result, "\n")
	for _, line := range lines {
		content := strings.Split(line, " ")
		if content[0] == "create" {
			sets[content[1]] = &Set{
				Name:    content[1],
				Options: content[2:],
			}
		} else if content[0] == "add" {
			set := sets[content[1]]
			set.Entries = append(set.Entries, &Entry{
				Options: content[2:],
			})
		}
	}
	r.Sets = sets
}
Parse ipset save stdout.
ex:
create KUBE-DST-3YNVZWWGX3UQQ4VQ hash:ip family inet hashsize 1024 maxelem 65536 timeout 0
add KUBE-DST-3YNVZWWGX3UQQ4VQ 100.96.1.6 timeout 0
package ipset
import (
	"bytes"
	"errors"
	"os/exec"
	"strings"
	"sync"
)
var (
	errIpsetNotFound = errors.New("ipset utility not found")
)
const (
	FamillyInet = "inet"
	FamillyInet6 = "inet6"
	DefaultMaxElem = "65536"
	DefaultHasSize = "1024"
	TypeBitmapPort = "bitmap:port"
	TypeHashIP = "hash:ip"
	TypeHashMac = "hash:mac"
	TypeHashNet = "hash:net"
	TypeHashNetNet = "hash:net,net"
	TypeHashIPPort = "hash:ip,port"
	TypeHashNetPort = "hash:net,port"
	TypeHashIPPortIP = "hash:ip,port,ip"
	TypeHashIPPortNet = "hash:ip,port,net"
	TypeHashIPMark = "hash:ip,mark"
	TypeHashIPNetPortNet = "hash:net,port,net"
	TypeHashNetIface = "hash:net,iface"
	TypeListSet = "list:set"
	OptionTimeout = "timeout"
	OptionCounters = "counters"
	OptionPackets = "packets"
	OptionBytes = "bytes"
	OptionComment = "comment"
	OptionSkbinfo = "skbinfo"
	OptionSkbmark = "skbmark"
	OptionSkbprio = "skbprio"
	OptionSkbqueue = "skbqueue"
	OptionHashSize = "hashsize"
	OptionMaxElem = "maxelem"
	OptionFamilly = "family"
	OptionNoMatch = "nomatch"
	OptionForceAdd = "forceadd"
)
type Interface interface {
	AddOrReplaceIPSet(setName string, members []string, createOptions ...string)
	AddOrReplaceIPSetWithBuiltinOptions(setName string, members [][]string, createOptions ...string)
	RemoveIPSet(setName string)
	ListIPSets() []string
	LoadData() error
	SyncOperations() error
}
type Set struct {
	Name    string
	Entries []*Entry
	Options []string
}
type Entry struct {
	Options []string
}
type runner struct {
	mu        sync.Mutex
	ipSetPath string
	Sets     map[string]*Set
	commands *bytes.Buffer
	isIpv6 bool
}
func New(isIpv6 bool) (Interface, error) {
	ipSetPath, err := getIPSetPath()
	if err != nil {
		return nil, err
	}
	runner := newInternal(isIpv6, *ipSetPath)
	return runner, nil
}
func (r *runner) LoadData() error {
	r.mu.Lock()
	defer r.mu.Unlock()
	stdout, err := r.run("save")
	if err != nil {
		return err
	}
	r.parseIPSetSave(stdout)
	return nil
}
func (r *runner) AddOrReplaceIPSet(setName string, members []string, createOptions ...string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	setToCreate := setName
	needSwap := false
	if _, exist := r.Sets[setName]; exist {
		setToCreate = setName + "-tmp"
		needSwap = true
	}
	args := append([]string{"create", setToCreate}, createOptions...)
	if r.isIpv6 {
		args = append(args, "family", FamillyInet6)
	}
	r.recordCommand(args...)
	for _, member := range members {
		r.recordCommand("add", setToCreate, member)
	}
	if needSwap {
		r.recordCommand("swap", setToCreate, setName)
		r.recordCommand("destroy", setToCreate)
	}
}
func (r *runner) AddOrReplaceIPSetWithBuiltinOptions(setName string, members [][]string, createOptions ...string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	setToCreate := setName
	needSwap := false
	if _, exist := r.Sets[setName]; exist {
		setToCreate = setName + "-tmp"
		needSwap = true
	}
	args := append([]string{"create", setToCreate}, createOptions...)
	if r.isIpv6 {
		args = append(args, "family", "inet6")
	}
	r.recordCommand(args...)
	for _, member := range members {
		r.recordCommand(append([]string{"add", setToCreate}, member...)...)
	}
	if needSwap {
		r.recordCommand("swap", setToCreate, setName)
		r.recordCommand("destroy", setToCreate)
	}
}
func (r *runner) RemoveIPSet(setName string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, exist := r.Sets[setName]; exist {
		r.recordCommand("destroy", setName)
	}
}
func (r *runner) ListIPSets() []string {
	r.mu.Lock()
	defer r.mu.Unlock()
	var setList []string
	for set := range r.Sets {
		setList = append(setList, set)
	}
	return setList
}
func (r *runner) SyncOperations() error {
	r.mu.Lock()
	defer r.mu.Unlock()
	_, err := r.runWithStdin(r.commands, "restore", "-exist")
	if err != nil {
		return err
	}
	return nil
}
func newInternal(isIpv6 bool, ipSetPath string) Interface {
	runner := &runner{
		ipSetPath: ipSetPath,
		isIpv6:    isIpv6,
		commands:  bytes.NewBuffer(nil),
		Sets:      make(map[string]*Set),
	}
	return runner
}
func getIPSetPath() (*string, error) {
	path, err := exec.LookPath("ipset")
	if err != nil {
		return nil, errIpsetNotFound
	}
	return &path, nil
}
func (r *runner) run(args ...string) (string, error) {
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	cmd := exec.Cmd{
		Path:   r.ipSetPath,
		Args:   append([]string{r.ipSetPath}, args...),
		Stderr: &stderr,
		Stdout: &stdout,
	}
	if err := cmd.Run(); err != nil {
		return "", errors.New(stderr.String())
	}
	return stdout.String(), nil
}
func (r *runner) runWithStdin(stdin *bytes.Buffer, args ...string) (string, error) {
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	cmd := exec.Cmd{
		Path:   r.ipSetPath,
		Args:   append([]string{r.ipSetPath}, args...),
		Stderr: &stderr,
		Stdout: &stdout,
		Stdin:  stdin,
	}
	if err := cmd.Run(); err != nil {
		return "", errors.New(stderr.String())
	}
	return stdout.String(), nil
}