107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
package net
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/coreos/go-iptables/iptables"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// AddChainWithRules creates a chain and appends given rules to it.
|
|
//
|
|
// If the chain exists, but its rules are not the same as the given ones, the
|
|
// function will flush the chain and then will append the rules.
|
|
func AddChainWithRules(ipt *iptables.IPTables, table, chain string, rulespecs [][]string) error {
|
|
if err := ensureChains(ipt, table, chain); err != nil {
|
|
return err
|
|
}
|
|
|
|
currRuleSpecs, err := ipt.List(table, chain)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "iptables -S. table: %q, chain: %q", table, chain)
|
|
}
|
|
|
|
// First returned rule is "-N $(chain)", so ignore it
|
|
currRules := strings.Join(currRuleSpecs[1:], "\n")
|
|
rules := make([]string, 0)
|
|
for _, r := range rulespecs {
|
|
rules = append(rules, strings.Join(r, " "))
|
|
}
|
|
reqRules := strings.Join(rules, "\n")
|
|
|
|
if currRules == reqRules {
|
|
return nil
|
|
}
|
|
|
|
if err := ipt.ClearChain(table, chain); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, r := range rulespecs {
|
|
if err := ipt.Append(table, chain, r...); err != nil {
|
|
return errors.Wrapf(err, "iptables -A. table: %q, chain: %q, rule: %s", table, chain, r)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ensureChains creates given chains if they do not exist.
|
|
func ensureChains(ipt *iptables.IPTables, table string, chains ...string) error {
|
|
existingChains, err := ipt.ListChains(table)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "ipt.ListChains(%s)", table)
|
|
}
|
|
chainMap := make(map[string]struct{})
|
|
for _, c := range existingChains {
|
|
chainMap[c] = struct{}{}
|
|
}
|
|
|
|
for _, c := range chains {
|
|
if _, found := chainMap[c]; !found {
|
|
if err := ipt.NewChain(table, c); err != nil {
|
|
return errors.Wrapf(err, "ipt.NewChain(%s, %s)", table, c)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ensureRulesAtTop ensures the presence of given iptables rules.
|
|
//
|
|
// If any rule from the list is missing, the function deletes all given
|
|
// rules and re-inserts them at the top of the chain to ensure the order of the rules.
|
|
func ensureRulesAtTop(table, chain string, rulespecs [][]string, ipt *iptables.IPTables) error {
|
|
allFound := true
|
|
|
|
for _, rs := range rulespecs {
|
|
found, err := ipt.Exists(table, chain, rs...)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "ipt.Exists(%s, %s, %s)", table, chain, rs)
|
|
}
|
|
if !found {
|
|
allFound = false
|
|
break
|
|
}
|
|
}
|
|
|
|
// All rules exist, do nothing.
|
|
if allFound {
|
|
return nil
|
|
}
|
|
|
|
for pos, rs := range rulespecs {
|
|
// If any is missing, then delete all, as we need to preserve the order of
|
|
// given rules. Ignore errors, as rule might not exist.
|
|
if !allFound {
|
|
ipt.Delete(table, chain, rs...)
|
|
}
|
|
if err := ipt.Insert(table, chain, pos+1, rs...); err != nil {
|
|
return errors.Wrapf(err, "ipt.Append(%s, %s, %s)", table, chain, rs)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|