Skip to main content

Crate netring

Crate netring 

Source
Expand description

§netring

High-performance, zero-copy packet I/O for Linux — async-first, pure Rust, no native C dependencies.

netring captures and injects packets over AF_PACKET (TPACKET_V3 block-based mmap rings) and AF_XDP (kernel-bypass XDP sockets), and pairs them with a declarative Monitor for flow tracking, L7 parsing, and anomaly detection. Flow/session logic lives in the companion crate flowscope (cross-platform, no tokio).

[dependencies]
netring = { version = "0.29", features = ["tokio"] }
// Zero-copy borrowed batches via AsyncFd — nothing is copied per packet.
let mut cap = netring::AsyncCapture::open("eth0")?;
loop {
    let mut guard = cap.readable().await?;
    while let Some(batch) = guard.next_batch() {
        for pkt in &batch {
            let _data: &[u8] = pkt.data();   // borrows from the ring
            let _ts = pkt.timestamp();       // nanosecond kernel timestamp
        }
    }
}

§Why netring

  • Zero-copy, zero-alloc hot path. Borrowed batches; the Monitor run loop does 0 allocations per packet (enforced by a dhat regression bench).
  • Two backends, one API. AF_PACKET everywhere; AF_XDP for kernel-bypass line rate — same shapes, no native C deps (pure libc/aya).
  • Async-first. tokio adapters with a Send + 'static run loop you can tokio::spawn; a runtime-agnostic channel adapter too.
  • Batteries included. Typed BPF builder, flow/session tracking, L7 parsers (HTTP/TLS/DNS/ICMP/QUIC + Tier-2: SSH, FTP, SMTP, NTP, SNMP, Modbus, DNP3, STUN, WireGuard, SMB/Kerberos/LDAP/RDP), fingerprinting (JA3/JA4/JA4H/JA4X, HASSH, p0f), and a fluent Monitor with detectors, middleware, sinks, and exporters.
  • Network security monitoring. Threat-intel IOC matching (flowscope’s threat backbone), FlowAnalyzer flow-risk scoring, pluggable NDR detectors with MITRE ATT&CK tagging, YARA-X payload scanning, Sigma rule evaluation (with live hot-reload of IOC / Sigma / YARA sets), RITA beacon detection, SSH (HASSH) / QUIC / TLS (JA3/JA4, PQ key-share) fingerprinting, encrypted-DNS (DoH/DoT/DoQ) visibility, IP-fragment reassembly (anti-evasion), passive asset inventory, nPrint + CICFlowMeter ML export, and an OCSF Detection-Finding sink.
  • Observability out of the box. Rolling traffic aggregation (top talkers, host-pair matrix, top domains/SNI), RED metrics (rate/errors/duration) per protocol, owner-attributed bandwidth (by tenant/container/subscriber), Community ID on flow records, and rotating + pre-trigger pcap capture.
  • Runs where the traffic is. Capture inside a container’s network namespace (NetNs + CaptureBuilder::netns), per-CPU sharding, and multi-NIC fan-in.

§The Monitor: subscriptions

The Monitor is the high-level “watch an interface and react to typed events” API. The front door is the typed subscription engine — three strongly-typed tiers whose filters split into a kernel part (pushed into BPF, so uninteresting traffic is shed before it reaches userspace) and a userspace remainder:

use netring::monitor::Monitor;
use netring::monitor::subscription::{packet, flow, session};
use netring::protocol::builtin::{Tcp, Tls};

Monitor::builder()
    .interface("eth0")
    .protocol::<Tcp>()
    .protocol::<Tls>()
    // every frame to :443 (the tcp+port part is pushed into the kernel BPF)
    .subscribe(packet().tcp().dst_port(443).to(|view, ctx| Ok(())))
    // once per flow, at its end, for flows over 1 MiB
    .subscribe(flow::<Tcp>().bytes_over(1 << 20).to(|ended, ctx| Ok(())))
    // each parsed TLS handshake whose SNI matches a glob
    .subscribe(session::<Tls>().sni_glob("*.bank.example").to(|msg, ctx| Ok(())))
    // a runtime filter string → the SAME predicate AST as the typed combinators
    .subscribe(packet().expr("udp and dst port 53")?.to(|view, ctx| Ok(())))
    .build()?
    .run_until_signal()
    .await?;
