github.com/cilium/cilium@v1.16.2/pkg/maps/lbmap/lbmap.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package lbmap
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"strconv"
    11  
    12  	"github.com/sirupsen/logrus"
    13  	"golang.org/x/sys/unix"
    14  
    15  	"github.com/cilium/cilium/pkg/bpf"
    16  	"github.com/cilium/cilium/pkg/cidr"
    17  	cmtypes "github.com/cilium/cilium/pkg/clustermesh/types"
    18  	datapathTypes "github.com/cilium/cilium/pkg/datapath/types"
    19  	"github.com/cilium/cilium/pkg/ip"
    20  	"github.com/cilium/cilium/pkg/loadbalancer"
    21  	"github.com/cilium/cilium/pkg/logging"
    22  	"github.com/cilium/cilium/pkg/logging/logfields"
    23  	"github.com/cilium/cilium/pkg/maglev"
    24  	"github.com/cilium/cilium/pkg/option"
    25  	"github.com/cilium/cilium/pkg/u8proto"
    26  )
    27  
    28  const DefaultMaxEntries = 65536
    29  
    30  var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-lb")
    31  
    32  var (
    33  	// MaxEntries contains the maximum number of entries that are allowed
    34  	// in Cilium LB service, backend and affinity maps.
    35  	ServiceMapMaxEntries        = DefaultMaxEntries
    36  	ServiceBackEndMapMaxEntries = DefaultMaxEntries
    37  	RevNatMapMaxEntries         = DefaultMaxEntries
    38  	AffinityMapMaxEntries       = DefaultMaxEntries
    39  	SourceRangeMapMaxEntries    = DefaultMaxEntries
    40  	MaglevMapMaxEntries         = DefaultMaxEntries
    41  )
    42  
    43  // LBBPFMap is an implementation of the LBMap interface.
    44  type LBBPFMap struct {
    45  	// Buffer used to avoid excessive allocations to temporarily store backend
    46  	// IDs. Concurrent access is protected by the
    47  	// pkg/service.go:(Service).UpsertService() lock.
    48  	maglevBackendIDsBuffer []loadbalancer.BackendID
    49  	maglevTableSize        uint64
    50  }
    51  
    52  func New() *LBBPFMap {
    53  	maglev := option.Config.NodePortAlg == option.NodePortAlgMaglev
    54  	maglevTableSize := option.Config.MaglevTableSize
    55  
    56  	m := &LBBPFMap{}
    57  
    58  	if maglev {
    59  		m.maglevBackendIDsBuffer = make([]loadbalancer.BackendID, maglevTableSize)
    60  		m.maglevTableSize = uint64(maglevTableSize)
    61  	}
    62  
    63  	return m
    64  }
    65  
    66  func (lbmap *LBBPFMap) upsertServiceProto(p *datapathTypes.UpsertServiceParams, ipv6 bool) error {
    67  	var svcKey ServiceKey
    68  	var svcVal ServiceValue
    69  
    70  	// Backends should be added to the backend maps for the case when:
    71  	// - Plain IPv6 (to IPv6) or IPv4 (to IPv4) service.
    72  	// - IPv4 to IPv6 will only have a dummy IPv4 service entry (0 backends)
    73  	//   as it will recicle the packet into the IPv6 path.
    74  	// - IPv6 to IPv4 will add its IPv4 backends as IPv4-in-IPv6 backends
    75  	//   to the IPv6 backend map.
    76  	backendsOk := ipv6 || !ipv6 && p.NatPolicy != loadbalancer.SVCNatPolicyNat46
    77  
    78  	if ipv6 {
    79  		svcKey = NewService6Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0)
    80  		svcVal = &Service6Value{}
    81  	} else {
    82  		svcKey = NewService4Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0)
    83  		svcVal = &Service4Value{}
    84  	}
    85  
    86  	slot := 1
    87  
    88  	// start off with #backends = 0 for updateMasterService()
    89  	backends := make(map[string]*loadbalancer.Backend)
    90  	if backendsOk {
    91  		backends = p.ActiveBackends
    92  		if len(p.PreferredBackends) > 0 {
    93  			backends = p.PreferredBackends
    94  		}
    95  		if p.UseMaglev && len(backends) != 0 {
    96  			if err := lbmap.UpsertMaglevLookupTable(p.ID, backends, ipv6); err != nil {
    97  				return err
    98  			}
    99  		}
   100  		backendIDs := p.GetOrderedBackends()
   101  		for _, backendID := range backendIDs {
   102  			if backendID == 0 {
   103  				return fmt.Errorf("Invalid backend ID 0")
   104  			}
   105  			svcVal.SetBackendID(loadbalancer.BackendID(backendID))
   106  			svcVal.SetRevNat(int(p.ID))
   107  			svcKey.SetBackendSlot(slot)
   108  			if err := updateServiceEndpoint(svcKey, svcVal); err != nil {
   109  				if errors.Is(err, unix.E2BIG) {
   110  					return fmt.Errorf("Unable to update service entry %+v => %+v: "+
   111  						"Unable to update element for LB bpf map: "+
   112  						"You can resize it with the flag \"--%s\". "+
   113  						"The resizing might break existing connections to services",
   114  						svcKey, svcVal, option.LBMapEntriesName)
   115  				}
   116  
   117  				return fmt.Errorf("Unable to update service entry %+v => %+v: %w", svcKey, svcVal, err)
   118  			}
   119  			slot++
   120  		}
   121  	}
   122  
   123  	zeroValue := svcVal.New().(ServiceValue)
   124  	zeroValue.SetRevNat(int(p.ID)) // TODO change to uint16
   125  	revNATKey := zeroValue.RevNatKey()
   126  	revNATValue := svcKey.RevNatValue()
   127  	if err := updateRevNatLocked(revNATKey, revNATValue); err != nil {
   128  		return fmt.Errorf("Unable to update reverse NAT %+v => %+v: %w", revNATKey, revNATValue, err)
   129  	}
   130  
   131  	if err := updateMasterService(svcKey, svcVal.New().(ServiceValue), len(backends), int(p.ID), p.Type, p.ExtLocal, p.IntLocal, p.NatPolicy,
   132  		p.SessionAffinity, p.SessionAffinityTimeoutSec, p.CheckSourceRange, p.L7LBProxyPort, p.LoopbackHostport); err != nil {
   133  		deleteRevNatLocked(revNATKey)
   134  		return fmt.Errorf("Unable to update service %+v: %w", svcKey, err)
   135  	}
   136  
   137  	if backendsOk {
   138  		for i := slot; i <= p.PrevBackendsCount; i++ {
   139  			svcKey.SetBackendSlot(i)
   140  			if err := deleteServiceLocked(svcKey); err != nil {
   141  				log.WithFields(logrus.Fields{
   142  					logfields.ServiceKey:  svcKey,
   143  					logfields.BackendSlot: svcKey.GetBackendSlot(),
   144  				}).WithError(err).Warn("Unable to delete service entry from BPF map")
   145  			}
   146  		}
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  // UpsertService inserts or updates the given service in a BPF map.
   153  //
   154  // The corresponding backend entries (identified with the given backendIDs)
   155  // have to exist before calling the function.
   156  //
   157  // The service's prevActiveBackendCount denotes the count of previously active
   158  // backend entries that were added to the BPF map so that the function can remove
   159  // obsolete ones.
   160  //
   161  // The service's non-active backends are appended to the active backends list,
   162  // and skipped from the service backends count set in the master key so that the
   163  // non-active backends will not be considered for load-balancing traffic. The
   164  // backends count is used in the datapath to determine if a service has any backends.
   165  // The non-active backends are, however, populated in the service map so that they
   166  // can be restored upon agent restart along with their state.
   167  func (lbmap *LBBPFMap) UpsertService(p *datapathTypes.UpsertServiceParams) error {
   168  	if p.ID == 0 {
   169  		return fmt.Errorf("Invalid svc ID 0")
   170  	}
   171  	if err := lbmap.upsertServiceProto(p,
   172  		p.IPv6 || p.NatPolicy == loadbalancer.SVCNatPolicyNat46); err != nil {
   173  		return err
   174  	}
   175  	if p.NatPolicy == loadbalancer.SVCNatPolicyNat46 {
   176  		if err := lbmap.upsertServiceProto(p, false); err != nil {
   177  			return err
   178  		}
   179  	}
   180  	return nil
   181  }
   182  
   183  // UpsertMaglevLookupTable calculates Maglev lookup table for given backends, and
   184  // inserts into the Maglev BPF map.
   185  func (lbmap *LBBPFMap) UpsertMaglevLookupTable(svcID uint16, backends map[string]*loadbalancer.Backend, ipv6 bool) error {
   186  	table := maglev.GetLookupTable(backends, lbmap.maglevTableSize)
   187  	for i, id := range table {
   188  		lbmap.maglevBackendIDsBuffer[i] = loadbalancer.BackendID(id)
   189  	}
   190  	if err := updateMaglevTable(ipv6, svcID, lbmap.maglevBackendIDsBuffer); err != nil {
   191  		return err
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  func deleteServiceProto(svc loadbalancer.L3n4AddrID, backendCount int, useMaglev, ipv6 bool) error {
   198  	var (
   199  		svcKey    ServiceKey
   200  		revNATKey RevNatKey
   201  	)
   202  
   203  	if ipv6 {
   204  		svcKey = NewService6Key(svc.AddrCluster.AsNetIP(), svc.Port, u8proto.ANY, svc.Scope, 0)
   205  		revNATKey = NewRevNat6Key(uint16(svc.ID))
   206  	} else {
   207  		svcKey = NewService4Key(svc.AddrCluster.AsNetIP(), svc.Port, u8proto.ANY, svc.Scope, 0)
   208  		revNATKey = NewRevNat4Key(uint16(svc.ID))
   209  	}
   210  
   211  	for slot := 0; slot <= backendCount; slot++ {
   212  		svcKey.SetBackendSlot(slot)
   213  		if err := svcKey.MapDelete(); err != nil {
   214  			return fmt.Errorf("Unable to delete service entry %+v: %w", svcKey, err)
   215  		}
   216  	}
   217  
   218  	if useMaglev {
   219  		if err := deleteMaglevTable(ipv6, uint16(svc.ID)); err != nil {
   220  			return fmt.Errorf("Unable to delete maglev lookup table %d: %w", svc.ID, err)
   221  		}
   222  	}
   223  
   224  	if err := deleteRevNatLocked(revNATKey); err != nil {
   225  		return fmt.Errorf("Unable to delete revNAT entry %+v: %w", revNATKey, err)
   226  	}
   227  
   228  	return nil
   229  }
   230  
   231  // DeleteService removes given service from a BPF map.
   232  func (*LBBPFMap) DeleteService(svc loadbalancer.L3n4AddrID, backendCount int, useMaglev bool,
   233  	natPolicy loadbalancer.SVCNatPolicy) error {
   234  	if svc.ID == 0 {
   235  		return fmt.Errorf("Invalid svc ID 0")
   236  	}
   237  	if err := deleteServiceProto(svc, backendCount, useMaglev,
   238  		svc.IsIPv6() || natPolicy == loadbalancer.SVCNatPolicyNat46); err != nil {
   239  		return err
   240  	}
   241  	if natPolicy == loadbalancer.SVCNatPolicyNat46 {
   242  		if err := deleteServiceProto(svc, 0, false, false); err != nil {
   243  			return err
   244  		}
   245  	}
   246  	return nil
   247  }
   248  
   249  // AddBackend adds a backend into a BPF map. ipv6 indicates if the backend needs
   250  // to be added in the v4 or v6 backend map.
   251  func (*LBBPFMap) AddBackend(b *loadbalancer.Backend, ipv6 bool) error {
   252  	var (
   253  		backend Backend
   254  		err     error
   255  	)
   256  
   257  	if backend, err = getBackend(b, ipv6); err != nil {
   258  		return err
   259  	}
   260  	if err := updateBackend(backend); err != nil {
   261  		return fmt.Errorf("unable to add backend %+v: %w", backend, err)
   262  	}
   263  
   264  	return nil
   265  }
   266  
   267  // UpdateBackendWithState updates the state for the given backend.
   268  //
   269  // This function should only be called to update backend's state.
   270  func (*LBBPFMap) UpdateBackendWithState(b *loadbalancer.Backend) error {
   271  	var (
   272  		backend Backend
   273  		err     error
   274  	)
   275  
   276  	if backend, err = getBackend(b, b.L3n4Addr.IsIPv6()); err != nil {
   277  		return err
   278  	}
   279  	if err := updateBackend(backend); err != nil {
   280  		return fmt.Errorf("unable to update backend state %+v: %w", b, err)
   281  	}
   282  
   283  	return nil
   284  }
   285  
   286  func deleteBackendByIDFamily(id loadbalancer.BackendID, ipv6 bool) error {
   287  	var key BackendKey
   288  
   289  	if ipv6 {
   290  		key = NewBackend6KeyV3(loadbalancer.BackendID(id))
   291  	} else {
   292  		key = NewBackend4KeyV3(loadbalancer.BackendID(id))
   293  	}
   294  
   295  	if err := deleteBackendLocked(key); err != nil {
   296  		return fmt.Errorf("Unable to delete backend %d (%t): %w", id, ipv6, err)
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  // DeleteBackendByID removes a backend identified with the given ID from a BPF map.
   303  func (*LBBPFMap) DeleteBackendByID(id loadbalancer.BackendID) error {
   304  	if id == 0 {
   305  		return fmt.Errorf("Invalid backend ID 0")
   306  	}
   307  
   308  	// The backend could be a backend for a NAT64 service, therefore
   309  	// attempt to remove from both backend maps.
   310  	if option.Config.EnableIPv6 {
   311  		deleteBackendByIDFamily(id, true)
   312  	}
   313  	if option.Config.EnableIPv4 {
   314  		deleteBackendByIDFamily(id, false)
   315  	}
   316  	return nil
   317  }
   318  
   319  // DeleteAffinityMatch removes the affinity match for the given svc and backend ID
   320  // tuple from the BPF map
   321  func (*LBBPFMap) DeleteAffinityMatch(revNATID uint16, backendID loadbalancer.BackendID) error {
   322  	return AffinityMatchMap.Delete(
   323  		NewAffinityMatchKey(revNATID, backendID).ToNetwork())
   324  }
   325  
   326  // AddAffinityMatch adds the given affinity match to the BPF map.
   327  func (*LBBPFMap) AddAffinityMatch(revNATID uint16, backendID loadbalancer.BackendID) error {
   328  	return AffinityMatchMap.Update(
   329  		NewAffinityMatchKey(revNATID, backendID).ToNetwork(),
   330  		&AffinityMatchValue{})
   331  }
   332  
   333  // DumpAffinityMatches returns the affinity match map represented as a nested
   334  // map which first key is svc ID and the second - backend ID.
   335  func (*LBBPFMap) DumpAffinityMatches() (datapathTypes.BackendIDByServiceIDSet, error) {
   336  	matches := datapathTypes.BackendIDByServiceIDSet{}
   337  
   338  	parse := func(key bpf.MapKey, value bpf.MapValue) {
   339  		matchKey := key.(*AffinityMatchKey).ToHost()
   340  		svcID := matchKey.RevNATID
   341  		backendID := matchKey.BackendID
   342  
   343  		if _, ok := matches[svcID]; !ok {
   344  			matches[svcID] = map[loadbalancer.BackendID]struct{}{}
   345  		}
   346  		matches[svcID][backendID] = struct{}{}
   347  	}
   348  
   349  	err := AffinityMatchMap.DumpWithCallback(parse)
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  
   354  	return matches, nil
   355  }
   356  
   357  func (*LBBPFMap) DumpSourceRanges(ipv6 bool) (datapathTypes.SourceRangeSetByServiceID, error) {
   358  	ret := datapathTypes.SourceRangeSetByServiceID{}
   359  	parser := func(key bpf.MapKey, value bpf.MapValue) {
   360  		k := key.(SourceRangeKey).ToHost()
   361  		revNATID := k.GetRevNATID()
   362  		if _, found := ret[revNATID]; !found {
   363  			ret[revNATID] = []*cidr.CIDR{}
   364  		}
   365  		ret[revNATID] = append(ret[revNATID], k.GetCIDR())
   366  	}
   367  
   368  	m := SourceRange4Map
   369  	if ipv6 {
   370  		m = SourceRange6Map
   371  	}
   372  	if err := m.DumpWithCallback(parser); err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	return ret, nil
   377  }
   378  
   379  func updateRevNatLocked(key RevNatKey, value RevNatValue) error {
   380  	if key.GetKey() == 0 {
   381  		return fmt.Errorf("invalid RevNat ID (0)")
   382  	}
   383  	if err := key.Map().OpenOrCreate(); err != nil {
   384  		return err
   385  	}
   386  
   387  	return key.Map().Update(key.ToNetwork(), value.ToNetwork())
   388  }
   389  
   390  func deleteRevNatLocked(key RevNatKey) error {
   391  	return key.Map().Delete(key.ToNetwork())
   392  }
   393  
   394  func (*LBBPFMap) UpdateSourceRanges(revNATID uint16, prevSourceRanges []*cidr.CIDR,
   395  	sourceRanges []*cidr.CIDR, ipv6 bool) error {
   396  
   397  	m := SourceRange4Map
   398  	if ipv6 {
   399  		m = SourceRange6Map
   400  	}
   401  
   402  	srcRangeMap := map[string]*cidr.CIDR{}
   403  	for _, cidr := range sourceRanges {
   404  		// k8s api server does not catch the IP family mismatch, so we need to catch it here
   405  		if ip.IsIPv6(cidr.IP) == !ipv6 {
   406  			log.WithFields(logrus.Fields{
   407  				logfields.ServiceID: revNATID,
   408  				logfields.CIDR:      cidr,
   409  			}).Warn("Source range's IP family does not match with the LB's. Ignoring the source range CIDR")
   410  			continue
   411  		}
   412  		srcRangeMap[cidr.String()] = cidr
   413  	}
   414  
   415  	for _, prevCIDR := range prevSourceRanges {
   416  		if _, found := srcRangeMap[prevCIDR.String()]; !found {
   417  			if err := m.Delete(srcRangeKey(prevCIDR, revNATID, ipv6)); err != nil {
   418  				return err
   419  			}
   420  		} else {
   421  			delete(srcRangeMap, prevCIDR.String())
   422  		}
   423  	}
   424  
   425  	for _, cidr := range srcRangeMap {
   426  		if err := m.Update(srcRangeKey(cidr, revNATID, ipv6), &SourceRangeValue{}); err != nil {
   427  			return err
   428  		}
   429  	}
   430  
   431  	return nil
   432  }
   433  
   434  // DumpServiceMaps dumps the services from the BPF maps.
   435  func (*LBBPFMap) DumpServiceMaps() ([]*loadbalancer.SVC, []error) {
   436  	newSVCMap := svcMap{}
   437  	errors := []error{}
   438  	flagsCache := map[string]loadbalancer.ServiceFlags{}
   439  	backendValueMap := map[loadbalancer.BackendID]BackendValue{}
   440  
   441  	parseBackendEntries := func(key bpf.MapKey, value bpf.MapValue) {
   442  		backendKey := key.(BackendKey)
   443  		backendValue := value.(BackendValue).ToHost()
   444  		backendValueMap[backendKey.GetID()] = backendValue
   445  	}
   446  
   447  	parseSVCEntries := func(key bpf.MapKey, value bpf.MapValue) {
   448  		svcKey := key.(ServiceKey).ToHost()
   449  		svcValue := value.(ServiceValue).ToHost()
   450  
   451  		fe := svcFrontend(svcKey, svcValue)
   452  
   453  		// Create master entry in case there are no backends.
   454  		if svcKey.GetBackendSlot() == 0 {
   455  			// Build a cache of flags stored in the value of the master key to
   456  			// map it later.
   457  			// FIXME proto is being ignored everywhere in the datapath.
   458  			addrStr := svcKey.GetAddress().String()
   459  			portStr := strconv.Itoa(int(svcKey.GetPort()))
   460  			flagsCache[net.JoinHostPort(addrStr, portStr)] = loadbalancer.ServiceFlags(svcValue.GetFlags())
   461  
   462  			newSVCMap.addFE(fe)
   463  			return
   464  		}
   465  
   466  		backendID := svcValue.GetBackendID()
   467  		backendValue, found := backendValueMap[backendID]
   468  		if !found {
   469  			errors = append(errors, fmt.Errorf("backend %d not found", backendID))
   470  			return
   471  		}
   472  
   473  		be := svcBackend(backendID, backendValue)
   474  		newSVCMap.addFEnBE(fe, be, svcKey.GetBackendSlot())
   475  	}
   476  
   477  	if option.Config.EnableIPv4 {
   478  		// TODO(brb) optimization: instead of dumping the backend map, we can
   479  		// pass its content to the function.
   480  		err := Backend4MapV3.DumpWithCallback(parseBackendEntries)
   481  		if err != nil {
   482  			errors = append(errors, err)
   483  		}
   484  		err = Service4MapV2.DumpWithCallback(parseSVCEntries)
   485  		if err != nil {
   486  			errors = append(errors, err)
   487  		}
   488  	}
   489  
   490  	if option.Config.EnableIPv6 {
   491  		// TODO(brb) same ^^ optimization applies here as well.
   492  		err := Backend6MapV3.DumpWithCallback(parseBackendEntries)
   493  		if err != nil {
   494  			errors = append(errors, err)
   495  		}
   496  		err = Service6MapV2.DumpWithCallback(parseSVCEntries)
   497  		if err != nil {
   498  			errors = append(errors, err)
   499  		}
   500  	}
   501  
   502  	newSVCList := make([]*loadbalancer.SVC, 0, len(newSVCMap))
   503  	for hash := range newSVCMap {
   504  		svc := newSVCMap[hash]
   505  		addrStr := svc.Frontend.AddrCluster.String()
   506  		portStr := strconv.Itoa(int(svc.Frontend.Port))
   507  		host := net.JoinHostPort(addrStr, portStr)
   508  		svc.Type = flagsCache[host].SVCType()
   509  		svc.ExtTrafficPolicy = flagsCache[host].SVCExtTrafficPolicy()
   510  		svc.IntTrafficPolicy = flagsCache[host].SVCIntTrafficPolicy()
   511  		svc.NatPolicy = flagsCache[host].SVCNatPolicy(svc.Frontend.L3n4Addr)
   512  		newSVCList = append(newSVCList, &svc)
   513  	}
   514  
   515  	return newSVCList, errors
   516  }
   517  
   518  // DumpBackendMaps dumps the backend entries from the BPF maps.
   519  func (*LBBPFMap) DumpBackendMaps() ([]*loadbalancer.Backend, error) {
   520  	backendValueMap := map[loadbalancer.BackendID]BackendValue{}
   521  	lbBackends := []*loadbalancer.Backend{}
   522  
   523  	parseBackendEntries := func(key bpf.MapKey, value bpf.MapValue) {
   524  		// No need to deep copy the key because we are using the ID which
   525  		// is a value.
   526  		backendKey := key.(BackendKey)
   527  		backendValue := value.(BackendValue).ToHost()
   528  		backendValueMap[backendKey.GetID()] = backendValue
   529  	}
   530  
   531  	if option.Config.EnableIPv4 {
   532  		err := Backend4MapV3.DumpWithCallback(parseBackendEntries)
   533  		if err != nil {
   534  			return nil, fmt.Errorf("Unable to dump lb4 backends map: %w", err)
   535  		}
   536  	}
   537  
   538  	if option.Config.EnableIPv6 {
   539  		err := Backend6MapV3.DumpWithCallback(parseBackendEntries)
   540  		if err != nil {
   541  			return nil, fmt.Errorf("Unable to dump lb6 backends map: %w", err)
   542  		}
   543  	}
   544  
   545  	for backendID, backendVal := range backendValueMap {
   546  		ip := backendVal.GetAddress()
   547  		addrCluster := cmtypes.MustAddrClusterFromIP(ip)
   548  		port := backendVal.GetPort()
   549  		proto := loadbalancer.NONE
   550  		state := loadbalancer.GetBackendStateFromFlags(backendVal.GetFlags())
   551  		zone := backendVal.GetZone()
   552  		lbBackend := loadbalancer.NewBackendWithState(backendID, proto, addrCluster, port, zone, state)
   553  		lbBackends = append(lbBackends, lbBackend)
   554  	}
   555  
   556  	return lbBackends, nil
   557  }
   558  
   559  // IsMaglevLookupTableRecreated returns true if the maglev lookup BPF map
   560  // was recreated due to the changed M param.
   561  func (*LBBPFMap) IsMaglevLookupTableRecreated(ipv6 bool) bool {
   562  	if ipv6 {
   563  		return maglevRecreatedIPv6
   564  	}
   565  	return maglevRecreatedIPv4
   566  }
   567  
   568  func updateMasterService(fe ServiceKey, v ServiceValue, activeBackends int, revNATID int, svcType loadbalancer.SVCType,
   569  	svcExtLocal, svcIntLocal bool, svcNatPolicy loadbalancer.SVCNatPolicy, sessionAffinity bool,
   570  	sessionAffinityTimeoutSec uint32, checkSourceRange bool, l7lbProxyPort uint16, loopbackHostport bool) error {
   571  
   572  	// isRoutable denotes whether this service can be accessed from outside the cluster.
   573  	isRoutable := !fe.IsSurrogate() &&
   574  		(svcType != loadbalancer.SVCTypeClusterIP || option.Config.ExternalClusterIP)
   575  
   576  	fe.SetBackendSlot(0)
   577  	v.SetCount(activeBackends)
   578  	v.SetRevNat(revNATID)
   579  	flag := loadbalancer.NewSvcFlag(&loadbalancer.SvcFlagParam{
   580  		SvcType:          svcType,
   581  		SvcExtLocal:      svcExtLocal,
   582  		SvcIntLocal:      svcIntLocal,
   583  		SvcNatPolicy:     svcNatPolicy,
   584  		SessionAffinity:  sessionAffinity,
   585  		IsRoutable:       isRoutable,
   586  		CheckSourceRange: checkSourceRange,
   587  		L7LoadBalancer:   l7lbProxyPort != 0,
   588  		LoopbackHostport: loopbackHostport,
   589  	})
   590  	v.SetFlags(flag.UInt16())
   591  	if sessionAffinity {
   592  		v.SetSessionAffinityTimeoutSec(sessionAffinityTimeoutSec)
   593  	}
   594  	if l7lbProxyPort != 0 {
   595  		v.SetL7LBProxyPort(l7lbProxyPort)
   596  	}
   597  
   598  	return updateServiceEndpoint(fe, v)
   599  }
   600  
   601  func deleteServiceLocked(key ServiceKey) error {
   602  	return key.Map().Delete(key.ToNetwork())
   603  }
   604  
   605  func getBackend(backend *loadbalancer.Backend, ipv6 bool) (Backend, error) {
   606  	var (
   607  		lbBackend Backend
   608  		err       error
   609  	)
   610  
   611  	if backend.ID == 0 {
   612  		return lbBackend, fmt.Errorf("invalid backend ID 0")
   613  	}
   614  
   615  	if ipv6 {
   616  		lbBackend, err = NewBackend6V3(backend.ID, backend.AddrCluster, backend.Port, u8proto.ANY,
   617  			backend.State, backend.ZoneID)
   618  	} else {
   619  		lbBackend, err = NewBackend4V3(backend.ID, backend.AddrCluster, backend.Port, u8proto.ANY,
   620  			backend.State, backend.ZoneID)
   621  	}
   622  	if err != nil {
   623  		return lbBackend, fmt.Errorf("unable to create lbBackend (%d, %s, %d, %t): %w",
   624  			backend.ID, backend.AddrCluster.String(), backend.Port, ipv6, err)
   625  	}
   626  
   627  	return lbBackend, nil
   628  }
   629  
   630  func updateBackend(backend Backend) error {
   631  	if err := backend.Map().OpenOrCreate(); err != nil {
   632  		return err
   633  	}
   634  
   635  	return backend.Map().Update(backend.GetKey(), backend.GetValue().ToNetwork())
   636  }
   637  
   638  func deleteBackendLocked(key BackendKey) error {
   639  	_, err := key.Map().SilentDelete(key)
   640  	return err
   641  }
   642  
   643  func updateServiceEndpoint(key ServiceKey, value ServiceValue) error {
   644  	if key.GetBackendSlot() != 0 && value.RevNatKey().GetKey() == 0 {
   645  		return fmt.Errorf("invalid RevNat ID (0) in the Service Value")
   646  	}
   647  	if err := key.Map().OpenOrCreate(); err != nil {
   648  		return err
   649  	}
   650  
   651  	if err := key.Map().Update(key.ToNetwork(), value.ToNetwork()); err != nil {
   652  		return err
   653  	}
   654  
   655  	log.WithFields(logrus.Fields{
   656  		logfields.ServiceKey:   key,
   657  		logfields.ServiceValue: value,
   658  		logfields.BackendSlot:  key.GetBackendSlot(),
   659  	}).Debug("Upserted service entry")
   660  
   661  	return nil
   662  }
   663  
   664  type svcMap map[string]loadbalancer.SVC
   665  
   666  // addFE adds the give 'fe' to the svcMap without any backends. If it does not
   667  // yet exist, an entry is created. Otherwise, the existing entry is left
   668  // unchanged.
   669  func (svcs svcMap) addFE(fe *loadbalancer.L3n4AddrID) *loadbalancer.SVC {
   670  	hash := fe.Hash()
   671  	lbsvc, ok := svcs[hash]
   672  	if !ok {
   673  		lbsvc = loadbalancer.SVC{Frontend: *fe}
   674  		svcs[hash] = lbsvc
   675  	}
   676  	return &lbsvc
   677  }
   678  
   679  // addFEnBE adds the given 'fe' and 'be' to the svcMap. If 'fe' exists and beIndex is 0,
   680  // the new 'be' will be appended to the list of existing backends. If beIndex is bigger
   681  // than the size of existing backends slice, it will be created a new array with size of
   682  // beIndex and the new 'be' will be inserted on index beIndex-1 of that new array. All
   683  // remaining be elements will be kept on the same index and, in case the new array is
   684  // larger than the number of backends, some elements will be empty.
   685  func (svcs svcMap) addFEnBE(fe *loadbalancer.L3n4AddrID, be *loadbalancer.Backend, beIndex int) *loadbalancer.SVC {
   686  	hash := fe.Hash()
   687  	lbsvc, ok := svcs[hash]
   688  	if !ok {
   689  		var bes []*loadbalancer.Backend
   690  		if beIndex == 0 {
   691  			bes = make([]*loadbalancer.Backend, 1)
   692  			bes[0] = be
   693  		} else {
   694  			bes = make([]*loadbalancer.Backend, beIndex)
   695  			bes[beIndex-1] = be
   696  		}
   697  		lbsvc = loadbalancer.SVC{
   698  			Frontend: *fe,
   699  			Backends: bes,
   700  		}
   701  	} else {
   702  		var bes []*loadbalancer.Backend
   703  		if len(lbsvc.Backends) < beIndex {
   704  			bes = make([]*loadbalancer.Backend, beIndex)
   705  			copy(bes, lbsvc.Backends)
   706  			lbsvc.Backends = bes
   707  		}
   708  		if beIndex == 0 {
   709  			lbsvc.Backends = append(lbsvc.Backends, be)
   710  		} else {
   711  			lbsvc.Backends[beIndex-1] = be
   712  		}
   713  	}
   714  
   715  	svcs[hash] = lbsvc
   716  	return &lbsvc
   717  }
   718  
   719  // Init updates the map info defaults for sock rev nat {4,6} and LB maps and
   720  // then initializes all LB-related maps.
   721  func Init(params InitParams) {
   722  	if params.MaxSockRevNatMapEntries != 0 {
   723  		MaxSockRevNat4MapEntries = params.MaxSockRevNatMapEntries
   724  		MaxSockRevNat6MapEntries = params.MaxSockRevNatMapEntries
   725  	}
   726  
   727  	MaglevMapMaxEntries = params.MaglevMapMaxEntries
   728  
   729  	initSVC(params)
   730  	initAffinity(params)
   731  	initSourceRange(params)
   732  }
   733  
   734  // ExistsSockRevNat checks if the passed entry exists in the sock rev nat map.
   735  func (*LBBPFMap) ExistsSockRevNat(cookie uint64, addr net.IP, port uint16) bool {
   736  	if addr.To4() != nil {
   737  		key := NewSockRevNat4Key(cookie, addr, port)
   738  		if _, err := key.Map().Lookup(key); err == nil {
   739  			return true
   740  		}
   741  	} else {
   742  		key := NewSockRevNat6Key(cookie, addr, port)
   743  		if _, err := key.Map().Lookup(key); err == nil {
   744  			return true
   745  		}
   746  	}
   747  
   748  	return false
   749  }
   750  
   751  // InitParams represents the parameters to be passed to Init().
   752  type InitParams struct {
   753  	IPv4, IPv6 bool
   754  
   755  	MaxSockRevNatMapEntries                                         int
   756  	ServiceMapMaxEntries, BackEndMapMaxEntries, RevNatMapMaxEntries int
   757  	AffinityMapMaxEntries                                           int
   758  	SourceRangeMapMaxEntries                                        int
   759  	MaglevMapMaxEntries                                             int
   760  }