Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ rustflags = [
"-Wclippy::useless_transmute",
"-Wclippy::verbose_file_reads",
"-Wclippy::zero_sized_map_values",
"-Wclippy::undocumented_unsafe_blocks",
"-Wfuture_incompatible",
"-Wnonstandard_style",
"-Wrust_2018_idioms",
"-Wlet-underscore",
]
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- next-header -->
## [Unreleased] - ReleaseDate
### Changed
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) changed `RxRing` and `TxRing` to use the new `slab::Slab` trait.
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) moved `HeapSlab` to the new `slab` module, and made it implement `slab::Slab`, changing it so that items are always pushed to the front and popped from the back, unlike the previous implementation which allowed both.

### Added
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) added a new `slab::StackSlab<N>` fixed size ring buffer that implements `slab::Slab`.

### Fixed
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) fixed some undefined behavior in the netlink code used to query NIC capabilities.
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) fixed a bug where TX metadata would not be added and would return an error if the packet headroom was not large enough for the metadata, this is irrelevant.

## [0.5.0] - 2025-02-27
### Changed
- [PR#15](https://github.com/Jake-Shadle/xdp/pull/15) renamed `UdpPacket` -> `UdpHeaders`, and changed the contents to be the actual headers that can be de/serialized from/to the packet buffer.
Expand Down
303 changes: 142 additions & 161 deletions crates/integ/tests/tx_checksum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use test_utils::netlink::VethPair;
use xdp::{
packet::{net_types as nt, *},
slab::Slab,
socket::*,
umem::*,
*,
Expand Down Expand Up @@ -39,8 +40,8 @@ fn do_checksum_test(software: bool, vpair: &VethPair) {
frame_size: FrameSize::TwoK,
head_room: 20,
frame_count: 64,
tx_metadata: software,
software_checksum: software,
..Default::default()
}
.build()
.expect("invalid umem cfg"),
Expand Down Expand Up @@ -110,168 +111,148 @@ fn do_checksum_test(software: bool, vpair: &VethPair) {
}};
}

