-
Notifications
You must be signed in to change notification settings - Fork 40
/
config.go
236 lines (207 loc) · 5.57 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
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
}