TierConstructorHandler seesFires
packetpacket()&PacketViewevery matching frame (pre-tracking)
flowflow::<P>()&FlowEnded<P>once per matching flow, at its end
sessionsession::<P>()&P::Messageeach parsed L7 message that matches

Runnable: examples/monitor/subscriptions.rs.

§The Monitor: handlers, detectors, sinks

on::<E> / on_ctx::<E> register typed handlers; detect(...) plugs in detector! / pattern_detector! rules; a tower-style layer chain processes the anomaly sink; and sinks ship the results.

use std::time::Duration;
use netring::prelude::*;

Monitor::builder()
    .interface("eth0")
    .protocol::<Tcp>()                                    // FlowStarted/Ended<Tcp>
    .protocol::<Http>()                                   // HttpMessage events
    .on_ctx::<Http>(|msg, ctx| {
        ctx.emit("HttpRequest", Severity::Info).with("path", msg.path).emit();
        Ok(())
    })
    .layer(MinSeverity::warning())                        // outermost: drop Info
    .layer(DedupeAnomalies::within(Duration::from_secs(60)))
    .sink(StdoutJsonSink::default())                      // innermost: NDJSON
    .run_until_signal()
    .await?;
  • Handlers: on (payload), on_ctx (payload + &mut Ctx), on_async (payload → Send future), and on_effect — read Ctx synchronously, do async I/O, return deferred Effects (0.25).
  • Middleware (5): MinSeverity, DedupeAnomalies, RateLimitAnomalies, Sample, Tee — compose freely, outermost-first.
  • Sinks (4 core): StdoutSink, StdoutJsonSink, TracingSink, ChannelSink; plus EveSink/EveTlsSink (Suricata EVE), SyslogSink (RFC 5424), IpfixExporter (RFC 7011), and OTLP/Kafka in netring-exporters.
  • Run modes: run_until_signal(), run_for(Duration), run_until(Instant), run_until_idle(window), replay() (offline pcap).

The Monitor is Send and its run-loop future is Send + 'static — so tokio::spawn(monitor.run_for(d)) works on the default multi-thread runtime.

See docs/WRITING_DETECTORS.md for the detector tutorial and examples/monitor/ for runnable demos (subscriptions, port-scan/beacon/DGA detectors, EVE/OTLP export, resilience, sharding, tracing-JSON).

§Capture & inject

Every type has a ::open(iface) shortcut and a ::builder() for full config.

// Async inject with backpressure (awaits POLLOUT when the ring is full):
let mut tx = netring::AsyncInjector::open("eth0")?;
tx.send(&frame).await?;
tx.flush().await?;

// TX symmetry (0.25): stream-inject, rate-paced, with egress timestamps.
use netring::TxPacer;
let inj = netring::Injector::builder().interface("eth0").tx_timestamps(true).build()?;
let mut tx = netring::AsyncInjector::new(inj)?;
tx.send_stream(frames, Some(TxPacer::packets_per_second(10_000.0))).await?;
let _egress_ts = tx.read_tx_timestamp();
// AF_XDP (kernel bypass) — same shape as AsyncCapture; needs an XDP redirect
// program. `xdp_interface_loaded` (feature `xdp-loader`) attaches one for you.
let monitor = netring::monitor::Monitor::builder()
    .xdp_interface_loaded("eth0")
    .protocol::<Tcp>()
    .build()?;

The sync Capture / Injector / XdpSocket power the async wrappers and are usable directly (flat packets() iterator or block-level batches). See docs/API_OVERVIEW.md and docs/ASYNC_GUIDE.md.

§Flow & session tracking

netring = { version = "0.29", features = ["tokio", "flow"] }
use futures::StreamExt;
use netring::AsyncCapture;
use netring::flow::FlowEvent;
use netring::flow::extract::FiveTuple;

let cap = netring::AsyncCapture::open("eth0")?;
let mut stream = cap.flow_stream(FiveTuple::bidirectional());
while let Some(evt) = stream.next().await {
    match evt? {
        FlowEvent::Started { key, .. } => println!("+ {} <-> {}", key.a, key.b),
        FlowEvent::Ended { key, history, .. } => println!("- {} <-> {}  {history}", key.a, key.b),
        _ => {}
    }
}

