use crate::{Timestamp, rx_metadata::RxMetadata};
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct PacketView<'a> {
pub frame: &'a [u8],
pub timestamp: Timestamp,
pub rx_metadata: RxMetadata,
pub snap_cut: u32,
wire_known: bool,
}
impl<'a> PacketView<'a> {
#[inline]
pub fn new(frame: &'a [u8], timestamp: Timestamp) -> Self {
Self {
frame,
timestamp,
rx_metadata: RxMetadata::default(),
snap_cut: 0,
wire_known: false,
}
}
#[inline]
pub fn with_wire_len(mut self, wire_len: usize) -> Self {
self.snap_cut = snap_cut_of(self.frame.len(), wire_len);
self.wire_known = true;
self
}
#[inline]
pub fn with_snap_cut(mut self, snap_cut: u32) -> Self {
self.snap_cut = snap_cut;
self.wire_known = true;
self
}
#[inline]
pub fn wire_len_known(&self) -> bool {
self.wire_known
}
#[inline]
pub fn wire_len(&self) -> usize {
self.frame.len().saturating_add(self.snap_cut as usize)
}
#[inline]
pub fn is_truncated(&self) -> bool {
self.snap_cut > 0
}
#[inline]
pub fn with_rx_metadata(mut self, rx_metadata: RxMetadata) -> Self {
self.rx_metadata = rx_metadata;
self
}
#[inline]
pub fn with_source_idx(mut self, source_idx: u32) -> Self {
self.rx_metadata.source_idx = source_idx;
self
}
#[cfg(feature = "extractors")]
pub fn layers(&self) -> crate::Result<crate::layers::Layers<'a>> {
crate::layers::Layers::parse_ethernet(self.frame)
}
#[inline]
pub fn with_frame<'b>(self, frame: &'b [u8]) -> PacketView<'b>
where
'a: 'b,
{
PacketView {
frame,
timestamp: self.timestamp,
rx_metadata: self.rx_metadata,
snap_cut: self.snap_cut,
wire_known: self.wire_known,
}
}
}
#[inline]
fn snap_cut_of(captured: usize, wire_len: usize) -> u32 {
u32::try_from(wire_len.saturating_sub(captured)).unwrap_or(u32::MAX)
}
pub trait AsPacketView {
fn as_packet_view(&self) -> PacketView<'_>;
}
impl<'a, T: AsPacketView + ?Sized> From<&'a T> for PacketView<'a> {
fn from(t: &'a T) -> Self {
t.as_packet_view()
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct OwnedPacketView {
pub frame: Vec<u8>,
pub timestamp: Timestamp,
pub rx_metadata: RxMetadata,
pub snap_cut: u32,
wire_known: bool,
}
impl OwnedPacketView {
#[inline]
pub fn new(frame: Vec<u8>, timestamp: Timestamp) -> Self {
Self {
frame,
timestamp,
rx_metadata: RxMetadata::default(),
snap_cut: 0,
wire_known: false,
}
}
#[inline]
pub fn with_wire_len(mut self, wire_len: usize) -> Self {
self.snap_cut = snap_cut_of(self.frame.len(), wire_len);
self.wire_known = true;
self
}
#[inline]
pub fn with_snap_cut(mut self, snap_cut: u32) -> Self {
self.snap_cut = snap_cut;
self.wire_known = true;
self
}
#[inline]
pub fn wire_len_known(&self) -> bool {
self.wire_known
}
#[inline]
pub fn wire_len(&self) -> usize {
self.frame.len().saturating_add(self.snap_cut as usize)
}
#[inline]
pub fn is_truncated(&self) -> bool {
self.snap_cut > 0
}
#[inline]
pub fn with_rx_metadata(mut self, rx_metadata: RxMetadata) -> Self {
self.rx_metadata = rx_metadata;
self
}
#[inline]
pub fn with_source_idx(mut self, source_idx: u32) -> Self {
self.rx_metadata.source_idx = source_idx;
self
}
pub fn as_view(&self) -> PacketView<'_> {
let mut view =
PacketView::new(&self.frame, self.timestamp).with_rx_metadata(self.rx_metadata);
view.snap_cut = self.snap_cut;
view.wire_known = self.wire_known;
view
}
}
impl AsPacketView for OwnedPacketView {
fn as_packet_view(&self) -> PacketView<'_> {
self.as_view()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_and_fields() {
let buf = [0u8; 8];
let v = PacketView::new(&buf, Timestamp::new(1, 2));
assert_eq!(v.frame.len(), 8);
assert_eq!(v.timestamp, Timestamp::new(1, 2));
}
#[test]
fn with_source_idx_sets_only_source_idx() {
let buf = [0u8; 8];
let v = PacketView::new(&buf, Timestamp::new(1, 2)).with_source_idx(5);
assert_eq!(v.rx_metadata.source_idx, 5);
assert!(v.rx_metadata.hw_timestamp.is_none());
assert!(v.rx_metadata.rx_hash.is_none());
let ov = OwnedPacketView::new(vec![0u8; 8], Timestamp::new(1, 2)).with_source_idx(9);
assert_eq!(ov.rx_metadata.source_idx, 9);
assert_eq!(ov.as_view().rx_metadata.source_idx, 9);
}
#[test]
fn with_frame_replaces_slice_keeps_ts() {
let outer = [1u8, 2, 3, 4];
let inner = [9u8, 9];
let v = PacketView::new(&outer, Timestamp::new(7, 0));
let w = v.with_frame(&inner);
assert_eq!(w.frame, &inner);
assert_eq!(w.timestamp, Timestamp::new(7, 0));
}
#[test]
fn wire_len_and_snap_cut() {
let buf = [0u8; 96];
let v = PacketView::new(&buf, Timestamp::new(0, 0));
assert_eq!((v.snap_cut, v.wire_len(), v.is_truncated()), (0, 96, false));
let v = v.with_wire_len(1514);
assert_eq!(
(v.snap_cut, v.wire_len(), v.is_truncated()),
(1418, 1514, true)
);
assert_eq!(v.with_wire_len(10).snap_cut, 0);
assert_eq!(v.with_wire_len(usize::MAX).snap_cut, u32::MAX);
let inner = v.with_frame(&buf[14..]);
assert_eq!(inner.snap_cut, 1418);
assert_eq!(inner.wire_len(), 1500);
assert!(!PacketView::new(&buf, Timestamp::new(0, 0)).wire_len_known());
assert!(v.wire_len_known() && inner.wire_len_known());
let ov = OwnedPacketView::new(vec![0u8; 96], Timestamp::new(0, 0)).with_wire_len(100);
assert!(ov.as_view().wire_len_known());
assert!(
!OwnedPacketView::new(vec![0u8; 4], Timestamp::new(0, 0))
.as_view()
.wire_len_known()
);
assert_eq!(
(ov.snap_cut, ov.wire_len(), ov.is_truncated()),
(4, 100, true)
);
assert_eq!(ov.as_view().snap_cut, 4);
assert_eq!(ov.clone().with_snap_cut(0).as_view().wire_len(), 96);
}
#[test]
fn as_packet_view_blanket_works() {
struct MyPacket {
bytes: Vec<u8>,
ts: Timestamp,
}
impl AsPacketView for MyPacket {
fn as_packet_view(&self) -> PacketView<'_> {
PacketView::new(&self.bytes, self.ts)
}
}
let pkt = MyPacket {
bytes: vec![1, 2, 3, 4],
ts: Timestamp::new(42, 0),
};
let v = pkt.as_packet_view();
assert_eq!(v.frame, &[1, 2, 3, 4]);
assert_eq!(v.timestamp, Timestamp::new(42, 0));
let v: PacketView<'_> = (&pkt).into();
assert_eq!(v.frame, &[1, 2, 3, 4]);
let boxed: Box<dyn AsPacketView> = Box::new(MyPacket {
bytes: vec![9],
ts: Timestamp::new(1, 0),
});
let v = boxed.as_packet_view();
assert_eq!(v.frame, &[9]);
}
}