Documentation
¶
Overview ¶
Package flow maintains the live table of bidirectional conversations assembled from the packet stream.
The table is the memory of the sensor: detectors ask it "what has this host been doing" rather than trying to reason about individual packets. Because it sits on the hot path — every single packet touches it — its cost model matters more than anything else in the program.
Index ¶
Constants ¶
const ( DefaultIdleTimeout = 2 * time.Minute DefaultMaxFlows = 500_000 )
Default table tuning. These are deliberately conservative: a mid-range host running tracehound on a busy /24 sits comfortably inside them.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Options ¶
type Options struct {
// IdleTimeout is how long a flow may go unobserved before it is reaped.
IdleTimeout time.Duration
// MaxFlows caps table size. When exceeded, the least-recently-touched
// flows are evicted early. This bounds memory against a scan or a flood
// that would otherwise create millions of one-packet flows.
MaxFlows int
}
Options configures a Table.
type Stats ¶
type Stats struct {
Active int `json:"active"`
Created uint64 `json:"created"`
Expired uint64 `json:"expired"`
Evicted uint64 `json:"evicted"`
}
Stats reports table counters for the /healthz endpoint and benchmarks.
type Table ¶
type Table struct {
// contains filtered or unexported fields
}
Table is a concurrent map of active flows ordered by recency.
Expiry is the interesting part. The obvious implementation scans every entry on a timer, which is O(n) per sweep and degrades exactly when the table is large — i.e. during the scan or flood you most want to detect. Instead the table threads every entry onto a doubly-linked list ordered by touch time. Reaping then pops from the head while the head is too old, costing O(expired) rather than O(total), and capacity eviction is the same pop from the head.
The ordering assumption is that packet timestamps are non-decreasing. That holds for live capture and for well-formed PCAPs; badly reordered input can leave a flow reaped slightly early, which is harmless (it is re-created on the next packet) and is why Reap compares against LastSeen rather than trusting list position alone.
func (*Table) Drain ¶
Drain removes and returns every remaining flow. Used at end-of-capture so that flows still open when a PCAP runs out are not silently dropped.
func (*Table) Observe ¶
Observe folds a packet into its flow, creating the flow if needed.
The returned pointer is owned by the table and is only safe to read while the caller is still on the packet-processing goroutine; callers that need to keep a flow must copy it. isNew reports whether this packet created the flow, which detectors use as the "new conversation" signal.
func (*Table) Reap ¶
Reap removes every flow untouched since now-IdleTimeout and returns them.
Returned flows are copies, so the caller may hand them to detectors or storage on another goroutine without racing the table.