#[cfg(all(target_os = "linux", feature = "wire-locale"))]
mod pktsock {
use std::io;
use std::os::unix::io::RawFd;
pub struct PktSock {
fd: RawFd,
ifindex: i32,
}
impl PktSock {
pub fn open(if_name: &str) -> io::Result<Self> {
let cname = std::ffi::CString::new(if_name).unwrap();
let ifindex = unsafe { libc::if_nametoindex(cname.as_ptr()) } as i32;
if ifindex == 0 {
return Err(io::Error::last_os_error());
}
let proto = (libc::ETH_P_ALL as u16).to_be() as i32;
let fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_RAW, proto) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
let mut sll: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
sll.sll_family = libc::AF_PACKET as u16;
sll.sll_protocol = (libc::ETH_P_ALL as u16).to_be();
sll.sll_ifindex = ifindex;
let rc = unsafe {
libc::bind(
fd,
&sll as *const _ as *const libc::sockaddr,
std::mem::size_of::<libc::sockaddr_ll>() as u32,
)
};
if rc < 0 {
let e = io::Error::last_os_error();
unsafe { libc::close(fd) };
return Err(e);
}
Ok(Self { fd, ifindex })
}
pub fn send(&self, frame: &[u8]) -> io::Result<usize> {
let mut sll: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
sll.sll_family = libc::AF_PACKET as u16;
sll.sll_ifindex = self.ifindex;
let n = unsafe {
libc::sendto(
self.fd,
frame.as_ptr() as *const libc::c_void,
frame.len(),
0,
&sll as *const _ as *const libc::sockaddr,
std::mem::size_of::<libc::sockaddr_ll>() as u32,
)
};
if n < 0 {
Err(io::Error::last_os_error())
} else {
Ok(n as usize)
}
}
pub fn recv(&self, buf: &mut [u8], timeout_ms: i32) -> io::Result<usize> {
let mut pfd = libc::pollfd { fd: self.fd, events: libc::POLLIN, revents: 0 };
let p = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
if p < 0 {
return Err(io::Error::last_os_error());
}
if p == 0 {
return Ok(0);
}
let n = unsafe {
libc::recv(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
};
if n < 0 {
Err(io::Error::last_os_error())
} else {
Ok(n as usize)
}
}
}
impl Drop for PktSock {
fn drop(&mut self) {
unsafe { libc::close(self.fd) };
}
}
}
#[cfg(all(target_os = "linux", feature = "wire-locale"))]
fn main() {
use std::process::Command;
const VETH_PEER: &str = "sxe_xdp_a"; const VETH_XDP: &str = "sxe_xdp_b"; const N: u32 = 200;
const FRAME_LEN: usize = 64;
fn ip(args: &[&str]) -> bool {
Command::new("ip")
.args(args)
.status()
.map(|s| s.success())
.unwrap_or(false)
}
if unsafe { libc::geteuid() } != 0 {
eprintln!("wire_veth_e2e must run as root (veth create + XDP attach).");
eprintln!(" sudo target/release/examples/wire_veth_e2e");
std::process::exit(0);
}
println!("=== Locale::Wire AF_XDP E2E (veth pair, socket stack bypassed) ===");
ip(&["link", "del", VETH_PEER]);
if !ip(&["link", "add", VETH_PEER, "type", "veth", "peer", "name", VETH_XDP]) {
eprintln!("failed to create veth pair");
std::process::exit(1);
}
ip(&["link", "set", VETH_PEER, "up"]);
ip(&["link", "set", VETH_XDP, "up"]);
println!("[setup] veth pair {VETH_PEER} (AF_PACKET peer) <-> {VETH_XDP} (AF_XDP) up");
let result = run(VETH_PEER, VETH_XDP, N, FRAME_LEN);
ip(&["link", "del", VETH_PEER]);
match result {
Ok((rx, tx, elapsed)) => {
println!();
println!("=== Result ===");
println!(" RX bypass: {rx} frames NIC-peer -> AF_XDP RX ring, verified in order");
println!(" TX bypass: {tx} frames AF_XDP TX ring -> NIC-peer, verified in order");
println!(" elapsed: {elapsed:?}");
println!(" integrity: PASS (userspace RX/TX rings, socket stack bypassed)");
}
Err(e) => {
eprintln!("E2E failed: {e}");
std::process::exit(1);
}
}
}
#[cfg(all(target_os = "linux", feature = "wire-locale"))]
fn run(
peer_if: &str,
xdp_if: &str,
n: u32,
frame_len: usize,
) -> std::io::Result<(u32, u32, std::time::Duration)> {
use pktsock::PktSock;
use std::time::Instant;
use subetha_cxc::locale_wire::WireSocket;
let mut wire = WireSocket::bind(xdp_if, 0)?;
let peer = PktSock::open(peer_if)?;
println!("[bind] AF_XDP WireSocket on {xdp_if}, AF_PACKET peer on {peer_if}");
let mut frame = vec![0xA5u8; frame_len];
frame[0..6].copy_from_slice(&[0xff; 6]); frame[6..12].copy_from_slice(&[0x02, 0, 0, 0, 0, 1]); frame[12..14].copy_from_slice(&0x88b5u16.to_be_bytes());
let mut out = vec![0u8; 2048];
let seq_of = |buf: &[u8]| -> Option<u32> {
if buf.len() >= 18 && buf[12..14] == 0x88b5u16.to_be_bytes() {
Some(u32::from_le_bytes(buf[14..18].try_into().unwrap()))
} else {
None
}
};
for w in 0..16u32 {
frame[14..18].copy_from_slice(&(0xF000_0000 | w).to_le_bytes());
peer.send(&frame)?;
wire.recv_frame(&mut out, 200)?;
}
while wire.recv_frame(&mut out, 50)? != 0 {}
let t0 = Instant::now();
let mut rx_ok = 0u32;
for seq in 0..n {
frame[14..18].copy_from_slice(&seq.to_le_bytes());
peer.send(&frame)?;
let mut tries = 0;
loop {
let got = wire.recv_frame(&mut out, 500)?;
if got > 0 && let Some(rseq) = seq_of(&out[..got]) {
if rseq == seq {
rx_ok += 1;
break;
} else if rseq & 0xF000_0000 != 0 {
} else {
return Err(std::io::Error::other(format!(
"RX out of order: got {rseq} want {seq}"
)));
}
}
tries += 1;
if tries > 40 {
return Err(std::io::Error::other(format!("RX: no frame for seq {seq}")));
}
}
}
while peer.recv(&mut out, 50)? != 0 {}
let mut tx_ok = 0u32;
for seq in 0..n {
frame[14..18].copy_from_slice(&seq.to_le_bytes());
wire.send_frame(&frame)?;
let mut tries = 0;
loop {
let got = peer.recv(&mut out, 500)?;
if got > 0 && let Some(rseq) = seq_of(&out[..got]) && rseq == seq {
tx_ok += 1;
break;
}
tries += 1;
if tries > 40 {
return Err(std::io::Error::other(format!("TX: peer missed seq {seq}")));
}
}
}
Ok((rx_ok, tx_ok, t0.elapsed()))
}
#[cfg(not(all(target_os = "linux", feature = "wire-locale")))]
fn main() {
eprintln!("wire_veth_e2e needs Linux + --features wire-locale.");
}