Skip to content
This repository was archived by the owner on Jun 20, 2024. It is now read-only.

Commit 28a9444

Browse files
committed
Workaround to fix Kernel bug related ipset entry deletion
if the kernel version is in affected range of Kernels, then resync the entries to expected set of entries. Kernel bug: https://bugzilla.netfilter.org/show_bug.cgi?id=1119 Fixes #3296 failed: ipset v6.32: Element cannot be deleted from the set: it's not added
1 parent dcaf77a commit 28a9444

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

net/ipset/ipset.go

+87
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import (
66
"fmt"
77
"log"
88
"os/exec"
9+
"regexp"
10+
"strconv"
911
"strings"
1012

1113
"github.com/pkg/errors"
14+
"github.com/weaveworks/weave/common"
1215
"k8s.io/apimachinery/pkg/types"
1316
)
1417

@@ -52,6 +55,31 @@ type ipset struct {
5255
users map[entryKey]map[types.UID]struct{}
5356
}
5457

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+
5583
func New(logger *log.Logger, maxListSize int) Interface {
5684
ips := &ipset{
5785
Logger: logger,
@@ -118,9 +146,58 @@ func (i *ipset) DelEntry(user types.UID, ipsetName Name, entry string) error {
118146

119147
i.Logger.Printf("deleted entry %s from %s of %s", entry, ipsetName, user)
120148

149+
if resyncOnDelete {
150+
return i.safeDelEntry(user, ipsetName, entry)
151+
}
152+
121153
return doExec("del", string(ipsetName), entry)
122154
}
123155

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
199+
}
200+
124201
func (i *ipset) Exist(user types.UID, ipsetName Name, entry string) bool {
125202
return i.existUser(user, ipsetName, entry)
126203
}
@@ -206,6 +283,16 @@ func (i *ipset) removeSetFromUsers(ipsetName Name) {
206283
}
207284
}
208285

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+
209296
func doExec(args ...string) error {
210297
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
211298
return errors.Wrapf(err, "ipset %v failed: %s", args, output)

0 commit comments

Comments
 (0)