feature/conn25: unify FlowTable storage to prepare for expiry
Previously we had two maps keyed on a direction-specific tuple, with distinct values containing the data (action) for that direction. Values pointed at each other across maps to ensure they were removed at the same time in the case of tuple overwrite, but LRU eviction was per-map. So if LRU was turned on, it was possible for one direction's data (action) to be evicted and leave the other direction dangling. NewFlow replaces the two direction-specific flow constructors, and lookups return the direction-specific PacketAction directly. Now the values in each map point to the same element, with data for both directions in the element. A linked list also points to the elements to implement LRU. The previous flowtrack.Cache is removed. The single LRU structure will allow us to implement idle time expiration by walking the list backward starting with the least recently used flow, and stopping after a fixed number of flows, or at the first non-expired flow. We add commented-out unused placeholder fields for tracking the "last seen" timestamp, and an on-removal hook, to document the intent for the follow-up expiry work. Updates tailscale/corp#38630 Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit is contained in:
committed by
mzbenami
parent
26952d53fa
commit
5877809097
+20
-14
@@ -108,17 +108,17 @@ func (dh *datapathHandler) HandlePacketFromWireGuard(p *packet.Parsed) filter.Re
|
||||
|
||||
// Check if this is an existing (return) flow on a client.
|
||||
// If found, perform the action for the existing client flow and return.
|
||||
existing, ok := dh.clientFlowTable.LookupFromWireGuard(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
action, ok := dh.clientFlowTable.LookupFromWireGuard(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
if ok {
|
||||
existing.Action(p)
|
||||
action(p)
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
// Check if this is an existing connector outbound flow.
|
||||
// If found, perform the action for the existing connector outbound flow and return.
|
||||
existing, ok = dh.connectorFlowTable.LookupFromWireGuard(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
action, ok = dh.connectorFlowTable.LookupFromWireGuard(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
if ok {
|
||||
existing.Action(p)
|
||||
action(p)
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
@@ -145,15 +145,18 @@ func (dh *datapathHandler) HandlePacketFromWireGuard(p *packet.Parsed) filter.Re
|
||||
// This is a new outbound flow on a connector. Install a DNAT TransitIP-to-RealIP action
|
||||
// for the outgoing direction, and an SNAT RealIP-to-TransitIP action for the
|
||||
// return direction.
|
||||
outgoing := FlowData{
|
||||
outgoing := TupleAndAction{
|
||||
Tuple: flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst),
|
||||
Action: dh.dnatAction(realIP),
|
||||
}
|
||||
incoming := FlowData{
|
||||
incoming := TupleAndAction{
|
||||
Tuple: flowtrack.MakeTuple(p.IPProto, netip.AddrPortFrom(realIP, p.Dst.Port()), p.Src),
|
||||
Action: dh.snatAction(transitIP),
|
||||
}
|
||||
if err := dh.connectorFlowTable.NewFlowFromWireGuard(outgoing, incoming); err != nil {
|
||||
if err := dh.connectorFlowTable.NewFlow(FlowData{
|
||||
FromTun: incoming,
|
||||
FromWG: outgoing,
|
||||
}); err != nil {
|
||||
dh.debugLogf("error installing flow, passing packet unmodified: %v", err)
|
||||
return filter.Accept
|
||||
}
|
||||
@@ -174,17 +177,17 @@ func (dh *datapathHandler) HandlePacketFromTunDevice(p *packet.Parsed) filter.Re
|
||||
|
||||
// Check if this is an existing client outbound flow.
|
||||
// If found, perform the action for the existing client flow and return.
|
||||
existing, ok := dh.clientFlowTable.LookupFromTunDevice(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
action, ok := dh.clientFlowTable.LookupFromTunDevice(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
if ok {
|
||||
existing.Action(p)
|
||||
action(p)
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
// Check if this is an existing connector return flow.
|
||||
// If found, perform the action for the existing connector return flow and return.
|
||||
existing, ok = dh.connectorFlowTable.LookupFromTunDevice(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
action, ok = dh.connectorFlowTable.LookupFromTunDevice(flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst))
|
||||
if ok {
|
||||
existing.Action(p)
|
||||
action(p)
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
@@ -211,15 +214,18 @@ func (dh *datapathHandler) HandlePacketFromTunDevice(p *packet.Parsed) filter.Re
|
||||
// This is a new outbound client flow. Install a DNAT MagicIP-to-TransitIP action
|
||||
// for the outgoing direction, and an SNAT TransitIP-to-MagicIP action for the
|
||||
// return direction.
|
||||
outgoing := FlowData{
|
||||
outgoing := TupleAndAction{
|
||||
Tuple: flowtrack.MakeTuple(p.IPProto, p.Src, p.Dst),
|
||||
Action: dh.dnatAction(transitIP),
|
||||
}
|
||||
incoming := FlowData{
|
||||
incoming := TupleAndAction{
|
||||
Tuple: flowtrack.MakeTuple(p.IPProto, netip.AddrPortFrom(transitIP, p.Dst.Port()), p.Src),
|
||||
Action: dh.snatAction(magicIP),
|
||||
}
|
||||
if err := dh.clientFlowTable.NewFlowFromTunDevice(outgoing, incoming); err != nil {
|
||||
if err := dh.clientFlowTable.NewFlow(FlowData{
|
||||
FromTun: outgoing,
|
||||
FromWG: incoming,
|
||||
}); err != nil {
|
||||
dh.debugLogf("error installing flow from tun device, passing packet unmodified: %v", err)
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user