Skip to main content

Crate netring

Crate netring 

Source
Expand description

§netring

High-performance zero-copy packet I/O for Linux, async-first.

netring provides packet capture and injection via AF_PACKET (TPACKET_V3 block-based mmap ring buffers) and AF_XDP (kernel-bypass via XDP sockets). The recommended API is async/tokio; sync types are first-class but mostly used as the underlying source for the async wrappers.

[dependencies]
netring = { version = "0.8", features = ["tokio"] }
// Capture: zero-copy borrowed batches via AsyncFd.
let mut cap = netring::AsyncCapture::open("eth0")?;
loop {
    let mut guard = cap.readable().await?;
    if let Some(batch) = guard.next_batch() {
        for pkt in &batch {
            handle(pkt.data()).await;
        }
    }
}
// Stream-style consumption with futures::StreamExt
// (add `futures = "0.3"` to your Cargo.toml):
use futures::StreamExt;

let mut stream = netring::AsyncCapture::open("eth0")?.into_stream();
while let Some(batch) = stream.next().await {
    for pkt in batch? {
        let _ = pkt.data;
    }
}
// Inject with backpressure (awaits POLLOUT when ring is full):
let mut tx = netring::AsyncInjector::open("eth0")?;
tx.send(&[0xff; 64]).await?;
tx.flush().await?;
// AF_XDP (kernel bypass, 10M+ pps) — same shape as AsyncCapture:
let mut xdp = netring::AsyncXdpSocket::open("eth0")?;
let batch = xdp.try_recv_batch().await?;
for pkt in &batch {
    let _ = pkt.data();
}

See docs/ASYNC_GUIDE.md for the full async story — patterns, trade-offs, when to use which entry point, and Send/!Send considerations.

§Flow & session tracking

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

let cap = 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!("- {} <-> {}  hist={history}", key.a, key.b),
        _ => {}
    }
}

Pluggable flow keys (5-tuple, IpPair, MacPair, VLAN/MPLS/VXLAN/GTP-U decap combinators, custom extractors), bidirectional sessions, TCP state machine with Zeek-style history string, idle-timeout sweep, LRU eviction, optional TCP reassembly hook (sync Reassembler or async AsyncReassembler with channel_factory for backpressure).

The flow types live in a separate cross-platform crate flowscope (no Linux, no tokio, no async runtime — usable with pcap, tun-tap, embedded). netring is the Linux capture integration; the underlying flow API works on any source of &[u8] frames.

flowscope also ships feature-gated L7 modules: http (HTTP/1.x), tls (TLS handshake observation, optional JA3), dns (DNS-over-UDP parser + correlator), and pcap (offline replay).

§BPF filtering

netring ships a typed classic-BPF builder — no shelling out to tcpdump -dd, no native-library deps:

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: eth_type / ipv4 / ipv6 / arp, vlan / vlan_id, ip_proto / tcp / udp / icmp, src_host / dst_host / host, src_net / dst_net / net, src_port / dst_port / port, plus negate() and or(|b| ...). See examples/bpf_filter.rs for a runnable demo. The escape hatch BpfFilter::new(insns) still accepts raw bytecode from tcpdump -dd or any other source.

BpfFilter::matches(&[u8]) -> bool runs the bytecode in pure Rust for offline validation against pcap data.

§Sync API

The sync types power the async wrappers and are also usable directly:

// Flat iterator — simplest path.
let mut cap = netring::Capture::open("eth0").unwrap();
for pkt in cap.packets().take(100) {
    println!("[{}] {} bytes", pkt.timestamp(), pkt.len());
}
// Batch processing with sequence-gap detection.
use netring::Capture;
use std::time::Duration;

let mut cap = Capture::builder()
    .interface("eth0")
    .block_size(1 << 22)
    .build()
    .unwrap();

while let Some(batch) = cap.next_batch_blocking(Duration::from_millis(100)).unwrap() {
    for pkt in &batch {
        let _ = pkt.data();
    }
}

§Features

FeatureDefaultDescription
tokiooffAsync wrappers (AsyncCapture, AsyncInjector, AsyncXdpSocket, PacketStream)
af-xdpoffAF_XDP kernel-bypass packet I/O (pure Rust, no native deps)
xdp-loaderoffBuilt-in redirect-all XDP program loader for AF_XDP via aya. Implies af-xdp. See async_xdp_self_loaded example.
channeloffThread + bounded channel adapter (runtime-agnostic)
parseoffPacket header parsing via etherparse
pcapoffStream packets to PCAP files
metricsoffmetrics crate counters (netring_capture_*_total)
flowoffPluggable flow & session tracking (pulls flowscope, see Flow & session tracking above)

§Public API

ConceptSync typeAsync wrapper
AF_PACKET RXCaptureAsyncCapture<Capture>
AF_PACKET TXInjectorAsyncInjector
AF_XDP (RX + TX)XdpSocketAsyncXdpSocket
Bridge two interfacesBridgeBridge::run_async
Channel adapterChannelCapture (sync threads)

