use std::fmt::{self, Formatter};
use zerocopy::byteorder::{BigEndian, U32};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use crate::packet::protocol::EtherProto;
use crate::packet::{HeaderParser, PacketHeader};
pub const VXLAN_PORT: u16 = 4789;
pub const VXLAN_MAX_VNI: u32 = 0xFFFFFF;
#[repr(C, packed)]
#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
pub struct VxlanHeader {
flags_reserved: U32<BigEndian>,
vni_reserved: U32<BigEndian>,
}
impl VxlanHeader {
const VNI_MASK: u32 = 0xFFFFFF00;
const VNI_SHIFT: u32 = 8;
const FLAGS_MASK: u32 = 0xFF000000;
const FLAG_I_MASK: u32 = 0x08000000;
const RESERVED1_MASK: u32 = 0x00FFFFFF;
const RESERVED2_MASK: u32 = 0x000000FF;
#[allow(unused)]
const NAME: &'static str = "VxlanHeader";
#[inline]
pub fn flags(&self) -> u8 {
((self.flags_reserved.get() & Self::FLAGS_MASK) >> 24) as u8
}
#[inline]
pub fn is_vni_valid(&self) -> bool {
self.flags_reserved.get() & Self::FLAG_I_MASK != 0
}
#[inline]
pub fn vni(&self) -> u32 {
(self.vni_reserved.get() & Self::VNI_MASK) >> Self::VNI_SHIFT
}
#[inline]
pub fn vni_raw(&self) -> u32 {
self.vni_reserved.get()
}
#[inline]
pub fn reserved1(&self) -> u32 {
self.flags_reserved.get() & Self::RESERVED1_MASK
}
#[inline]
pub fn reserved2(&self) -> u8 {
(self.vni_reserved.get() & Self::RESERVED2_MASK) as u8
}
#[inline]
fn is_valid(&self) -> bool {
self.is_vni_valid()
}
#[inline]
pub fn is_valid_strict(&self) -> bool {
self.is_vni_valid() && self.reserved1() == 0 && self.reserved2() == 0
}
}
impl PacketHeader for VxlanHeader {
const NAME: &'static str = "VxlanHeader";
type InnerType = EtherProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
EtherProto::TEB
}
#[inline]
fn total_len(&self, _buf: &[u8]) -> usize {
Self::FIXED_LEN
}
#[inline]
fn is_valid(&self) -> bool {
self.is_valid()
}
}
impl HeaderParser for VxlanHeader {
type Output<'a> = &'a VxlanHeader;
#[inline]
fn into_view<'a>(header: &'a Self, _raw_options: &'a [u8]) -> Self::Output<'a> {
header
}
}
impl fmt::Display for VxlanHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"VXLAN vni={} flags=0x{:02x}{}",
self.vni(),
self.flags(),
if self.is_vni_valid() { " [I]" } else { "" }
)
}
}
#[inline]
pub fn is_vxlan_port(dst_port: u16) -> bool {
dst_port == VXLAN_PORT
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vxlan_header_size() {
assert_eq!(std::mem::size_of::<VxlanHeader>(), 8);
assert_eq!(VxlanHeader::FIXED_LEN, 8);
}
#[test]
fn test_vxlan_basic_header() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000), vni_reserved: U32::new(0x00006400), };
assert!(header.is_vni_valid());
assert_eq!(header.vni(), 100);
assert_eq!(header.flags(), 0x08);
assert!(header.is_valid());
assert!(header.is_valid_strict());
}
#[test]
fn test_vxlan_vni_values() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0x12345600),
};
assert_eq!(header.vni(), 0x123456);
assert!(header.is_valid());
}
#[test]
fn test_vxlan_max_vni() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0xFFFFFF00),
};
assert_eq!(header.vni(), VXLAN_MAX_VNI);
assert!(header.is_valid());
}
#[test]
fn test_vxlan_zero_vni() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0x00000000),
};
assert_eq!(header.vni(), 0);
assert!(header.is_valid());
}
#[test]
fn test_vxlan_invalid_no_i_flag() {
let header = VxlanHeader {
flags_reserved: U32::new(0x00000000), vni_reserved: U32::new(0x00006400),
};
assert!(!header.is_vni_valid());
assert!(!header.is_valid());
}
#[test]
fn test_vxlan_reserved_bits() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08123456), vni_reserved: U32::new(0x00006401), };
assert!(header.is_vni_valid());
assert!(header.is_valid()); assert!(!header.is_valid_strict()); assert_eq!(header.reserved1(), 0x123456);
assert_eq!(header.reserved2(), 0x01);
}
#[test]
fn test_vxlan_parsing_basic() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x08000000u32.to_be_bytes()); packet.extend_from_slice(&0x00006400u32.to_be_bytes());
packet.extend_from_slice(b"ethernet");
let result = VxlanHeader::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert!(header.is_vni_valid());
assert_eq!(header.vni(), 100);
assert_eq!(payload, b"ethernet");
}
#[test]
fn test_vxlan_parsing_with_vni() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x08000000u32.to_be_bytes()); packet.extend_from_slice(&0xABCDEF00u32.to_be_bytes());
let result = VxlanHeader::from_bytes(&packet);
assert!(result.is_ok());
let (header, _) = result.unwrap();
assert_eq!(header.vni(), 0xABCDEF);
}
#[test]
fn test_vxlan_parsing_too_small() {
let packet = vec![0u8; 7];
let result = VxlanHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_vxlan_parsing_invalid_no_i_flag() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x00000000u32.to_be_bytes()); packet.extend_from_slice(&0x00006400u32.to_be_bytes());
let result = VxlanHeader::from_bytes(&packet);
assert!(result.is_err()); }
#[test]
fn test_vxlan_inner_type() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0x00006400),
};
assert_eq!(header.inner_type(), EtherProto::TEB);
}
#[test]
fn test_vxlan_display() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0x00006400),
};
let display = format!("{}", header);
assert!(display.contains("VXLAN"));
assert!(display.contains("vni=100"));
assert!(display.contains("[I]"));
}
#[test]
fn test_vxlan_display_no_i_flag() {
let header = VxlanHeader {
flags_reserved: U32::new(0x00000000), vni_reserved: U32::new(0x00006400),
};
let display = format!("{}", header);
assert!(display.contains("VXLAN"));
assert!(!display.contains("[I]"));
}
#[test]
fn test_vxlan_port_check() {
assert!(is_vxlan_port(4789));
assert!(!is_vxlan_port(4788));
assert!(!is_vxlan_port(80));
}
#[test]
fn test_vxlan_flags_byte() {
let header1 = VxlanHeader {
flags_reserved: U32::new(0x08000000), vni_reserved: U32::new(0x00000000),
};
assert_eq!(header1.flags(), 0x08);
let header2 = VxlanHeader {
flags_reserved: U32::new(0xFF000000), vni_reserved: U32::new(0x00000000),
};
assert_eq!(header2.flags(), 0xFF);
assert!(header2.is_vni_valid()); }
#[test]
fn test_vxlan_multicast_vni() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0x000FA000), };
assert_eq!(header.vni(), 4000);
assert!(header.is_valid());
}
#[test]
fn test_vxlan_real_world_scenario() {
let mut packet = Vec::new();
packet.extend_from_slice(&[0x08, 0x00, 0x00, 0x00]); packet.extend_from_slice(&[0x00, 0x13, 0x88, 0x00]);
packet.extend_from_slice(&[
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, ]);
let (header, payload) = VxlanHeader::from_bytes(&packet).unwrap();
assert_eq!(header.vni(), 5000);
assert!(header.is_vni_valid());
assert_eq!(payload.len(), 14); }
#[test]
fn test_vxlan_header_length() {
let header = VxlanHeader {
flags_reserved: U32::new(0x08000000),
vni_reserved: U32::new(0x00006400),
};
assert_eq!(header.total_len(&[]), 8);
}
}