func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error {
	return ipt.run("-t", table, "-E", oldChain, newChain)
}
RenameChain renames the old chain to the new one.
package iptables
import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"syscall"
)
type Error struct {
	exec.ExitError
	cmd        exec.Cmd
	msg        string
	proto      Protocol
	exitStatus *int  
}
func (e *Error) ExitStatus() int {
	if e.exitStatus != nil {
		return *e.exitStatus
	}
	return e.Sys().(syscall.WaitStatus).ExitStatus()
}
func (e *Error) Error() string {
	return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg)
}
func (e *Error) IsNotExist() bool {
	return e.ExitStatus() == 1 &&
		(e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) ||
			e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto)))
}
type Protocol byte
const (
	ProtocolIPv4 Protocol = iota
	ProtocolIPv6
)
type IPTables struct {
	path           string
	proto          Protocol
	hasCheck       bool
	hasWait        bool
	hasRandomFully bool
	v1             int
	v2             int
	v3             int
	mode           string  
}
type Stat struct {
	Packets     uint64     `json:"pkts"`
	Bytes       uint64     `json:"bytes"`
	Target      string     `json:"target"`
	Protocol    string     `json:"prot"`
	Opt         string     `json:"opt"`
	Input       string     `json:"in"`
	Output      string     `json:"out"`
	Source      *net.IPNet `json:"source"`
	Destination *net.IPNet `json:"destination"`
	Options     string     `json:"options"`
}
func New() (*IPTables, error) {
	return NewWithProtocol(ProtocolIPv4)
}
func NewWithProtocol(proto Protocol) (*IPTables, error) {
	path, err := exec.LookPath(getIptablesCommand(proto))
	if err != nil {
		return nil, err
	}
	vstring, err := getIptablesVersionString(path)
	v1, v2, v3, mode, err := extractIptablesVersion(vstring)
	checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
	ipt := IPTables{
		path:           path,
		proto:          proto,
		hasCheck:       checkPresent,
		hasWait:        waitPresent,
		hasRandomFully: randomFullyPresent,
		v1:             v1,
		v2:             v2,
		v3:             v3,
		mode:           mode,
	}
	return &ipt, nil
}
func (ipt *IPTables) Proto() Protocol {
	return ipt.proto
}
func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) {
	if !ipt.hasCheck {
		return ipt.existsForOldIptables(table, chain, rulespec)
	}
	cmd := append([]string{"-t", table, "-C", chain}, rulespec...)
	err := ipt.run(cmd...)
	eerr, eok := err.(*Error)
	switch {
	case err == nil:
		return true, nil
	case eok && eerr.ExitStatus() == 1:
		return false, nil
	default:
		return false, err
	}
}
func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...)
	return ipt.run(cmd...)
}
func (ipt *IPTables) Append(table, chain string, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
	return ipt.run(cmd...)
}
func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error {
	exists, err := ipt.Exists(table, chain, rulespec...)
	if err != nil {
		return err
	}
	if !exists {
		return ipt.Append(table, chain, rulespec...)
	}
	return nil
}
func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-D", chain}, rulespec...)
	return ipt.run(cmd...)
}
func (ipt *IPTables) List(table, chain string) ([]string, error) {
	args := []string{"-t", table, "-S", chain}
	return ipt.executeList(args)
}
func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) {
	args := []string{"-t", table, "-v", "-S", chain}
	return ipt.executeList(args)
}
func (ipt *IPTables) ListChains(table string) ([]string, error) {
	args := []string{"-t", table, "-S"}
	result, err := ipt.executeList(args)
	if err != nil {
		return nil, err
	}
	var chains []string
	for _, val := range result {
		if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") {
			chains = append(chains, strings.Fields(val)[1])
		} else {
			break
		}
	}
	return chains, nil
}
func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
	args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
	lines, err := ipt.executeList(args)
	if err != nil {
		return nil, err
	}
	appendSubnet := func(addr string) string {
		if strings.IndexByte(addr, byte('/')) < 0 {
			if strings.IndexByte(addr, '.') < 0 {
				return addr + "/128"
			}
			return addr + "/32"
		}
		return addr
	}
	ipv6 := ipt.proto == ProtocolIPv6
	rows := [][]string{}
	for i, line := range lines {
		if i < 2 {
			continue
		}
		line = strings.TrimSpace(line)
		fields := strings.Fields(line)
		if ipv6 {
			dest := fields[6]
			ip, _, _ := net.ParseCIDR(dest)
			if ip == nil {
				ip = net.ParseIP(dest)
			}
			if ip != nil {
				f := []string{}
				f = append(f, fields[:4]...)
				f = append(f, "  ")  
				f = append(f, fields[4:]...)
				fields = f
			}
		}
		fields[7] = appendSubnet(fields[7])
		fields[8] = appendSubnet(fields[8])
		options := fields[9:]
		fields = fields[:9]
		fields = append(fields, strings.Join(options, " "))
		rows = append(rows, fields)
	}
	return rows, nil
}
func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) {
	if len(stat) < 10 {
		return parsed, fmt.Errorf("stat contained fewer fields than expected")
	}
	parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64)
	if err != nil {
		return parsed, fmt.Errorf(err.Error(), "could not parse packets")
	}
	parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64)
	if err != nil {
		return parsed, fmt.Errorf(err.Error(), "could not parse bytes")
	}
	_, parsed.Source, err = net.ParseCIDR(stat[7])
	if err != nil {
		return parsed, fmt.Errorf(err.Error(), "could not parse source")
	}
	_, parsed.Destination, err = net.ParseCIDR(stat[8])
	if err != nil {
		return parsed, fmt.Errorf(err.Error(), "could not parse destination")
	}
	parsed.Target = stat[2]
	parsed.Protocol = stat[3]
	parsed.Opt = stat[4]
	parsed.Input = stat[5]
	parsed.Output = stat[6]
	parsed.Options = stat[9]
	return parsed, nil
}
func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) {
	rawStats, err := ipt.Stats(table, chain)
	if err != nil {
		return nil, err
	}
	structStats := []Stat{}
	for _, rawStat := range rawStats {
		stat, err := ipt.ParseStat(rawStat)
		if err != nil {
			return nil, err
		}
		structStats = append(structStats, stat)
	}
	return structStats, nil
}
func (ipt *IPTables) executeList(args []string) ([]string, error) {
	var stdout bytes.Buffer
	if err := ipt.runWithOutput(args, &stdout); err != nil {
		return nil, err
	}
	rules := strings.Split(stdout.String(), "\n")
	if len(rules) > 0 && rules[len(rules)-1] == "" {
		rules = rules[:len(rules)-1]
	}
	if len(rules) == 0 && ipt.mode == "nf_tables" {
		v := 1
		return nil, &Error{
			cmd:        exec.Cmd{Args: args},
			msg:        fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)),
			proto:      ipt.proto,
			exitStatus: &v,
		}
	}
	for i, rule := range rules {
		rules[i] = filterRuleOutput(rule)
	}
	return rules, nil
}
func (ipt *IPTables) NewChain(table, chain string) error {
	return ipt.run("-t", table, "-N", chain)
}
const existsErr = 1
func (ipt *IPTables) ClearChain(table, chain string) error {
	err := ipt.NewChain(table, chain)
	eerr, eok := err.(*Error)
	switch {
	case err == nil:
		return nil
	case eok && eerr.ExitStatus() == existsErr:
		return ipt.run("-t", table, "-F", chain)
	default:
		return err
	}
}