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  }