@@ -6,9 +6,12 @@ import (
6
6
"fmt"
7
7
"log"
8
8
"os/exec"
9
+ "regexp"
10
+ "strconv"
9
11
"strings"
10
12
11
13
"github.com/pkg/errors"
14
+ "github.com/weaveworks/weave/common"
12
15
"k8s.io/apimachinery/pkg/types"
13
16
)
14
17
@@ -52,6 +55,31 @@ type ipset struct {
52
55
users map [entryKey ]map [types.UID ]struct {}
53
56
}
54
57
58
+ var resyncOnDelete bool
59
+
60
+ func init () {
61
+
62
+ // check if Kernel version is in between 4.2 and 4.10. There is a kerenl bug
63
+ // due to which ipset delete sometimes ends up in deleting unintended entries
64
+ // https://bugzilla.netfilter.org/show_bug.cgi?id=1119
65
+ // if Kernel version has this issue then we need to workaround by resyncing
66
+ // ipset to the expected list of entries
67
+ kernelVersion , err := exec .Command ("uname" , "-r" ).Output ()
68
+ if err != nil {
69
+ common .Log .Fatalf ("Failed to get Kernel version" )
70
+ }
71
+ splitVersion := strings .SplitN (string (kernelVersion [:]), "." , 3 )
72
+ if splitVersion [0 ] == "4" {
73
+ majorVersion , err := strconv .Atoi (splitVersion [1 ])
74
+ if err != nil {
75
+ common .Log .Fatalf ("Failed to process Kernel major version" )
76
+ }
77
+ if majorVersion >= 2 && majorVersion <= 10 {
78
+ resyncOnDelete = true
79
+ }
80
+ }
81
+ }
82
+
55
83
func New (logger * log.Logger , maxListSize int ) Interface {
56
84
ips := & ipset {
57
85
Logger : logger ,
@@ -118,7 +146,56 @@ func (i *ipset) DelEntry(user types.UID, ipsetName Name, entry string) error {
118
146
119
147
i .Logger .Printf ("deleted entry %s from %s of %s" , entry , ipsetName , user )
120
148
121
- return doExec ("del" , string (ipsetName ), entry )
149
+ if ! resyncOnDelete {
150
+ return doExec ("del" , string (ipsetName ), entry )
151
+ } else {
152
+ return i .safeDelEntry (user , ipsetName , entry )
153
+ }
154
+ }
155
+
156
+ // logic to workaround Kernel bug https://bugzilla.netfilter.org/show_bug.cgi?id=1119
157
+ func (i * ipset ) safeDelEntry (user types.UID , ipsetName Name , entry string ) error {
158
+ oldEntries , err := listEntries (ipsetName )
159
+ if err != nil {
160
+ return err
161
+ }
162
+ err = doExec ("del" , string (ipsetName ), entry )
163
+ if ! strings .Contains (err .Error (), "Element cannot be deleted from the set: it's not added" ) {
164
+ return err
165
+ }
166
+ newEntries , err := listEntries (ipsetName )
167
+ if err != nil {
168
+ return err
169
+ }
170
+ expectedEntries := make ([]string , 0 )
171
+ for _ , oe := range oldEntries {
172
+ if ! strings .Contains (oe , entry ) {
173
+ expectedEntries = append (expectedEntries , oe )
174
+ }
175
+ }
176
+ for _ , ee := range expectedEntries {
177
+ exists := false
178
+ for _ , ne := range newEntries {
179
+ if strings .Compare (ee , ne ) == 0 {
180
+ exists = true
181
+ break
182
+ }
183
+ }
184
+ if exists {
185
+ continue
186
+ }
187
+ args := make ([]string , 0 )
188
+ if i .enableComments {
189
+ splitEntry := strings .Split (ee , " comment " )
190
+ args = append (args , "add" , string (ipsetName ), splitEntry [0 ])
191
+ args = append (args , "comment" , splitEntry [1 ])
192
+ } else {
193
+ args = append (args , "add" , string (ipsetName ), ee )
194
+ }
195
+ err = doExec (args ... )
196
+ return err
197
+ }
198
+ return nil
122
199
}
123
200
124
201
func (i * ipset ) Exist (user types.UID , ipsetName Name , entry string ) bool {
@@ -206,6 +283,16 @@ func (i *ipset) removeSetFromUsers(ipsetName Name) {
206
283
}
207
284
}
208
285
286
+ func listEntries (ipsetName Name ) ([]string , error ) {
287
+ output , err := exec .Command ("ipset" , "list" , string (ipsetName )).CombinedOutput ()
288
+ if err != nil {
289
+ return nil , errors .Wrapf (err , "list ipset %s failed: %s" , ipsetName , output )
290
+ }
291
+ r := regexp .MustCompile ("(?m)^(.*\n )*Members:\n " )
292
+ list := r .ReplaceAllString (string (output [:]), "" )
293
+ return strings .Split (list , "\n " ), nil
294
+ }
295
+
209
296
func doExec (args ... string ) error {
210
297
if output , err := exec .Command ("ipset" , args ... ).CombinedOutput (); err != nil {
211
298
return errors .Wrapf (err , "ipset %v failed: %s" , args , output )
0 commit comments