#![cfg(feature = "sys")]
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::unix::io::{AsRawFd, RawFd};
use std::ptr;
use std::sync::Arc;
use crate::error::Error;
use crate::ffi;
use crate::ring::{Ring, RxRing, TxRing};
pub struct NetmapBuilder {
ifname_raw: String, base_ifname: String,
wants_host_rings: bool, is_pipe_if: bool,
req_num_tx_rings: u16,
req_num_rx_rings: u16,
additional_flags: u32,
}
impl NetmapBuilder {
pub fn new(ifname_str: &str) -> Self {
let mut raw_name_to_use = ifname_str.to_string();
let mut base_name_for_req = ifname_str;
if let Some(stripped_prefix) = ifname_str.strip_prefix("netmap:") {
base_name_for_req = stripped_prefix;
} else {
if !ifname_str.contains(':') && !ifname_str.starts_with("pipe{") {
raw_name_to_use = format!("netmap:{}", ifname_str);
}
}
let wants_host_rings = base_name_for_req.ends_with('^');
let mut effective_base_name_for_req = base_name_for_req.to_string();
if wants_host_rings {
effective_base_name_for_req.pop(); }
let is_pipe = base_name_for_req.starts_with("pipe{") && base_name_for_req.ends_with('}');
let default_rings = if is_pipe { 1 } else { 0 };
Self {
ifname_raw: raw_name_to_use,
base_ifname: effective_base_name_for_req, wants_host_rings,
is_pipe_if: is_pipe,
req_num_tx_rings: default_rings,
req_num_rx_rings: default_rings,
additional_flags: 0,
}
}
pub fn num_tx_rings(mut self, num: usize) -> Self {
self.req_num_tx_rings = num as u16;
self
}
pub fn num_rx_rings(mut self, num: usize) -> Self {
self.req_num_rx_rings = num as u16;
self
}
pub fn flags(mut self, flags: u32) -> Self {
self.additional_flags = flags;
self
}
fn build_nmreq(&self) -> Result<ffi::nmreq, Error> {
if self.base_ifname.len() >= ffi::IFNAMSIZ as usize {
return Err(Error::BindFail(format!(
"Base interface name '{}' is too long.",
self.base_ifname
)));
}
let mut nr_name_bytes = [0i8; ffi::IFNAMSIZ as usize];
for (i, byte) in self.base_ifname.bytes().enumerate() {
nr_name_bytes[i] = byte as i8;
}
let mut req_flags = self.additional_flags;
let mut hw_tx_rings = 0;
let mut hw_rx_rings = 0;
let mut host_tx_rings = 0;
let mut host_rx_rings = 0;
if self.is_pipe_if {
hw_tx_rings = self.req_num_tx_rings; hw_rx_rings = self.req_num_rx_rings; } else if self.wants_host_rings {
req_flags |= ffi::NR_REG_SW_ONLY; host_tx_rings = self.req_num_tx_rings;
host_rx_rings = self.req_num_rx_rings;
} else {
req_flags |= ffi::NR_REG_NIC_ONLY; hw_tx_rings = self.req_num_tx_rings;
hw_rx_rings = self.req_num_rx_rings;
}
Ok(ffi::nmreq {
nr_name: nr_name_bytes,
nr_version: ffi::NETMAP_API as u16,
nr_offset: 0,
nr_memsize: 0,
nr_tx_slots: 0, nr_rx_slots: 0, nr_tx_rings: hw_tx_rings, nr_rx_rings: hw_rx_rings, nr_host_tx_rings: host_tx_rings,
nr_host_rx_rings: host_rx_rings,
nr_ringid: 0, nr_flags: req_flags,
nr_arg1: 0,
nr_arg2: 0,
nr_arg3: 0, spare1: [0; 1], })
}
pub fn build(self) -> Result<Netmap, Error> {
let req = self.build_nmreq()?;
let c_ifname_raw = CString::new(self.ifname_raw.as_str())
.map_err(|_| Error::BindFail(format!("Invalid raw interface name: {}", self.ifname_raw)))?;
let desc_ptr = unsafe { ffi::nm_open(c_ifname_raw.as_ptr(), &req as *const _, ptr::null_mut(), ptr::null_mut()) };
if desc_ptr.is_null() {
return Err(Error::BindFail(format!(
"Failed to open interface via nm_open for '{}'. Errno: {}",
self.ifname_raw, std::io::Error::last_os_error()
)));
}
let nifp = unsafe { (*desc_ptr).nifp };
let (actual_num_tx, actual_num_rx, final_is_host_if) = if self.is_pipe_if {
(unsafe { (*nifp).ni_tx_rings } as usize, unsafe { (*nifp).ni_rx_rings } as usize, false)
} else if self.wants_host_rings {
(unsafe { (*nifp).ni_host_tx_rings } as usize, unsafe { (*nifp).ni_host_rx_rings } as usize, true)
} else {
(unsafe { (*nifp).ni_tx_rings } as usize, unsafe { (*nifp).ni_rx_rings } as usize, false)
};
Ok(Netmap {
desc: desc_ptr,
num_tx_rings: actual_num_tx,
num_rx_rings: actual_num_rx,
is_host_if: final_is_host_if,
_marker: PhantomData,
})
}
}
pub struct Netmap {
desc: *mut ffi::nm_desc,
num_tx_rings: usize, num_rx_rings: usize, is_host_if: bool, _marker: PhantomData<*mut u8>,
}
unsafe impl Send for Netmap {}
impl Netmap {
pub fn num_tx_rings(&self) -> usize {
self.num_tx_rings
}
pub fn num_rx_rings(&self) -> usize {
self.num_rx_rings
}
pub fn is_host_if(&self) -> bool {
self.is_host_if
}
pub fn tx_ring(&self, index: usize) -> Result<TxRing, Error> {
if index >= self.num_tx_rings {
return Err(Error::InvalidRingIndex(index));
}
unsafe {
let ring = ffi::NETMAP_TXRING((*self.desc).nifp, index as u32);
Ok(TxRing::new(ring, index))
}
}
pub fn rx_ring(&self, index: usize) -> Result<RxRing, Error> {
if index >= self.num_rx_rings {
return Err(Error::InvalidRingIndex(index));
}
unsafe {
let ring = ffi::NETMAP_RXRING((*self.desc).nifp, index as u32);
Ok(RxRing::new(ring, index))
}
}
}
impl Drop for Netmap {
fn drop(&mut self) {
unsafe {
ffi::nm_close(self.desc);
}
}
}
impl AsRawFd for Netmap {
fn as_raw_fd(&self) -> RawFd {
unsafe { (*self.desc).fd }
}
}