use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::error::ParserError;
use crate::models::*;
use crate::parser::ReadUtils;
pub fn parse_tunnel_encapsulation_attribute(
mut data: Bytes,
) -> Result<AttributeValue, ParserError> {
let mut attr = TunnelEncapAttribute::new();
while data.remaining() >= 4 {
let tunnel_type = data.read_u16()?;
let tunnel_length = data.read_u16()?;
if data.remaining() < tunnel_length as usize {
return Err(ParserError::TruncatedMsg(format!(
"Expected {} bytes for Tunnel TLV, but only {} remaining",
tunnel_length,
data.remaining()
)));
}
let tunnel_data = data.read_n_bytes(tunnel_length as usize)?;
let tunnel_tlv = parse_tunnel_tlv(tunnel_type, tunnel_data.into())?;
attr.add_tunnel_tlv(tunnel_tlv);
}
Ok(AttributeValue::TunnelEncapsulation(attr))
}
fn parse_tunnel_tlv(tunnel_type: u16, mut data: Bytes) -> Result<TunnelEncapTlv, ParserError> {
let tunnel_type = TunnelType::from(tunnel_type);
let mut tunnel_tlv = TunnelEncapTlv::new(tunnel_type);
while data.remaining() > 0 {
if data.remaining() < 2 {
return Err(ParserError::TruncatedMsg(
"Not enough data for Sub-TLV header".to_string(),
));
}
let sub_tlv_type = data.read_u8()?;
let sub_tlv_length: u16 = if sub_tlv_type < 128 {
if data.remaining() < 1 {
return Err(ParserError::TruncatedMsg(
"Not enough data for Sub-TLV length".to_string(),
));
}
data.read_u8()? as u16
} else {
if data.remaining() < 2 {
return Err(ParserError::TruncatedMsg(
"Not enough data for extended Sub-TLV length".to_string(),
));
}
data.read_u16()?
};
if data.remaining() < sub_tlv_length as usize {
return Err(ParserError::TruncatedMsg(format!(
"Expected {} bytes for Sub-TLV data, but only {} remaining",
sub_tlv_length,
data.remaining()
)));
}
let sub_tlv_data = data.read_n_bytes(sub_tlv_length as usize)?;
let sub_tlv_type_enum = SubTlvType::from(sub_tlv_type as u16);
let sub_tlv = SubTlv::new(sub_tlv_type_enum, sub_tlv_data.to_vec());
tunnel_tlv.add_sub_tlv(sub_tlv);
}
Ok(tunnel_tlv)
}
pub fn encode_tunnel_encapsulation_attribute(attr: &TunnelEncapAttribute) -> Bytes {
let mut bytes = BytesMut::new();
for tunnel_tlv in &attr.tunnel_tlvs {
bytes.put_u16(tunnel_tlv.tunnel_type as u16);
let mut sub_tlv_bytes = BytesMut::new();
for sub_tlv in &tunnel_tlv.sub_tlvs {
let sub_tlv_type = sub_tlv.sub_tlv_type as u16;
if sub_tlv_type < 128 {
sub_tlv_bytes.put_u8(sub_tlv_type as u8);
sub_tlv_bytes.put_u8(sub_tlv.value.len() as u8);
} else {
sub_tlv_bytes.put_u8(sub_tlv_type as u8);
sub_tlv_bytes.put_u16(sub_tlv.value.len() as u16);
}
sub_tlv_bytes.extend_from_slice(&sub_tlv.value);
}
bytes.put_u16(sub_tlv_bytes.len() as u16);
bytes.extend_from_slice(&sub_tlv_bytes);
}
bytes.freeze()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_tunnel_encapsulation_simple() {
let mut data = BytesMut::new();
data.put_u16(8);
data.put_u16(6);
data.put_u8(4);
data.put_u8(4);
data.put_u32(100);
let bytes = data.freeze();
let result = parse_tunnel_encapsulation_attribute(bytes).unwrap();
if let AttributeValue::TunnelEncapsulation(attr) = result {
assert_eq!(attr.tunnel_tlvs.len(), 1);
let tunnel = &attr.tunnel_tlvs[0];
assert_eq!(tunnel.tunnel_type, TunnelType::Vxlan);
assert_eq!(tunnel.sub_tlvs.len(), 1);
let sub_tlv = &tunnel.sub_tlvs[0];
assert_eq!(sub_tlv.sub_tlv_type, SubTlvType::Color);
assert_eq!(sub_tlv.value, vec![0, 0, 0, 100]);
assert_eq!(tunnel.get_color(), Some(100));
} else {
panic!("Expected TunnelEncapsulation attribute");
}
}
#[test]
fn test_parse_tunnel_encapsulation_multiple_sub_tlvs() {
let mut data = BytesMut::new();
data.put_u16(8);
data.put_u16(10);
data.put_u8(4);
data.put_u8(4);
data.put_u32(100);
data.put_u8(8);
data.put_u8(2);
data.put_u16(4789);
let bytes = data.freeze();
let result = parse_tunnel_encapsulation_attribute(bytes).unwrap();
if let AttributeValue::TunnelEncapsulation(attr) = result {
assert_eq!(attr.tunnel_tlvs.len(), 1);
let tunnel = &attr.tunnel_tlvs[0];
assert_eq!(tunnel.tunnel_type, TunnelType::Vxlan);
assert_eq!(tunnel.sub_tlvs.len(), 2);
assert_eq!(tunnel.get_color(), Some(100));
assert_eq!(tunnel.get_udp_destination_port(), Some(4789));
} else {
panic!("Expected TunnelEncapsulation attribute");
}
}
#[test]
fn test_parse_tunnel_encapsulation_extended_length() {
let mut data = BytesMut::new();
data.put_u16(15);
data.put_u16(5);
data.put_u8(128);
data.put_u16(2); data.put_u16(0);
let bytes = data.freeze();
let result = parse_tunnel_encapsulation_attribute(bytes).unwrap();
if let AttributeValue::TunnelEncapsulation(attr) = result {
assert_eq!(attr.tunnel_tlvs.len(), 1);
let tunnel = &attr.tunnel_tlvs[0];
assert_eq!(tunnel.tunnel_type, TunnelType::SrPolicy);
assert_eq!(tunnel.sub_tlvs.len(), 1);
let sub_tlv = &tunnel.sub_tlvs[0];
assert_eq!(sub_tlv.sub_tlv_type, SubTlvType::SegmentList);
} else {
panic!("Expected TunnelEncapsulation attribute");
}
}
#[test]
fn test_encode_tunnel_encapsulation() {
let mut attr = TunnelEncapAttribute::new();
let mut tunnel_tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
let color_sub_tlv = SubTlv::new(SubTlvType::Color, vec![0, 0, 0, 100]);
tunnel_tlv.add_sub_tlv(color_sub_tlv);
attr.add_tunnel_tlv(tunnel_tlv);
let encoded = encode_tunnel_encapsulation_attribute(&attr);
let parsed = parse_tunnel_encapsulation_attribute(encoded).unwrap();
if let AttributeValue::TunnelEncapsulation(parsed_attr) = parsed {
assert_eq!(parsed_attr.tunnel_tlvs.len(), 1);
assert_eq!(parsed_attr.tunnel_tlvs[0].tunnel_type, TunnelType::Vxlan);
assert_eq!(parsed_attr.tunnel_tlvs[0].get_color(), Some(100));
} else {
panic!("Expected TunnelEncapsulation attribute");
}
}
#[test]
fn test_parse_tunnel_encapsulation_truncated() {
let mut data = BytesMut::new();
data.put_u16(8); data.put_u16(10); data.put_u16(0);
let bytes = data.freeze();
let result = parse_tunnel_encapsulation_attribute(bytes);
assert!(result.is_err());
if let Err(ParserError::TruncatedMsg(msg)) = result {
assert!(msg.contains("Expected 10 bytes"));
} else {
panic!("Expected TruncatedMsg error");
}
}
#[test]
fn test_tunnel_type_from_unknown() {
let tunnel_type = TunnelType::from(999u16);
assert_eq!(tunnel_type, TunnelType::Reserved);
}
#[test]
fn test_sub_tlv_type_from_unknown() {
let sub_tlv_type = SubTlvType::from(999u16);
assert_eq!(sub_tlv_type, SubTlvType::Reserved);
}
}