Pluggable flow keys (5-tuple, IpPair, MacPair, VLAN/MPLS/VXLAN/GTP-U decap), bidirectional sessions, a TCP state machine with Zeek-style history, idle-timeout sweep + LRU eviction, optional TCP reassembly, and feature-gated L7 parsers (http, tls with JA3/JA4 fingerprinting, dns, icmp). The flow API itself lives in flowscope and works on any source of &[u8] frames (pcap, tun/tap, embedded).

§BPF filters

A typed classic-BPF builder — no tcpdump -dd, no libpcap:

use netring::{BpfFilter, Capture};

let filter = BpfFilter::builder()
    .tcp().dst_port(443)
    .or(|b| b.udp().dst_port(53))
    .build()
    .unwrap();

let _cap = Capture::builder().interface("eth0").bpf_filter(filter).build().unwrap();

Vocabulary: ipv4/ipv6/arp, vlan/vlan_id, tcp/udp/icmp, src_host/dst_host/host, src_net/dst_net/net, src_port/dst_port/port, negate(), or(|b| …). AsyncCapture::set_filter swaps the filter atomically on a running ring; BpfFilter::matches(&[u8]) runs the bytecode in pure Rust for offline testing.

§Performance & scaling

  • Per-CPU sharding: ShardedRunner::new(iface, FanoutMode::Cpu, group, n, build) fans one interface across N sockets in a PACKET_FANOUT group; .pin_cpus(true) binds each shard to its core.
  • AF_XDP tuning: UMEM hugepages (MAP_HUGETLB) + NUMA binding (mbind); busy-poll trio (busy_poll_us/prefer_busy_poll/busy_poll_budget).
  • Numbers & methodology: docs/PERFORMANCE.md (capture vs. dispatch split, the dispatch-throughput bench, tuning levers) and docs/scaling.md (FanoutMode matrix + anti-patterns).

§Companion crate: netring-exporters

OTLP and Kafka anomaly export live in netring-exporters, keeping those heavy dependency trees out of netring’s core. OtlpAnomalySink (feature otlp) and KafkaSink (feature kafka) both implement netring’s AnomalySink, so they drop straight into .sink(...).

§Features

Features are organized as orthogonal axes (full matrix + recipes in docs/FEATURES.md). The common ones:

FeatureDescription
tokioAsync wrappers (AsyncCapture/AsyncInjector/AsyncXdpSocket) + the Monitor
af-xdp / xdp-loaderAF_XDP kernel bypass; xdp-loader bundles the redirect program (via aya)
channelRuntime-agnostic thread + bounded-channel adapter
flowFlow & session tracking (pulls flowscope)
http / dns / tls / icmp / quicL7 parsers; ja4plus adds JA4S (FoxIO License)
arp / ndp / lldp / cdp / assetL2/discovery: ARP + IPv6 NDP watch, LLDP/CDP, passive asset::Inventory
ssh / infra-protocols / ot-protocols / ftp / smtp / stun / wireguardTier-2 protocol markers (SSH/HASSH, NTP/SNMP/TFTP/RADIUS, Modbus/DNP3, …)
ad-protocols / asset-protocolsActive-Directory (SMB/Kerberos/LDAP/RDP) + DHCP/SSDP/NBNS device facts
ioc / sigma / yara / p0fThreat-intel IOC, Sigma rules, YARA-X payload scan, p0f OS fingerprint
nprint / ml-featuresPer-flow ML export (nPrint header-bit matrix, CICFlowMeter features)
pcapPCAP/PCAPNG read + write
eve-sink / syslog / ipfix / metrics / ocsf-sinkSuricata EVE / RFC 5424 / RFC 7011 / Prometheus / OCSF Detection Finding
monitor / monitor-lite / monitor-quickstartMonitor umbrellas (full / lean / app-tier)

§Public API

ConceptSyncAsync
AF_PACKET RXCaptureAsyncCapture<Capture>
AF_PACKET TXInjectorAsyncInjector
AF_XDP (RX+TX)XdpSocketAsyncXdpSocket
Bridge two interfacesBridgeBridge::run_async
Multi-source fan-inAsyncMultiCapture
Declarative pipelineMonitor / ShardedRunner

§Requirements

  • Linux kernel 3.2+ (TPACKET_V3), 5.4+ (AF_XDP).
  • Rust 1.95+ (edition 2024).
  • Capabilities: CAP_NET_RAW (open sockets), CAP_NET_ADMIN (promiscuous, XDP attach), CAP_IPC_LOCK (MAP_LOCKED, or a sufficient RLIMIT_MEMLOCK).

