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 }