|
8 | 8 | "errors" |
9 | 9 | "fmt" |
10 | 10 | "os/exec" |
| 11 | + "slices" |
11 | 12 | "sort" |
12 | 13 | "strings" |
13 | 14 |
|
@@ -163,6 +164,7 @@ type IPSetHandler interface { |
163 | 164 | DestroyAllWithin() error |
164 | 165 | Save() error |
165 | 166 | Restore() error |
| 167 | + RestoreSets([]string) error |
166 | 168 | Flush() error |
167 | 169 | Get(setName string) *Set |
168 | 170 | Sets() map[string]*Set |
@@ -510,13 +512,35 @@ func scrubInitValFromOptions(options []string) []string { |
510 | 512 | return options |
511 | 513 | } |
512 | 514 |
|
| 515 | +// buildIPSetRestore creates a set of ipset rules that can be fed into ipset restore. ipset contains a list of "sets" |
| 516 | +// that we will act on. If setIncludeNames is not null, then the list of sets will be dynamically filtered to ensure |
| 517 | +// that the string for ipset restore only includes sets with those names. |
| 518 | +// |
| 519 | +// In order to ensure that our changes are atomic, we create a temporary set per unique ipset options, then we load |
| 520 | +// that temporary set with the new list of entries, then we swap them to the final name of the set, and then flush the |
| 521 | +// temporary set before potentially reusing it in a future load. |
| 522 | +// |
| 523 | +// The temporary set is created only as needed to ensure that we don't needlessly create temporary sets. Temporary sets |
| 524 | +// have to be unique to the "Type" of the set. This is things like: hash:ip and others. |
| 525 | +// |
513 | 526 | // Build ipset restore input |
514 | 527 | // ex: |
515 | 528 | // create KUBE-DST-3YNVZWWGX3UQQ4VQ hash:ip family inet hashsize 1024 maxelem 65536 timeout 0 |
516 | 529 | // add KUBE-DST-3YNVZWWGX3UQQ4VQ 100.96.1.6 timeout 0 |
517 | | -func buildIPSetRestore(ipset *IPSet) string { |
| 530 | +func buildIPSetRestore(ipset *IPSet, setIncludeNames []string) string { |
518 | 531 | setNames := make([]string, 0, len(ipset.sets)) |
519 | | - for setName := range ipset.sets { |
| 532 | + for setName, set := range ipset.sets { |
| 533 | + // If we've been passed a set of filter names, check to see if this set is contained within that set before |
| 534 | + // adding it to the restore to ensure that we don't impact other unrelated sets |
| 535 | + if setIncludeNames != nil { |
| 536 | + origName := setName |
| 537 | + if set.Parent.isIpv6 { |
| 538 | + origName = strings.Replace(setName, fmt.Sprintf("%s:", IPv6SetPrefix), "", 1) |
| 539 | + } |
| 540 | + if !slices.Contains(setIncludeNames, origName) { |
| 541 | + continue |
| 542 | + } |
| 543 | + } |
520 | 544 | // we need setNames in some consistent order so that we can unit-test this method has a predictable output: |
521 | 545 | setNames = append(setNames, setName) |
522 | 546 | } |
@@ -591,7 +615,14 @@ func (ipset *IPSet) Save() error { |
591 | 615 | // mode except list, help, version, interactive mode and restore itself. |
592 | 616 | // Send formatted ipset.sets into stdin of "ipset restore" command. |
593 | 617 | func (ipset *IPSet) Restore() error { |
594 | | - restoreString := buildIPSetRestore(ipset) |
| 618 | + return ipset.RestoreSets(nil) |
| 619 | +} |
| 620 | + |
| 621 | +// RestoreSets is very similar to Restore, except that it filters by set names that are passed in a string array |
| 622 | +// so that we don't disrumpt other things that might be using ipsets on the host. In general, this function should be |
| 623 | +// preferred over the Restore() function. |
| 624 | +func (ipset *IPSet) RestoreSets(setNames []string) error { |
| 625 | + restoreString := buildIPSetRestore(ipset, setNames) |
595 | 626 | klog.V(3).Infof("ipset (ipv6? %t) restore looks like:\n%s", ipset.isIpv6, restoreString) |
596 | 627 | stdin := bytes.NewBufferString(restoreString) |
597 | 628 | err := ipset.runWithStdin(stdin, "restore", "-exist") |
|
0 commit comments