Every type has a ::open(iface) shortcut for the simple case and a ::builder() for full configuration.

§Default Configuration

ParameterDefaultDescription
block_size4 MiBRing buffer block size
block_count64Number of blocks (256 MiB total)
frame_size2048Minimum frame size
block_timeout_ms60Block retirement timeout
fill_rxhashtrueKernel fills RX flow hash

§Performance Tuning

Profileblock_sizeblock_counttimeout_msNotes
High throughput4 MiB128–25660+ FanoutMode::Cpu + thread pinning
Low latency256 KiB641–10+ busy_poll_us(50).prefer_busy_poll(true).busy_poll_budget(64) (kernel ≥ 5.11)
Memory-constrained1 MiB1610016 MiB total ring
Jumbo frames4 MiB6460frame_size(65536)

See docs/TUNING_GUIDE.md for detailed tuning advice.

§Fanout Modes

Distribute packets across multiple sockets for multi-threaded capture:

ModeStrategy
HashFlow hash (same flow → same socket)
CpuRoute to CPU that received the NIC interrupt
LoadBalanceRound-robin
RolloverFill one socket, overflow to next
RandomRandom distribution
QueueMappingNIC hardware queue mapping
use netring::{Capture, FanoutMode, FanoutFlags};

let cap = Capture::builder()
    .interface("eth0")
    .fanout(FanoutMode::Cpu, 42)
    .fanout_flags(FanoutFlags::ROLLOVER | FanoutFlags::DEFRAG)
    .build()
    .unwrap();

§Statistics

let stats = cap.stats().unwrap();
println!("received: {}, dropped: {}, frozen: {}",
    stats.packets, stats.drops, stats.freeze_count);

Reading stats resets the kernel counters — call periodically for rate calculation.

§System Requirements

  • Linux kernel 3.2+ (for TPACKET_V3), 5.4+ (for AF_XDP)
  • Rust 1.85+ (edition 2024)

§Capabilities

CapabilityRequired For
CAP_NET_RAWCreating AF_PACKET / AF_XDP sockets
CAP_IPC_LOCKMAP_LOCKED (or sufficient RLIMIT_MEMLOCK)
CAP_NET_ADMINPromiscuous mode
# Recommended: use justfile (sudo only once for setcap)
just setcap          # grants CAP_NET_RAW on all binaries
just test            # runs without sudo
just capture eth0    # runs without sudo

# Manual alternative
sudo setcap cap_net_raw+ep target/release/examples/capture

§Examples

just setcap                  # grant capabilities once (needs sudo)
just capture eth0            # basic packet capture
just batch eth0              # low-level batch API with sequence gap detection
just fanout eth0 4           # multi-threaded fanout capture
just inject lo               # packet injection
just stats eth0              # live statistics monitor (pkt/s, drops)
just low-latency eth0        # low-latency tuning demo
just dpi eth0                # deep packet inspection (HTTP/TLS/DNS/SSH detection)
just channel eth0            # channel adapter (runtime-agnostic)
just async eth0              # async capture with tokio (readable() pattern)
just async-stream eth0       # async capture as a futures::Stream
just async-inject lo 1000    # async TX with backpressure (AsyncInjector)
just async-signal eth0       # async capture with Ctrl-C graceful shutdown
just async-pipeline eth0 4   # async capture → tokio::mpsc → 4 worker tasks
just async-bridge eth0 eth1  # async transparent bridge (Bridge::run_async)
just ebpf                    # eBPF/aya integration demo (AsFd verification)
cargo run --example xdp_send --features af-xdp -- lo  # AF_XDP TX-only (uses XdpMode::Tx)

§Documentation

§License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

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::BpfFilter;
pub use config::BpfFilterBuilder;
pub use config::BpfInsn;
pub use config::BuildError;
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 stats::CaptureStats;
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_xdp::AsyncXdpSocket;
pub use async_adapters::tokio_xdp::XdpReadableGuard;
pub use async_adapters::tokio_xdp::XdpStream;
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;

Modules§

afpacket
AF_PACKET backend implementation.
afxdp
AF_XDP backend for kernel-bypass packet I/O.
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.
dedup
Loopback / content-hash packet deduplication.
error
Error types for netring.
flow
Source-agnostic flow & session tracking types from flowscope.
interface
Interface capability detection.
metrics
Metrics integration via the metrics façade (feature: metrics).
packet
Packet types: zero-copy views, batch iteration, timestamps, and owned packets.
pcap
PCAP/PCAPNG export helpers (feature: pcap).
stats
Capture statistics.
traits
Core traits for packet capture and injection.
xdp
XDP program loader (built-in redirect-all program). Requires the xdp-loader Cargo feature.

Structs§

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