github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/maps/ctmap/ctmap.go (about)

     1  // Copyright 2016-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ctmap
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"math"
    22  	"net"
    23  	"os"
    24  	"reflect"
    25  	"time"
    26  	"unsafe"
    27  
    28  	"github.com/cilium/cilium/pkg/bpf"
    29  	"github.com/cilium/cilium/pkg/defaults"
    30  	"github.com/cilium/cilium/pkg/logging"
    31  	"github.com/cilium/cilium/pkg/logging/logfields"
    32  	"github.com/cilium/cilium/pkg/maps/nat"
    33  	"github.com/cilium/cilium/pkg/metrics"
    34  	"github.com/cilium/cilium/pkg/option"
    35  	"github.com/cilium/cilium/pkg/tuple"
    36  
    37  	"github.com/sirupsen/logrus"
    38  )
    39  
    40  var (
    41  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-ct")
    42  
    43  	// labelIPv6CTDumpInterrupts marks the count for conntrack dump resets (IPv6).
    44  	labelIPv6CTDumpInterrupts = map[string]string{
    45  		metrics.LabelDatapathArea:   "conntrack",
    46  		metrics.LabelDatapathName:   "dump_interrupts",
    47  		metrics.LabelDatapathFamily: "ipv6",
    48  	}
    49  	// labelIPv4CTDumpInterrupts marks the count for conntrack dump resets (IPv4).
    50  	labelIPv4CTDumpInterrupts = map[string]string{
    51  		metrics.LabelDatapathArea:   "conntrack",
    52  		metrics.LabelDatapathName:   "dump_interrupts",
    53  		metrics.LabelDatapathFamily: "ipv4",
    54  	}
    55  
    56  	mapInfo = make(map[MapType]mapAttributes)
    57  )
    58  
    59  const (
    60  	// mapCount counts the maximum number of CT maps that one endpoint may
    61  	// access at once.
    62  	mapCount = 4
    63  
    64  	// Map names for TCP CT tables are retained from Cilium 1.0 naming
    65  	// scheme to minimize disruption of ongoing connections during upgrade.
    66  	MapNamePrefix     = "cilium_ct"
    67  	MapNameTCP6       = MapNamePrefix + "6_"
    68  	MapNameTCP4       = MapNamePrefix + "4_"
    69  	MapNameTCP6Global = MapNameTCP6 + "global"
    70  	MapNameTCP4Global = MapNameTCP4 + "global"
    71  
    72  	// Map names for "any" protocols indicate CT for non-TCP protocols.
    73  	MapNameAny6       = MapNamePrefix + "_any6_"
    74  	MapNameAny4       = MapNamePrefix + "_any4_"
    75  	MapNameAny6Global = MapNameAny6 + "global"
    76  	MapNameAny4Global = MapNameAny4 + "global"
    77  
    78  	MapNumEntriesLocal = 64000
    79  
    80  	TUPLE_F_OUT     = 0
    81  	TUPLE_F_IN      = 1
    82  	TUPLE_F_RELATED = 2
    83  	TUPLE_F_SERVICE = 4
    84  
    85  	// MaxTime specifies the last possible time for GCFilter.Time
    86  	MaxTime = math.MaxUint32
    87  
    88  	noAction = iota
    89  	deleteEntry
    90  
    91  	metricsAlive   = "alive"
    92  	metricsDeleted = "deleted"
    93  )
    94  
    95  type NatMap interface {
    96  	Open() error
    97  	Close() error
    98  	DeleteMapping(key tuple.TupleKey) error
    99  }
   100  
   101  type mapAttributes struct {
   102  	mapKey     bpf.MapKey
   103  	keySize    int
   104  	mapValue   bpf.MapValue
   105  	valueSize  int
   106  	maxEntries int
   107  	parser     bpf.DumpParser
   108  	bpfDefine  string
   109  	natMap     NatMap
   110  }
   111  
   112  func setupMapInfo(mapType MapType, define string, mapKey bpf.MapKey, keySize int, maxEntries int, nat NatMap) {
   113  	mapInfo[mapType] = mapAttributes{
   114  		bpfDefine: define,
   115  		mapKey:    mapKey,
   116  		keySize:   keySize,
   117  		// the value type is CtEntry for all CT maps
   118  		mapValue:   &CtEntry{},
   119  		valueSize:  int(unsafe.Sizeof(CtEntry{})),
   120  		maxEntries: maxEntries,
   121  		parser:     bpf.ConvertKeyValue,
   122  		natMap:     nat,
   123  	}
   124  }
   125  
   126  // InitMapInfo builds the information about different CT maps for the
   127  // combination of L3/L4 protocols, using the specified limits on TCP vs non-TCP
   128  // maps.
   129  func InitMapInfo(tcpMaxEntries, anyMaxEntries int, v4, v6 bool) {
   130  	mapInfo = make(map[MapType]mapAttributes)
   131  
   132  	global4Map, global6Map := nat.GlobalMaps(v4, v6)
   133  
   134  	// SNAT also only works if the CT map is global so all local maps will be nil
   135  	natMaps := map[MapType]NatMap{
   136  		MapTypeIPv4TCPLocal:  nil,
   137  		MapTypeIPv6TCPLocal:  nil,
   138  		MapTypeIPv4TCPGlobal: global4Map,
   139  		MapTypeIPv6TCPGlobal: global6Map,
   140  		MapTypeIPv4AnyLocal:  nil,
   141  		MapTypeIPv6AnyLocal:  nil,
   142  		MapTypeIPv4AnyGlobal: global4Map,
   143  		MapTypeIPv6AnyGlobal: global6Map,
   144  	}
   145  
   146  	setupMapInfo(MapType(MapTypeIPv4TCPLocal), "CT_MAP_TCP4",
   147  		&CtKey4{}, int(unsafe.Sizeof(CtKey4{})),
   148  		MapNumEntriesLocal, natMaps[MapTypeIPv4TCPLocal])
   149  
   150  	setupMapInfo(MapType(MapTypeIPv6TCPLocal), "CT_MAP_TCP6",
   151  		&CtKey6{}, int(unsafe.Sizeof(CtKey6{})),
   152  		MapNumEntriesLocal, natMaps[MapTypeIPv6TCPLocal])
   153  
   154  	setupMapInfo(MapType(MapTypeIPv4TCPGlobal), "CT_MAP_TCP4",
   155  		&CtKey4Global{}, int(unsafe.Sizeof(CtKey4Global{})),
   156  		tcpMaxEntries, natMaps[MapTypeIPv4TCPGlobal])
   157  
   158  	setupMapInfo(MapType(MapTypeIPv6TCPGlobal), "CT_MAP_TCP6",
   159  		&CtKey6Global{}, int(unsafe.Sizeof(CtKey6Global{})),
   160  		tcpMaxEntries, natMaps[MapTypeIPv6TCPGlobal])
   161  
   162  	setupMapInfo(MapType(MapTypeIPv4AnyLocal), "CT_MAP_ANY4",
   163  		&CtKey4{}, int(unsafe.Sizeof(CtKey4{})),
   164  		MapNumEntriesLocal, natMaps[MapTypeIPv4AnyLocal])
   165  
   166  	setupMapInfo(MapType(MapTypeIPv6AnyLocal), "CT_MAP_ANY6",
   167  		&CtKey6{}, int(unsafe.Sizeof(CtKey6{})),
   168  		MapNumEntriesLocal, natMaps[MapTypeIPv6AnyLocal])
   169  
   170  	setupMapInfo(MapType(MapTypeIPv4AnyGlobal), "CT_MAP_ANY4",
   171  		&CtKey4Global{}, int(unsafe.Sizeof(CtKey4Global{})),
   172  		anyMaxEntries, natMaps[MapTypeIPv4AnyGlobal])
   173  
   174  	setupMapInfo(MapType(MapTypeIPv6AnyGlobal), "CT_MAP_ANY6",
   175  		&CtKey6Global{}, int(unsafe.Sizeof(CtKey6Global{})),
   176  		anyMaxEntries, natMaps[MapTypeIPv6AnyGlobal])
   177  }
   178  
   179  func init() {
   180  	InitMapInfo(option.CTMapEntriesGlobalTCPDefault, option.CTMapEntriesGlobalAnyDefault, true, true)
   181  }
   182  
   183  // CtEndpoint represents an endpoint for the functions required to manage
   184  // conntrack maps for the endpoint.
   185  type CtEndpoint interface {
   186  	GetID() uint64
   187  }
   188  
   189  // Map represents an instance of a BPF connection tracking map.
   190  type Map struct {
   191  	bpf.Map
   192  
   193  	mapType MapType
   194  	// define maps to the macro used in the datapath portion for the map
   195  	// name, for example 'CT_MAP4'.
   196  	define string
   197  }
   198  
   199  // GCFilter contains the necessary fields to filter the CT maps.
   200  // Filtering by endpoint requires both EndpointID to be > 0 and
   201  // EndpointIP to be not nil.
   202  type GCFilter struct {
   203  	// RemoveExpired enables removal of all entries that have expired
   204  	RemoveExpired bool
   205  
   206  	// Time is the reference timestamp to reomove expired entries. If
   207  	// RemoveExpired is true and lifetime is lesser than Time, the entry is
   208  	// removed
   209  	Time uint32
   210  
   211  	// ValidIPs is the list of valid IPs to scrub all entries for which the
   212  	// source or destination IP is *not* matching one of the valid IPs.
   213  	// The key is the IP in string form: net.IP.String()
   214  	ValidIPs map[string]struct{}
   215  
   216  	// MatchIPs is the list of IPs to remove from the conntrack table
   217  	MatchIPs map[string]struct{}
   218  }
   219  
   220  // ToString iterates through Map m and writes the values of the ct entries in m
   221  // to a string.
   222  func (m *Map) DumpEntries() (string, error) {
   223  	var buffer bytes.Buffer
   224  
   225  	cb := func(k bpf.MapKey, v bpf.MapValue) {
   226  		// No need to deep copy as the values are used to create new strings
   227  		key := k.(CtKey)
   228  		if !key.ToHost().Dump(&buffer, true) {
   229  			return
   230  		}
   231  		value := v.(*CtEntry)
   232  		buffer.WriteString(value.String())
   233  	}
   234  	// DumpWithCallback() must be called before buffer.String().
   235  	err := m.DumpWithCallback(cb)
   236  	return buffer.String(), err
   237  }
   238  
   239  // NewMap creates a new CT map of the specified type with the specified name.
   240  func NewMap(mapName string, mapType MapType) *Map {
   241  	result := &Map{
   242  		Map: *bpf.NewMap(mapName,
   243  			bpf.MapTypeLRUHash,
   244  			mapInfo[mapType].mapKey,
   245  			mapInfo[mapType].keySize,
   246  			mapInfo[mapType].mapValue,
   247  			mapInfo[mapType].valueSize,
   248  			mapInfo[mapType].maxEntries,
   249  			0, 0,
   250  			mapInfo[mapType].parser,
   251  		),
   252  		mapType: mapType,
   253  		define:  mapInfo[mapType].bpfDefine,
   254  	}
   255  	return result
   256  }
   257  
   258  func purgeCtEntry6(m *Map, key CtKey, natMap NatMap) error {
   259  	err := m.Delete(key)
   260  	if err == nil && natMap != nil {
   261  		natMap.DeleteMapping(key.GetTupleKey())
   262  	}
   263  	return err
   264  }
   265  
   266  // doGC6 iterates through a CTv6 map and drops entries based on the given
   267  // filter.
   268  func doGC6(m *Map, filter *GCFilter) gcStats {
   269  	natMap := mapInfo[m.mapType].natMap
   270  	stats := statStartGc(m)
   271  	defer stats.finish()
   272  
   273  	if natMap != nil {
   274  		err := natMap.Open()
   275  		if err == nil {
   276  			defer natMap.Close()
   277  		} else {
   278  			natMap = nil
   279  		}
   280  	}
   281  
   282  	filterCallback := func(key bpf.MapKey, value bpf.MapValue) {
   283  		entry := value.(*CtEntry)
   284  
   285  		switch obj := key.(type) {
   286  		case *CtKey6Global:
   287  			currentKey6Global := obj
   288  			// In CT entries, the source address of the conntrack entry (`SourceAddr`) is
   289  			// the destination of the packet received, therefore it's the packet's
   290  			// destination IP
   291  			action := filter.doFiltering(currentKey6Global.DestAddr.IP(), currentKey6Global.SourceAddr.IP(), currentKey6Global.SourcePort,
   292  				uint8(currentKey6Global.NextHeader), currentKey6Global.Flags, entry)
   293  
   294  			switch action {
   295  			case deleteEntry:
   296  				err := purgeCtEntry6(m, currentKey6Global, natMap)
   297  				if err != nil {
   298  					log.WithError(err).WithField(logfields.Key, currentKey6Global.String()).Error("Unable to delete CT entry")
   299  				} else {
   300  					stats.deleted++
   301  				}
   302  			default:
   303  				stats.aliveEntries++
   304  			}
   305  		case *CtKey6:
   306  			currentKey6 := obj
   307  			// In CT entries, the source address of the conntrack entry (`SourceAddr`) is
   308  			// the destination of the packet received, therefore it's the packet's
   309  			// destination IP
   310  			action := filter.doFiltering(currentKey6.DestAddr.IP(), currentKey6.SourceAddr.IP(), currentKey6.SourcePort,
   311  				uint8(currentKey6.NextHeader), currentKey6.Flags, entry)
   312  
   313  			switch action {
   314  			case deleteEntry:
   315  				err := purgeCtEntry6(m, currentKey6, natMap)
   316  				if err != nil {
   317  					log.WithError(err).WithField(logfields.Key, currentKey6.String()).Error("Unable to delete CT entry")
   318  				} else {
   319  					stats.deleted++
   320  				}
   321  			default:
   322  				stats.aliveEntries++
   323  			}
   324  		default:
   325  			log.Warningf("Encountered unknown type while scanning conntrack table: %v", reflect.TypeOf(key))
   326  		}
   327  	}
   328  	stats.dumpError = m.DumpReliablyWithCallback(filterCallback, stats.DumpStats)
   329  
   330  	return stats
   331  }
   332  
   333  func purgeCtEntry4(m *Map, key CtKey, natMap NatMap) error {
   334  	err := m.Delete(key)
   335  	if err == nil && natMap != nil {
   336  		natMap.DeleteMapping(key.GetTupleKey())
   337  	}
   338  	return err
   339  }
   340  
   341  // doGC4 iterates through a CTv4 map and drops entries based on the given
   342  // filter.
   343  func doGC4(m *Map, filter *GCFilter) gcStats {
   344  	natMap := mapInfo[m.mapType].natMap
   345  	stats := statStartGc(m)
   346  	defer stats.finish()
   347  
   348  	if natMap != nil {
   349  		if err := natMap.Open(); err == nil {
   350  			defer natMap.Close()
   351  		} else {
   352  			natMap = nil
   353  		}
   354  	}
   355  
   356  	filterCallback := func(key bpf.MapKey, value bpf.MapValue) {
   357  		entry := value.(*CtEntry)
   358  
   359  		switch obj := key.(type) {
   360  		case *CtKey4Global:
   361  			currentKey4Global := obj
   362  			// In CT entries, the source address of the conntrack entry (`SourceAddr`) is
   363  			// the destination of the packet received, therefore it's the packet's
   364  			// destination IP
   365  			action := filter.doFiltering(currentKey4Global.DestAddr.IP(), currentKey4Global.SourceAddr.IP(), currentKey4Global.SourcePort,
   366  				uint8(currentKey4Global.NextHeader), currentKey4Global.Flags, entry)
   367  
   368  			switch action {
   369  			case deleteEntry:
   370  				err := purgeCtEntry4(m, currentKey4Global, natMap)
   371  				if err != nil {
   372  					log.WithError(err).WithField(logfields.Key, currentKey4Global.String()).Error("Unable to delete CT entry")
   373  				} else {
   374  					stats.deleted++
   375  				}
   376  			default:
   377  				stats.aliveEntries++
   378  			}
   379  		case *CtKey4:
   380  			currentKey4 := obj
   381  			// In CT entries, the source address of the conntrack entry (`SourceAddr`) is
   382  			// the destination of the packet received, therefore it's the packet's
   383  			// destination IP
   384  			action := filter.doFiltering(currentKey4.DestAddr.IP(), currentKey4.SourceAddr.IP(), currentKey4.SourcePort,
   385  				uint8(currentKey4.NextHeader), currentKey4.Flags, entry)
   386  
   387  			switch action {
   388  			case deleteEntry:
   389  				err := purgeCtEntry4(m, currentKey4, natMap)
   390  				if err != nil {
   391  					log.WithError(err).WithField(logfields.Key, currentKey4.String()).Error("Unable to delete CT entry")
   392  				} else {
   393  					stats.deleted++
   394  				}
   395  			default:
   396  				stats.aliveEntries++
   397  			}
   398  		default:
   399  			log.Warningf("Encountered unknown type while scanning conntrack table: %v", reflect.TypeOf(key))
   400  		}
   401  	}
   402  	stats.dumpError = m.DumpReliablyWithCallback(filterCallback, stats.DumpStats)
   403  
   404  	return stats
   405  }
   406  
   407  func (f *GCFilter) doFiltering(srcIP net.IP, dstIP net.IP, dstPort uint16, nextHdr, flags uint8, entry *CtEntry) (action int) {
   408  	if f.RemoveExpired && entry.Lifetime < f.Time {
   409  		return deleteEntry
   410  	}
   411  
   412  	if f.ValidIPs != nil {
   413  		_, srcIPExists := f.ValidIPs[srcIP.String()]
   414  		_, dstIPExists := f.ValidIPs[dstIP.String()]
   415  		if !srcIPExists && !dstIPExists {
   416  			return deleteEntry
   417  		}
   418  	}
   419  
   420  	if f.MatchIPs != nil {
   421  		_, srcIPExists := f.MatchIPs[srcIP.String()]
   422  		_, dstIPExists := f.MatchIPs[dstIP.String()]
   423  		if srcIPExists || dstIPExists {
   424  			return deleteEntry
   425  		}
   426  	}
   427  
   428  	return noAction
   429  }
   430  
   431  func doGC(m *Map, filter *GCFilter) int {
   432  	if m.mapType.isIPv6() {
   433  		return int(doGC6(m, filter).deleted)
   434  	} else if m.mapType.isIPv4() {
   435  		return int(doGC4(m, filter).deleted)
   436  	}
   437  	log.Fatalf("Unsupported ct map type: %s", m.mapType.String())
   438  	return 0
   439  }
   440  
   441  // GC runs garbage collection for map m with name mapType with the given filter.
   442  // It returns how many items were deleted from m.
   443  func GC(m *Map, filter *GCFilter) int {
   444  	if filter.RemoveExpired {
   445  		t, _ := bpf.GetMtime()
   446  		tsec := t / 1000000000
   447  		filter.Time = uint32(tsec)
   448  	}
   449  
   450  	return doGC(m, filter)
   451  }
   452  
   453  // Flush runs garbage collection for map m with the name mapType, deleting all
   454  // entries. The specified map must be already opened using bpf.OpenMap().
   455  func (m *Map) Flush() int {
   456  	return doGC(m, &GCFilter{
   457  		RemoveExpired: true,
   458  		Time:          MaxTime,
   459  	})
   460  }
   461  
   462  // DeleteIfUpgradeNeeded attempts to open the conntrack maps associated with
   463  // the specified endpoint, and delete the maps from the filesystem if any
   464  // properties do not match the properties defined in this package.
   465  //
   466  // The typical trigger for this is when, for example, the CT entry size changes
   467  // from one version of Cilium to the next. When Cilium restarts, it may opt
   468  // to restore endpoints from the prior life. Existing endpoints that use the
   469  // old map style are incompatible with the new version, so the CT map must be
   470  // destroyed and recreated during upgrade. By removing the old map location
   471  // from the filesystem, we ensure that the next time that the endpoint is
   472  // regenerated, it will recreate a new CT map with the new properties.
   473  //
   474  // Note that if an existing BPF program refers to the map at the canonical
   475  // paths (as fetched via the getMapPathsToKeySize() call below), then that BPF
   476  // program will continue to operate on the old map, even once the map is
   477  // removed from the filesystem. The old map will only be completely cleaned up
   478  // once all referenced to the map are cleared - that is, all BPF programs which
   479  // refer to the old map and removed/reloaded.
   480  func DeleteIfUpgradeNeeded(e CtEndpoint) {
   481  	for _, newMap := range maps(e, true, true) {
   482  		path, err := newMap.Path()
   483  		if err != nil {
   484  			log.WithError(err).Warning("Failed to get path for CT map")
   485  			continue
   486  		}
   487  		scopedLog := log.WithField(logfields.Path, path)
   488  		oldMap, err := bpf.OpenMap(path)
   489  		if err != nil {
   490  			scopedLog.WithError(err).Debug("Couldn't open CT map for upgrade")
   491  			continue
   492  		}
   493  		if oldMap.CheckAndUpgrade(&newMap.Map.MapInfo) {
   494  			scopedLog.Warning("CT Map upgraded, expect brief disruption of ongoing connections")
   495  		}
   496  		oldMap.Close()
   497  	}
   498  }
   499  
   500  // maps returns all connecting tracking maps associated with endpoint 'e' (or
   501  // the global maps if 'e' is nil).
   502  func maps(e CtEndpoint, ipv4, ipv6 bool) []*Map {
   503  	result := make([]*Map, 0, mapCount)
   504  	if e == nil {
   505  		if ipv4 {
   506  			result = append(result, NewMap(MapNameTCP4Global, MapTypeIPv4TCPGlobal))
   507  			result = append(result, NewMap(MapNameAny4Global, MapTypeIPv4AnyGlobal))
   508  		}
   509  		if ipv6 {
   510  			result = append(result, NewMap(MapNameTCP6Global, MapTypeIPv6TCPGlobal))
   511  			result = append(result, NewMap(MapNameAny6Global, MapTypeIPv6AnyGlobal))
   512  		}
   513  	} else {
   514  		if ipv4 {
   515  			result = append(result, NewMap(bpf.LocalMapName(MapNameTCP4, uint16(e.GetID())),
   516  				MapTypeIPv4TCPLocal))
   517  			result = append(result, NewMap(bpf.LocalMapName(MapNameAny4, uint16(e.GetID())),
   518  				MapTypeIPv4AnyLocal))
   519  		}
   520  		if ipv6 {
   521  			result = append(result, NewMap(bpf.LocalMapName(MapNameTCP6, uint16(e.GetID())),
   522  				MapTypeIPv6TCPLocal))
   523  			result = append(result, NewMap(bpf.LocalMapName(MapNameAny6, uint16(e.GetID())),
   524  				MapTypeIPv6AnyLocal))
   525  		}
   526  	}
   527  	return result
   528  }
   529  
   530  // LocalMaps returns a slice of CT maps for the endpoint, which are local to
   531  // the endpoint and not shared with other endpoints. If ipv4 or ipv6 are false,
   532  // the maps for that protocol will not be returned.
   533  //
   534  // The returned maps are not yet opened.
   535  func LocalMaps(e CtEndpoint, ipv4, ipv6 bool) []*Map {
   536  	return maps(e, ipv4, ipv6)
   537  }
   538  
   539  // GlobalMaps returns a slice of CT maps that are used globally by all
   540  // endpoints that are not otherwise configured to use their own local maps.
   541  // If ipv4 or ipv6 are false, the maps for that protocol will not be returned.
   542  //
   543  // The returned maps are not yet opened.
   544  func GlobalMaps(ipv4, ipv6 bool) []*Map {
   545  	return maps(nil, ipv4, ipv6)
   546  }
   547  
   548  // NameIsGlobal returns true if the specified filename (basename) denotes a
   549  // global conntrack map.
   550  func NameIsGlobal(filename string) bool {
   551  	switch filename {
   552  	case MapNameTCP4Global, MapNameAny4Global, MapNameTCP6Global, MapNameAny6Global:
   553  		return true
   554  	}
   555  	return false
   556  }
   557  
   558  // WriteBPFMacros writes the map names for conntrack maps into the specified
   559  // writer, defining usage of the global map or local maps depending on whether
   560  // the specified CtEndpoint is nil.
   561  func WriteBPFMacros(fw io.Writer, e CtEndpoint) {
   562  	var mapEntriesTCP, mapEntriesAny int
   563  	for _, m := range maps(e, true, true) {
   564  		fmt.Fprintf(fw, "#define %s %s\n", m.define, m.Name())
   565  		if m.mapType.isTCP() {
   566  			mapEntriesTCP = mapInfo[m.mapType].maxEntries
   567  		} else {
   568  			mapEntriesAny = mapInfo[m.mapType].maxEntries
   569  		}
   570  	}
   571  	fmt.Fprintf(fw, "#define CT_MAP_SIZE_TCP %d\n", mapEntriesTCP)
   572  	fmt.Fprintf(fw, "#define CT_MAP_SIZE_ANY %d\n", mapEntriesAny)
   573  }
   574  
   575  // Exists returns false if the CT maps for the specified endpoint (or global
   576  // maps if nil) are not pinned to the filesystem, or true if they exist or
   577  // an internal error occurs.
   578  func Exists(e CtEndpoint, ipv4, ipv6 bool) bool {
   579  	result := true
   580  	for _, m := range maps(e, ipv4, ipv6) {
   581  		path, err := m.Path()
   582  		if err != nil {
   583  			// Catch this error early
   584  			return true
   585  		}
   586  		if _, err = os.Stat(path); os.IsNotExist(err) {
   587  			result = false
   588  		}
   589  	}
   590  
   591  	return result
   592  }
   593  
   594  var cachedGCInterval time.Duration
   595  
   596  // GetInterval returns the interval adjusted based on the deletion ratio of the
   597  // last run
   598  func GetInterval(mapType bpf.MapType, maxDeleteRatio float64) (interval time.Duration) {
   599  	if val := option.Config.ConntrackGCInterval; val != time.Duration(0) {
   600  		interval = val
   601  		return
   602  	}
   603  
   604  	if interval = cachedGCInterval; interval == time.Duration(0) {
   605  		interval = defaults.ConntrackGCStartingInterval
   606  	}
   607  
   608  	return calculateInterval(mapType, interval, maxDeleteRatio)
   609  }
   610  
   611  func calculateInterval(mapType bpf.MapType, prevInterval time.Duration, maxDeleteRatio float64) (interval time.Duration) {
   612  	interval = prevInterval
   613  
   614  	if maxDeleteRatio == 0.0 {
   615  		return
   616  	}
   617  
   618  	switch {
   619  	case maxDeleteRatio > 0.25:
   620  		if maxDeleteRatio > 0.9 {
   621  			maxDeleteRatio = 0.9
   622  		}
   623  		// 25%..90% => 1.3x..10x shorter
   624  		interval = time.Duration(float64(interval) * (1.0 - maxDeleteRatio)).Round(time.Second)
   625  
   626  		if interval < defaults.ConntrackGCMinInterval {
   627  			interval = defaults.ConntrackGCMinInterval
   628  		}
   629  
   630  	case maxDeleteRatio < 0.05:
   631  		// When less than 5% of entries were deleted, increase the
   632  		// interval. Use a simple 1.5x multiplier to start growing slowly
   633  		// as a new node may not be seeing workloads yet and thus the
   634  		// scan will return a low deletion ratio at first.
   635  		interval = time.Duration(float64(interval) * 1.5).Round(time.Second)
   636  
   637  		switch mapType {
   638  		case bpf.MapTypeLRUHash:
   639  			if interval > defaults.ConntrackGCMaxLRUInterval {
   640  				interval = defaults.ConntrackGCMaxLRUInterval
   641  			}
   642  		default:
   643  			if interval > defaults.ConntrackGCMaxInterval {
   644  				interval = defaults.ConntrackGCMaxInterval
   645  			}
   646  		}
   647  	}
   648  
   649  	if interval != prevInterval {
   650  		log.WithFields(logrus.Fields{
   651  			"newInterval": interval,
   652  			"deleteRatio": maxDeleteRatio,
   653  		}).Info("Conntrack garbage collector interval recalculated")
   654  	}
   655  
   656  	cachedGCInterval = interval
   657  
   658  	return
   659  }