📡 netmap-rs

Safe, zero-cost abstractions for Netmap kernel-bypass networking in Rust.

Table of Contents

Introduction

netmap-rs provides safe, zero-cost abstractions for Netmap, a high-performance packet I/O framework. It allows you to directly access packet buffers in memory shared with the kernel, eliminating copies and system call overhead for extremely fast networking.

Features

High Performance

Direct memory access to packet buffers with zero-copy operations for maximum throughput.

Memory Safe

Leverages Rust's ownership model to ensure memory safety without performance penalties.

Async Support

Optional integration with Tokio for asynchronous packet processing.

Cross-Platform

Works on Linux, FreeBSD, and other platforms supported by Netmap.

Getting Started

Prerequisites

You must have the Netmap C library installed. Follow the instructions in the official Netmap repository to build and install it on your system.

Adding to Your Project

Add netmap-rs to your Cargo.toml, enabling the sys feature for core functionality:

[dependencies]
netmap-rs = { version = "0.3", features = ["sys"] }

Basic Usage

Here's a simple example of sending and receiving a packet on an interface:

use netmap_rs::prelude::*;
use std::thread::sleep;
use std::time::Duration;

fn main() -> Result<(), Error> {
    let nm = NetmapBuilder::new("eth0")
        .num_tx_rings(1)
        .num_rx_rings(1)
        .build()?;

    let mut tx_ring = nm.tx_ring(0)?;
    let mut rx_ring = nm.rx_ring(0)?;

    let packet_data = b"hello netmap!";

    tx_ring.send(packet_data)?;
    tx_ring.sync();
    println!("Sent packet: {:?}", packet_data);

    // ... (receive logic) ...

    Ok(())
}

Public API

This section provides a detailed overview of the public API of netmap-rs.

NetmapBuilder

The NetmapBuilder is used to configure and create a Netmap instance.

use netmap_rs::NetmapBuilder;

let builder = NetmapBuilder::new("eth0");
use netmap_rs::NetmapBuilder;

let builder = NetmapBuilder::new("eth0").num_tx_rings(2);
use netmap_rs::NetmapBuilder;

let builder = NetmapBuilder::new("eth0").num_rx_rings(2);
use netmap_rs::NetmapBuilder;

let nm = NetmapBuilder::new("eth0").build();

Netmap

A Netmap instance represents an open Netmap interface.

Ring

Represents a generic Netmap ring.

TxRing

A handle to a transmission (TX) ring.

BatchReservation

A reservation for a batch of packets to be sent.

RxRing

A handle to a reception (RX) ring.

Frame

A Frame represents a received packet. It can be either a zero-copy view of a packet buffer (from a Netmap ring) or an owned buffer (in fallback mode).

if let Some(frame) = rx_ring.recv() {
    println!("Received packet of length {}: {:?}", frame.len(), frame.payload());
}

Async API (tokio-async feature)

When the tokio-async feature is enabled, you can use the following async wrappers for non-blocking I/O with Tokio.

TokioNetmap

The TokioNetmap is the entry point for async operations.

use netmap_rs::NetmapBuilder;
use netmap_rs::tokio_async::TokioNetmap;

# async fn run() -> Result<(), Box> {
let nm = NetmapBuilder::new("eth0").build()?;
let tokio_nm = TokioNetmap::new(nm)?;
# Ok(())
# }

AsyncNetmapRxRing

An AsyncRead implementation for a Netmap RX ring. You can use the methods from tokio::io::AsyncReadExt to read from the ring.

# use netmap_rs::NetmapBuilder;
# use netmap_rs::tokio_async::TokioNetmap;
# use tokio::io::AsyncReadExt;
# async fn run() -> Result<(), Box> {
# let nm = NetmapBuilder::new("eth0").build()?;
# let tokio_nm = TokioNetmap::new(nm)?;
let mut rx_ring = tokio_nm.rx_ring(0)?;
let mut buf = [0; 1500];
let n = rx_ring.read(&mut buf).await?;
# Ok(())
# }

AsyncNetmapTxRing

An AsyncWrite implementation for a Netmap TX ring. You can use the methods from tokio::io::AsyncWriteExt to write to the ring.

# use netmap_rs::NetmapBuilder;
# use netmap_rs::tokio_async::TokioNetmap;
# use tokio::io::AsyncWriteExt;
# async fn run() -> Result<(), Box> {
# let nm = NetmapBuilder::new("eth0").build()?;
# let tokio_nm = TokioNetmap::new(nm)?;
let mut tx_ring = tokio_nm.tx_ring(0)?;
tx_ring.write_all(b"hello async netmap").await?;
tx_ring.flush().await?;
# Ok(())
# }

Error Enum

The Error enum represents all possible errors that can occur in netmap-rs.

Fallback API

For platforms without Netmap support, a fallback implementation is provided.

use netmap_rs::fallback::create_fallback_channel;

let (tx, rx) = create_fallback_channel(64);
tx.send(b"hello fallback").unwrap();
if let Some(frame) = rx.recv() {
    assert_eq!(frame.payload(), b"hello fallback");
}

Advanced Usage

Thread-per-Ring

For maximum performance, dedicate a thread to each transmission (TX) and reception (RX) ring. This pattern is ideal for multi-core systems.

use netmap_rs::prelude::*;
use std::thread;

fn main() -> Result<(), Error> {
    let nm = NetmapBuilder::new("eth0")
        .num_tx_rings(4)
        .num_rx_rings(4)
        .build()?;

    // Spawn threads for each ring...
    // See the full example in the repository.
    Ok(())
}

Async Support with Tokio

Enable the tokio-async feature for async/await support:

[dependencies]
netmap-rs = { version = "0.3", features = ["sys", "tokio-async"] }
use netmap_rs::tokio_async::*;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let nm = TokioNetmap::new("eth0").await?;
    let mut rx_ring = nm.async_rx_ring(0).await?;

    // ... (async receive logic) ...
    Ok(())
}

Troubleshooting

Common issues include "netmap_user.h not found" (install Netmap), permission errors (run with sudo), and "NetmapBuilder not found" (enable the sys feature).

Performance Tips