use num_enum::{FromPrimitive, IntoPrimitive};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum TunnelType {
#[num_enum(default)]
Reserved = 0,
L2tpv3OverIp = 1,
Gre = 2,
TransmitTunnelEndpoint = 3,
IpsecTunnelMode = 4,
IpInIpWithIpsecTransport = 5,
MplsInIpWithIpsecTransport = 6,
IpInIp = 7,
Vxlan = 8,
Nvgre = 9,
Mpls = 10,
MplsInGre = 11,
VxlanGpe = 12,
MplsInUdp = 13,
Ipv6Tunnel = 14,
SrPolicy = 15,
Bare = 16,
SrTunnel = 17,
CloudSecurity = 18,
Geneve = 19,
AnyEncapsulation = 20,
GtpTunnel = 21,
DpsTunnel = 22,
}
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum SubTlvType {
#[num_enum(default)]
Reserved = 0,
Encapsulation = 1,
ProtocolType = 2,
IpsecTunnelAuthenticator = 3,
Color = 4,
LoadBalancingBlock = 5,
TunnelEgressEndpoint = 6,
DsField = 7,
UdpDestinationPort = 8,
EmbeddedLabelHandling = 9,
MplsLabelStack = 10,
PrefixSid = 11,
Preference = 12,
BindingSid = 13,
Enlp = 14,
Priority = 15,
SpiSiRepresentation = 16,
Ipv6SidStructure = 17,
Ipv4Sid = 18,
Ipv6Sid = 19,
Srv6BindingSid = 20,
SegmentList = 128,
PolicyCandidatePathName = 129,
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SubTlv {
pub sub_tlv_type: SubTlvType,
pub value: Vec<u8>,
}
impl SubTlv {
pub fn new(sub_tlv_type: SubTlvType, value: Vec<u8>) -> Self {
Self {
sub_tlv_type,
value,
}
}
pub fn length(&self) -> u16 {
self.value.len() as u16
}
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TunnelEncapTlv {
pub tunnel_type: TunnelType,
pub sub_tlvs: Vec<SubTlv>,
}
impl TunnelEncapTlv {
pub fn new(tunnel_type: TunnelType) -> Self {
Self {
tunnel_type,
sub_tlvs: Vec::new(),
}
}
pub fn add_sub_tlv(&mut self, sub_tlv: SubTlv) {
self.sub_tlvs.push(sub_tlv);
}
pub fn get_tunnel_egress_endpoint(&self) -> Option<IpAddr> {
self.sub_tlvs
.iter()
.find(|tlv| tlv.sub_tlv_type == SubTlvType::TunnelEgressEndpoint)
.and_then(|tlv| match tlv.value.len() {
4 => {
let bytes = &tlv.value[0..4];
Some(IpAddr::V4(Ipv4Addr::new(
bytes[0], bytes[1], bytes[2], bytes[3],
)))
}
16 => {
let mut bytes = [0u8; 16];
bytes.copy_from_slice(&tlv.value[0..16]);
Some(IpAddr::V6(Ipv6Addr::from(bytes)))
}
_ => None,
})
}
pub fn get_color(&self) -> Option<u32> {
self.sub_tlvs
.iter()
.find(|tlv| tlv.sub_tlv_type == SubTlvType::Color)
.and_then(|tlv| {
if tlv.value.len() >= 4 {
Some(u32::from_be_bytes([
tlv.value[0],
tlv.value[1],
tlv.value[2],
tlv.value[3],
]))
} else {
None
}
})
}
pub fn get_udp_destination_port(&self) -> Option<u16> {
self.sub_tlvs
.iter()
.find(|tlv| tlv.sub_tlv_type == SubTlvType::UdpDestinationPort)
.and_then(|tlv| {
if tlv.value.len() >= 2 {
Some(u16::from_be_bytes([tlv.value[0], tlv.value[1]]))
} else {
None
}
})
}
pub fn get_preference(&self) -> Option<u32> {
self.sub_tlvs
.iter()
.find(|tlv| tlv.sub_tlv_type == SubTlvType::Preference)
.and_then(|tlv| {
if tlv.value.len() >= 4 {
Some(u32::from_be_bytes([
tlv.value[0],
tlv.value[1],
tlv.value[2],
tlv.value[3],
]))
} else {
None
}
})
}
}
#[derive(Debug, PartialEq, Clone, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TunnelEncapAttribute {
pub tunnel_tlvs: Vec<TunnelEncapTlv>,
}
impl TunnelEncapAttribute {
pub fn new() -> Self {
Self::default()
}
pub fn add_tunnel_tlv(&mut self, tlv: TunnelEncapTlv) {
self.tunnel_tlvs.push(tlv);
}
pub fn get_tunnels_by_type(&self, tunnel_type: TunnelType) -> Vec<&TunnelEncapTlv> {
self.tunnel_tlvs
.iter()
.filter(|tlv| tlv.tunnel_type == tunnel_type)
.collect()
}
pub fn has_tunnel_type(&self, tunnel_type: TunnelType) -> bool {
self.tunnel_tlvs
.iter()
.any(|tlv| tlv.tunnel_type == tunnel_type)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tunnel_type_conversion() {
assert_eq!(TunnelType::Vxlan as u16, 8);
assert_eq!(TunnelType::Nvgre as u16, 9);
assert_eq!(TunnelType::SrPolicy as u16, 15);
assert_eq!(TunnelType::Geneve as u16, 19);
}
#[test]
fn test_sub_tlv_type_conversion() {
assert_eq!(SubTlvType::Color as u16, 4);
assert_eq!(SubTlvType::TunnelEgressEndpoint as u16, 6);
assert_eq!(SubTlvType::UdpDestinationPort as u16, 8);
assert_eq!(SubTlvType::SegmentList as u16, 128);
}
#[test]
fn test_tunnel_encap_tlv_creation() {
let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
let color_sub_tlv = SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]); tlv.add_sub_tlv(color_sub_tlv);
let udp_port_sub_tlv = SubTlv::new(SubTlvType::UdpDestinationPort, vec![0x12, 0xB5]); tlv.add_sub_tlv(udp_port_sub_tlv);
assert_eq!(tlv.tunnel_type, TunnelType::Vxlan);
assert_eq!(tlv.sub_tlvs.len(), 2);
assert_eq!(tlv.get_color(), Some(100));
assert_eq!(tlv.get_udp_destination_port(), Some(4789));
}
#[test]
fn test_tunnel_encap_attribute() {
let mut attr = TunnelEncapAttribute::new();
let mut vxlan_tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
vxlan_tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]));
let mut gre_tlv = TunnelEncapTlv::new(TunnelType::Gre);
gre_tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0xC8]));
attr.add_tunnel_tlv(vxlan_tlv);
attr.add_tunnel_tlv(gre_tlv);
assert_eq!(attr.tunnel_tlvs.len(), 2);
assert!(attr.has_tunnel_type(TunnelType::Vxlan));
assert!(attr.has_tunnel_type(TunnelType::Gre));
assert!(!attr.has_tunnel_type(TunnelType::Nvgre));
let vxlan_tunnels = attr.get_tunnels_by_type(TunnelType::Vxlan);
assert_eq!(vxlan_tunnels.len(), 1);
assert_eq!(vxlan_tunnels[0].get_color(), Some(100));
}
#[test]
fn test_tunnel_egress_endpoint_parsing() {
let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
let ipv4_endpoint = SubTlv::new(
SubTlvType::TunnelEgressEndpoint,
vec![192, 168, 1, 1], );
tlv.add_sub_tlv(ipv4_endpoint);
if let Some(IpAddr::V4(addr)) = tlv.get_tunnel_egress_endpoint() {
assert_eq!(addr, Ipv4Addr::new(192, 168, 1, 1));
} else {
panic!("Expected IPv4 address");
}
}
#[test]
fn test_sub_tlv_length() {
let sub_tlv = SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]);
assert_eq!(sub_tlv.length(), 4);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialization() {
let mut attr = TunnelEncapAttribute::new();
let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]));
attr.add_tunnel_tlv(tlv);
let serialized = serde_json::to_string(&attr).unwrap();
let deserialized: TunnelEncapAttribute = serde_json::from_str(&serialized).unwrap();
assert_eq!(attr, deserialized);
}
}