just setcap grants the capabilities once so tests/examples run without sudo.

§Examples

Organized by topic under examples/basic/, async_basics/, filter/, scaling/, xdp/, flow/, l7/, pcap/, monitor/. Each is listed with its required --features in examples/README.md. Start with monitor_subscriptions for the typed subscription API.

§Documentation

§License

Licensed under either Apache-2.0 or MIT at your option. (The optional ja4plus feature is FoxIO License 1.1 — see docs/FINGERPRINTS.md.)

Re-exports§

pub use afpacket::rx::Capture;
pub use afpacket::rx::CaptureBuilder;
pub use afpacket::rx::Packets;
pub use afpacket::tx::Injector;
pub use afpacket::tx::InjectorBuilder;
pub use afpacket::tx::TxSlot;
pub use bridge::Bridge;
pub use bridge::BridgeAction;
pub use bridge::BridgeBuilder;
pub use bridge::BridgeDirection;
pub use bridge::BridgeHandles;
pub use bridge::BridgeStats;
pub use config::BpfBuildError;
pub use config::BpfFilter;
pub use config::BpfFilterBuilder;
pub use config::BpfInsn;
pub use config::BusyPollConfig;
pub use config::FanoutFlags;
pub use config::FanoutMode;
pub use config::IpNet;
pub use config::ParseIpNetError;
pub use config::RingProfile;
pub use config::TimestampSource;
pub use dedup::Dedup;
pub use error::Error;
pub use interface::InterfaceInfo;
pub use interface::interface_info;
pub use packet::BatchIter;
pub use packet::OwnedPacket;
pub use packet::Packet;
pub use packet::PacketBatch;
pub use packet::PacketDirection;
pub use packet::PacketStatus;
pub use packet::TimestampClock;
pub use stats::CaptureStats;
pub use stats::DropBreakdown;
pub use traits::PacketSetFilter;
pub use traits::PacketSink;
pub use traits::PacketSource;
pub use afxdp::XdpBatch;
pub use afxdp::XdpBatchIter;
pub use afxdp::XdpPacket;
pub use afxdp::XdpMode;
pub use afxdp::XdpSocket;
pub use afxdp::XdpSocketBuilder;
pub use afxdp::XdpStats;
pub use async_adapters::channel::ChannelCapture;
pub use async_adapters::dedup_stream::DedupStream;
pub use async_adapters::tokio_adapter::AsyncCapture;
pub use async_adapters::tokio_adapter::PacketStream;
pub use async_adapters::tokio_adapter::ReadableGuard;
pub use async_adapters::tokio_injector::AsyncInjector;
pub use async_adapters::tokio_injector::TxPacer;
pub use async_adapters::tokio_xdp::AsyncXdpSocket;
pub use async_adapters::tokio_xdp::XdpReadableGuard;
pub use async_adapters::tokio_xdp::XdpStream;
pub use async_adapters::tokio_xdp_capture::AsyncXdpCapture;
pub use traits::AsyncPacketSource;
pub use async_adapters::conversation::Conversation;
pub use async_adapters::conversation::ConversationChunk;
pub use async_adapters::conversation::ConversationStream;
pub use async_adapters::flow_broadcast::BroadcastRecvError;
pub use async_adapters::flow_broadcast::FlowBroadcast;
pub use async_adapters::flow_broadcast::FlowSubscriber;
pub use async_adapters::flow_stream::AsyncReassemblerSlot;
pub use async_adapters::flow_stream::FlowStream;
pub use async_adapters::flow_stream::NoReassembler;
pub use async_adapters::multi_capture::AsyncMultiCapture;
pub use async_adapters::multi_capture::AsyncXdpMultiCapture;
pub use async_adapters::multi_config::MultiStreamConfig;
pub use async_adapters::multi_streams::MergedFlowStream;
pub use async_adapters::multi_streams::MultiDatagramStream;
pub use async_adapters::multi_streams::MultiFlowStream;
pub use async_adapters::multi_streams::MultiSessionStream;
pub use async_adapters::multi_streams::TaggedEvent;
pub use async_adapters::multi_streams::XdpMultiDatagramStream;
pub use async_adapters::multi_streams::XdpMultiFlowStream;
pub use async_adapters::multi_streams::XdpMultiSessionStream;
pub use async_adapters::stream_capture::StreamCapture;
pub use async_adapters::stream_capture::StreamSetFilter;
pub use pcap_flow::PcapDatagramStream;
pub use pcap_flow::PcapFlowStream;
pub use pcap_flow::PcapSessionStream;
pub use pcap_source::AsyncPcapConfig;
pub use pcap_source::AsyncPcapSource;
pub use pcap_source::PcapFormat;
pub use pcap_tap::PcapTap;
pub use pcap_tap::TapErrorPolicy;
pub use protocol::Dispatch;
pub use protocol::FlowKey;
pub use protocol::FlowProtocol;
pub use protocol::MessageProtocol;
pub use protocol::Protocol;
pub use protocol::ProtocolInitError;
pub use protocol::SignatureMatch;
pub use anomaly::Anomaly;
pub use anomaly::AnomalyContext;
pub use anomaly::Severity;

