Skip to main content

retina_core/memory/
mbuf.rs

1//! Packet buffer manipulation.
2//!
3//! ## Remarks
4//! Retina 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    /// Creates a new Mbuf from a byte slice.
49    pub(crate) fn from_bytes(data: &[u8], mp: *mut dpdk::rte_mempool) -> Result<Mbuf> {
50        let mut mbuf = unsafe { Mbuf::new(dpdk::rte_pktmbuf_alloc(mp))? };
51        if data.len() <= mbuf.raw().buf_len.into() {
52            mbuf.raw_mut().data_len += data.len() as u16;
53            mbuf.raw_mut().pkt_len += data.len() as u32;
54            unsafe {
55                let src = data.as_ptr();
56                let dst = mbuf.get_data_address(0) as *mut u8;
57                std::ptr::copy_nonoverlapping(src, dst, data.len());
58            }
59        } else {
60            bail!(MbufError::WritePastBuffer);
61        }
62        Ok(mbuf)
63    }
64
65    /// Returns a reference to the inner rte_mbuf for use with DPDK functions.
66    pub(crate) fn raw(&self) -> &dpdk::rte_mbuf {
67        unsafe { self.raw.as_ref() }
68    }
69
70    /// Returns a mutable reference to the inner rte_mbuf.
71    fn raw_mut(&mut self) -> &mut dpdk::rte_mbuf {
72        unsafe { self.raw.as_mut() }
73    }
74
75    /// Returns the UNIX timestamp of the packet.
76    #[allow(dead_code)]
77    pub(crate) fn timestamp(&self) -> usize {
78        unimplemented!();
79    }
80
81    /// Returns the length of the data in the Mbuf.
82    pub fn data_len(&self) -> usize {
83        self.raw().data_len as usize
84    }
85
86    /// Returns the contents of the Mbuf as a byte slice.
87    pub fn data(&self) -> &[u8] {
88        let ptr = self.get_data_address(0);
89        unsafe { slice::from_raw_parts(ptr, self.data_len()) as &[u8] }
90    }
91
92    /// Returns a byte slice of data with length count at offset.
93    ///
94    /// Errors if `offset` is greater than or equal to the buffer length or `count` exceeds the size
95    /// of the data stored at `offset`.
96    pub fn get_data_slice(&self, offset: usize, count: usize) -> Result<&[u8]> {
97        if offset < self.data_len() {
98            if offset + count <= self.data_len() {
99                let ptr = self.get_data_address(offset);
100                unsafe { Ok(slice::from_raw_parts(ptr, count) as &[u8]) }
101            } else {
102                bail!(MbufError::ReadPastBuffer)
103            }
104        } else {
105            bail!(MbufError::BadOffset)
106        }
107    }
108
109    /// Reads the data at `offset` as `T` and returns it as a raw pointer. Errors if `offset` is
110    /// greater than or equal to the buffer length or the size of `T` exceeds the size of the data
111    /// stored at `offset`.
112    pub(crate) fn get_data<T: PacketHeader>(&self, offset: usize) -> Result<*const T> {
113        if offset < self.data_len() {
114            if offset + T::size_of() <= self.data_len() {
115                Ok(self.get_data_address(offset) as *const T)
116            } else {
117                bail!(MbufError::ReadPastBuffer)
118            }
119        } else {
120            bail!(MbufError::BadOffset)
121        }
122    }
123
124    /// Returns the raw pointer from the offset.
125    fn get_data_address(&self, offset: usize) -> *const u8 {
126        let raw = self.raw();
127        unsafe { (raw.buf_addr as *const u8).offset(raw.data_off as isize + offset as isize) }
128    }
129
130    /// Returns the RSS hash of the Mbuf computed by the NIC.
131    #[allow(dead_code)]
132    pub(crate) fn rss_hash(&self) -> u32 {
133        unsafe { self.raw().__bindgen_anon_2.hash.rss }
134    }
135
136    /// Returns any MARKs tagged on the Mbuf by the NIC.
137    #[allow(dead_code)]
138    pub(crate) fn mark(&self) -> u32 {
139        unsafe { self.raw().__bindgen_anon_2.hash.fdir.hi }
140    }
141}
142
143impl<'a> Packet<'a> for Mbuf {
144    fn mbuf(&self) -> &Mbuf {
145        self
146    }
147
148    fn header_len(&self) -> usize {
149        0
150    }
151
152    fn next_header_offset(&self) -> usize {
153        0
154    }
155
156    fn next_header(&self) -> Option<usize> {
157        None
158    }
159
160    fn parse_from(_outer: &'a impl Packet<'a>) -> Result<Self>
161    where
162        Self: Sized,
163    {
164        // parse_from should never be called for Mbuf.
165        bail!(PacketParseError::InvalidProtocol)
166    }
167}
168
169impl Drop for Mbuf {
170    fn drop(&mut self) {
171        // log::debug!("Dropping a Mbuf, freeing mbuf@{:p}", self.raw().buf_addr);
172        unsafe { dpdk::rte_pktmbuf_free(self.raw()) };
173    }
174}
175
176impl fmt::Debug for Mbuf {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        let raw = self.raw();
179        f.debug_struct("Mbuf")
180            .field("buf_addr", &raw.buf_addr)
181            .field("buf_len", &raw.buf_len)
182            .field("pkt_len", &raw.pkt_len)
183            .field("data_len", &raw.data_len)
184            .field("data_off", &raw.data_off)
185            .finish()
186    }
187}
188
189// displays the actual packet data of the frame (first segment only)
190impl fmt::Display for Mbuf {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        for byte in 0..self.raw().data_len {
193            write!(
194                f,
195                "{:02x} ",
196                self.get_data_slice(byte as usize, 1).unwrap()[0]
197            )?;
198            if byte % 16 == 15 {
199                writeln!(f,)?;
200            }
201        }
202        Ok(())
203    }
204}
205
206#[derive(Error, Debug)]
207pub(crate) enum MbufError {
208    #[error("Offset exceeds Mbuf segment buffer length")]
209    BadOffset,
210
211    #[error("Data read exceeds Mbuf segment buffer")]
212    ReadPastBuffer,
213
214    #[error("Data write exceeds Mbuf segment buffer")]
215    WritePastBuffer,
216}