Skip to content

Commit

Permalink
moved logic of adding multiple rules into iptables handler
Browse files Browse the repository at this point in the history
Signed-off-by: Filip Gschwandtner <filip.gschwandtner@pantheon.tech>
  • Loading branch information
fgschwan committed Apr 7, 2020
1 parent 1985ede commit 2102934
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 67 deletions.
45 changes: 4 additions & 41 deletions plugins/linux/iptablesplugin/descriptor/rulechain.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
package descriptor

import (
"bytes"
"fmt"
"strings"

"github.com/golang/protobuf/proto"
Expand Down Expand Up @@ -208,48 +206,13 @@ func (d *RuleChainDescriptor) Create(key string, rch *linux_iptables.RuleChain)
// wipe all rules in the chain that may have existed before
err = d.ipTablesHandler.DeleteAllRules(protocolType(rch), tableNameStr(rch), chainNameStr(rch))
if err != nil {
d.log.Warnf("Error by wiping iptables rules: %v", err)
return nil, errors.Errorf("Error by wiping iptables rules: %v", err)
}

// append all rules
if len(rch.Rules) > 0 {
if len(rch.Rules) < d.minRuleCountForPerfRuleAddition { // use normal method of addition
for _, rule := range rch.Rules {
err := d.ipTablesHandler.AppendRule(protocolType(rch), tableNameStr(rch), chainNameStr(rch), rule)
if err != nil {
d.log.Errorf("Error by appending iptables rule: %v", err)
break
}
}
} else { // use performance solution (this makes performance difference with higher count of appended rules)
// export existing iptables data
data, err := d.ipTablesHandler.SaveTable(protocolType(rch), tableNameStr(rch), true)
if err != nil {
return nil, errors.Errorf("Error by adding rules: Can't export all rules due to: %v", err)
}

// add rules to exported data
insertPoint := bytes.Index(data, []byte("COMMIT"))
if insertPoint == -1 {
return nil, errors.Errorf("Error by adding rules: Can't find COMMIT statement in iptables-save data")
}
var rules strings.Builder
chain := chainNameStr(rch)
for _, rule := range rch.Rules {
rules.WriteString(fmt.Sprintf("[0:0] -A %s %s\n", chain, rule))
}
insertData := []byte(rules.String())
updatedData := make([]byte, len(data)+len(insertData))
copy(updatedData[:insertPoint], data[:insertPoint])
copy(updatedData[insertPoint:insertPoint+len(insertData)], insertData)
copy(updatedData[insertPoint+len(insertData):], data[insertPoint:])

// import modified data to linux
err = d.ipTablesHandler.RestoreTable(protocolType(rch), tableNameStr(rch), updatedData, true, true)
if err != nil {
return nil, errors.Errorf("Error by adding rules: Can't restore modified iptables data due to: %v", err)
}
}
err = d.ipTablesHandler.AppendRules(protocolType(rch), tableNameStr(rch), chainNameStr(rch), rch.Rules...)
if err != nil {
return nil, errors.Errorf("Error by adding rules: %v", err)
}

return nil, err
Expand Down
15 changes: 9 additions & 6 deletions plugins/linux/iptablesplugin/iptablesplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ type Deps struct {

// Config holds the plugin configuration.
type Config struct {
Disabled bool `json:"disabled"`
GoRoutinesCnt int `json:"go-routines-count"`
MinRuleCountForPerfRuleAddition int `json:"min-rule-count-for-performance-rule-addition"`
linuxcalls.HandlerConfig `json:"handler"`

Disabled bool `json:"disabled"`
GoRoutinesCnt int `json:"go-routines-count"`
}

// Init initializes and registers descriptors and handlers for Linux iptables rules.
Expand All @@ -84,7 +85,7 @@ func (p *IPTablesPlugin) Init() error {

// init iptables handler
p.iptHandler = linuxcalls.NewIPTablesHandler()
err = p.iptHandler.Init()
err = p.iptHandler.Init(&config.HandlerConfig)
if err != nil && p.configFound {
// just warn here, iptables / ip6tables just may not be installed - will return
// an error by attempt to configure it
Expand Down Expand Up @@ -112,8 +113,10 @@ func (p *IPTablesPlugin) Close() error {
func (p *IPTablesPlugin) retrieveConfig() (*Config, error) {
config := &Config{
// default configuration
GoRoutinesCnt: defaultGoRoutinesCnt,
MinRuleCountForPerfRuleAddition: defaultMinRuleCountForPerfRuleAddition,
GoRoutinesCnt: defaultGoRoutinesCnt,
HandlerConfig: linuxcalls.HandlerConfig{
MinRuleCountForPerfRuleAddition: defaultMinRuleCountForPerfRuleAddition,
},
}
found, err := p.Cfg.LoadValue(config)
if !found {
Expand Down
15 changes: 8 additions & 7 deletions plugins/linux/iptablesplugin/linux-iptablesplugin.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ disabled: false
# the Retrieve operation in parallel.
go-routines-count: 10

# Minimal rule count needed to perform alternative rule additions by creating RuleChain.
# Alternative method is based on exporting iptables data(iptables-save), adding rules
# into that exported data and import them back (iptables-restore). This can very efficient
# in case of filling many rules at once.
# By default off (rule count to activate alternative method is super high and therefore
# practically turned off)
min-rule-count-for-performance-rule-addition: 2147483647
handler:
# Minimal rule count needed to perform alternative rule additions by creating RuleChain.
# Alternative method is based on exporting iptables data(iptables-save), adding rules
# into that exported data and import them back (iptables-restore). This can very efficient
# in case of filling many rules at once.
# By default off (rule count to activate alternative method is super high and therefore
# practically turned off)
min-rule-count-for-performance-rule-addition: 2147483647
14 changes: 8 additions & 6 deletions plugins/linux/iptablesplugin/linuxcalls/iptables_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const (
// to manage linux iptables rules.
type IPTablesAPI interface {
// Init initializes an iptables handler.
Init() error
Init(config *HandlerConfig) error

IPTablesAPIWrite
IPTablesAPIRead
Expand All @@ -47,24 +47,26 @@ type IPTablesAPIWrite interface {
// AppendRule appends a rule into the specified chain.
AppendRule(protocol L3Protocol, table, chain string, rule string) error

// AppendRules appends rules into the specified chain.
AppendRules(protocol L3Protocol, table, chain string, rules ...string) error

// DeleteRule deletes a rule from the specified chain.
DeleteRule(protocol L3Protocol, table, chain string, rule string) error

// DeleteAllRules deletes all rules within the specified chain.
DeleteAllRules(protocol L3Protocol, table, chain string) error

// RestoreTable import all data (in IPTable-save output format) for given table
RestoreTable(protocol L3Protocol, table string, data []byte, flush bool, importCounters bool) error
}

// IPTablesAPIRead interface covers read methods inside linux calls package
// needed to manage linux iptables rules.
type IPTablesAPIRead interface {
// ListRules lists all rules within the specified chain.
ListRules(protocol L3Protocol, table, chain string) (rules []string, err error)
}

// SaveTable exports all data for given table in IPTable-save output format
SaveTable(protocol L3Protocol, table string, exportCounters bool) ([]byte, error)
// HandlerConfig holds the IPTablesHandler related configuration.
type HandlerConfig struct {
MinRuleCountForPerfRuleAddition int `json:"min-rule-count-for-performance-rule-addition"`
}

// NewIPTablesHandler creates new instance of iptables handler.
Expand Down
62 changes: 55 additions & 7 deletions plugins/linux/iptablesplugin/linuxcalls/iptables_linuxcalls.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ const (

// IPTablesHandler is a handler for all operations on Linux iptables / ip6tables.
type IPTablesHandler struct {
v4Handler *iptables.IPTables
v6Handler *iptables.IPTables
v4Handler *iptables.IPTables
v6Handler *iptables.IPTables
minRuleCountForPerfRuleAddition int
}

// Init initializes an iptables handler.
func (h *IPTablesHandler) Init() error {
func (h *IPTablesHandler) Init(config *HandlerConfig) error {
var err error

h.v4Handler, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
Expand All @@ -60,6 +61,8 @@ func (h *IPTablesHandler) Init() error {
// continue, ip6tables just may not be installed
}

h.minRuleCountForPerfRuleAddition = config.MinRuleCountForPerfRuleAddition

return err
}

Expand Down Expand Up @@ -101,6 +104,51 @@ func (h *IPTablesHandler) AppendRule(protocol L3Protocol, table, chain string, r
return handler.Append(table, chain, ruleSlice[:]...)
}

// AppendRules appends rules into the specified chain.
func (h *IPTablesHandler) AppendRules(protocol L3Protocol, table, chain string, rules ...string) error {
if len(rules) == 0 {
return nil // nothing to do
}

if len(rules) < h.minRuleCountForPerfRuleAddition { // use normal method of addition
for _, rule := range rules {
err := h.AppendRule(protocol, table, chain, rule)
if err != nil {
return errors.Errorf("Error by appending iptables rule: %v", err)
}
}
} else { // use performance solution (this makes performance difference with higher count of appended rules)
// export existing iptables data
data, err := h.saveTable(protocol, table, true)
if err != nil {
return errors.Errorf(": Can't export all rules due to: %v", err)
}

// add rules to exported data
insertPoint := bytes.Index(data, []byte("COMMIT"))
if insertPoint == -1 {
return errors.Errorf("Error by adding rules: Can't find COMMIT statement in iptables-save data")
}
var rulesSB strings.Builder
for _, rule := range rules {
rulesSB.WriteString(fmt.Sprintf("[0:0] -A %s %s\n", chain, rule))
}
insertData := []byte(rulesSB.String())
updatedData := make([]byte, len(data)+len(insertData))
copy(updatedData[:insertPoint], data[:insertPoint])
copy(updatedData[insertPoint:insertPoint+len(insertData)], insertData)
copy(updatedData[insertPoint+len(insertData):], data[insertPoint:])

// import modified data to linux
err = h.restoreTable(protocol, table, updatedData, true, true)
if err != nil {
return errors.Errorf("Error by adding rules: Can't restore modified iptables data due to: %v", err)
}
}

return nil
}

// DeleteRule deletes a rule from the specified chain.
func (h *IPTablesHandler) DeleteRule(protocol L3Protocol, table, chain string, rule string) error {
handler, err := h.getHandler(protocol)
Expand Down Expand Up @@ -145,8 +193,8 @@ func (h *IPTablesHandler) ListRules(protocol L3Protocol, table, chain string) (r
return
}

// SaveTable exports all data for given table in IPTable-save output format
func (h *IPTablesHandler) SaveTable(protocol L3Protocol, table string, exportCounters bool) ([]byte, error) {
// saveTable exports all data for given table in IPTable-save output format
func (h *IPTablesHandler) saveTable(protocol L3Protocol, table string, exportCounters bool) ([]byte, error) {
// create command with arguments
saveCmd := IPv4SaveCmd
if protocol == ProtocolIPv6 {
Expand All @@ -169,8 +217,8 @@ func (h *IPTablesHandler) SaveTable(protocol L3Protocol, table string, exportCou
return stdout.Bytes(), nil
}

// RestoreTable import all data (in IPTable-save output format) for given table
func (h *IPTablesHandler) RestoreTable(protocol L3Protocol, table string, data []byte, flush bool, importCounters bool) error {
// restoreTable import all data (in IPTable-save output format) for given table
func (h *IPTablesHandler) restoreTable(protocol L3Protocol, table string, data []byte, flush bool, importCounters bool) error {
// create command with arguments
restoreCmd := IPv4RestoreCmd
if protocol == ProtocolIPv6 {
Expand Down

0 comments on commit 2102934

Please sign in to comment.