github.com/cilium/cilium@v1.16.2/pkg/datapath/iptables/ipset/ops.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ipset 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "net/netip" 11 "sync/atomic" 12 13 "github.com/sirupsen/logrus" 14 "k8s.io/apimachinery/pkg/util/sets" 15 16 "github.com/cilium/statedb" 17 "github.com/cilium/statedb/reconciler" 18 19 "github.com/cilium/cilium/pkg/datapath/tables" 20 ) 21 22 func newOps(logger logrus.FieldLogger, ipset *ipset, cfg config) *ops { 23 return &ops{ 24 enabled: cfg.NodeIPSetNeeded, 25 ipset: ipset, 26 } 27 } 28 29 type ops struct { 30 enabled bool 31 doPrune atomic.Bool 32 ipset *ipset 33 } 34 35 // UpdateBatch implements reconciler.BatchOperations. 36 func (ops *ops) UpdateBatch(ctx context.Context, txn statedb.ReadTxn, batch []reconciler.BatchEntry[*tables.IPSetEntry]) { 37 if !ops.enabled { 38 return 39 } 40 41 addrsByName := map[string][]netip.Addr{} 42 for _, entry := range batch { 43 addrsByName[entry.Object.Name] = append(addrsByName[entry.Object.Name], entry.Object.Addr) 44 } 45 err := ops.ipset.addBatch(ctx, addrsByName) 46 if err != nil { 47 // Fail the whole batch. 48 for i := range batch { 49 batch[i].Result = err 50 } 51 } 52 } 53 54 // DeleteBatch implements reconciler.BatchOperations. 55 func (ops *ops) DeleteBatch(ctx context.Context, txn statedb.ReadTxn, batch []reconciler.BatchEntry[*tables.IPSetEntry]) { 56 if !ops.enabled { 57 return 58 } 59 60 addrsByName := map[string][]netip.Addr{} 61 for _, entry := range batch { 62 addrsByName[entry.Object.Name] = append(addrsByName[entry.Object.Name], entry.Object.Addr) 63 } 64 err := ops.ipset.delBatch(ctx, addrsByName) 65 if err != nil { 66 // Fail the whole batch. 67 for i := range batch { 68 batch[i].Result = err 69 } 70 } 71 } 72 73 var _ reconciler.Operations[*tables.IPSetEntry] = &ops{} 74 var _ reconciler.BatchOperations[*tables.IPSetEntry] = &ops{} 75 76 func (ops *ops) Update(ctx context.Context, _ statedb.ReadTxn, entry *tables.IPSetEntry) error { 77 panic("Unexpectedly Update() called for reconciliation") 78 } 79 80 func (ops *ops) Delete(ctx context.Context, _ statedb.ReadTxn, entry *tables.IPSetEntry) error { 81 panic("Unexpectedly Delete() called for reconciliation") 82 } 83 84 func (ops *ops) Prune(ctx context.Context, _ statedb.ReadTxn, iter statedb.Iterator[*tables.IPSetEntry]) error { 85 if !ops.enabled || !ops.doPrune.Load() { 86 return nil 87 } 88 89 desiredV4Set, desiredV6Set := sets.Set[netip.Addr]{}, sets.Set[netip.Addr]{} 90 statedb.ProcessEach(iter, func(obj *tables.IPSetEntry, _ uint64) error { 91 if obj.Name == CiliumNodeIPSetV4 { 92 desiredV4Set.Insert(obj.Addr) 93 } else if obj.Name == CiliumNodeIPSetV6 { 94 desiredV6Set.Insert(obj.Addr) 95 } 96 return nil 97 }) 98 99 return errors.Join( 100 reconcile(ctx, ops.ipset, CiliumNodeIPSetV4, INetFamily, desiredV4Set), 101 reconcile(ctx, ops.ipset, CiliumNodeIPSetV6, INet6Family, desiredV6Set), 102 ) 103 } 104 105 func reconcile( 106 ctx context.Context, 107 ipset *ipset, 108 name string, 109 family Family, 110 desired sets.Set[netip.Addr], 111 ) error { 112 // create the IP set if it doesn't exist 113 if err := ipset.create(ctx, name, string(family)); err != nil { 114 return fmt.Errorf("unable to create ipset %s: %w", name, err) 115 } 116 117 curSet, err := ipset.list(ctx, name) 118 if err != nil { 119 return fmt.Errorf("unable to list ipset %s: %w", name, err) 120 } 121 122 toDel := curSet.Difference(desired) 123 delBatch := map[string][]netip.Addr{name: toDel.UnsortedList()} 124 if err := ipset.delBatch(ctx, delBatch); err != nil { 125 return fmt.Errorf("unable to delete from ipset: %w", err) 126 } 127 128 toAdd := desired.Difference(curSet) 129 addBatch := map[string][]netip.Addr{name: toAdd.UnsortedList()} 130 if err := ipset.addBatch(ctx, addBatch); err != nil { 131 return fmt.Errorf("unable to delete from ipset: %w", err) 132 } 133 return nil 134 } 135 136 func (ops *ops) enablePrune() { 137 ops.doPrune.Store(true) 138 }