Modules§

afpacket
AF_PACKET backend implementation.
afxdp
AF_XDP backend for kernel-bypass packet I/O.
anomaly
Anomaly value types + emission sinks.
async_adapters
Async and channel adapters for packet capture.
bridge
Bidirectional packet bridge between two interfaces (IPS mode).
config
Configuration types: fanout, BPF filters, timestamps, ring profiles.
correlate
0.21 G: netring’s TimeBucketedCounter is now re-exported from flowscope (the new_unbounded ctor lands the 2-arg shape). Three extra primitives (BurstDetector, Ewma, TopK, TimeBucketedSet, SequencePattern, KeylessSequencePattern, FlowStateMap) join the module for free. KeyIndexed stays netring-side until flowscope adds a drain_expired-style iterator method (see module docstring). Correlation primitives for multi-protocol anomaly detection.
ctx
Per-event context passed to handlers.
dedup
Loopback / content-hash packet deduplication.
detector_macro
Declarative detector! macro — terse stateless detector definitions for use with crate::monitor::Monitor.
error
Error types for netring.
export
0.24 Phase D: flow export — FlowRecord / FlowExporter / MonitorBuilder::export_flows. The fourth output shape beside anomalies, reports, and broadcast streams: one record per completed flow (NetFlow/IPFIX-style). 0.24 Phase D — flow export.
flow
Source-agnostic flow & session tracking types from flowscope.
interface
Interface capability detection.
layer
Middleware over the AnomalySink chain.
metrics
Metrics integration via the metrics façade (feature: metrics).
monitor
Top-level Monitor builder + run loop.
netns
Linux network-namespace capture (issue #126).
packet
Packet types: zero-copy views, batch iteration, timestamps, and owned packets.
pcap
PCAP/PCAPNG export helpers (feature: pcap).
pcap_flow
PcapFlowStream — flow tracking over offline pcap files.
pcap_rotate
Rotating + triggered pcap writers (issue #125).
pcap_source
Async pcap source for offline replay — feeds the same downstream tooling (flow trackers, decoders) as a live AF_PACKET capture.
pcap_tap
Pcap tap — record each captured packet to a CaptureWriter before the flow tracker processes it.
prelude
Glob-importable re-exports of the canonical netring API.
protocol
Protocol plugin layer + typed event surface.
report
0.22 §3: periodic structured reports (Report / ReportSink / ReportSnapshot) — the third output stream beside anomalies and broadcast event streams. 0.22 §3 — periodic structured reports.
stats
Capture statistics.
traits
Core traits for packet capture and injection.
well_known
0.22 §2.2: well-known port → app/protocol label table, re-exported from flowscope for a stable netring::well_known::LabelTable path. Pass a custom table to MonitorBuilder::label_table.
xdp
AF_XDP capture surface: queue discovery + multi-queue capture (issue #6) and, with the xdp-loader feature, the built-in XDP program loader.

Macros§

detector
Build a stateless detector for the 0.20 Monitor builder.
pattern_detector
0.21 I.1: declarative pattern-detector glue for flowscope’s stateful detectors (PortScanDetector, BeaconDetector, DgaScorer, …) plus any third-party detector whose score implements flowscope::DetectorScore.

Structs§

PacketView
What a crate::FlowExtractor is given.
Timestamp
Nanosecond-precision kernel timestamp.