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.
§Quick start (async, recommended)
[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
| Feature | Default | Description |
|---|---|---|
tokio | off | Async wrappers (AsyncCapture, AsyncInjector, AsyncXdpSocket, PacketStream) |
af-xdp | off | AF_XDP kernel-bypass packet I/O (pure Rust, no native deps) |
xdp-loader | off | Built-in redirect-all XDP program loader for AF_XDP via aya. Implies af-xdp. See async_xdp_self_loaded example. |
channel | off | Thread + bounded channel adapter (runtime-agnostic) |
parse | off | Packet header parsing via etherparse |
pcap | off | Stream packets to PCAP files |
metrics | off | metrics crate counters (netring_capture_*_total) |
flow | off | Pluggable flow & session tracking (pulls flowscope, see Flow & session tracking above) |
§Public API
| Concept | Sync type | Async wrapper |
|---|---|---|
| AF_PACKET RX | Capture | AsyncCapture<Capture> |
| AF_PACKET TX | Injector | AsyncInjector |
| AF_XDP (RX + TX) | XdpSocket | AsyncXdpSocket |
| Bridge two interfaces | Bridge | Bridge::run_async |
| Channel adapter | — | ChannelCapture (sync threads) |
Every type has a ::open(iface) shortcut for the simple case and a
::builder() for full configuration.
§Default Configuration
| Parameter | Default | Description |
|---|---|---|
block_size | 4 MiB | Ring buffer block size |
block_count | 64 | Number of blocks (256 MiB total) |
frame_size | 2048 | Minimum frame size |
block_timeout_ms | 60 | Block retirement timeout |
fill_rxhash | true | Kernel fills RX flow hash |
§Performance Tuning
| Profile | block_size | block_count | timeout_ms | Notes |
|---|---|---|---|---|
| High throughput | 4 MiB | 128–256 | 60 | + FanoutMode::Cpu + thread pinning |
| Low latency | 256 KiB | 64 | 1–10 | + busy_poll_us(50).prefer_busy_poll(true).busy_poll_budget(64) (kernel ≥ 5.11) |
| Memory-constrained | 1 MiB | 16 | 100 | 16 MiB total ring |
| Jumbo frames | 4 MiB | 64 | 60 | frame_size(65536) |
See docs/TUNING_GUIDE.md for detailed tuning advice.
§Fanout Modes
Distribute packets across multiple sockets for multi-threaded capture:
| Mode | Strategy |
|---|---|
Hash | Flow hash (same flow → same socket) |
Cpu | Route to CPU that received the NIC interrupt |
LoadBalance | Round-robin |
Rollover | Fill one socket, overflow to next |
Random | Random distribution |
QueueMapping | NIC 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
| Capability | Required For |
|---|---|
CAP_NET_RAW | Creating AF_PACKET / AF_XDP sockets |
CAP_IPC_LOCK | MAP_LOCKED (or sufficient RLIMIT_MEMLOCK) |
CAP_NET_ADMIN | Promiscuous 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
- Architecture — system design, lifetime model, ring layout
- API Overview — all types, methods, and configuration
- Tuning Guide — performance profiles, system tuning, monitoring
- Troubleshooting — common errors and fixes
§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
metricsfaç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-loaderCargo feature.
Structs§
- Packet
View - What a
crate::FlowExtractoris given. - Timestamp
- Nanosecond-precision kernel timestamp.