use crate::error::ParserError;
use crate::models::network::{Afi, Safi};
#[cfg(feature = "parser")]
use crate::parser::ReadUtils;
#[cfg(feature = "parser")]
use bytes::{BufMut, Bytes, BytesMut};
use num_enum::{FromPrimitive, IntoPrimitive};
#[cfg(feature = "parser")]
use zerocopy::big_endian::{U16, U32};
#[cfg(feature = "parser")]
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
#[cfg(feature = "parser")]
#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct RawMultiprotocolExtensions {
afi: U16,
reserved: u8,
safi: u8,
}
#[cfg(feature = "parser")]
#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct RawFourOctetAs {
asn: U32,
}
#[allow(non_camel_case_types)]
#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum BgpCapabilityType {
MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4 = 1,
ROUTE_REFRESH_CAPABILITY_FOR_BGP_4 = 2,
OUTBOUND_ROUTE_FILTERING_CAPABILITY = 3,
EXTENDED_NEXT_HOP_ENCODING = 5,
BGP_EXTENDED_MESSAGE = 6,
BGPSEC_CAPABILITY = 7,
MULTIPLE_LABELS_CAPABILITY = 8,
BGP_ROLE = 9,
GRACEFUL_RESTART_CAPABILITY = 64,
SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY = 65,
SUPPORT_FOR_DYNAMIC_CAPABILITY = 67,
MULTISESSION_BGP_CAPABILITY = 68,
ADD_PATH_CAPABILITY = 69,
ENHANCED_ROUTE_REFRESH_CAPABILITY = 70,
LONG_LIVED_GRACEFUL_RESTART_CAPABILITY = 71,
ROUTING_POLICY_DISTRIBUTION = 72,
FQDN_CAPABILITY = 73,
#[num_enum(catch_all)]
Unknown(u8),
}
impl BgpCapabilityType {
pub const fn is_deprecated(&self) -> bool {
matches!(
self,
BgpCapabilityType::Unknown(4 | 66 | 128 | 129 | 130 | 131 | 184 | 185)
)
}
pub const fn is_reserved(&self) -> bool {
matches!(self, BgpCapabilityType::Unknown(0 | 255))
}
pub const fn is_reserved_for_experimental_use(&self) -> bool {
matches!(self, BgpCapabilityType::Unknown(239..=254))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExtendedNextHopEntry {
pub nlri_afi: Afi,
pub nlri_safi: Safi,
pub nexthop_afi: Afi,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExtendedNextHopCapability {
pub entries: Vec<ExtendedNextHopEntry>,
}
impl ExtendedNextHopCapability {
pub fn new(entries: Vec<ExtendedNextHopEntry>) -> Self {
Self { entries }
}
pub fn supports(&self, nlri_afi: Afi, nlri_safi: Safi, nexthop_afi: Afi) -> bool {
self.entries.iter().any(|entry| {
entry.nlri_afi == nlri_afi
&& entry.nlri_safi == nlri_safi
&& entry.nexthop_afi == nexthop_afi
})
}
pub fn supported_nlri_for_nexthop(&self, nexthop_afi: Afi) -> Vec<(Afi, Safi)> {
self.entries
.iter()
.filter(|entry| entry.nexthop_afi == nexthop_afi)
.map(|entry| (entry.nlri_afi, entry.nlri_safi))
.collect()
}
#[cfg(feature = "parser")]
pub fn parse(mut data: Bytes) -> Result<Self, ParserError> {
let mut entries = Vec::new();
if !data.len().is_multiple_of(6) {
return Err(ParserError::ParseError(format!(
"Extended Next Hop capability length {} is not divisible by 6",
data.len()
)));
}
while data.len() >= 6 {
let nlri_afi = data.read_afi()?;
let nlri_safi_u16 = data.read_u16()?;
let nlri_safi = Safi::try_from(nlri_safi_u16 as u8).map_err(|_| {
ParserError::ParseError(format!("Unknown SAFI type: {}", nlri_safi_u16))
})?;
let nexthop_afi = data.read_afi()?;
entries.push(ExtendedNextHopEntry {
nlri_afi,
nlri_safi,
nexthop_afi,
});
}
Ok(ExtendedNextHopCapability::new(entries))
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
let mut bytes = BytesMut::with_capacity(self.entries.len() * 6);
for entry in &self.entries {
bytes.put_u16(entry.nlri_afi as u16); bytes.put_u16(entry.nlri_safi as u8 as u16); bytes.put_u16(entry.nexthop_afi as u16); }
bytes.freeze()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiprotocolExtensionsCapability {
pub afi: Afi,
pub safi: Safi,
}
impl MultiprotocolExtensionsCapability {
pub fn new(afi: Afi, safi: Safi) -> Self {
Self { afi, safi }
}
#[cfg(feature = "parser")]
pub fn parse(data: Bytes) -> Result<Self, ParserError> {
let raw = RawMultiprotocolExtensions::ref_from_bytes(&data).map_err(|_| {
ParserError::ParseError(format!(
"Multiprotocol Extensions capability length {} is not 4",
data.len()
))
})?;
let afi = Afi::try_from(raw.afi.get())?;
let safi = Safi::try_from(raw.safi)
.map_err(|_| ParserError::ParseError(format!("Unknown SAFI type: {}", raw.safi)))?;
Ok(MultiprotocolExtensionsCapability::new(afi, safi))
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
let raw = RawMultiprotocolExtensions {
afi: U16::new(self.afi as u16),
reserved: 0,
safi: self.safi as u8,
};
Bytes::copy_from_slice(raw.as_bytes())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GracefulRestartCapability {
pub restart_state: bool,
pub restart_time: u16,
pub address_families: Vec<GracefulRestartAddressFamily>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GracefulRestartAddressFamily {
pub afi: Afi,
pub safi: Safi,
pub forwarding_state: bool,
}
impl GracefulRestartCapability {
pub fn new(
restart_state: bool,
restart_time: u16,
address_families: Vec<GracefulRestartAddressFamily>,
) -> Self {
Self {
restart_state,
restart_time,
address_families,
}
}
#[cfg(feature = "parser")]
pub fn parse(mut data: Bytes) -> Result<Self, ParserError> {
if data.len() < 2 {
return Err(ParserError::ParseError(format!(
"Graceful Restart capability length {} is less than minimum 2 bytes",
data.len()
)));
}
let restart_flags_and_time = data.read_u16()?;
let restart_state = (restart_flags_and_time & 0x8000) != 0; let restart_time = restart_flags_and_time & 0x0FFF;
let mut address_families = Vec::new();
if !data.len().is_multiple_of(4) {
return Err(ParserError::ParseError(format!(
"Graceful Restart capability remaining length {} is not divisible by 4",
data.len()
)));
}
while data.len() >= 4 {
let afi = data.read_afi()?;
let safi_u8 = data.read_u8()?;
let safi = Safi::try_from(safi_u8)
.map_err(|_| ParserError::ParseError(format!("Unknown SAFI type: {}", safi_u8)))?;
let flags = data.read_u8()?;
let forwarding_state = (flags & 0x80) != 0;
address_families.push(GracefulRestartAddressFamily {
afi,
safi,
forwarding_state,
});
}
Ok(GracefulRestartCapability::new(
restart_state,
restart_time,
address_families,
))
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
let mut bytes = BytesMut::with_capacity(2 + self.address_families.len() * 4);
let restart_flags_and_time = if self.restart_state {
0x8000 | (self.restart_time & 0x0FFF)
} else {
self.restart_time & 0x0FFF
};
bytes.put_u16(restart_flags_and_time);
for af in &self.address_families {
bytes.put_u16(af.afi as u16); bytes.put_u8(af.safi as u8); let flags = if af.forwarding_state { 0x80 } else { 0x00 };
bytes.put_u8(flags); }
bytes.freeze()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AddPathCapability {
pub address_families: Vec<AddPathAddressFamily>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AddPathAddressFamily {
pub afi: Afi,
pub safi: Safi,
pub send_receive: AddPathSendReceive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AddPathSendReceive {
Receive = 1,
Send = 2,
SendReceive = 3,
}
impl TryFrom<u8> for AddPathSendReceive {
type Error = ParserError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(AddPathSendReceive::Receive),
2 => Ok(AddPathSendReceive::Send),
3 => Ok(AddPathSendReceive::SendReceive),
_ => Err(ParserError::ParseError(format!(
"Invalid ADD-PATH Send/Receive value: {}",
value
))),
}
}
}
impl AddPathCapability {
pub fn new(address_families: Vec<AddPathAddressFamily>) -> Self {
Self { address_families }
}
#[cfg(feature = "parser")]
pub fn parse(mut data: Bytes) -> Result<Self, ParserError> {
let mut address_families = Vec::new();
if !data.len().is_multiple_of(4) {
return Err(ParserError::ParseError(format!(
"ADD-PATH capability length {} is not divisible by 4",
data.len()
)));
}
while data.len() >= 4 {
let afi = data.read_afi()?;
let safi_u8 = data.read_u8()?;
let safi = Safi::try_from(safi_u8)
.map_err(|_| ParserError::ParseError(format!("Unknown SAFI type: {}", safi_u8)))?;
let send_receive_u8 = data.read_u8()?;
let send_receive = AddPathSendReceive::try_from(send_receive_u8)?;
address_families.push(AddPathAddressFamily {
afi,
safi,
send_receive,
});
}
Ok(AddPathCapability::new(address_families))
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
let mut bytes = BytesMut::with_capacity(self.address_families.len() * 4);
for af in &self.address_families {
bytes.put_u16(af.afi as u16); bytes.put_u8(af.safi as u8); bytes.put_u8(af.send_receive as u8); }
bytes.freeze()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RouteRefreshCapability;
impl RouteRefreshCapability {
pub fn new() -> Self {
Self
}
#[cfg(feature = "parser")]
pub fn parse(data: Bytes) -> Result<Self, ParserError> {
if !data.is_empty() {
return Err(ParserError::ParseError(format!(
"Route Refresh capability should have length 0, got {}",
data.len()
)));
}
Ok(RouteRefreshCapability::new())
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
Bytes::new()
}
}
impl Default for RouteRefreshCapability {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BgpExtendedMessageCapability;
impl BgpExtendedMessageCapability {
pub fn new() -> Self {
Self
}
#[cfg(feature = "parser")]
pub fn parse(data: Bytes) -> Result<Self, ParserError> {
if !data.is_empty() {
return Err(ParserError::ParseError(format!(
"BGP Extended Message capability should have length 0, got {}",
data.len()
)));
}
Ok(BgpExtendedMessageCapability::new())
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
Bytes::new()
}
}
impl Default for BgpExtendedMessageCapability {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FourOctetAsCapability {
pub asn: u32,
}
impl FourOctetAsCapability {
pub fn new(asn: u32) -> Self {
Self { asn }
}
#[cfg(feature = "parser")]
pub fn parse(data: Bytes) -> Result<Self, ParserError> {
let raw = RawFourOctetAs::ref_from_bytes(&data).map_err(|_| {
ParserError::ParseError(format!(
"4-octet AS capability length {} is not 4",
data.len()
))
})?;
Ok(FourOctetAsCapability::new(raw.asn.get()))
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
let raw = RawFourOctetAs {
asn: U32::new(self.asn),
};
Bytes::copy_from_slice(raw.as_bytes())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BgpRoleCapability {
pub role: BgpRole,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BgpRole {
Provider = 0,
RouteServer = 1,
RouteServerClient = 2,
Customer = 3,
Peer = 4,
}
impl TryFrom<u8> for BgpRole {
type Error = ParserError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(BgpRole::Provider),
1 => Ok(BgpRole::RouteServer),
2 => Ok(BgpRole::RouteServerClient),
3 => Ok(BgpRole::Customer),
4 => Ok(BgpRole::Peer),
_ => Err(ParserError::ParseError(format!(
"Unknown BGP Role value: {}",
value
))),
}
}
}
impl BgpRoleCapability {
pub fn new(role: BgpRole) -> Self {
Self { role }
}
#[cfg(feature = "parser")]
pub fn parse(mut data: Bytes) -> Result<Self, ParserError> {
if data.len() != 1 {
return Err(ParserError::ParseError(format!(
"BGP Role capability length {} is not 1",
data.len()
)));
}
let role_u8 = data.read_u8()?;
let role = BgpRole::try_from(role_u8)?;
Ok(BgpRoleCapability::new(role))
}
#[cfg(feature = "parser")]
pub fn encode(&self) -> Bytes {
let mut bytes = BytesMut::with_capacity(1);
bytes.put_u8(self.role as u8);
bytes.freeze()
}
}
#[cfg(all(test, feature = "parser"))]
mod tests {
use super::*;
#[test]
fn test_parsing_capability() {
assert!(BgpCapabilityType::from(0).is_reserved());
assert!(BgpCapabilityType::from(255).is_reserved());
for code in [4, 66, 128, 129, 130, 131, 184, 185] {
assert!(BgpCapabilityType::from(code).is_deprecated());
}
let unassigned_ranges = [10..=63, 74..=127, 132..=183, 186..=238];
for code in <[_; 4]>::into_iter(unassigned_ranges).flatten() {
let ty = BgpCapabilityType::from(code);
assert_eq!(ty, BgpCapabilityType::Unknown(code));
assert!(!ty.is_deprecated() && !ty.is_reserved());
}
assert_eq!(
BgpCapabilityType::from(1),
BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4
);
assert_eq!(
BgpCapabilityType::from(2),
BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4
);
assert_eq!(
BgpCapabilityType::from(3),
BgpCapabilityType::OUTBOUND_ROUTE_FILTERING_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(5),
BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING
);
assert_eq!(
BgpCapabilityType::from(6),
BgpCapabilityType::BGP_EXTENDED_MESSAGE
);
assert_eq!(
BgpCapabilityType::from(7),
BgpCapabilityType::BGPSEC_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(8),
BgpCapabilityType::MULTIPLE_LABELS_CAPABILITY
);
assert_eq!(BgpCapabilityType::from(9), BgpCapabilityType::BGP_ROLE);
assert_eq!(
BgpCapabilityType::from(64),
BgpCapabilityType::GRACEFUL_RESTART_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(65),
BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(67),
BgpCapabilityType::SUPPORT_FOR_DYNAMIC_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(68),
BgpCapabilityType::MULTISESSION_BGP_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(69),
BgpCapabilityType::ADD_PATH_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(70),
BgpCapabilityType::ENHANCED_ROUTE_REFRESH_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(71),
BgpCapabilityType::LONG_LIVED_GRACEFUL_RESTART_CAPABILITY
);
assert_eq!(
BgpCapabilityType::from(72),
BgpCapabilityType::ROUTING_POLICY_DISTRIBUTION
);
assert_eq!(
BgpCapabilityType::from(73),
BgpCapabilityType::FQDN_CAPABILITY
);
}
#[test]
fn test_reserved_for_experimental() {
let experimental_ranges = [239..=254];
for code in <[_; 1]>::into_iter(experimental_ranges).flatten() {
let ty = BgpCapabilityType::from(code);
assert_eq!(ty, BgpCapabilityType::Unknown(code));
assert!(ty.is_reserved_for_experimental_use());
}
}
#[test]
#[cfg(feature = "serde")]
fn test_serde() {
let ty = BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4;
let serialized = serde_json::to_string(&ty).unwrap();
let deserialized: BgpCapabilityType = serde_json::from_str(&serialized).unwrap();
assert_eq!(ty, deserialized);
}
#[test]
fn test_extended_next_hop_capability() {
use crate::models::network::{Afi, Safi};
let entries = vec![
ExtendedNextHopEntry {
nlri_afi: Afi::Ipv4,
nlri_safi: Safi::Unicast,
nexthop_afi: Afi::Ipv6,
},
ExtendedNextHopEntry {
nlri_afi: Afi::Ipv4,
nlri_safi: Safi::MplsVpn,
nexthop_afi: Afi::Ipv6,
},
];
let capability = ExtendedNextHopCapability::new(entries);
assert!(capability.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
assert!(capability.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
assert!(!capability.supports(Afi::Ipv4, Safi::Multicast, Afi::Ipv6));
assert!(!capability.supports(Afi::Ipv6, Safi::Unicast, Afi::Ipv6));
let supported = capability.supported_nlri_for_nexthop(Afi::Ipv6);
assert_eq!(supported.len(), 2);
assert!(supported.contains(&(Afi::Ipv4, Safi::Unicast)));
assert!(supported.contains(&(Afi::Ipv4, Safi::MplsVpn)));
let no_support = capability.supported_nlri_for_nexthop(Afi::Ipv4);
assert!(no_support.is_empty());
}
#[test]
fn test_extended_next_hop_capability_parsing() {
use crate::models::network::{Afi, Safi};
let capability_bytes = Bytes::from(vec![
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x80, 0x00, 0x02, ]);
let parsed = ExtendedNextHopCapability::parse(capability_bytes).unwrap();
assert_eq!(parsed.entries.len(), 2);
assert_eq!(parsed.entries[0].nlri_afi, Afi::Ipv4);
assert_eq!(parsed.entries[0].nlri_safi, Safi::Unicast);
assert_eq!(parsed.entries[0].nexthop_afi, Afi::Ipv6);
assert_eq!(parsed.entries[1].nlri_afi, Afi::Ipv4);
assert_eq!(parsed.entries[1].nlri_safi, Safi::MplsVpn);
assert_eq!(parsed.entries[1].nexthop_afi, Afi::Ipv6);
assert!(parsed.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
assert!(parsed.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
assert!(!parsed.supports(Afi::Ipv4, Safi::Multicast, Afi::Ipv6));
}
#[test]
fn test_extended_next_hop_capability_encoding() {
use crate::models::network::{Afi, Safi};
let entries = vec![
ExtendedNextHopEntry {
nlri_afi: Afi::Ipv4,
nlri_safi: Safi::Unicast,
nexthop_afi: Afi::Ipv6,
},
ExtendedNextHopEntry {
nlri_afi: Afi::Ipv4,
nlri_safi: Safi::MplsVpn,
nexthop_afi: Afi::Ipv6,
},
];
let capability = ExtendedNextHopCapability::new(entries);
let encoded = capability.encode();
let expected = vec![
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x80, 0x00, 0x02, ];
assert_eq!(encoded.to_vec(), expected);
}
#[test]
fn test_extended_next_hop_capability_round_trip() {
use crate::models::network::{Afi, Safi};
let original_entries = vec![
ExtendedNextHopEntry {
nlri_afi: Afi::Ipv4,
nlri_safi: Safi::Unicast,
nexthop_afi: Afi::Ipv6,
},
ExtendedNextHopEntry {
nlri_afi: Afi::Ipv4,
nlri_safi: Safi::MulticastVpn,
nexthop_afi: Afi::Ipv6,
},
];
let original = ExtendedNextHopCapability::new(original_entries);
let encoded = original.encode();
let parsed = ExtendedNextHopCapability::parse(encoded).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_extended_next_hop_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x00, 0x01, 0x00, 0x01, 0x00]); let result = ExtendedNextHopCapability::parse(invalid_bytes);
assert!(result.is_err());
if let Err(ParserError::ParseError(msg)) = result {
assert!(msg.contains("not divisible by 6"));
} else {
panic!("Expected ParseError with divisibility message");
}
}
#[test]
fn test_extended_next_hop_capability_empty() {
let empty_bytes = Bytes::from(vec![]);
let parsed = ExtendedNextHopCapability::parse(empty_bytes).unwrap();
assert_eq!(parsed.entries.len(), 0);
let empty_capability = ExtendedNextHopCapability::new(vec![]);
let encoded = empty_capability.encode();
assert_eq!(encoded.len(), 0);
}
#[test]
fn test_multiprotocol_extensions_capability() {
use crate::models::network::{Afi, Safi};
let capability = MultiprotocolExtensionsCapability::new(Afi::Ipv4, Safi::Unicast);
let encoded = capability.encode();
assert_eq!(encoded.len(), 4);
assert_eq!(encoded[0], 0x00); assert_eq!(encoded[1], 0x01); assert_eq!(encoded[2], 0x00); assert_eq!(encoded[3], 0x01);
let parsed = MultiprotocolExtensionsCapability::parse(encoded).unwrap();
assert_eq!(parsed.afi, Afi::Ipv4);
assert_eq!(parsed.safi, Safi::Unicast);
assert_eq!(parsed, capability);
}
#[test]
fn test_multiprotocol_extensions_capability_ipv6() {
use crate::models::network::{Afi, Safi};
let capability = MultiprotocolExtensionsCapability::new(Afi::Ipv6, Safi::Multicast);
let encoded = capability.encode();
let parsed = MultiprotocolExtensionsCapability::parse(encoded).unwrap();
assert_eq!(parsed.afi, Afi::Ipv6);
assert_eq!(parsed.safi, Safi::Multicast);
}
#[test]
fn test_multiprotocol_extensions_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x00, 0x01, 0x00]); let result = MultiprotocolExtensionsCapability::parse(invalid_bytes);
assert!(result.is_err());
}
#[test]
fn test_graceful_restart_capability() {
use crate::models::network::{Afi, Safi};
let address_families = vec![
GracefulRestartAddressFamily {
afi: Afi::Ipv4,
safi: Safi::Unicast,
forwarding_state: true,
},
GracefulRestartAddressFamily {
afi: Afi::Ipv6,
safi: Safi::Unicast,
forwarding_state: false,
},
];
let capability = GracefulRestartCapability::new(true, 180, address_families);
let encoded = capability.encode();
assert_eq!(encoded.len(), 2 + 2 * 4);
let parsed = GracefulRestartCapability::parse(encoded).unwrap();
assert!(parsed.restart_state);
assert_eq!(parsed.restart_time, 180);
assert_eq!(parsed.address_families.len(), 2);
assert_eq!(parsed.address_families[0].afi, Afi::Ipv4);
assert_eq!(parsed.address_families[0].safi, Safi::Unicast);
assert!(parsed.address_families[0].forwarding_state);
assert_eq!(parsed.address_families[1].afi, Afi::Ipv6);
assert_eq!(parsed.address_families[1].safi, Safi::Unicast);
assert!(!parsed.address_families[1].forwarding_state);
}
#[test]
fn test_graceful_restart_capability_no_restart_state() {
let capability = GracefulRestartCapability::new(false, 300, vec![]);
let encoded = capability.encode();
let parsed = GracefulRestartCapability::parse(encoded).unwrap();
assert!(!parsed.restart_state);
assert_eq!(parsed.restart_time, 300);
assert_eq!(parsed.address_families.len(), 0);
}
#[test]
fn test_graceful_restart_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x80, 0xB4, 0x00, 0x01, 0x01]); let result = GracefulRestartCapability::parse(invalid_bytes);
assert!(result.is_err());
}
#[test]
fn test_add_path_capability() {
use crate::models::network::{Afi, Safi};
let address_families = vec![
AddPathAddressFamily {
afi: Afi::Ipv4,
safi: Safi::Unicast,
send_receive: AddPathSendReceive::SendReceive,
},
AddPathAddressFamily {
afi: Afi::Ipv6,
safi: Safi::Unicast,
send_receive: AddPathSendReceive::Receive,
},
];
let capability = AddPathCapability::new(address_families);
let encoded = capability.encode();
assert_eq!(encoded.len(), 2 * 4);
let parsed = AddPathCapability::parse(encoded).unwrap();
assert_eq!(parsed.address_families.len(), 2);
assert_eq!(parsed.address_families[0].afi, Afi::Ipv4);
assert_eq!(parsed.address_families[0].safi, Safi::Unicast);
assert_eq!(
parsed.address_families[0].send_receive,
AddPathSendReceive::SendReceive
);
assert_eq!(parsed.address_families[1].afi, Afi::Ipv6);
assert_eq!(parsed.address_families[1].safi, Safi::Unicast);
assert_eq!(
parsed.address_families[1].send_receive,
AddPathSendReceive::Receive
);
}
#[test]
fn test_add_path_send_receive_values() {
use AddPathSendReceive::*;
assert_eq!(Receive as u8, 1);
assert_eq!(Send as u8, 2);
assert_eq!(SendReceive as u8, 3);
assert_eq!(AddPathSendReceive::try_from(1).unwrap(), Receive);
assert_eq!(AddPathSendReceive::try_from(2).unwrap(), Send);
assert_eq!(AddPathSendReceive::try_from(3).unwrap(), SendReceive);
assert!(AddPathSendReceive::try_from(4).is_err());
}
#[test]
fn test_add_path_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x00, 0x01, 0x01]); let result = AddPathCapability::parse(invalid_bytes);
assert!(result.is_err());
}
#[test]
fn test_add_path_capability_empty() {
let empty_bytes = Bytes::from(vec![]);
let parsed = AddPathCapability::parse(empty_bytes).unwrap();
assert_eq!(parsed.address_families.len(), 0);
let empty_capability = AddPathCapability::new(vec![]);
let encoded = empty_capability.encode();
assert_eq!(encoded.len(), 0);
}
#[test]
fn test_route_refresh_capability() {
let capability = RouteRefreshCapability::new();
let encoded = capability.encode();
assert_eq!(encoded.len(), 0);
let parsed = RouteRefreshCapability::parse(encoded).unwrap();
assert_eq!(parsed, capability);
}
#[test]
fn test_route_refresh_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x01]);
let result = RouteRefreshCapability::parse(invalid_bytes);
assert!(result.is_err());
}
#[test]
fn test_four_octet_as_capability() {
let test_cases = [0, 65535, 65536, 4294967295];
for asn in test_cases {
let capability = FourOctetAsCapability::new(asn);
let encoded = capability.encode();
assert_eq!(encoded.len(), 4);
let parsed = FourOctetAsCapability::parse(encoded).unwrap();
assert_eq!(parsed.asn, asn);
assert_eq!(parsed, capability);
}
}
#[test]
fn test_four_octet_as_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x00, 0x01, 0x00]); let result = FourOctetAsCapability::parse(invalid_bytes);
assert!(result.is_err());
}
#[test]
fn test_bgp_role_capability() {
use BgpRole::*;
let test_cases = [
(Provider, 0),
(RouteServer, 1),
(RouteServerClient, 2),
(Customer, 3),
(Peer, 4),
];
for (role, expected_value) in test_cases {
let capability = BgpRoleCapability::new(role);
let encoded = capability.encode();
assert_eq!(encoded.len(), 1);
assert_eq!(encoded[0], expected_value);
let parsed = BgpRoleCapability::parse(encoded).unwrap();
assert_eq!(parsed.role, role);
assert_eq!(parsed, capability);
}
}
#[test]
fn test_bgp_role_values() {
use BgpRole::*;
assert_eq!(Provider as u8, 0);
assert_eq!(RouteServer as u8, 1);
assert_eq!(RouteServerClient as u8, 2);
assert_eq!(Customer as u8, 3);
assert_eq!(Peer as u8, 4);
assert_eq!(BgpRole::try_from(0).unwrap(), Provider);
assert_eq!(BgpRole::try_from(1).unwrap(), RouteServer);
assert_eq!(BgpRole::try_from(2).unwrap(), RouteServerClient);
assert_eq!(BgpRole::try_from(3).unwrap(), Customer);
assert_eq!(BgpRole::try_from(4).unwrap(), Peer);
assert!(BgpRole::try_from(5).is_err());
assert!(BgpRole::try_from(255).is_err());
}
#[test]
fn test_bgp_role_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x00, 0x01]); let result = BgpRoleCapability::parse(invalid_bytes);
assert!(result.is_err());
let empty_bytes = Bytes::from(vec![]);
let result = BgpRoleCapability::parse(empty_bytes);
assert!(result.is_err());
}
#[test]
fn test_bgp_role_capability_invalid_value() {
let invalid_bytes = Bytes::from(vec![5]); let result = BgpRoleCapability::parse(invalid_bytes);
assert!(result.is_err());
}
#[test]
fn test_bgp_extended_message_capability() {
let capability = BgpExtendedMessageCapability::new();
let encoded = capability.encode();
assert_eq!(encoded.len(), 0);
let parsed = BgpExtendedMessageCapability::parse(encoded).unwrap();
assert_eq!(parsed, capability);
}
#[test]
fn test_bgp_extended_message_capability_invalid_length() {
let invalid_bytes = Bytes::from(vec![0x01]);
let result = BgpExtendedMessageCapability::parse(invalid_bytes);
assert!(result.is_err());
}
}