mod setup;
use libbpf_sys::XDP_PACKET_HEADROOM;
use rusty_fork::rusty_fork_test;
use std::{thread, time::Duration};
use xdpsock::{
socket::{SocketConfig, SocketConfigBuilder},
umem::{UmemConfig, UmemConfigBuilder},
xsk::Xsk,
};
fn build_configs() -> (Option<UmemConfig>, Option<SocketConfig>) {
let umem_config = UmemConfigBuilder::new()
.frame_count(8)
.frame_size(2048)
.fill_queue_size(4)
.comp_queue_size(4)
.build()
.expect("failed to build umem config");
let socket_config = SocketConfigBuilder::new()
.tx_queue_size(4)
.rx_queue_size(4)
.build()
.expect("failed to build socket config");
(Some(umem_config), Some(socket_config))
}
rusty_fork_test! {
#[test]
fn rx_queue_consumes_nothing_if_no_tx_and_fill_q_empty() {
fn test_fn(mut dev1: Xsk, _dev2: Xsk) {
assert_eq!(dev1.rx_q.consume(&mut dev1.rx_frames[..2]), 0);
assert_eq!(
dev1.rx_q
.poll_and_consume(&mut dev1.rx_frames[..2], 100)
.unwrap(),
0
);
}
let (dev1_umem_config, dev1_socket_config) = build_configs();
let (dev2_umem_config, dev2_socket_config) = build_configs();
setup::run_test(
dev1_umem_config,
dev1_socket_config,
dev2_umem_config,
dev2_socket_config,
test_fn,
);
}
}
rusty_fork_test! {
#[test]
fn rx_queue_consume_returns_nothing_if_fill_q_empty() {
fn test_fn(mut dev1: Xsk, mut dev2: Xsk) {
assert_eq!(
unsafe {
dev2.tx_q
.produce_and_wakeup(&dev2.tx_frames[..4])
.unwrap()
},
4
);
assert_eq!(dev1.rx_q.consume(&mut dev1.rx_frames[..4]), 0);
assert_eq!(
dev1.rx_q
.poll_and_consume(&mut dev1.rx_frames[..4], 100)
.unwrap(),
0
);
}
let (dev1_umem_config, dev1_socket_config) = build_configs();
let (dev2_umem_config, dev2_socket_config) = build_configs();
setup::run_test(
dev1_umem_config,
dev1_socket_config,
dev2_umem_config,
dev2_socket_config,
test_fn,
);
}
}
rusty_fork_test! {
#[test]
fn rx_queue_consumes_frame_correctly_after_tx() {
fn test_fn(mut dev1: Xsk, mut dev2: Xsk) {
assert_eq!(unsafe { dev1.fill_q.produce(&mut dev1.rx_frames[0..1]) }, 1);
let pkt = vec![b'H', b'e', b'l', b'l', b'o'];
unsafe {
dev2.tx_frames[0].write_to_umem_checked(&pkt[..]).unwrap();
}
assert_eq!(dev2.tx_frames[0].len(), 5);
assert_eq!(
unsafe {
dev2.tx_q
.produce_and_wakeup(&dev2.tx_frames[0..1])
.unwrap()
},
1
);
thread::sleep(Duration::from_millis(5));
let frames_consumed = dev1.rx_q.consume(&mut dev1.rx_frames[..]);
assert_eq!(frames_consumed, 1);
assert_eq!(dev1.rx_frames[0].len(), 5);
let recvd = unsafe {
dev1.rx_frames[0]
.read_from_umem_checked(dev1.rx_frames[0].len())
.unwrap()
};
assert_eq!(recvd[..5], pkt[..]);
}
let (dev1_umem_config, dev1_socket_config) = build_configs();
let (dev2_umem_config, dev2_socket_config) = build_configs();
setup::run_test(
dev1_umem_config,
dev1_socket_config,
dev2_umem_config,
dev2_socket_config,
test_fn,
);
}
}
rusty_fork_test! {
#[test]
fn rx_queue_recvd_packet_offset_after_tx_includes_xdp_and_frame_headroom() {
fn test_fn(mut dev1: Xsk, mut dev2: Xsk) {
assert_eq!(unsafe { dev1.fill_q.produce(&mut dev1.rx_frames[0..1]) }, 1);
let pkt = vec![b'H', b'e', b'l', b'l', b'o'];
unsafe {
dev2.tx_frames[0].write_to_umem_checked(&pkt[..]).unwrap();
}
assert_eq!(dev2.tx_frames[0].len(), 5);
assert_eq!(
unsafe {
dev2.tx_q
.produce_and_wakeup(&dev2.tx_frames[0..1])
.unwrap()
},
1
);
thread::sleep(Duration::from_millis(5));
assert_eq!(dev1.rx_q.consume(&mut dev1.rx_frames[..]), 1);
assert_eq!(dev1.rx_frames[0].len(), 5);
assert_eq!(
dev1.rx_frames[0].addr(),
(XDP_PACKET_HEADROOM + 512 + 8192) as usize );
}
let (dev2_umem_config, dev2_socket_config) = build_configs();
let dev1_umem_config = UmemConfigBuilder::new()
.frame_count(8)
.frame_size(2048)
.fill_queue_size(4)
.comp_queue_size(4)
.frame_headroom(512).build().expect("failed to build umem config");
let dev1_socket_config = SocketConfigBuilder::new().tx_queue_size(4).rx_queue_size(4).build().expect("failed to build socket config");
setup::run_test(
Some(dev1_umem_config),
Some(dev1_socket_config),
dev2_umem_config,
dev2_socket_config,
test_fn,
);
}
}