Skip to main content

iris_core/memory/
mbuf.rs

1//! Packet buffer manipulation.
2//!
3//! ## Remarks
4//! Iris does not support multi-segment Mbufs, but does support setting the maximum Mbuf size in
5//! the runtime configuration (see [configuration parameters](crate::config)). However, all Mbufs
6//! will be allocated with the specified size, so allowing jumbo frames will limit the maximum
7//! number of Mbufs available in the memory pool.
8//!
9//! This module is adapted from
10//! [capsule::Mbuf](https://docs.rs/capsule/0.1.5/capsule/struct.Mbuf.html).
11
12use crate::dpdk;
13use crate::memory::mempool::MempoolError;
14use crate::protocols::packet::{Packet, PacketHeader, PacketParseError};
15
16use std::fmt;
17use std::ptr::NonNull;
18use std::slice;
19
20use anyhow::{bail, Result};
21use thiserror::Error;
22
23#[derive(Clone)]
24/// A packet buffer.
25///
26/// This is a wrapper around a DPDK message buffer that represents a single Ethernet frame.
27pub struct Mbuf {
28    raw: NonNull<dpdk::rte_mbuf>,
29}
30
31impl Mbuf {
32    /// Creates a new Mbuf from rte_mbuf raw pointer. `mbuf` must be non-null.
33    pub(crate) fn new_unchecked(mbuf: *mut dpdk::rte_mbuf) -> Mbuf {
34        unsafe {
35            Mbuf {
36                raw: NonNull::new_unchecked(mbuf),
37            }
38        }
39    }
40
41    /// Creates a new Mbuf from rte_mbuf raw pointer.
42    pub(crate) fn new(mbuf: *mut dpdk::rte_mbuf) -> Result<Mbuf> {
43        Ok(Mbuf {
44            raw: NonNull::new(mbuf).ok_or(MempoolError::Exhausted)?,
45        })
46    }
47
48    pub fn new_ref(mbuf: &Mbuf) -> Mbuf {
49        unsafe {
50            dpdk::rte_mbuf_refcnt_update(mbuf.raw.as_ptr(), 1);
51        }
52        Mbuf::new_unchecked(mbuf.raw.as_ptr())
53    }
54
55    /// Creates a new Mbuf from a byte slice.
56    pub(crate) fn from_bytes(data: &[u8], mp: *mut dpdk::rte_mempool) -> Result<Mbuf> {
57        let mut mbuf = unsafe { Mbuf::new(dpdk::rte_pktmbuf_alloc(mp))? };
58        if data.len() <= mbuf.raw().get_buf_len() {
59            let current_data_len = mbuf.raw().get_data_len();
60            mbuf.raw_mut()
61                .set_data_len(current_data_len + data.len() as u16);
62
63            let current_pkt_len = mbuf.raw().get_pkt_len();
64            mbuf.raw_mut()
65                .set_pkt_len(current_pkt_len + data.len() as u32);
66
67            unsafe {
68                let src = data.as_ptr();
69                let dst = mbuf.get_data_address(0) as *mut u8;
70                std::ptr::copy_nonoverlapping(src, dst, data.len());
71            }
72        } else {
73            bail!(MbufError::WritePastBuffer);
74        }
75        Ok(mbuf)
76    }
77
78    /// Returns a reference to the inner rte_mbuf for use with DPDK functions.
79    pub(crate) fn raw(&self) -> &dpdk::rte_mbuf {
80        unsafe { self.raw.as_ref() }
81    }
82
83    /// Returns a mutable reference to the inner rte_mbuf.
84    fn raw_mut(&mut self) -> &mut dpdk::rte_mbuf {
85        unsafe { self.raw.as_mut() }
86    }
87
88    /// Returns the UNIX timestamp of the packet.
89    #[allow(dead_code)]
90    pub(crate) fn timestamp(&self) -> usize {
91        unimplemented!();
92    }
93
94    /// Returns the length of the data in the Mbuf.
95    pub fn data_len(&self) -> usize {
96        self.raw().get_data_len() as usize
97    }
98
99    /// Returns the contents of the Mbuf as a byte slice.
100    pub fn data(&self) -> &[u8] {
101        let ptr = self.get_data_address(0);
102        unsafe { slice::from_raw_parts(ptr, self.data_len()) as &[u8] }
103    }
104
105    /// Returns a byte slice of data with length count at offset.
106    ///
107    /// Errors if `offset` is greater than or equal to the buffer length or `count` exceeds the size
108    /// of the data stored at `offset`.
109    pub fn get_data_slice(&self, offset: usize, count: usize) -> Result<&[u8]> {
110        if offset < self.data_len() {
111            if offset + count <= self.data_len() {
112                let ptr = self.get_data_address(offset);
113                unsafe { Ok(slice::from_raw_parts(ptr, count) as &[u8]) }
114            } else {
115                bail!(MbufError::ReadPastBuffer)
116            }
117        } else {
118            bail!(MbufError::BadOffset)
119        }
120    }
121
122    /// Reads the data at `offset` as `T` and returns it as a raw pointer. Errors if `offset` is
123    /// greater than or equal to the buffer length or the size of `T` exceeds the size of the data
124    /// stored at `offset`.
125    pub(crate) fn get_data<T: PacketHeader>(&self, offset: usize) -> Result<*const T> {
126        if offset < self.data_len() {
127            if offset + T::size_of() <= self.data_len() {
128                Ok(self.get_data_address(offset) as *const T)
129            } else {
130                bail!(MbufError::ReadPastBuffer)
131            }
132        } else {
133            bail!(MbufError::BadOffset)
134        }
135    }
136
137    /// Returns the raw pointer from the offset.
138    fn get_data_address(&self, offset: usize) -> *const u8 {
139        let raw = self.raw();
140        unsafe { (raw.buf_addr as *const u8).offset(raw.get_data_off() as isize + offset as isize) }
141    }
142
143    /// Returns the RSS hash of the Mbuf computed by the NIC.
144    #[allow(dead_code)]
145    pub(crate) fn rss_hash(&self) -> u32 {
146        self.raw().get_rss_hash()
147    }
148
149    /// Returns any MARKs tagged on the Mbuf by the NIC.
150    #[allow(dead_code)]
151    pub(crate) fn mark(&self) -> u32 {
152        self.raw().get_mark()
153    }
154
155    #[allow(dead_code)]
156    pub(crate) fn add_mark(&mut self, mark: u32) {
157        self.raw_mut().set_mark(mark);
158    }
159
160    // NICE-TO-HAVE
161    #[allow(dead_code)]
162    pub fn has_mark(&mut self, _mark: u32) -> bool {
163        // unsafe { self.raw().ol_flags & PKT_RX_FDIR != 0 }
164        true
165    }
166}
167
168impl<'a> Packet<'a> for Mbuf {
169    fn mbuf(&self) -> &Mbuf {
170        self
171    }
172
173    fn header_len(&self) -> usize {
174        0
175    }
176
177    fn next_header_offset(&self) -> usize {
178        0
179    }
180
181    fn next_header(&self) -> Option<usize> {
182        None
183    }
184
185    fn parse_from(_outer: &'a impl Packet<'a>) -> Result<Self>
186    where
187        Self: Sized,
188    {
189        // parse_from should never be called for Mbuf.
190        bail!(PacketParseError::InvalidProtocol)
191    }
192}
193
194impl Drop for Mbuf {
195    fn drop(&mut self) {
196        // log::debug!("Dropping a Mbuf, freeing mbuf@{:p}", self.raw().buf_addr);
197
198        // Reference counting allows Mbufs to be shared across data structures
199        // (e.g., store in pre-reassembly and post-reassembly order) and cores.
200        // Using DPDK built-ins to manage reference counting is more efficient
201        // than Rust standard library (Rc or Arc). Note `rte_pktmbuf_free` updates
202        // refcount internally and only releases the mbuf if refcount becomes 0.
203        unsafe { dpdk::rte_pktmbuf_free(self.raw()) };
204    }
205}
206
207impl fmt::Debug for Mbuf {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        let raw = self.raw();
210        f.debug_struct("Mbuf")
211            .field("buf_addr", &raw.buf_addr)
212            .field("buf_len", &raw.get_buf_len())
213            .field("pkt_len", &raw.get_pkt_len())
214            .field("data_len", &raw.get_data_len())
215            .field("data_off", &raw.get_data_off())
216            .finish()
217    }
218}
219
220// displays the actual packet data of the frame (first segment only)
221impl fmt::Display for Mbuf {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        for byte in 0..self.raw().get_data_len() {
224            write!(
225                f,
226                "{:02x} ",
227                self.get_data_slice(byte as usize, 1).unwrap()[0]
228            )?;
229            if byte % 16 == 15 {
230                writeln!(f,)?;
231            }
232        }
233        Ok(())
234    }
235}
236
237#[derive(Error, Debug)]
238pub(crate) enum MbufError {
239    #[error("Offset exceeds Mbuf segment buffer length")]
240    BadOffset,
241
242    #[error("Data read exceeds Mbuf segment buffer")]
243    ReadPastBuffer,
244
245    #[error("Data write exceeds Mbuf segment buffer")]
246    WritePastBuffer,
247}