Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.

Commit 1f0c9d3

Browse files
authored
Add doc examples (#17)
1 parent 7892fa5 commit 1f0c9d3

File tree

8 files changed

+400
-30
lines changed

8 files changed

+400
-30
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
### Changed
1212
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) changed `RxRing` and `TxRing` to use the new `slab::Slab` trait.
1313
- [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.
14+
- [PR#17](https://github.com/Jake-Shadle/xdp/pull/17) changed `CsumOffload::Request(xdp::libc::xdp::xsk_tx_request)` -> `CsumOffload::Request { start: u16, offset: u16 }`
1415

1516
### Added
1617
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) added a new `slab::StackSlab<N>` fixed size ring buffer that implements `slab::Slab`.
18+
- [PR#17](https://github.com/Jake-Shadle/xdp/pull/17) added various doc examples.
1719

1820
### Fixed
1921
- [PR#16](https://github.com/Jake-Shadle/xdp/pull/16) fixed some undefined behavior in the netlink code used to query NIC capabilities.
2022
- [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.
23+
- [PR#17](https://github.com/Jake-Shadle/xdp/pull/17) fixed the exceptional case where a UDP checksum is calculated to be 0, in which case it is set to `0xffff` instead.
2124

2225
## [0.5.0] - 2025-02-27
2326
### Changed

crates/integ/tests/tx_checksum.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,9 @@ use xdp::{
99

1010
/// Validates that we can offload (most of) the layer 4 checksum calculation to
1111
/// hardware when the hardware + driver supports the `XDP_TXMD_FLAGS_CHECKSUM`
12-
/// flag
12+
/// flag. Note that this will fail if the kernel version is too low.
1313
#[test]
1414
fn offloads_tx_checksum() {
15-
if std::env::var_os("CI").is_some() {
16-
println!(
17-
"::notice file={},line={}::Skipping TX offload test with sofware checksum, Ubunut kernel version is too old",
18-
file!(),
19-
line!()
20-
);
21-
return;
22-
}
23-
2415
let vpair = test_utils::veth_pair!("tx_off", 0);
2516

2617
do_checksum_test(true, &vpair);

src/packet.rs

Lines changed: 252 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ pub unsafe trait Pod: Sized {
100100
/// Configures TX checksum offload when setting TX metadata via [`Packet::set_tx_metadata`]
101101
pub enum CsumOffload {
102102
/// Requests checksum offload
103-
Request(libc::xdp::xsk_tx_request),
103+
Request {
104+
/// The offset from the start of the packet where the checksum calculation should start
105+
start: u16,
106+
/// The offset from `start` where the checksum should be stored
107+
offset: u16,
108+
},
104109
/// Offload is not requested
105110
None,
106111
}
@@ -169,12 +174,34 @@ impl Packet {
169174
}
170175

171176
/// The number of initialized/valid bytes in the packet
177+
///
178+
/// # Examples
179+
///
180+
/// ```
181+
/// # let mut buf = [0u8; 2 * 1024];
182+
/// # let mut packet = xdp::Packet::testing_new(&mut buf);
183+
///
184+
/// assert_eq!(0, packet.len());
185+
/// packet.insert(0, &[2; 21]).expect("failed to insert slice");
186+
/// assert_eq!(21, packet.len());
187+
/// ```
172188
#[inline]
173189
pub fn len(&self) -> usize {
174190
self.tail - self.head
175191
}
176192

177193
/// True if the packet is empty
194+
///
195+
/// # Examples
196+
///
197+
/// ```
198+
/// # let mut buf = [0u8; 2 * 1024];
199+
/// # let mut packet = xdp::Packet::testing_new(&mut buf);
200+
///
201+
/// assert!(packet.is_empty());
202+
/// packet.insert(0, &[1]).expect("failed to insert slice");
203+
/// assert!(!packet.is_empty());
204+
/// ```
178205
#[inline]
179206
pub fn is_empty(&self) -> bool {
180207
self.head == self.tail
@@ -184,29 +211,54 @@ impl Packet {
184211
///
185212
/// Note that this never includes the [`libc::xdp::XDP_PACKET_HEADROOM`]
186213
/// part of every packet
214+
///
215+
/// # Examples
216+
///
217+
/// ```
218+
/// let mut umem = xdp::Umem::map(
219+
/// xdp::umem::UmemCfgBuilder::default().build().unwrap()
220+
/// ).expect("failed to map Umem");
221+
///
222+
/// unsafe {
223+
/// let packet = umem.alloc().expect("failed to allocate packet");
224+
/// // The default size is 4k (page size)
225+
/// assert_eq!(packet.capacity(), 4 * 1024 - xdp::libc::xdp::XDP_PACKET_HEADROOM as usize);
226+
/// }
227+
/// ```
187228
#[inline]
188229
pub fn capacity(&self) -> usize {
189230
self.capacity
190231
}
191232

192233
/// Resets the tail of this packet, causing it to become empty
234+
///
235+
/// # Examples
236+
///
237+
/// ```
238+
/// # let mut buf = [0u8; 2 * 1024];
239+
/// # let mut packet = xdp::Packet::testing_new(&mut buf);
240+
///
241+
/// assert!(packet.is_empty());
242+
/// packet.insert(0, &[2; 21]).expect("failed to insert slice");
243+
/// packet.clear();
244+
/// assert!(packet.is_empty());
245+
/// ```
193246
#[inline]
194247
pub fn clear(&mut self) {
195248
self.tail = self.head;
196249
}
197250

198-
/// If true, this packet is partial, and the next packet in the RX continues
199-
/// this packet, until this returns fals
251+
/// If true, this packet is fragmented, and the next packet in the queue
252+
/// continues this packet, until this returns `false`
200253
#[inline]
201254
pub fn is_continued(&self) -> bool {
202255
(self.options & libc::xdp::XdpPktOptions::XDP_PKT_CONTD) != 0
203256
}
204257

258+
// TODO: Create a different type to indicate checksum since it's not going
259+
// to change so the user can choose at init time whether they want checksum
260+
// offload or not
205261
/// Checks if the NIC this packet is being sent on supports tx checksum offload
206-
///
207-
/// TODO: Create a different type to indicate checksum since it's not going
208-
/// to change so the user can choose at init time whether they want checksum
209-
/// offload or not
210262
#[inline]
211263
pub fn can_offload_checksum(&self) -> bool {
212264
(self.options & libc::InternalXdpFlags::SUPPORTS_CHECKSUM_OFFLOAD) != 0
@@ -219,6 +271,38 @@ impl Packet {
219271
/// to copy the entirety of the packet data up or down.
220272
///
221273
/// Adjusting the head down requires that headroom was configured for the [`crate::Umem`]
274+
///
275+
/// # Examples
276+
///
277+
/// ```
278+
/// let mut umem = xdp::Umem::map(
279+
/// xdp::umem::UmemCfgBuilder {
280+
/// head_room: 20,
281+
/// ..Default::default()
282+
/// }.build().unwrap()
283+
/// ).expect("failed to map Umem");
284+
///
285+
/// unsafe {
286+
/// let mut packet = umem.alloc().expect("failed to allocate packet");
287+
///
288+
/// // We can't extend the head past the tail, so first insert some data
289+
/// packet.insert(0, &[0xff; 33]).unwrap();
290+
/// assert_eq!(33, packet.len());
291+
///
292+
/// // Adjust the head up to match the tail, making the packet empty
293+
/// packet.adjust_head(33).unwrap();
294+
/// assert!(packet.is_empty());
295+
///
296+
/// // When using alloc, the head is already adjust to the headroom, the
297+
/// // same as the kernel would do when receiving a packet, so we can
298+
/// // adjust the head down further
299+
/// packet.adjust_head(-53).unwrap();
300+
/// assert_eq!(53, packet.len());
301+
///
302+
/// // ...but no further
303+
/// assert!(packet.adjust_head(-1).is_err());
304+
/// }
305+
/// ```
222306
#[inline]
223307
pub fn adjust_head(&mut self, diff: i32) -> Result<(), PacketError> {
224308
if diff < 0 {
@@ -247,6 +331,32 @@ impl Packet {
247331
///
248332
/// This method is the equivalent of [`bpf_xdp_adjust_tail`](https://docs.ebpf.io/linux/helper-function/bpf_xdp_adjust_tail/),
249333
/// and allows extending or truncating the data portion of a packet
334+
///
335+
/// # Examples
336+
///
337+
/// ```
338+
/// let mut umem = xdp::Umem::map(
339+
/// xdp::umem::UmemCfgBuilder {
340+
/// head_room: 20,
341+
/// ..Default::default()
342+
/// }.build().unwrap()
343+
/// ).expect("failed to map Umem");
344+
///
345+
/// unsafe {
346+
/// let mut packet = umem.alloc().expect("failed to allocate packet");
347+
/// packet.insert(0, &[0xff; 10]).unwrap();
348+
/// assert_eq!(10, packet.len());
349+
///
350+
/// for _ in 0..10 {
351+
/// packet.adjust_tail(-1).expect("failed to reduce tail");
352+
/// }
353+
///
354+
/// assert!(packet.is_empty());
355+
///
356+
/// packet.adjust_tail(10).expect("failed to extend the tail");
357+
/// assert_eq!(&packet[..10], &[0xff; 10]);
358+
/// }
359+
/// ```
250360
#[inline]
251361
pub fn adjust_tail(&mut self, diff: i32) -> Result<(), PacketError> {
252362
if diff < 0 {
@@ -277,6 +387,40 @@ impl Packet {
277387
///
278388
/// - The offset is not within bounds
279389
/// - The offset + size of `T` is not within bounds
390+
///
391+
/// # Examples
392+
///
393+
/// ```
394+
/// use xdp::packet::net_types;
395+
/// use std::net::Ipv4Addr;
396+
/// # use xdp::packet::Pod;
397+
/// # let mut umem = xdp::Umem::map(
398+
/// # xdp::umem::UmemCfgBuilder {
399+
/// # head_room: 20,
400+
/// # ..Default::default()
401+
/// # }.build().unwrap()
402+
/// # ).expect("failed to map Umem");
403+
/// # let mut packet = unsafe {
404+
/// # let mut packet = umem.alloc().expect("failed to allocate packet");
405+
/// # packet.adjust_tail(34).unwrap();
406+
/// # packet.write(0, net_types::EthHdr {
407+
/// # source: net_types::MacAddress([1; 6]),
408+
/// # destination: net_types::MacAddress([2; 6]),
409+
/// # ether_type: net_types::EtherType::Ipv4 }
410+
/// # ).unwrap();
411+
/// # let mut ip = net_types::Ipv4Hdr::zeroed();
412+
/// # ip.reset(64, net_types::IpProto::Udp);
413+
/// # ip.source = u32::from_be_bytes([100, 1, 2, 100]).into();
414+
/// # ip.destination = u32::from_be_bytes([200, 2, 1, 200]).into();
415+
/// # packet.write(net_types::EthHdr::LEN, ip).unwrap();
416+
/// # assert_eq!(packet.len(), net_types::EthHdr::LEN + net_types::Ipv4Hdr::LEN);
417+
/// # packet
418+
/// # };
419+
/// // Read an Ipv4 header, which directly follows an Ethernet II header
420+
/// let ip_hdr = packet.read::<net_types::Ipv4Hdr>(net_types::EthHdr::LEN).unwrap();
421+
/// assert_eq!(ip_hdr.source.host(), Ipv4Addr::new(100, 1, 2, 100).to_bits());
422+
/// assert_eq!(ip_hdr.destination.host(), Ipv4Addr::new(200, 2, 1, 200).to_bits());
423+
/// ```
280424
#[inline]
281425
pub fn read<T: Pod>(&self, offset: usize) -> Result<T, PacketError> {
282426
let start = self.head + offset;
@@ -309,6 +453,50 @@ impl Packet {
309453
///
310454
/// - The offset is not within bounds
311455
/// - The offset + size of `T` is not within bounds
456+
///
457+
/// # Examples
458+
///
459+
/// ```
460+
/// use xdp::packet::net_types;
461+
/// use std::net::Ipv4Addr;
462+
/// # use xdp::packet::Pod;
463+
/// # let mut umem = xdp::Umem::map(
464+
/// # xdp::umem::UmemCfgBuilder {
465+
/// # head_room: 0,
466+
/// # ..Default::default()
467+
/// # }.build().unwrap()
468+
/// # ).expect("failed to map Umem");
469+
/// # let mut packet = unsafe {
470+
/// # umem.alloc().expect("failed to allocate packet")
471+
/// # };
472+
/// // Extend the tail so we have enough space for the writes
473+
/// packet.adjust_tail(42).unwrap();
474+
///
475+
/// packet.write(0, net_types::EthHdr {
476+
/// source: net_types::MacAddress([1; 6]),
477+
/// destination: net_types::MacAddress([2; 6]),
478+
/// ether_type: net_types::EtherType::Ipv4 }
479+
/// ).expect("failed to write ethhdr");
480+
///
481+
/// let mut ip = net_types::Ipv4Hdr::zeroed();
482+
/// ip.reset(64, net_types::IpProto::Udp);
483+
/// ip.source = u32::from_be_bytes([100, 1, 2, 100]).into();
484+
/// ip.destination = u32::from_be_bytes([200, 2, 1, 200]).into();
485+
/// ip.total_length = ((net_types::Ipv4Hdr::LEN + net_types::UdpHdr::LEN + 5) as u16).into();
486+
/// packet.write(net_types::EthHdr::LEN, ip).expect("failed to write ip hdr");
487+
///
488+
/// packet.write(net_types::EthHdr::LEN + net_types::Ipv4Hdr::LEN, net_types::UdpHdr {
489+
/// source: 50000.into(),
490+
/// destination: 80.into(),
491+
/// length: ((net_types::UdpHdr::LEN + 5) as u16).into(),
492+
/// check: 0,
493+
/// }).expect("failed to write ip hdr");
494+
///
495+
/// packet.insert(
496+
/// net_types::EthHdr::LEN + net_types::Ipv4Hdr::LEN + net_types::UdpHdr::LEN,
497+
/// &[0xf0; 5]
498+
/// ).unwrap();
499+
/// ```
312500
#[inline]
313501
pub fn write<T: Pod>(&mut self, offset: usize, item: T) -> Result<(), PacketError> {
314502
let start = self.head + offset;
@@ -344,6 +532,28 @@ impl Packet {
344532
///
345533
/// - The offset is not within bounds
346534
/// - The offset + `N` is not within bounds
535+
///
536+
/// # Examples
537+
///
538+
/// ```
539+
/// # use xdp::packet::Pod;
540+
/// # let mut umem = xdp::Umem::map(
541+
/// # xdp::umem::UmemCfgBuilder {
542+
/// # head_room: 0,
543+
/// # ..Default::default()
544+
/// # }.build().unwrap()
545+
/// # ).expect("failed to map Umem");
546+
/// # let mut packet = unsafe {
547+
/// # umem.alloc().expect("failed to allocate packet")
548+
/// # };
549+
/// // Insert a u32
550+
/// packet.insert(0, &0xaabbccddu32.to_ne_bytes()).unwrap();
551+
///
552+
/// let mut bytes = [0u8; 4];
553+
/// packet.array_at_offset(0, &mut bytes).unwrap();
554+
///
555+
/// assert_eq!(u32::from_ne_bytes(bytes), 0xaabbccddu32);
556+
/// ```
347557
#[inline]
348558
pub fn array_at_offset<const N: usize>(
349559
&self,
@@ -377,6 +587,36 @@ impl Packet {
377587
///
378588
/// - The offset is not within bounds
379589
/// - The offset + `slice.len()` would exceed the capacity
590+
///
591+
/// # Examples
592+
///
593+
/// ```
594+
/// # use xdp::packet::Pod;
595+
/// # let mut umem = xdp::Umem::map(
596+
/// # xdp::umem::UmemCfgBuilder {
597+
/// # head_room: 0,
598+
/// # ..Default::default()
599+
/// # }.build().unwrap()
600+
/// # ).expect("failed to map Umem");
601+
/// # let mut packet = unsafe {
602+
/// # umem.alloc().expect("failed to allocate packet")
603+
/// # };
604+
/// // Insert a u32
605+
/// packet.insert(0, &0xf0f1f2f3u32.to_ne_bytes()).unwrap();
606+
///
607+
/// // Insert a u64
608+
/// packet.insert(0, &u64::MAX.to_ne_bytes()).unwrap();
609+
///
610+
/// let mut bytes = [0u8; 8];
611+
/// packet.array_at_offset(0, &mut bytes).unwrap();
612+
///
613+
/// assert_eq!(u64::from_ne_bytes(bytes), u64::MAX);
614+
///
615+
/// let mut bytes = [0u8; 4];
616+
/// packet.array_at_offset(8, &mut bytes).unwrap();
617+
///
618+
/// assert_eq!(u32::from_ne_bytes(bytes), 0xf0f1f2f3);
619+
/// ```
380620
#[inline]
381621
pub fn insert(&mut self, offset: usize, slice: &[u8]) -> Result<(), PacketError> {
382622
if self.tail + slice.len() > self.capacity {
@@ -454,9 +694,12 @@ impl Packet {
454694
unsafe {
455695
let mut tx_meta = std::mem::zeroed::<xdp::xsk_tx_metadata>();
456696

457-
if let CsumOffload::Request(csum_req) = csum {
697+
if let CsumOffload::Request { start, offset } = csum {
458698
tx_meta.flags |= xdp::XdpTxFlags::XDP_TXMD_FLAGS_CHECKSUM;
459-
tx_meta.offload.request = csum_req;
699+
tx_meta.offload.request = xdp::xsk_tx_request {
700+
csum_start: start,
701+
csum_offset: offset,
702+
};
460703
}
461704

462705
if request_timestamp {

0 commit comments

Comments
 (0)