let res = std::thread::scope(|s| {
let client = s.spawn(|| -> Result<(), (&'static str, std::io::Error)> {
let dest: std::net::SocketAddr = (vpair.inside.ipv4, 7777).into();
let local = client_socket.local_addr().unwrap();
println!("sending {}b {local} -> {dest}", clientp.len());
client_socket
.send_to(clientp, dest)
.map_err(|err| ("failed to send first request", err))?;

let mut response = [0u8; 20];

println!("receiving {}b {local} <- {dest}", serverp.len());
let (read, addr) = client_socket
.recv_from(&mut response)
.map_err(|err| ("failed to receive first response", err))?;
assert_eq!(&response[..read], serverp);
assert_eq!(addr, (vpair.inside.ipv4, sport).into());

println!("sending {}b {local} -> {dest}", clientp.len());
client_socket
.send_to(clientp, dest)
.map_err(|err| ("failed to send second request", err))?;

println!("receiving {}b {local} <- {dest}", serverp.len());
let (read, addr) = client_socket
.recv_from(&mut response)
.map_err(|err| ("failed to receive second response", err))?;

assert_eq!(&response[..read], serverp);
assert_eq!(addr, (vpair.inside.ipv4, sport).into());

Ok(())
});

let server = s.spawn(|| {
let timeout = PollTimeout::new(Some(std::time::Duration::from_millis(100)));

let mut slab = xdp::HeapSlab::with_capacity(BATCH_SIZE);

unsafe {
poll_loop!({
xdp_socket.poll_read(timeout).unwrap();
if rx.recv(&umem, &mut slab) == 1 {
break;
}
});

let mut packet = slab.pop_back().unwrap();
let udp = nt::UdpHeaders::parse_packet(&packet)
.expect("failed to parse packet")
.expect("not a UDP packet");

// For this packet, we calculate the full checksum
packet.adjust_tail(-(udp.data_length as i32)).unwrap();
packet.insert(udp.data_offset, serverp).unwrap();

let nt::IpHdr::V4(mut copy) = udp.ip else {
unreachable!()
};
std::mem::swap(&mut copy.destination, &mut copy.source);
copy.time_to_live -= 1;

let mut new = nt::UdpHeaders {
eth: nt::EthHdr {
source: udp.eth.destination,
destination: udp.eth.source,
ether_type: udp.eth.ether_type,
},
ip: nt::IpHdr::V4(copy),
udp: nt::UdpHdr {
destination: udp.udp.source,
source: sport.into(),
length: 0.into(),
check: 0,
},
data_offset: udp.data_offset,
data_length: serverp.len(),
};

// For this packet, we calculate the full checksum
let data_checksum = csum::partial(serverp, 0);
let full_checksum = new.calc_checksum(serverp.len(), data_checksum);
new.set_packet_headers(&mut packet, true).unwrap();
println!("Full checksum: {full_checksum:04x}");

slab.push_back(packet);
assert_eq!(tx.send(&mut slab), 1);

poll_loop!({
xdp_socket.poll(timeout).unwrap();
if cr.dequeue(&mut umem, 1) == 1 {
break;
}
});

poll_loop!({
xdp_socket.poll_read(timeout).unwrap();
if rx.recv(&umem, &mut slab) == 1 {
break;
}
});

let mut packet = slab.pop_back().unwrap();
let udp = nt::UdpHeaders::parse_packet(&packet)
.expect("failed to parse packet")
.expect("not a UDP packet");

packet.adjust_tail(-(udp.data_length as i32)).unwrap();
packet.insert(udp.data_offset, serverp).unwrap();

let nt::IpHdr::V4(mut copy) = udp.ip else {
unreachable!()
};
std::mem::swap(&mut copy.destination, &mut copy.source);
copy.time_to_live -= 1;

let mut new = nt::UdpHeaders {
eth: nt::EthHdr {
source: udp.eth.destination,
destination: udp.eth.source,
ether_type: udp.eth.ether_type,
},
ip: nt::IpHdr::V4(copy),
udp: nt::UdpHdr {
destination: udp.udp.source,
source: sport.into(),
length: 0.into(),
check: 0,
},
data_offset: udp.data_offset,
data_length: serverp.len(),
};
new.set_packet_headers(&mut packet, true).unwrap();
println!(
"partial checksum: {:04x}",
packet.calc_udp_checksum().unwrap()
);

slab.push_back(packet);
assert_eq!(tx.send(&mut slab), 1);

poll_loop!({
xdp_socket.poll(timeout).unwrap();
if cr.dequeue(&mut umem, 1) == 1 {
break;
}
});
}
});
std::thread::spawn(move || {
let timeout = PollTimeout::new(Some(std::time::Duration::from_millis(100)));

let mut slab = xdp::slab::StackSlab::<BATCH_SIZE>::new();

let err = if let Err(err) = client.join().unwrap() {
run.store(false, std::sync::atomic::Ordering::Relaxed);
Some(err)
} else {
None
};
unsafe {
poll_loop!({
xdp_socket.poll_read(timeout).unwrap();
if rx.recv(&umem, &mut slab) == 1 {
break;
}
});

let mut packet = slab.pop_back().unwrap();
let udp = nt::UdpHeaders::parse_packet(&packet)
.expect("failed to parse packet")
.expect("not a UDP packet");

// For this packet, we calculate the full checksum
packet.adjust_tail(-(udp.data_length as i32)).unwrap();
packet.insert(udp.data_offset, serverp).unwrap();

let nt::IpHdr::V4(mut copy) = udp.ip else {
unreachable!()
};
std::mem::swap(&mut copy.destination, &mut copy.source);
copy.time_to_live -= 1;

let mut new = nt::UdpHeaders {
eth: nt::EthHdr {
source: udp.eth.destination,
destination: udp.eth.source,
ether_type: udp.eth.ether_type,
},
ip: nt::IpHdr::V4(copy),
udp: nt::UdpHdr {
destination: udp.udp.source,
source: sport.into(),
length: 0.into(),
check: 0,
},
data_offset: udp.data_offset,
data_length: serverp.len(),
};

// For this packet, we calculate the full checksum
let data_checksum = csum::partial(serverp, 0);
let full_checksum = new.calc_checksum(serverp.len(), data_checksum);
new.set_packet_headers(&mut packet, true).unwrap();
println!("Full checksum: {full_checksum:04x}");

slab.push_front(packet);
assert_eq!(tx.send(&mut slab), 1);

poll_loop!({
xdp_socket.poll(timeout).unwrap();
if cr.dequeue(&mut umem, 1) == 1 {
break;
}
});

server.join().unwrap();
err
poll_loop!({
xdp_socket.poll_read(timeout).unwrap();
if rx.recv(&umem, &mut slab) == 1 {
break;
}
});

let mut packet = slab.pop_back().unwrap();
let udp = nt::UdpHeaders::parse_packet(&packet)
.expect("failed to parse packet")
.expect("not a UDP packet");

packet.adjust_tail(-(udp.data_length as i32)).unwrap();
packet.insert(udp.data_offset, serverp).unwrap();

let nt::IpHdr::V4(mut copy) = udp.ip else {
unreachable!()
};
std::mem::swap(&mut copy.destination, &mut copy.source);
copy.time_to_live -= 1;

let mut new = nt::UdpHeaders {
eth: nt::EthHdr {
source: udp.eth.destination,
destination: udp.eth.source,
ether_type: udp.eth.ether_type,
},
ip: nt::IpHdr::V4(copy),
udp: nt::UdpHdr {
destination: udp.udp.source,
source: sport.into(),
length: 0.into(),
check: 0,
},
data_offset: udp.data_offset,
data_length: serverp.len(),
};
new.set_packet_headers(&mut packet, true).unwrap();
println!(
"partial checksum: {:04x}",
packet.calc_udp_checksum().unwrap()
);

slab.push_front(packet);
assert_eq!(tx.send(&mut slab), 1);

poll_loop!({
xdp_socket.poll(timeout).unwrap();
if cr.dequeue(&mut umem, 1) == 1 {
break;
}
});
}
});

if let Some((msg, err)) = res {
panic!("{msg}: {err}");
}
let dest: std::net::SocketAddr = (vpair.inside.ipv4, 7777).into();
let local = client_socket.local_addr().unwrap();
println!("sending {}b {local} -> {dest}", clientp.len());
client_socket
.send_to(clientp, dest)
.expect("failed to send first request");

let mut response = [0u8; 20];

println!("receiving {}b {local} <- {dest}", serverp.len());
let (read, addr) = client_socket
.recv_from(&mut response)
.expect("failed to receive first response");
assert_eq!(&response[..read], serverp);
assert_eq!(addr, (vpair.inside.ipv4, sport).into());

println!("sending {}b {local} -> {dest}", clientp.len());
client_socket
.send_to(clientp, dest)
.expect("failed to send second request");

println!("receiving {}b {local} <- {dest}", serverp.len());
let (read, addr) = client_socket
.recv_from(&mut response)
.expect("failed to receive second response");

assert_eq!(&response[..read], serverp);
assert_eq!(addr, (vpair.inside.ipv4, sport).into());
}
Loading