feature/conn25: expire idle flows from FlowTable

Track lastSeen on each cached flow and add a sweeper goroutine
that periodically removes flows idle past the idle timeout.

Introduce tunables for idle timeout, maximum flows removed per sweep (to
limit mutex hold time), and the sweeper interval.

Also cap the previously-unlimited tables: 10k client flows, 100k
connector flows.

Updates tailscale/corp#38630

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit is contained in:
Michael Ben-Ami
2026-06-08 11:03:25 -04:00
committed by mzbenami
parent 65a117184b
commit 618b606b46
4 changed files with 286 additions and 17 deletions
+17 -6
View File
@@ -4,6 +4,7 @@
package conn25
import (
"context"
"errors"
"net/netip"
@@ -83,19 +84,29 @@ type datapathHandler struct {
debugLogging bool
}
const (
maxClientFlows = 10_000
maxConnectorFlows = 100_000
)
func newDatapathHandler(ipMapper IPMapper, logf logger.Logf) *datapathHandler {
return &datapathHandler{
ipMapper: ipMapper,
// TODO(mzb): Figure out sensible default max size for flow tables.
// Don't do any LRU eviction until we figure out deletion and expiration.
clientFlowTable: NewFlowTable(0),
connectorFlowTable: NewFlowTable(0),
ipMapper: ipMapper,
clientFlowTable: NewFlowTable(maxClientFlows),
connectorFlowTable: NewFlowTable(maxConnectorFlows),
logf: logf,
debugLogging: envknob.Bool("TS_CONN25_DATAPATH_DEBUG"),
}
}
// StartFlowExpirySweepers starts the sweepers that remove expired flows
// for both the client and connector flow tables. Each sweeper runs in
// its own new goroutine.
func (dh *datapathHandler) StartFlowExpirySweepers(ctx context.Context) {
go dh.clientFlowTable.StartExpiredSweeper(ctx)
go dh.connectorFlowTable.StartExpiredSweeper(ctx)
}
// HandlePacketFromWireGuard inspects packets coming from WireGuard, and performs
// appropriate DNAT or SNAT actions for Connectors 2025. Returning [filter.Accept] signals
// that the packet should pass through subsequent stages of the datapath pipeline.