use crate::Timestamp;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct RxMetadata {
pub hw_timestamp: Option<Timestamp>,
pub rx_hash: Option<RxHash>,
pub vlan: Option<VlanTag>,
pub checksum: ChecksumStatus,
pub source_idx: u32,
}
impl RxMetadata {
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct RxHash {
pub value: u32,
pub ty: RssHashType,
}
impl RxHash {
#[inline]
pub fn new(value: u32, ty: RssHashType) -> Self {
Self { value, ty }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum RssHashType {
L2,
L3Ipv4,
L3Ipv6,
L4TcpIpv4,
L4UdpIpv4,
L4SctpIpv4,
L4TcpIpv6,
L4UdpIpv6,
L4SctpIpv6,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct VlanTag {
pub tci: u16,
pub proto: VlanProto,
}
impl VlanTag {
#[inline]
pub fn new(tci: u16, proto: VlanProto) -> Self {
Self { tci, proto }
}
}
impl VlanTag {
#[inline]
pub fn vid(&self) -> u16 {
self.tci & 0x0FFF
}
#[inline]
pub fn pcp(&self) -> u8 {
((self.tci >> 13) & 0x07) as u8
}
#[inline]
pub fn dei(&self) -> bool {
(self.tci >> 12) & 0x1 != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum VlanProto {
Dot1Q,
Dot1Ad,
Other(u16),
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum ChecksumStatus {
#[default]
Unknown,
Unnecessary,
Complete(u16),
Bad,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_empty() {
let m = RxMetadata::default();
assert!(m.is_empty());
assert!(m.hw_timestamp.is_none());
assert!(m.rx_hash.is_none());
assert!(m.vlan.is_none());
assert!(matches!(m.checksum, ChecksumStatus::Unknown));
assert_eq!(m.source_idx, 0);
}
#[test]
fn populated_is_not_empty() {
let m = RxMetadata {
source_idx: 1,
..RxMetadata::default()
};
assert!(!m.is_empty());
}
#[test]
fn is_empty_flips_for_every_field_independently() {
let with_ts = RxMetadata {
hw_timestamp: Some(crate::Timestamp::default()),
..RxMetadata::default()
};
assert!(!with_ts.is_empty());
let with_hash = RxMetadata {
rx_hash: Some(RxHash::new(1, RssHashType::Unknown)),
..RxMetadata::default()
};
assert!(!with_hash.is_empty());
let with_vlan = RxMetadata {
vlan: Some(VlanTag::new(100, VlanProto::Dot1Q)),
..RxMetadata::default()
};
assert!(!with_vlan.is_empty());
let with_chk = RxMetadata {
checksum: ChecksumStatus::Unnecessary,
..RxMetadata::default()
};
assert!(!with_chk.is_empty());
let with_src = RxMetadata {
source_idx: 42,
..RxMetadata::default()
};
assert!(!with_src.is_empty());
}
#[test]
fn rx_hash_carries_type() {
let m = RxMetadata {
rx_hash: Some(RxHash {
value: 0xdeadbeef,
ty: RssHashType::L4TcpIpv4,
}),
..RxMetadata::default()
};
let h = m.rx_hash.unwrap();
assert_eq!(h.value, 0xdeadbeef);
assert!(matches!(h.ty, RssHashType::L4TcpIpv4));
}
#[test]
fn vlan_tag_decomposes_tci() {
let tag = VlanTag {
tci: 0xB064,
proto: VlanProto::Dot1Q,
};
assert_eq!(tag.vid(), 100);
assert_eq!(tag.pcp(), 5);
assert!(tag.dei());
}
#[test]
fn vlan_tag_zero_dei() {
let tag = VlanTag {
tci: 0x0064, proto: VlanProto::Dot1Q,
};
assert_eq!(tag.vid(), 100);
assert_eq!(tag.pcp(), 0);
assert!(!tag.dei());
}
#[test]
fn checksum_default_is_unknown() {
let c = ChecksumStatus::default();
assert!(matches!(c, ChecksumStatus::Unknown));
}
#[test]
fn checksum_complete_carries_value() {
let c = ChecksumStatus::Complete(0xABCD);
match c {
ChecksumStatus::Complete(v) => assert_eq!(v, 0xABCD),
_ => panic!("expected Complete"),
}
}
#[test]
fn rx_metadata_is_send_sync_copy() {
fn assert_traits<T: Send + Sync + Copy + 'static>() {}
assert_traits::<RxMetadata>();
}
}