use std::fmt::{self, Formatter};
use zerocopy::byteorder::{BigEndian, U16, U32};
use zerocopy::{FromBytes, IntoBytes, Unaligned};
use crate::packet::protocol::EtherProto;
use crate::packet::{HeaderParser, PacketHeader};
pub const NVGRE_PROTOCOL_TEB: u16 = 0x6558;
pub const NVGRE_MAX_VSID: u32 = 0xFFFFFF;
pub const NVGRE_VSID_RESERVED_MIN: u32 = 0xFFFFF0;
pub const NVGRE_VSID_RESERVED_MAX: u32 = 0xFFFFFF;
#[repr(C, packed)]
#[derive(
FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
)]
pub struct NvgreHeader {
flags_version: U16<BigEndian>,
protocol_type: U16<BigEndian>,
vsid_flowid: U32<BigEndian>,
}
impl NvgreHeader {
pub const FLAG_CHECKSUM: u16 = 0x8000; pub const FLAG_ROUTING: u16 = 0x4000; pub const FLAG_KEY: u16 = 0x2000; pub const FLAG_SEQUENCE: u16 = 0x1000; pub const FLAG_STRICT_ROUTE: u16 = 0x0800;
pub const VERSION_MASK: u16 = 0x0007; pub const RECUR_MASK: u16 = 0x0700; pub const FLAGS_MASK: u16 = 0x00F8;
pub const VERSION_NVGRE: u16 = 0x0000;
const VSID_MASK: u32 = 0xFFFFFF00;
const VSID_SHIFT: u32 = 8;
const FLOWID_MASK: u32 = 0x000000FF;
#[allow(unused)]
const NAME: &'static str = "NvgreHeader";
#[inline]
pub fn flags_version(&self) -> u16 {
self.flags_version.get()
}
#[inline]
pub fn version(&self) -> u8 {
(self.flags_version() & Self::VERSION_MASK) as u8
}
#[inline]
pub fn protocol_type(&self) -> EtherProto {
self.protocol_type.get().into()
}
#[inline]
pub fn protocol_type_raw(&self) -> u16 {
self.protocol_type.get()
}
#[inline]
pub fn vsid(&self) -> u32 {
(self.vsid_flowid.get() & Self::VSID_MASK) >> Self::VSID_SHIFT
}
#[inline]
pub fn flow_id(&self) -> u8 {
(self.vsid_flowid.get() & Self::FLOWID_MASK) as u8
}
#[inline]
pub fn key(&self) -> u32 {
self.vsid_flowid.get()
}
#[inline]
pub fn has_checksum(&self) -> bool {
self.flags_version() & Self::FLAG_CHECKSUM != 0
}
#[inline]
pub fn has_routing(&self) -> bool {
self.flags_version() & Self::FLAG_ROUTING != 0
}
#[inline]
pub fn has_key(&self) -> bool {
self.flags_version() & Self::FLAG_KEY != 0
}
#[inline]
pub fn has_sequence(&self) -> bool {
self.flags_version() & Self::FLAG_SEQUENCE != 0
}
#[inline]
pub fn has_strict_route(&self) -> bool {
self.flags_version() & Self::FLAG_STRICT_ROUTE != 0
}
#[inline]
pub fn recursion_control(&self) -> u8 {
((self.flags_version() & Self::RECUR_MASK) >> 8) as u8
}
#[inline]
pub fn is_vsid_reserved(&self) -> bool {
let vsid = self.vsid();
(NVGRE_VSID_RESERVED_MIN..=NVGRE_VSID_RESERVED_MAX).contains(&vsid)
}
#[inline]
fn is_valid(&self) -> bool {
if self.version() != 0 {
return false;
}
if !self.has_key() {
return false;
}
if self.has_routing() {
return false;
}
if self.has_strict_route() {
return false;
}
if self.recursion_control() != 0 {
return false;
}
if self.protocol_type_raw() != NVGRE_PROTOCOL_TEB {
return false;
}
true
}
#[inline]
pub fn is_valid_relaxed(&self) -> bool {
self.version() == 0 && self.has_key()
}
pub fn flags_string(&self) -> String {
let mut flags = Vec::new();
if self.has_checksum() {
flags.push("C");
}
if self.has_routing() {
flags.push("R");
}
if self.has_key() {
flags.push("K");
}
if self.has_sequence() {
flags.push("S");
}
if self.has_strict_route() {
flags.push("s");
}
if flags.is_empty() {
"none".to_string()
} else {
flags.join("")
}
}
}
impl PacketHeader for NvgreHeader {
const NAME: &'static str = "NvgreHeader";
type InnerType = EtherProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
self.protocol_type()
}
#[inline]
fn total_len(&self, _buf: &[u8]) -> usize {
Self::FIXED_LEN
}
#[inline]
fn is_valid(&self) -> bool {
self.is_valid()
}
}
impl HeaderParser for NvgreHeader {
type Output<'a> = &'a NvgreHeader;
#[inline]
fn into_view<'a>(header: &'a Self, _raw_options: &'a [u8]) -> Self::Output<'a> {
header
}
}
impl fmt::Display for NvgreHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"NVGRE vsid={} flow_id={} proto={}(0x{:04x}) flags={}",
self.vsid(),
self.flow_id(),
self.protocol_type(),
self.protocol_type().0,
self.flags_string()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nvgre_header_size() {
assert_eq!(std::mem::size_of::<NvgreHeader>(), 8);
assert_eq!(NvgreHeader::FIXED_LEN, 8);
}
#[test]
fn test_nvgre_basic_header() {
let header = NvgreHeader {
flags_version: U16::new(0x2000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000), };
assert_eq!(header.version(), 0);
assert_eq!(header.protocol_type(), EtherProto::TEB);
assert!(header.has_key());
assert!(!header.has_checksum());
assert!(!header.has_sequence());
assert!(header.is_valid());
assert_eq!(header.vsid(), 256);
assert_eq!(header.flow_id(), 0);
}
#[test]
fn test_nvgre_vsid_flowid() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x123456AB),
};
assert_eq!(header.vsid(), 0x123456);
assert_eq!(header.flow_id(), 0xAB);
assert_eq!(header.key(), 0x123456AB);
}
#[test]
fn test_nvgre_max_vsid() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0xFFFFFFFF),
};
assert_eq!(header.vsid(), 0xFFFFFF);
assert_eq!(header.flow_id(), 0xFF);
assert!(header.is_vsid_reserved());
}
#[test]
fn test_nvgre_reserved_vsid_range() {
let header1 = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0xFFFFF000),
};
assert!(header1.is_vsid_reserved());
let header2 = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0xFFFFEF00),
};
assert!(!header2.is_vsid_reserved());
}
#[test]
fn test_nvgre_version_validation() {
let header = NvgreHeader {
flags_version: U16::new(0x2001), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert!(!header.is_valid());
assert!(!header.is_valid_relaxed());
}
#[test]
fn test_nvgre_key_flag_required() {
let header = NvgreHeader {
flags_version: U16::new(0x0000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert!(!header.is_valid());
assert!(!header.is_valid_relaxed());
}
#[test]
fn test_nvgre_routing_forbidden() {
let header = NvgreHeader {
flags_version: U16::new(0x6000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert!(!header.is_valid());
assert!(header.is_valid_relaxed());
}
#[test]
fn test_nvgre_wrong_protocol_type() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(0x0800), vsid_flowid: U32::new(0x00010000),
};
assert!(!header.is_valid());
assert!(header.is_valid_relaxed());
}
#[test]
fn test_nvgre_with_checksum_flag() {
let header = NvgreHeader {
flags_version: U16::new(0xA000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert!(header.has_checksum());
assert!(header.is_valid());
}
#[test]
fn test_nvgre_with_sequence_flag() {
let header = NvgreHeader {
flags_version: U16::new(0x3000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert!(header.has_sequence());
assert!(header.is_valid());
}
#[test]
fn test_nvgre_parsing_basic() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x2000u16.to_be_bytes()); packet.extend_from_slice(&NVGRE_PROTOCOL_TEB.to_be_bytes()); packet.extend_from_slice(&0x00010001u32.to_be_bytes());
packet.extend_from_slice(b"payload");
let result = NvgreHeader::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert_eq!(header.version(), 0);
assert_eq!(header.protocol_type(), EtherProto::TEB);
assert_eq!(header.vsid(), 256);
assert_eq!(header.flow_id(), 1);
assert_eq!(payload, b"payload");
}
#[test]
fn test_nvgre_parsing_with_vsid() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x2000u16.to_be_bytes()); packet.extend_from_slice(&NVGRE_PROTOCOL_TEB.to_be_bytes()); packet.extend_from_slice(&0x12345600u32.to_be_bytes());
let result = NvgreHeader::from_bytes(&packet);
assert!(result.is_ok());
let (header, _) = result.unwrap();
assert_eq!(header.vsid(), 0x123456);
assert_eq!(header.flow_id(), 0);
}
#[test]
fn test_nvgre_parsing_too_small() {
let packet = vec![0u8; 7];
let result = NvgreHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_nvgre_parsing_invalid_version() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x2001u16.to_be_bytes()); packet.extend_from_slice(&NVGRE_PROTOCOL_TEB.to_be_bytes());
packet.extend_from_slice(&0x00010000u32.to_be_bytes());
let result = NvgreHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_nvgre_parsing_missing_key() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0000u16.to_be_bytes()); packet.extend_from_slice(&NVGRE_PROTOCOL_TEB.to_be_bytes());
packet.extend_from_slice(&0x00010000u32.to_be_bytes());
let result = NvgreHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_nvgre_flags_string() {
let header1 = NvgreHeader {
flags_version: U16::new(0x2000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert_eq!(header1.flags_string(), "K");
let header2 = NvgreHeader {
flags_version: U16::new(0xA000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert_eq!(header2.flags_string(), "CK");
let header3 = NvgreHeader {
flags_version: U16::new(0x3000), protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert_eq!(header3.flags_string(), "KS");
}
#[test]
fn test_nvgre_display() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010001),
};
let display = format!("{}", header);
assert!(display.contains("NVGRE"));
assert!(display.contains("vsid=256"));
assert!(display.contains("flow_id=1"));
}
#[test]
fn test_nvgre_zero_vsid() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00000000), };
assert_eq!(header.vsid(), 0);
assert_eq!(header.flow_id(), 0);
assert!(header.is_valid());
assert!(!header.is_vsid_reserved());
}
#[test]
fn test_nvgre_inner_type() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00010000),
};
assert_eq!(header.inner_type(), EtherProto::TEB);
}
#[test]
fn test_nvgre_multicast_vsid() {
let header = NvgreHeader {
flags_version: U16::new(0x2000),
protocol_type: U16::new(NVGRE_PROTOCOL_TEB),
vsid_flowid: U32::new(0x00ABCD00), };
assert_eq!(header.vsid(), 0x00ABCD);
assert!(!header.is_vsid_reserved());
}
}