flowscope 0.8.0

Passive flow & session tracking for packet capture (runtime-free, cross-platform)
Documentation

flowscope

crates.io docs.rs CI

Passive flow & session tracking for packet capture.

flowscope is a runtime-free, cross-platform Rust library for observing what's happening on the wire. It pairs with any source of &[u8] frames: netring (Linux AF_PACKET / AF_XDP), pcap files, tun/tap, eBPF, embedded — anywhere bytes show up.

No tokio, no futures, no async runtime in the core. (For tokio integration, see netring's AsyncCapture::flow_stream etc., which consume this crate's traits.)

What's here

PacketView   →   FlowExtractor   →   FlowTracker   →   Reassembler   →   SessionParser / DatagramParser
   ↑                                                                              ↓
   anything                                                              typed L7 messages

Core (always on):

  • FlowExtractor trait + built-in extractors (5-tuple, IP-pair, MAC-pair) + decap combinators (VLAN, MPLS, VXLAN, GTP-U, GRE) + AutoDetectEncap combinator + FlowLabel IPv6 augmentation.
  • FlowTracker — bidirectional flow accounting, TCP state machine, idle timeouts, LRU eviction.
  • Reassembler — sync per-(flow, side) hook for TCP byte streams.
  • SessionParser / DatagramParser — typed L7 message parsing per flow.

Protocol parsers (each behind its own feature):

Feature What you get
http HTTP/1.x request/response parsing — both HttpFactory (callback) and HttpParser (SessionParser)
tls TLS handshake observer (ClientHello/ServerHello/Alert) — passive only, no decryption
ja3 JA3 client fingerprinting (sub-feature of tls)
dns DNS message parser, per-flow query/response correlator. UDP via DnsUdpParser (DatagramParser); TCP via DnsTcpParser (SessionParser, RFC 1035 §4.2.2 length-framed)
pcap pcap file source for offline replay
l7 Umbrella: enables http + tls + dns together
full All of the above (incl. ja3, pcap, observability)

Quick start

[dependencies]
flowscope = { version = "0.5", features = ["full"] }
use flowscope::extract::FiveTuple;
use flowscope::pcap::PcapFlowSource;
use flowscope::FlowEvent;

# fn main() -> Result<(), Box<dyn std::error::Error>> {
for evt in PcapFlowSource::open("trace.pcap")?.with_extractor(FiveTuple::bidirectional()) {
    if let FlowEvent::Started { key, .. } = evt? {
        println!("flow started: {key:?}");
    }
}
# Ok(()) }

Typed L7 messages are one iterator away — sessions() runs an extractor plus a per-flow SessionParser over the pcap, with the end-of-input flush folded in:

use flowscope::extract::FiveTuple;
use flowscope::pcap::PcapFlowSource;
use flowscope::http::HttpParser;
use flowscope::SessionEvent;

# fn main() -> Result<(), Box<dyn std::error::Error>> {
for evt in PcapFlowSource::open("trace.pcap")?
    .sessions(FiveTuple::bidirectional(), HttpParser::default())
{
    if let SessionEvent::Application { message, .. } = evt? {
        println!("{message:?}");
    }
}
# Ok(()) }

datagrams() is the UDP mirror. For TLS / DNS and the callback-style *Factory<H> APIs, see examples/ and the per-module documentation on docs.rs.

Custom protocols

For an end-to-end example of writing a SessionParser for your own wire format — including the synchronous offline pcap path via FlowSessionDriver — see examples/length_prefixed_pcap.rs. The example demonstrates a length-prefixed binary protocol (PSMSG-shaped) with two variable-length markers and is paired with a deterministic pcap fixture under tests/fixtures/length_prefixed/.

Tokio integration

flowscope itself is runtime-free. To consume a live capture into a stream of FlowEvent / SessionEvent via tokio, use netring:

use netring::AsyncCapture;
use flowscope::extract::FiveTuple;
use flowscope::http::HttpParser;
use futures::StreamExt;

# async fn ex() -> Result<(), Box<dyn std::error::Error>> {
let mut s = AsyncCapture::open("eth0")?
    .flow_stream(FiveTuple::bidirectional())
    .session_stream(HttpParser::default());
while let Some(evt) = s.next().await { /* ... */ }
# Ok(()) }

Status

0.5.0 — TCP rich diagnostics + periodic ticks + parser identity, driven by the simple-nms upstream wishlist. Core flow APIs (FlowExtractor, FlowTracker, Reassembler, SessionParser, DatagramParser) are settled; public structs and enums are #[non_exhaustive] so future variants and fields are additive.

0.5.0 ships:

  • TCP retransmit classification on BufferedReassembler (Reassembler::retransmits(), on_duplicate(seq, payload, ts) hook, AnomalyKind::RetransmittedSegment, FlowStats::retransmits_*, flowscope_retransmits_total).
  • Reassembler::segment(seq, payload, ts) — segment timestamps for downstream RTT estimation (one-line trait break vs 0.4.0).
  • TcpInfo::window raw receive window + #[non_exhaustive].
  • Opt-in periodic FlowEvent::Tick / SessionEvent::FlowTick via FlowTrackerConfig::flow_tick_interval. Driven by packet timestamps; pairs with flowscope_flow_ticks_total.
  • SessionParser::parser_kind() / DatagramParser::parser_kind() threaded into SessionEvent::Application::parser_kind. Shipped parsers report http/1, tls, dns-udp, dns-tcp.
  • Reassembly-diagnostic metrics now fire correctly on natural flow ends (latent bug — the tracker called record_flow_ended before the driver patched reassembler-derived fields).
  • New SESSION_GUIDE walkthrough for the consumer-loop pattern that updates per-flow rich state without piping &mut S through SessionParser::feed_*.

See CHANGELOG.md for the full migration recipes, docs/SESSION_GUIDE.md for the decision-flow on which API to pick, and docs/OBSERVABILITY.md for the metric vocabulary.

0.4.0 features remain: drivers without the S type parameter, SessionParser/DatagramParser timestamp-aware data methods + on_tick, three-driver finish(), PcapFlowSource::sessions() / datagrams() one-step pipelines, DNS-UDP unified on DnsUdpParser. 0.3.0 / 0.2.0 features remain: FlowDatagramDriver, sync Dedup, per-key idle-timeout predicates, monotonic timestamps, parser-poison synthesis, anomaly forwarding, criterion bench harness, buffer caps with SlidingWindow / DropFlow policies.

License

MIT OR Apache-2.0, your choice.