mod fd;
pub use fd::{Fd, XdpStatistics};
mod rx_queue;
pub use rx_queue::RxQueue;
mod tx_queue;
pub use tx_queue::TxQueue;
use libxdp_sys::xsk_socket;
use std::{
error::Error,
fmt, io,
ptr::{self, NonNull},
sync::{Arc, Mutex},
};
use crate::{
config::{Interface, SocketConfig},
ring::{XskRingCons, XskRingConsHandle, XskRingProd, XskRingProdHandle},
umem::{CompQueue, CtxRings, FillQueue, Umem},
};
#[derive(Debug)]
struct SocketInner {
ptr: NonNull<xsk_socket>,
_rx_ring: XskRingConsHandle,
_tx_ring: XskRingProdHandle,
umem: Umem,
}
impl SocketInner {
unsafe fn new(
ptr: NonNull<xsk_socket>,
rx_ring: XskRingConsHandle,
tx_ring: XskRingProdHandle,
umem: Umem,
) -> Self {
Self {
ptr,
_rx_ring: rx_ring,
_tx_ring: tx_ring,
umem,
}
}
fn as_ptr(&self) -> *mut xsk_socket {
self.ptr.as_ptr()
}
}
impl Drop for SocketInner {
fn drop(&mut self) {
unsafe { self.umem.delete_socket(self.as_ptr()) };
}
}
unsafe impl Send for SocketInner {}
#[derive(Debug)]
pub struct Socket {
fd: Fd,
_inner: Arc<Mutex<SocketInner>>,
}
impl Socket {
#[allow(clippy::new_ret_no_self)]
#[allow(clippy::type_complexity)]
pub unsafe fn new(
config: SocketConfig,
umem: &Umem,
if_name: &Interface,
queue_id: u32,
) -> Result<(TxQueue, RxQueue, Option<(FillQueue, CompQueue)>), SocketCreateError> {
let mut socket_ptr = ptr::null_mut();
let tx_q = XskRingProd::default();
let rx_q = XskRingCons::default();
let rings = umem
.with_ptr_and_fq_and_cq(|xsk_umem, fq, cq| unsafe {
libxdp_sys::xsk_socket__create_shared(
&mut socket_ptr,
if_name.as_cstr().as_ptr(),
queue_id,
xsk_umem,
rx_q.as_ptr(),
tx_q.as_ptr(),
fq.as_ptr(),
cq.as_ptr(),
&config.into(),
)
})
.map_err(|err| SocketCreateError {
reason: "non-zero error code returned when creating AF_XDP socket",
err: Some(io::Error::from_raw_os_error(-err)),
})?;
let inner = match NonNull::new(socket_ptr) {
Some(init_xsk) => {
unsafe { SocketInner::new(init_xsk, rx_q.handle(), tx_q.handle(), umem.clone()) }
}
None => {
return Err(SocketCreateError {
reason: "returned socket pointer was null",
err: None,
});
}
};
let fd = unsafe { libxdp_sys::xsk_socket__fd(inner.as_ptr()) };
if fd < 0 {
return Err(SocketCreateError {
reason: "failed to retrieve AF_XDP socket file descriptor",
err: None,
});
}
let socket = Socket {
fd: Fd::new(fd),
_inner: Arc::new(Mutex::new(inner)),
};
let tx_q = if tx_q.is_ring_null() {
return Err(SocketCreateError {
reason: "returned tx queue ring is null",
err: None,
});
} else {
TxQueue::new(tx_q, socket.clone())
};
let rx_q = if rx_q.is_ring_null() {
return Err(SocketCreateError {
reason: "returned rx queue ring is null",
err: None,
});
} else {
RxQueue::new(rx_q, socket.clone())
};
let fq_and_cq = match rings {
CtxRings::New(fq, cq) => {
let fq = FillQueue::new(fq, socket.clone());
let cq = CompQueue::new(cq, socket);
Some((fq, cq))
}
CtxRings::Existing => None,
CtxRings::Mismatched => {
return Err(SocketCreateError {
reason: "fill queue xor comp queue ring is null, either both or neither should be non-null",
err: None,
});
}
};
Ok((tx_q, rx_q, fq_and_cq))
}
}
impl Clone for Socket {
fn clone(&self) -> Self {
Self {
fd: self.fd.clone(),
_inner: self._inner.clone(),
}
}
}
#[derive(Debug)]
pub struct SocketCreateError {
reason: &'static str,
err: Option<io::Error>,
}
impl fmt::Display for SocketCreateError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.reason)
}
}
impl Error for SocketCreateError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.err.as_ref().map(|err| err as _)
}
}