pkg/config/config.go (183 lines of code) (raw):
/*
Copyright 2018 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"os"
"strings"
"syscall"
"github.com/coreos/go-iptables/iptables"
"github.com/golang/glog"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
)
// Config interface
type Config interface {
Ensure(enabled bool) error
}
// Set defines the set of Config
type Set struct {
Enabled bool
FeatureName string
Configs []Config
}
type sysctler func(name string, params ...string) (string, error)
// SysctlConfig defines sysctl config
type SysctlConfig struct {
Key, Value, DefaultValue string
SysctlFunc sysctler
}
type routeAdder func(route *netlink.Route) error
type routeDeler func(route *netlink.Route) error
// IPRouteConfig defines route config
type IPRouteConfig struct {
Route netlink.Route
RouteAdd routeAdder
RouteDel routeDeler
}
type ruleAdder func(rule *netlink.Rule) error
type ruleDeler func(rule *netlink.Rule) error
type ruleLister func(family int) ([]netlink.Rule, error)
// IPRuleConfig defines the config for ip rule
type IPRuleConfig struct {
Rule netlink.Rule
RuleAdd ruleAdder
RuleDel ruleDeler
RuleList ruleLister
}
// IPTablesRuleSpec defines the config for ip table rule
type IPTablesRuleSpec []string
type iptabler interface {
NewChain(table, chain string) error
ClearChain(table, chain string) error
DeleteChain(table, chain string) error
AppendUnique(table, chain string, rulespec ...string) error
Delete(table, chain string, rulespec ...string) error
}
// IPTablesChainSpec defines iptable chain
type IPTablesChainSpec struct {
TableName, ChainName string
IsDefaultChain bool // Is a System default chain, if yes, we won't delete it.
IPT iptabler
}
// IPTablesRuleConfig defines iptable rule
type IPTablesRuleConfig struct {
Spec IPTablesChainSpec
RuleSpecs []IPTablesRuleSpec
IPT iptabler
}
var ipt *iptables.IPTables
func init() {
var err error
if ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4); err != nil {
glog.Errorf("failed to initialize iptables: %v", err)
}
}
// Ensure SysctlConfig
func (s SysctlConfig) Ensure(enabled bool) error {
var value string
if enabled {
value = s.Value
} else {
value = s.DefaultValue
}
_, err := s.SysctlFunc(s.Key, value)
return err
}
// Ensure IPRouteConfig
func (r IPRouteConfig) Ensure(enabled bool) error {
var err error
if enabled {
err = r.RouteAdd(&r.Route)
if os.IsExist(err) {
err = nil
}
} else if err = r.RouteDel(&r.Route); err != nil && err.(syscall.Errno) == syscall.ESRCH {
err = nil
}
return err
}
// Ensure IPRuleConfig
func (r IPRuleConfig) Ensure(enabled bool) error {
if enabled {
return r.ensureHelper(1)
}
return r.ensureHelper(0)
}
func (r IPRuleConfig) ensureHelper(ensureCount int) error {
var err error
ruleCount, err := r.count()
if err != nil {
glog.Errorf("failed to get IP rule count for rule: %v, error: %v", r.Rule, err)
return err
}
for ruleCount != ensureCount {
if ruleCount > ensureCount {
if err = r.RuleDel(&r.Rule); err != nil {
glog.Errorf("failed to delete duplicated ip rule: %v, error: %v", r.Rule, err)
}
ruleCount--
} else {
err = r.RuleAdd(&r.Rule)
if err != nil {
if os.IsExist(err) {
err = nil
} else {
glog.Errorf("failed to add ip rule: %v, error: %v", r.Rule, err)
}
}
ruleCount++
}
}
return err
}
func (r IPRuleConfig) count() (int, error) {
rules, err := r.RuleList(unix.AF_INET)
if err != nil {
return 0, err
}
count := 0
for _, rule := range rules {
if rule == r.Rule {
count++
}
}
return count, nil
}
func (c IPTablesChainSpec) ensure(enabled bool) error {
var err error
if enabled {
if err = c.IPT.NewChain(c.TableName, c.ChainName); err != nil {
if eerr, eok := err.(*iptables.Error); !eok || eerr.ExitStatus() != 1 {
return err
}
}
} else {
if !c.IsDefaultChain {
err = c.IPT.ClearChain(c.TableName, c.ChainName)
if err != nil {
glog.Errorf("failed to clean chain %s in table %s: %v", c.TableName, c.ChainName, err)
return err
}
if err = c.IPT.DeleteChain(c.TableName, c.ChainName); err != nil {
if eerr, eok := err.(*iptables.Error); !eok || eerr.ExitStatus() != 1 {
glog.Errorf("failed to delete chain %s in table %s: %v", c.TableName, c.ChainName, err)
return err
}
}
}
}
return nil
}
// Ensure IPTablesRuleConfig
func (r IPTablesRuleConfig) Ensure(enabled bool) error {
var err error
if err = r.Spec.ensure(enabled); err != nil {
return err
}
if enabled {
for _, rs := range r.RuleSpecs {
err = r.IPT.AppendUnique(r.Spec.TableName, r.Spec.ChainName, rs...)
if err != nil {
glog.Errorf("failed to append rule %v in table %s chain %s: %v", rs, r.Spec.TableName, r.Spec.ChainName, err)
return err
}
}
} else if r.Spec.IsDefaultChain {
for _, rs := range r.RuleSpecs {
if err := r.IPT.Delete(r.Spec.TableName, r.Spec.ChainName, rs...); err != nil {
if eerr, eok := err.(*iptables.Error); !eok || eerr.ExitStatus() != 2 {
if !strings.Contains(eerr.Error(), "No chain/target/match") {
return err
}
}
}
}
}
return nil
}