use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::collections::BTreeMap;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::packet::ether::EtherHeaderVlan;
use crate::packet::header::{LinkLayer, NetworkLayer, NetworkTunnelLayer, TunnelLayer};
use crate::packet::Packet;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VniError {
InvalidHeaderLength,
InvalidIpVersion,
}
impl fmt::Display for VniError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidHeaderLength => write!(f, "Invalid IP header length"),
Self::InvalidIpVersion => write!(f, "Invalid IP version"),
}
}
}
impl std::error::Error for VniError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum VniLayer {
Vlan { vid: u16 },
Mpls { label: u32 },
Gre {
protocol_type: u16,
key: Option<u32>,
endpoints: [IpAddr; 2],
},
NvGre {
protocol_type: u16,
vsid_flowid: u32,
endpoints: [IpAddr; 2],
},
Vxlan {
vni: u32,
group_id: u16,
endpoints: [IpAddr; 2],
},
Geneve {
vni: u32,
protocol_type: u16,
endpoints: [IpAddr; 2],
},
Ipip { endpoints: [IpAddr; 2] },
Ip4in6 { endpoints: [IpAddr; 2] },
Sit { endpoints: [IpAddr; 2] },
Ip6Tnl { endpoints: [IpAddr; 2] },
GtpU { teid: u32, endpoints: [IpAddr; 2] },
Teredo { endpoints: [IpAddr; 2] },
L2tpV2 {
tunnel_id: u16,
session_id: u16,
endpoints: [IpAddr; 2],
},
L2tpV3 {
session_id: u32,
endpoints: [IpAddr; 2],
},
Pbb { isid: u32, bvid: Option<u16> },
Stt {
context_id: u64,
endpoints: [IpAddr; 2],
},
Pptp {
call_id: u16,
endpoints: [IpAddr; 2],
},
}
impl fmt::Display for VniLayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Vlan { vid } => write!(f, "vlan({})", vid),
Self::Mpls { label } => write!(f, "mpls({})", label),
Self::Gre {
protocol_type,
key,
endpoints,
} => {
write!(f, "gre(ptype:0x{:04x}", protocol_type)?;
if let Some(k) = key {
write!(f, " key:{}", k)?;
}
write!(f, " {}↔{})", endpoints[0], endpoints[1])
}
Self::NvGre {
protocol_type,
vsid_flowid,
endpoints,
} => {
write!(
f,
"nvgre(ptype:0x{:04x} vsid:{} {}↔{})",
protocol_type, vsid_flowid, endpoints[0], endpoints[1]
)
}
Self::Vxlan {
vni,
group_id,
endpoints,
} => {
write!(f, "vxlan(vni:{}", vni)?;
if *group_id != 0 {
write!(f, " gid:{}", group_id)?;
}
write!(f, " {}↔{})", endpoints[0], endpoints[1])
}
Self::Geneve {
vni,
protocol_type,
endpoints,
} => {
write!(
f,
"geneve(vni:{} ptype:0x{:04x} {}↔{})",
vni, protocol_type, endpoints[0], endpoints[1]
)
}
Self::Ipip { endpoints } => {
write!(f, "ipip({}↔{})", endpoints[0], endpoints[1])
}
Self::Ip4in6 { endpoints } => {
write!(f, "ip4in6({}↔{})", endpoints[0], endpoints[1])
}
Self::Sit { endpoints } => {
write!(f, "sit({}↔{})", endpoints[0], endpoints[1])
}
Self::Ip6Tnl { endpoints } => {
write!(f, "ip6tnl({}↔{})", endpoints[0], endpoints[1])
}
Self::GtpU { teid, endpoints } => {
write!(
f,
"gtp-u(teid:0x{:08x} {}↔{})",
teid, endpoints[0], endpoints[1]
)
}
Self::Teredo { endpoints } => {
write!(f, "teredo({}↔{})", endpoints[0], endpoints[1])
}
Self::L2tpV2 {
tunnel_id,
session_id,
endpoints,
} => {
write!(
f,
"l2tpv2(tid:{} sid:{} {}↔{})",
tunnel_id, session_id, endpoints[0], endpoints[1]
)
}
Self::L2tpV3 {
session_id,
endpoints,
} => {
write!(
f,
"l2tpv3(sid:{} {}↔{})",
session_id, endpoints[0], endpoints[1]
)
}
Self::Pbb { isid, bvid } => {
write!(f, "pbb(isid:{}", isid)?;
if let Some(b) = bvid {
write!(f, " bvid:{}", b)?;
}
write!(f, ")")
}
Self::Stt {
context_id,
endpoints,
} => {
write!(
f,
"stt(ctx:0x{:016x} {}↔{})",
context_id, endpoints[0], endpoints[1]
)
}
Self::Pptp { call_id, endpoints } => {
write!(
f,
"pptp(call:{} {}↔{})",
call_id, endpoints[0], endpoints[1]
)
}
}
}
}
impl From<&LinkLayer<'_>> for SmallVec<[VniLayer; 2]> {
fn from(value: &LinkLayer<'_>) -> Self {
match value {
LinkLayer::Ethernet(EtherHeaderVlan::VLAN8021Q(_, eth8021q)) => {
let mut sv = SmallVec::<[VniLayer; 2]>::new();
sv.push(VniLayer::Vlan {
vid: eth8021q.vlan_id(),
});
sv
}
LinkLayer::Ethernet(EtherHeaderVlan::VLAN8021QNested(_, eth8021q, eth8021q_n)) => {
let mut sv = SmallVec::<[VniLayer; 2]>::new();
sv.push(VniLayer::Vlan {
vid: eth8021q.vlan_id(),
});
sv.push(VniLayer::Vlan {
vid: eth8021q_n.vlan_id(),
});
sv
}
_ => SmallVec::<[VniLayer; 2]>::new(),
}
}
}
impl TryFrom<&NetworkTunnelLayer<'_>> for VniLayer {
type Error = ();
fn try_from(ip_tunnel: &NetworkTunnelLayer<'_>) -> Result<Self, Self::Error> {
let endpoints: Option<[IpAddr; 2]> = ip_tunnel.outer().and_then(|outer| {
match outer {
NetworkLayer::Ipv4(ipv4) => Some([ipv4.src_ip().into(), ipv4.dst_ip().into()]),
NetworkLayer::Ipv6(ipv6) => Some([ipv6.src_ip().into(), ipv6.dst_ip().into()]),
NetworkLayer::Mpls(_) => None, }
});
match ip_tunnel.tunnel() {
TunnelLayer::Vxlan(vxlan) => endpoints.ok_or(()).map(|endpoints| VniLayer::Vxlan {
vni: vxlan.vni(),
group_id: 0,
endpoints,
}),
TunnelLayer::Geneve(geneve) => endpoints.ok_or(()).map(|endpoints| VniLayer::Geneve {
vni: geneve.header.vni(),
protocol_type: geneve.header.protocol_type_raw(),
endpoints,
}),
TunnelLayer::Gre(gre) => endpoints.ok_or(()).map(|endpoints| VniLayer::Gre {
protocol_type: gre.header.protocol_type().into(),
key: gre.key(),
endpoints,
}),
TunnelLayer::Teredo(_teredo) => endpoints
.ok_or(())
.map(|endpoints| VniLayer::Teredo { endpoints }),
TunnelLayer::Gtpv1(gtp) => endpoints.ok_or(()).map(|endpoints| VniLayer::GtpU {
teid: gtp.header.teid(),
endpoints,
}),
TunnelLayer::Gtpv2(_gtp) => {
Err(())
}
TunnelLayer::L2tpv2(l2tp) => endpoints.ok_or(()).map(|endpoints| VniLayer::L2tpV2 {
tunnel_id: l2tp.tunnel_id(),
session_id: l2tp.session_id(),
endpoints,
}),
TunnelLayer::L2tpv3(l2tp) => endpoints.ok_or(()).map(|endpoints| VniLayer::L2tpV3 {
session_id: l2tp.session_id(),
endpoints,
}),
TunnelLayer::Nvgre(nvgre) => endpoints.ok_or(()).map(|endpoints| VniLayer::NvGre {
protocol_type: nvgre.protocol_type_raw(),
vsid_flowid: nvgre.vsid() << 8 | nvgre.flow_id() as u32,
endpoints,
}),
TunnelLayer::Pbb(pbb) => {
Ok(VniLayer::Pbb {
isid: pbb.isid(),
bvid: pbb.bvid(),
})
}
TunnelLayer::Stt(stt) => endpoints.ok_or(()).map(|endpoints| VniLayer::Stt {
context_id: stt.context_id(),
endpoints,
}),
TunnelLayer::Pptp(pptp) => endpoints.ok_or(()).map(|endpoints| VniLayer::Pptp {
call_id: pptp.header.call_id(),
endpoints,
}),
TunnelLayer::Ipip(ipip) => {
match ipip.outer_header() {
crate::packet::tunnel::ipip::OuterIpHeader::V4(ipv4) => {
let src = ipv4.header.src_ip_raw();
let dst = ipv4.header.dst_ip_raw();
let mut endpoints = [
IpAddr::V4(Ipv4Addr::from(src)),
IpAddr::V4(Ipv4Addr::from(dst)),
];
endpoints.sort();
match ipip.tunnel_type() {
crate::packet::tunnel::ipip::IpipType::Ipip => {
Ok(VniLayer::Ipip { endpoints })
}
crate::packet::tunnel::ipip::IpipType::Sit => {
Ok(VniLayer::Sit { endpoints })
}
_ => Err(()),
}
}
crate::packet::tunnel::ipip::OuterIpHeader::V6(ipv6) => {
let src = ipv6.header.src_ip_raw();
let dst = ipv6.header.dst_ip_raw();
let mut endpoints = [
IpAddr::V6(Ipv6Addr::from(dst)),
IpAddr::V6(Ipv6Addr::from(src)),
];
endpoints.sort();
match ipip.tunnel_type() {
crate::packet::tunnel::ipip::IpipType::Ip4in6 => {
Ok(VniLayer::Ip4in6 { endpoints })
}
crate::packet::tunnel::ipip::IpipType::Ip6Tnl => {
Ok(VniLayer::Ip6Tnl { endpoints })
}
_ => Err(()),
}
}
}
}
}
}
}
impl TryFrom<&Packet<'_>> for SmallVec<[VniLayer; 4]> {
type Error = ();
fn try_from(packet: &Packet<'_>) -> Result<Self, Self::Error> {
let mut vni_stack: SmallVec<[VniLayer; 4]> = SmallVec::new();
match packet.link() {
LinkLayer::Ethernet(EtherHeaderVlan::VLAN8021Q(_, eth8021q)) => {
vni_stack.push(VniLayer::Vlan {
vid: eth8021q.vlan_id(),
});
}
LinkLayer::Ethernet(EtherHeaderVlan::VLAN8021QNested(_, eth8021q, eth8021q_n)) => {
vni_stack.push(VniLayer::Vlan {
vid: eth8021q.vlan_id(),
});
vni_stack.push(VniLayer::Vlan {
vid: eth8021q_n.vlan_id(),
});
}
_ => {
}
}
for network_tunnel in packet.tunnels() {
match VniLayer::try_from(network_tunnel) {
Ok(vni_layer) => vni_stack.push(vni_layer),
Err(_) => {
continue;
}
}
}
Ok(vni_stack)
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
)]
pub struct VniId(u32);
impl VniId {
#[inline]
pub const fn as_u32(&self) -> u32 {
self.0
}
#[inline]
pub const fn from_u32(value: u32) -> Self {
Self(value)
}
}
impl fmt::Display for VniId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VniId({})", self.0)
}
}
pub struct VniMapper {
forward: BTreeMap<SmallVec<[VniLayer; 4]>, VniId>,
reverse: BTreeMap<VniId, SmallVec<[VniLayer; 4]>>,
counter: u32,
}
impl VniMapper {
pub fn new() -> Self {
Self {
forward: BTreeMap::new(),
reverse: BTreeMap::new(),
counter: 0,
}
}
pub fn get_or_create_vni_id(&mut self, vni_stack: &[VniLayer]) -> VniId {
let stack_vec: SmallVec<[VniLayer; 4]> = vni_stack.iter().cloned().collect();
if let Some(&id) = self.forward.get(&stack_vec) {
return id;
}
self.counter += 1;
let id = VniId(self.counter);
self.forward.insert(stack_vec.clone(), id);
self.reverse.insert(id, stack_vec);
id
}
pub fn lookup_vni(&self, id: VniId) -> Option<&[VniLayer]> {
self.reverse.get(&id).map(|v| v.as_slice())
}
pub fn len(&self) -> usize {
self.reverse.len()
}
pub fn is_empty(&self) -> bool {
self.reverse.is_empty()
}
pub fn clear(&mut self) {
self.forward.clear();
self.reverse.clear();
self.counter = 0;
}
pub fn iter(&self) -> impl Iterator<Item = (VniId, &[VniLayer])> {
self.reverse
.iter()
.map(|(&id, stack)| (id, stack.as_slice()))
}
}
impl Default for VniMapper {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[test]
fn test_vni_error_display() {
let err1 = VniError::InvalidHeaderLength;
assert_eq!(err1.to_string(), "Invalid IP header length");
let err2 = VniError::InvalidIpVersion;
assert_eq!(err2.to_string(), "Invalid IP version");
}
#[test]
fn test_vni_error_clone_eq() {
let err1 = VniError::InvalidHeaderLength;
let err2 = err1.clone();
assert_eq!(err1, err2);
let err3 = VniError::InvalidIpVersion;
assert_ne!(err1, err3);
}
#[test]
fn test_vni_layer_equality() {
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 100 };
let vlan3 = VniLayer::Vlan { vid: 200 };
assert_eq!(vlan1, vlan2);
assert_ne!(vlan1, vlan3);
let mpls1 = VniLayer::Mpls { label: 100 };
assert_ne!(vlan1, mpls1); }
#[test]
fn test_vni_layer_ordering() {
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 200 };
let _mpls = VniLayer::Mpls { label: 100 };
assert!(vlan1 < vlan2);
}
#[test]
fn test_vni_layer_clone_hash() {
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vxlan1 = VniLayer::Vxlan {
vni: 100,
group_id: 0,
endpoints,
};
let vxlan2 = vxlan1.clone();
assert_eq!(vxlan1, vxlan2);
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(vxlan1.clone());
assert!(set.contains(&vxlan2));
}
#[test]
fn test_vni_id_creation() {
let id = VniId::from_u32(42);
assert_eq!(id.as_u32(), 42);
}
#[test]
fn test_vni_id_display() {
let id = VniId::from_u32(100);
assert_eq!(format!("{}", id), "VniId(100)");
}
#[test]
fn test_vni_id_equality() {
let id1 = VniId::from_u32(42);
let id2 = VniId::from_u32(42);
let id3 = VniId::from_u32(43);
assert_eq!(id1, id2);
assert_ne!(id1, id3);
}
#[test]
fn test_vni_mapper_new() {
let mapper = VniMapper::new();
assert_eq!(mapper.len(), 0);
assert!(mapper.is_empty());
}
#[test]
fn test_vni_mapper_default() {
let mapper = VniMapper::default();
assert_eq!(mapper.len(), 0);
assert!(mapper.is_empty());
}
#[test]
fn test_vni_mapper_single_layer() {
let mut mapper = VniMapper::new();
let vlan = VniLayer::Vlan { vid: 100 };
let id = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan));
assert_eq!(mapper.len(), 1);
assert!(!mapper.is_empty());
let id2 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan));
assert_eq!(id, id2);
assert_eq!(mapper.len(), 1);
let stack = mapper.lookup_vni(id).unwrap();
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], vlan);
}
#[test]
fn test_vni_mapper_multi_layer() {
let mut mapper = VniMapper::new();
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 200 };
let id = mapper.get_or_create_vni_id(&[vlan1.clone(), vlan2.clone()]);
assert_eq!(mapper.len(), 1);
let stack = mapper.lookup_vni(id).unwrap();
assert_eq!(stack.len(), 2);
assert_eq!(stack[0], vlan1);
assert_eq!(stack[1], vlan2);
}
#[test]
fn test_vni_mapper_different_stacks() {
let mut mapper = VniMapper::new();
let vlan100 = VniLayer::Vlan { vid: 100 };
let vlan200 = VniLayer::Vlan { vid: 200 };
let mpls = VniLayer::Mpls { label: 1000 };
let id1 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan100));
let id2 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan200));
let id3 = mapper.get_or_create_vni_id(&[vlan100.clone(), mpls.clone()]);
assert_ne!(id1, id2);
assert_ne!(id1, id3);
assert_ne!(id2, id3);
assert_eq!(mapper.len(), 3);
}
#[test]
fn test_vni_mapper_order_matters() {
let mut mapper = VniMapper::new();
let vlan = VniLayer::Vlan { vid: 100 };
let mpls = VniLayer::Mpls { label: 1000 };
let id1 = mapper.get_or_create_vni_id(&[vlan.clone(), mpls.clone()]);
let id2 = mapper.get_or_create_vni_id(&[mpls.clone(), vlan.clone()]);
assert_ne!(id1, id2);
assert_eq!(mapper.len(), 2);
}
#[test]
fn test_vni_mapper_lookup_nonexistent() {
let mapper = VniMapper::new();
let id = VniId::from_u32(999);
assert!(mapper.lookup_vni(id).is_none());
}
#[test]
fn test_vni_mapper_clear() {
let mut mapper = VniMapper::new();
let vlan = VniLayer::Vlan { vid: 100 };
let id1 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan));
assert_eq!(mapper.len(), 1);
mapper.clear();
assert_eq!(mapper.len(), 0);
assert!(mapper.is_empty());
assert!(mapper.lookup_vni(id1).is_none());
let id2 = mapper.get_or_create_vni_id(&[vlan]);
assert_eq!(id2.as_u32(), 1);
}
#[test]
fn test_vni_mapper_iter() {
let mut mapper = VniMapper::new();
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 200 };
let mpls = VniLayer::Mpls { label: 1000 };
mapper.get_or_create_vni_id(std::slice::from_ref(&vlan1));
mapper.get_or_create_vni_id(std::slice::from_ref(&vlan2));
mapper.get_or_create_vni_id(std::slice::from_ref(&mpls));
let entries: Vec<_> = mapper.iter().collect();
assert_eq!(entries.len(), 3);
let stacks: Vec<_> = entries.iter().map(|(_, stack)| stack).collect();
assert!(stacks.iter().any(|s| s.len() == 1 && s[0] == vlan1));
assert!(stacks.iter().any(|s| s.len() == 1 && s[0] == vlan2));
assert!(stacks.iter().any(|s| s.len() == 1 && s[0] == mpls));
}
#[test]
fn test_vni_mapper_counter_increment() {
let mut mapper = VniMapper::new();
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 200 };
let vlan3 = VniLayer::Vlan { vid: 300 };
let id1 = mapper.get_or_create_vni_id(&[vlan1]);
let id2 = mapper.get_or_create_vni_id(&[vlan2]);
let id3 = mapper.get_or_create_vni_id(&[vlan3]);
assert_eq!(id1.as_u32(), 1);
assert_eq!(id2.as_u32(), 2);
assert_eq!(id3.as_u32(), 3);
}
#[test]
fn test_complex_tunnel_stack() {
let mut mapper = VniMapper::new();
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let outer_vlan = VniLayer::Vlan { vid: 100 };
let vxlan = VniLayer::Vxlan {
vni: 5000,
group_id: 0,
endpoints,
};
let inner_vlan = VniLayer::Vlan { vid: 200 };
let id =
mapper.get_or_create_vni_id(&[outer_vlan.clone(), vxlan.clone(), inner_vlan.clone()]);
let stack = mapper.lookup_vni(id).unwrap();
assert_eq!(stack.len(), 3);
assert_eq!(stack[0], outer_vlan);
assert_eq!(stack[1], vxlan);
assert_eq!(stack[2], inner_vlan);
}
#[test]
fn test_multiple_tunnel_types() {
let mut mapper = VniMapper::new();
let endpoints_v4 = [
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)),
];
let endpoints_v6 = [
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2)),
];
let vxlan = VniLayer::Vxlan {
vni: 100,
group_id: 0,
endpoints: endpoints_v4,
};
let geneve = VniLayer::Geneve {
vni: 200,
protocol_type: 0x6558,
endpoints: endpoints_v6,
};
let gre = VniLayer::Gre {
protocol_type: 0x0800,
key: Some(300),
endpoints: endpoints_v4,
};
let id1 = mapper.get_or_create_vni_id(&[vxlan]);
let id2 = mapper.get_or_create_vni_id(&[geneve]);
let id3 = mapper.get_or_create_vni_id(&[gre]);
assert_eq!(mapper.len(), 3);
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_ne!(id1, id3);
}
#[test]
fn test_empty_stack() {
let mut mapper = VniMapper::new();
let id = mapper.get_or_create_vni_id(&[]);
assert_eq!(mapper.len(), 1);
let stack = mapper.lookup_vni(id).unwrap();
assert_eq!(stack.len(), 0);
}
#[test]
fn test_vni_mapper_reuse_after_partial_clear() {
let mut mapper = VniMapper::new();
let vlan = VniLayer::Vlan { vid: 100 };
let id1 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan));
assert_eq!(id1.as_u32(), 1);
mapper.clear();
let id2 = mapper.get_or_create_vni_id(&[vlan]);
assert_eq!(id2.as_u32(), 1); }
#[test]
fn test_max_vlan_id() {
let vlan = VniLayer::Vlan { vid: 4095 }; assert_eq!(format!("{}", vlan), "vlan(4095)");
}
#[test]
fn test_max_mpls_label() {
let mpls = VniLayer::Mpls { label: 0xFFFFF }; assert_eq!(format!("{}", mpls), "mpls(1048575)");
}
#[test]
fn test_vxlan_24bit_vni() {
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vxlan = VniLayer::Vxlan {
vni: 0xFFFFFF, group_id: 0,
endpoints,
};
assert!(format!("{}", vxlan).contains("vni:16777215"));
}
#[test]
fn test_mixed_ipv4_ipv6_endpoints() {
let endpoints1 = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let endpoints2 = [
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2)),
];
let ipip_v4 = VniLayer::Ipip {
endpoints: endpoints1,
};
let ipip_v6 = VniLayer::Ip6Tnl {
endpoints: endpoints2,
};
assert_ne!(ipip_v4, ipip_v6);
}
#[test]
fn test_vni_layer_size() {
use std::mem::size_of;
let size = size_of::<VniLayer>();
assert!(
size <= 128,
"VniLayer size is {}, expected <= 128 bytes",
size
);
}
#[test]
fn test_smallvec_inline_capacity() {
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 200 };
let mut sv = SmallVec::<[VniLayer; 4]>::new();
sv.push(vlan1);
sv.push(vlan2);
assert!(!sv.spilled());
}
#[test]
fn test_btreemap_ordering() {
let mut mapper = VniMapper::new();
let vlan200 = VniLayer::Vlan { vid: 200 };
let vlan100 = VniLayer::Vlan { vid: 100 };
let vlan300 = VniLayer::Vlan { vid: 300 };
mapper.get_or_create_vni_id(&[vlan200]);
mapper.get_or_create_vni_id(&[vlan100]);
mapper.get_or_create_vni_id(&[vlan300]);
let entries: Vec<_> = mapper.iter().collect();
assert_eq!(entries.len(), 3);
}
#[test]
fn test_vni_layer_debug_format() {
let vlan = VniLayer::Vlan { vid: 100 };
let debug_str = format!("{:?}", vlan);
assert!(debug_str.contains("Vlan"));
assert!(debug_str.contains("100"));
}
#[test]
fn test_multiple_identical_stacks() {
let mut mapper = VniMapper::new();
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vxlan1 = VniLayer::Vxlan {
vni: 100,
group_id: 0,
endpoints,
};
let vxlan2 = VniLayer::Vxlan {
vni: 100,
group_id: 0,
endpoints,
};
let id1 = mapper.get_or_create_vni_id(&[vxlan1]);
let id2 = mapper.get_or_create_vni_id(&[vxlan2]);
assert_eq!(id1, id2);
assert_eq!(mapper.len(), 1);
}
#[test]
fn test_endpoint_ordering_consistency() {
let endpoints1 = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let endpoints2 = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
];
let ipip1 = VniLayer::Ipip {
endpoints: endpoints1,
};
let ipip2 = VniLayer::Ipip {
endpoints: endpoints2,
};
assert_ne!(ipip1, ipip2);
}
#[test]
fn test_gre_with_and_without_key() {
let mut mapper = VniMapper::new();
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let gre_no_key = VniLayer::Gre {
protocol_type: 0x0800,
key: None,
endpoints,
};
let gre_with_key = VniLayer::Gre {
protocol_type: 0x0800,
key: Some(100),
endpoints,
};
let id1 = mapper.get_or_create_vni_id(&[gre_no_key]);
let id2 = mapper.get_or_create_vni_id(&[gre_with_key]);
assert_ne!(id1, id2);
assert_eq!(mapper.len(), 2);
}
#[test]
fn test_vni_mapper_with_all_layer_types() {
let mut mapper = VniMapper::new();
let endpoints_v4 = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let endpoints_v6 = [
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2)),
];
let layers = vec![
VniLayer::Vlan { vid: 100 },
VniLayer::Mpls { label: 1000 },
VniLayer::Gre {
protocol_type: 0x0800,
key: None,
endpoints: endpoints_v4,
},
VniLayer::NvGre {
protocol_type: 0x6558,
vsid_flowid: 100,
endpoints: endpoints_v4,
},
VniLayer::Vxlan {
vni: 5000,
group_id: 0,
endpoints: endpoints_v4,
},
VniLayer::Geneve {
vni: 1000,
protocol_type: 0x6558,
endpoints: endpoints_v6,
},
VniLayer::Ipip {
endpoints: endpoints_v4,
},
VniLayer::Ip4in6 {
endpoints: endpoints_v6,
},
VniLayer::Sit {
endpoints: endpoints_v4,
},
VniLayer::Ip6Tnl {
endpoints: endpoints_v6,
},
VniLayer::GtpU {
teid: 0x12345678,
endpoints: endpoints_v4,
},
VniLayer::Teredo {
endpoints: endpoints_v4,
},
VniLayer::L2tpV2 {
tunnel_id: 100,
session_id: 200,
endpoints: endpoints_v4,
},
VniLayer::L2tpV3 {
session_id: 0xabcdef,
endpoints: endpoints_v6,
},
VniLayer::Pbb {
isid: 0x123456,
bvid: Some(100),
},
VniLayer::Stt {
context_id: 0x123456789abcdef0,
endpoints: endpoints_v4,
},
VniLayer::Pptp {
call_id: 1234,
endpoints: endpoints_v4,
},
];
let mut ids = Vec::new();
for layer in &layers {
let id = mapper.get_or_create_vni_id(std::slice::from_ref(layer));
ids.push(id);
}
assert_eq!(mapper.len(), layers.len());
for i in 0..ids.len() {
for j in (i + 1)..ids.len() {
assert_ne!(
ids[i], ids[j],
"IDs at positions {} and {} should be different",
i, j
);
}
}
}
#[test]
fn test_vni_id_ordering() {
let id1 = VniId::from_u32(1);
let id2 = VniId::from_u32(2);
let id3 = VniId::from_u32(2);
assert!(id1 < id2);
assert_eq!(id2, id3);
assert!(id1 != id2);
}
#[test]
fn test_smallvec_spill_behavior() {
let vlan1 = VniLayer::Vlan { vid: 100 };
let vlan2 = VniLayer::Vlan { vid: 200 };
let vlan3 = VniLayer::Vlan { vid: 300 };
let vlan4 = VniLayer::Vlan { vid: 400 };
let vlan5 = VniLayer::Vlan { vid: 500 };
let mut sv = SmallVec::<[VniLayer; 4]>::new();
sv.push(vlan1);
sv.push(vlan2);
sv.push(vlan3);
sv.push(vlan4);
assert!(!sv.spilled());
sv.push(vlan5);
assert!(sv.spilled()); assert_eq!(sv.len(), 5);
}
#[test]
fn test_vni_mapper_consistency_after_many_insertions() {
let mut mapper = VniMapper::new();
for i in 0..100 {
let vlan = VniLayer::Vlan { vid: i };
mapper.get_or_create_vni_id(&[vlan]);
}
assert_eq!(mapper.len(), 100);
for i in 1..=100 {
let id = VniId::from_u32(i);
assert!(mapper.lookup_vni(id).is_some());
}
}
#[test]
fn test_vni_error_is_error_trait() {
use std::error::Error;
let err = VniError::InvalidHeaderLength;
let _: &dyn Error = &err;
assert!(err.source().is_none());
}
#[test]
fn test_regression_vni_mapper_id_stability() {
let mut mapper = VniMapper::new();
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vxlan = VniLayer::Vxlan {
vni: 5000,
group_id: 0,
endpoints,
};
let id1 = mapper.get_or_create_vni_id(std::slice::from_ref(&vxlan));
let id2 = mapper.get_or_create_vni_id(std::slice::from_ref(&vxlan));
let id3 = mapper.get_or_create_vni_id(std::slice::from_ref(&vxlan));
assert_eq!(id1, id2);
assert_eq!(id2, id3);
assert_eq!(mapper.len(), 1);
}
#[test]
fn test_regression_vni_layer_protocol_type_variations() {
let endpoints = [
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)),
];
let gre_ipv4 = VniLayer::Gre {
protocol_type: 0x0800, key: None,
endpoints,
};
let gre_ipv6 = VniLayer::Gre {
protocol_type: 0x86DD, key: None,
endpoints,
};
assert_ne!(gre_ipv4, gre_ipv6);
}
#[test]
fn test_regression_vxlan_group_id_zero_vs_nonzero() {
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vxlan1 = VniLayer::Vxlan {
vni: 100,
group_id: 0,
endpoints,
};
let vxlan2 = VniLayer::Vxlan {
vni: 100,
group_id: 1,
endpoints,
};
assert_ne!(vxlan1, vxlan2);
}
#[test]
fn test_regression_nvgre_vsid_flowid_encoding() {
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vsid: u32 = 0x123456;
let flow_id: u8 = 0xAB;
let combined = (vsid << 8) | flow_id as u32;
let _nvgre = VniLayer::NvGre {
protocol_type: 0x6558,
vsid_flowid: combined,
endpoints,
};
}
#[test]
fn test_regression_pbb_with_optional_bvid() {
let pbb1 = VniLayer::Pbb {
isid: 0x123456,
bvid: None,
};
let pbb2 = VniLayer::Pbb {
isid: 0x123456,
bvid: Some(0),
};
let pbb3 = VniLayer::Pbb {
isid: 0x123456,
bvid: Some(100),
};
assert_ne!(pbb1, pbb2);
assert_ne!(pbb2, pbb3);
}
#[test]
fn test_regression_l2tp_versions_distinct() {
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let l2tpv2 = VniLayer::L2tpV2 {
tunnel_id: 100,
session_id: 200,
endpoints,
};
let l2tpv3 = VniLayer::L2tpV3 {
session_id: 200,
endpoints,
};
assert_ne!(format!("{:?}", l2tpv2), format!("{:?}", l2tpv3));
}
#[test]
fn test_regression_endpoint_ipv4_vs_ipv6() {
let endpoints_v4 = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let endpoints_v6 = [
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x0a00, 0x0001)),
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x0a00, 0x0002)),
];
let ipip_v4 = VniLayer::Ipip {
endpoints: endpoints_v4,
};
let ipip_v6_as_ip6tnl = VniLayer::Ip6Tnl {
endpoints: endpoints_v6,
};
assert_ne!(format!("{:?}", ipip_v4), format!("{:?}", ipip_v6_as_ip6tnl));
}
#[test]
fn test_regression_vni_mapper_large_scale() {
let mut mapper = VniMapper::new();
let mut all_ids = Vec::new();
for i in 0..1000 {
let vlan = VniLayer::Vlan { vid: i };
let id = mapper.get_or_create_vni_id(&[vlan]);
all_ids.push(id);
}
assert_eq!(mapper.len(), 1000);
for i in 0..all_ids.len() {
for j in (i + 1)..all_ids.len() {
assert_ne!(all_ids[i], all_ids[j]);
}
}
for (idx, id) in all_ids.iter().enumerate() {
let stack = mapper.lookup_vni(*id).unwrap();
assert_eq!(stack.len(), 1);
match &stack[0] {
VniLayer::Vlan { vid } => {
assert_eq!(*vid, idx as u16);
}
_ => panic!("Expected Vlan variant"),
}
}
}
#[test]
fn test_regression_deep_vni_stack() {
let mut mapper = VniMapper::new();
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let layers = vec![
VniLayer::Vlan { vid: 100 },
VniLayer::Mpls { label: 1000 },
VniLayer::Vlan { vid: 200 },
VniLayer::Vxlan {
vni: 5000,
group_id: 0,
endpoints,
},
VniLayer::Vlan { vid: 300 },
VniLayer::Mpls { label: 2000 },
];
let id = mapper.get_or_create_vni_id(&layers);
let retrieved = mapper.lookup_vni(id).unwrap();
assert_eq!(retrieved.len(), layers.len());
for (i, layer) in layers.iter().enumerate() {
assert_eq!(&retrieved[i], layer);
}
}
#[test]
fn test_packet_to_vni_empty() {
use crate::packet::iter::LinkType;
use crate::packet::{Packet, ParseMode};
let packet_bytes = vec![
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, ];
let packet = Packet::from_bytes(&packet_bytes, LinkType::Ethernet, ParseMode::Outermost)
.expect("Should parse packet");
let vni_stack: SmallVec<[VniLayer; 4]> =
(&packet).try_into().expect("Should convert to VNI stack");
assert_eq!(vni_stack.len(), 0);
}
#[test]
fn test_packet_to_vni_vlan_only() {
use crate::packet::iter::LinkType;
use crate::packet::{Packet, ParseMode};
let packet_bytes = vec![
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x81, 0x00, 0x00, 0x64, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
];
let packet = Packet::from_bytes(&packet_bytes, LinkType::Ethernet, ParseMode::Outermost)
.expect("Should parse packet");
let vni_stack: SmallVec<[VniLayer; 4]> =
(&packet).try_into().expect("Should convert to VNI stack");
assert_eq!(vni_stack.len(), 1);
assert!(matches!(vni_stack[0], VniLayer::Vlan { vid: 100 }));
}
#[test]
fn test_packet_to_vni_vxlan_tunnel() {
use crate::packet::iter::LinkType;
use crate::packet::{Packet, ParseMode};
let mut packet_bytes = Vec::new();
packet_bytes.extend_from_slice(&[
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x08, 0x00, ]);
packet_bytes.extend_from_slice(&[
0x45, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, ]);
packet_bytes.extend_from_slice(&[
0x30, 0x39, 0x12, 0xb5, 0x00, 0x28, 0x00, 0x00, ]);
packet_bytes.extend_from_slice(&[
0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x00, ]);
packet_bytes.extend_from_slice(&[
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x08,
0x00, ]);
packet_bytes.extend_from_slice(&[
0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 192, 168, 1, 1,
192, 168, 1, 2, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
]);
let packet = Packet::from_bytes(&packet_bytes, LinkType::Ethernet, ParseMode::Innermost)
.expect("Should parse VXLAN packet");
let vni_stack: SmallVec<[VniLayer; 4]> =
(&packet).try_into().expect("Should convert to VNI stack");
assert_eq!(vni_stack.len(), 1);
match &vni_stack[0] {
VniLayer::Vxlan { vni, endpoints, .. } => {
assert_eq!(*vni, 500);
assert!(endpoints.contains(&IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))));
assert!(endpoints.contains(&IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2))));
}
_ => panic!("Expected VXLAN layer"),
}
}
#[test]
fn test_packet_to_vni_vlan_plus_vxlan() {
use crate::packet::iter::LinkType;
use crate::packet::{Packet, ParseMode};
let mut packet_bytes = Vec::new();
packet_bytes.extend_from_slice(&[
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x81,
0x00, 0x00, 0xc8, 0x08, 0x00, ]);
packet_bytes.extend_from_slice(&[
0x45, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, 172, 16, 0, 1,
172, 16, 0, 2,
]);
packet_bytes.extend_from_slice(&[0x30, 0x39, 0x12, 0xb5, 0x00, 0x28, 0x00, 0x00]);
packet_bytes.extend_from_slice(&[
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, ]);
packet_bytes.extend_from_slice(&[
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x08, 0x00,
]);
packet_bytes.extend_from_slice(&[
0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 192, 168, 1, 1,
192, 168, 1, 2, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
]);
let packet = Packet::from_bytes(&packet_bytes, LinkType::Ethernet, ParseMode::Innermost)
.expect("Should parse packet");
let vni_stack: SmallVec<[VniLayer; 4]> =
(&packet).try_into().expect("Should convert to VNI stack");
assert_eq!(
vni_stack.len(),
1,
"Expected 1 layer (VXLAN only), got {}",
vni_stack.len()
);
match &vni_stack[0] {
VniLayer::Vxlan { vni, endpoints, .. } => {
assert_eq!(*vni, 10);
assert!(endpoints.contains(&IpAddr::V4(Ipv4Addr::new(172, 16, 0, 1))));
assert!(endpoints.contains(&IpAddr::V4(Ipv4Addr::new(172, 16, 0, 2))));
}
_ => panic!("Expected VXLAN layer"),
}
}
#[test]
fn test_packet_to_vni_nested_vlan() {
use crate::packet::iter::LinkType;
use crate::packet::{Packet, ParseMode};
let packet_bytes = vec![
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x81,
0x00, 0x00, 0x64, 0x81, 0x00, 0x00, 0xc8, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
];
let packet = Packet::from_bytes(&packet_bytes, LinkType::Ethernet, ParseMode::Outermost)
.expect("Should parse packet");
let vni_stack: SmallVec<[VniLayer; 4]> =
(&packet).try_into().expect("Should convert to VNI stack");
assert_eq!(vni_stack.len(), 2);
assert!(matches!(vni_stack[0], VniLayer::Vlan { vid: 100 }));
assert!(matches!(vni_stack[1], VniLayer::Vlan { vid: 200 }));
}
#[test]
fn test_packet_to_vni_with_vni_mapper() {
use crate::packet::iter::LinkType;
use crate::packet::{Packet, ParseMode};
let packet_bytes = vec![
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x81, 0x00,
0x01, 0x2c, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
];
let packet = Packet::from_bytes(&packet_bytes, LinkType::Ethernet, ParseMode::Outermost)
.expect("Should parse packet");
let vni_stack: SmallVec<[VniLayer; 4]> =
(&packet).try_into().expect("Should convert to VNI stack");
let mut mapper = VniMapper::new();
let vni_id = mapper.get_or_create_vni_id(&vni_stack);
let retrieved = mapper.lookup_vni(vni_id).unwrap();
assert_eq!(retrieved.len(), 1);
assert!(matches!(retrieved[0], VniLayer::Vlan { vid: 300 }));
}
#[test]
fn test_regression_vni_id_u32_boundary() {
let id_zero = VniId::from_u32(0);
let id_max = VniId::from_u32(u32::MAX);
let id_mid = VniId::from_u32(u32::MAX / 2);
assert_eq!(id_zero.as_u32(), 0);
assert_eq!(id_max.as_u32(), u32::MAX);
assert_eq!(id_mid.as_u32(), u32::MAX / 2);
assert_ne!(id_zero, id_max);
assert_ne!(id_mid, id_max);
}
#[test]
fn test_regression_mapper_clear_and_reuse() {
let mut mapper = VniMapper::new();
let vlan = VniLayer::Vlan { vid: 100 };
let id1 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan));
assert_eq!(id1.as_u32(), 1);
assert_eq!(mapper.len(), 1);
mapper.clear();
assert_eq!(mapper.len(), 0);
assert!(mapper.is_empty());
let id2 = mapper.get_or_create_vni_id(std::slice::from_ref(&vlan));
assert_eq!(id2.as_u32(), 1); assert_eq!(mapper.len(), 1);
let vlan2 = VniLayer::Vlan { vid: 200 };
let id3 = mapper.get_or_create_vni_id(&[vlan2]);
assert_eq!(id3.as_u32(), 2);
assert_eq!(mapper.len(), 2);
}
#[test]
fn test_regression_mixed_tunnel_stack_uniqueness() {
let mut mapper = VniMapper::new();
let endpoints = [
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
];
let vlan100 = VniLayer::Vlan { vid: 100 };
let vlan200 = VniLayer::Vlan { vid: 200 };
let vxlan = VniLayer::Vxlan {
vni: 5000,
group_id: 0,
endpoints,
};
let id1 = mapper.get_or_create_vni_id(&[vlan100.clone(), vxlan.clone()]);
let id2 = mapper.get_or_create_vni_id(&[vxlan.clone(), vlan100.clone()]);
let id3 = mapper.get_or_create_vni_id(&[vlan100.clone(), vlan200.clone(), vxlan.clone()]);
let id4 = mapper.get_or_create_vni_id(&[vlan100.clone(), vxlan.clone(), vlan200.clone()]);
assert_ne!(id1, id2);
assert_ne!(id1, id3);
assert_ne!(id1, id4);
assert_ne!(id2, id3);
assert_ne!(id2, id4);
assert_ne!(id3, id4);
assert_eq!(mapper.len(), 4);
}
}