| Crates.io | tokio-nbd |
| lib.rs | tokio-nbd |
| version | 0.3.0 |
| created_at | 2025-07-31 22:09:47.877281+00 |
| updated_at | 2025-08-09 02:27:03.657784+00 |
| description | Network Block Device (NBD) server with pluggable backend support using Rust and the tokio runtime |
| homepage | |
| repository | https://github.com/john-parton/tokio-nbd |
| max_upload_size | |
| id | 1775818 |
| size | 192,842 |
Network Block Device (NBD) server with pluggable backend support using Rust and the tokio runtime.
tokio-nbd is a Rust implementation of the Network Block Device (NBD) protocol that leverages the tokio asynchronous runtime. It provides a modern, high-performance, and extensible NBD server implementation that can be used with various storage backends.
NbdDriver trait for custom storage systemsThe library implements the most of the NBD protocol specification as defined at NetworkBlockDevice/nbd, with the exception of structured replies.
Add tokio-nbd to your Cargo.toml using cargo add tokio-nbd
use std::sync::RwLock;
use tokio;
use tokio_nbd::device::NbdDriver;
use tokio_nbd::server::NbdServer;
use tokio_nbd::errors::{OptionReplyError, ProtocolError};
use tokio_nbd::flags::{CommandFlags, ServerFeatures};
#[derive(Debug)]
pub(crate) struct MemoryDriver {
data: RwLock<Vec<u8>>,
read_only: bool,
name: String,
}
impl Default for MemoryDriver {
fn default() -> Self {
MemoryDriver {
data: RwLock::new(vec![0; 1024]), // 1KB of zeroed memory for tests
read_only: false,
name: "".to_string(),
}
}
}
impl NbdDriver for MemoryDriver {
fn get_features(&self) -> ServerFeatures {
// Support basic read/write operations but not advanced features
ServerFeatures::SEND_FUA
}
fn get_name(&self) -> String {
self.name.clone()
}
async fn get_read_only(&self) -> Result<bool, OptionReplyError> {
Ok(self.read_only)
}
async fn get_block_size(&self) -> Result<(u32, u32, u32), OptionReplyError> {
Err(OptionReplyError::Unsupported)
}
async fn get_canonical_name(&self) -> Result<String, OptionReplyError> {
Err(OptionReplyError::Unsupported)
}
async fn get_description(&self) -> Result<String, OptionReplyError> {
Err(OptionReplyError::Unsupported)
}
async fn get_device_size(&self) -> Result<u64, OptionReplyError> {
Ok(self.data.read().unwrap().len() as u64)
}
async fn read(
&self,
_flags: CommandFlags,
offset: u64,
length: u32,
) -> Result<Vec<u8>, ProtocolError> {
let data = self.data.read().unwrap();
let start = offset as usize;
let end = start + length as usize;
// Check if read is within bounds
if start >= data.len() || (length > 0 && end > data.len()) {
return Err(ProtocolError::InvalidArgument);
}
Ok(data[start..end].to_vec())
}
async fn write(
&self,
_flags: CommandFlags,
offset: u64,
data: Vec<u8>,
) -> Result<(), ProtocolError> {
let mut memory = self.data.write().unwrap();
let start = offset as usize;
let end = start + data.len();
// Check if write is within bounds
if start >= memory.len() || (data.len() > 0 && end > memory.len()) {
return Err(ProtocolError::InvalidArgument);
}
memory[start..end].copy_from_slice(&data);
Ok(())
}
async fn disconnect(&self, _flags: CommandFlags) -> Result<(), ProtocolError> {
Ok(())
}
// Other methods implementation...
}
async fn start_nbd(host: &str, port: u16, driver: Arc<MemoryDriver>) -> std::io::Result<()> {
let listener = TcpListener::bind(format!("{}:{}", host, port)).await?;
println!("NBD server listening on {}:{}", host, port);
loop {
let (stream, addr) = listener.accept().await?;
println!("NBD client connected from {}", addr);
let driver = Arc::clone(&driver);
tokio::spawn(async move {
let server = NbdServer::new(driver);
if let Err(e) = server.start(stream).await {
println!("Error starting NBD server: {:?}", e);
return;
}
});
}
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let port: u16 = 10809; // Default NBD port
// Create a driver with 1MB of storage
let driver = Arc::new(MemoryDriver {
data: RwLock::new(vec![0; 1024 * 1024]),
});
start_nbd("127.0.0.1", port, driver).await
}
NBD does not provide built-in authentication or encryption. For secure deployments:
START_TLS option)When implementing the NBDDriver trait:
get_features() methodProtocolError::CommandNotSupported from the corresponding methodContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the GPL-2.0-or-later license.