use crate::models::*;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::convert::TryFrom;
use std::net::Ipv4Addr;
use crate::error::ParserError;
use crate::models::capabilities::{
AddPathCapability, BgpCapabilityType, BgpExtendedMessageCapability, BgpRoleCapability,
ExtendedNextHopCapability, FourOctetAsCapability, GracefulRestartCapability,
MultiprotocolExtensionsCapability, RouteRefreshCapability,
};
use crate::models::error::BgpError;
use crate::parser::bgp::attributes::parse_attributes;
use crate::parser::{encode_nlri_prefixes, parse_nlri_list, ReadUtils};
use log::warn;
use zerocopy::big_endian::{U16, U32};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct RawBgpOpenHeader {
version: u8,
asn: U16,
hold_time: U16,
bgp_identifier: U32,
opt_params_len: u8,
}
const _: () = assert!(size_of::<RawBgpOpenHeader>() == 10);
pub fn parse_bgp_message(
data: &mut Bytes,
add_path: bool,
asn_len: &AsnLength,
) -> Result<BgpMessage, ParserError> {
let total_size = data.len();
data.has_n_remaining(19)?;
let mut marker = [0u8; 16];
data.copy_to_slice(&mut marker);
if marker != [0xFF; 16] {
warn!("BGP message marker is not all 0xFF bytes (invalid per RFC 4271)");
}
let length = data.read_u16()?;
let max_length = 65535; if !(19..=max_length).contains(&length) {
return Err(ParserError::ParseError(format!(
"invalid BGP message length {length}"
)));
}
let length_usize = length as usize;
let bgp_msg_length = if length_usize > total_size {
total_size.saturating_sub(19)
} else {
length_usize.saturating_sub(19)
};
let msg_type: BgpMessageType = match BgpMessageType::try_from(data.read_u8()?) {
Ok(t) => t,
Err(_) => {
return Err(ParserError::ParseError(
"Unknown BGP Message Type".to_string(),
))
}
};
match msg_type {
BgpMessageType::OPEN | BgpMessageType::KEEPALIVE => {
if length > 4096 {
return Err(ParserError::ParseError(format!(
"BGP {} message length {} exceeds maximum allowed 4096 bytes (RFC 8654)",
match msg_type {
BgpMessageType::OPEN => "OPEN",
BgpMessageType::KEEPALIVE => "KEEPALIVE",
_ => unreachable!(),
},
length
)));
}
}
BgpMessageType::UPDATE | BgpMessageType::NOTIFICATION => {
}
}
if data.remaining() != bgp_msg_length {
warn!(
"BGP message length {} does not match the actual length {} (parsing BGP message)",
bgp_msg_length,
data.remaining()
);
}
data.has_n_remaining(bgp_msg_length)?;
let mut msg_data = data.split_to(bgp_msg_length);
Ok(match msg_type {
BgpMessageType::OPEN => BgpMessage::Open(parse_bgp_open_message(&mut msg_data)?),
BgpMessageType::UPDATE => {
BgpMessage::Update(parse_bgp_update_message(msg_data, add_path, asn_len)?)
}
BgpMessageType::NOTIFICATION => {
BgpMessage::Notification(parse_bgp_notification_message(msg_data)?)
}
BgpMessageType::KEEPALIVE => BgpMessage::KeepAlive,
})
}
pub fn parse_bgp_notification_message(
mut input: Bytes,
) -> Result<BgpNotificationMessage, ParserError> {
let error_code = input.read_u8()?;
let error_subcode = input.read_u8()?;
Ok(BgpNotificationMessage {
error: BgpError::new(error_code, error_subcode),
data: input.read_n_bytes(input.len())?,
})
}
impl BgpNotificationMessage {
pub fn encode(&self) -> Bytes {
let mut buf = BytesMut::new();
let (code, subcode) = self.error.get_codes();
buf.put_u8(code);
buf.put_u8(subcode);
buf.put_slice(&self.data);
buf.freeze()
}
}
pub fn parse_bgp_open_message(input: &mut Bytes) -> Result<BgpOpenMessage, ParserError> {
input.has_n_remaining(10)?;
let mut header_bytes = [0u8; 10];
input.copy_to_slice(&mut header_bytes);
let raw = RawBgpOpenHeader::ref_from_bytes(&header_bytes)
.expect("header_bytes is exactly 10 bytes with no alignment requirement");
let version = raw.version;
let asn = Asn::new_16bit(raw.asn.get());
let hold_time = raw.hold_time.get();
let bgp_identifier = Ipv4Addr::from(raw.bgp_identifier.get());
let mut opt_params_len: u16 = raw.opt_params_len as u16;
let mut extended_length = false;
let mut first = true;
let mut params: Vec<OptParam> = vec![];
while input.remaining() >= 2 {
let mut param_type = input.read_u8()?;
if first {
if opt_params_len == 0 && param_type == 255 {
return Err(ParserError::ParseError(
"RFC 9072 violation: Non-Extended Optional Parameters Length must not be 0 when using extended format".to_string()
));
}
if opt_params_len != 0 && param_type == 255 {
extended_length = true;
opt_params_len = input.read_u16()?;
if opt_params_len == 0 {
break;
}
if input.remaining() != opt_params_len as usize {
warn!(
"BGP open message length {} does not match the actual length {} (parsing BGP OPEN message)",
opt_params_len,
input.remaining()
);
}
param_type = input.read_u8()?;
}
first = false;
}
let param_len = match extended_length {
true => input.read_u16()?,
false => input.read_u8()? as u16,
};
let param_value = match param_type {
2 => {
let mut capacities = vec![];
input.has_n_remaining(param_len as usize)?;
let mut param_data = input.split_to(param_len as usize);
while param_data.remaining() >= 2 {
let code = param_data.read_u8()?;
let len = param_data.read_u8()? as u16;
let capability_data = param_data.read_n_bytes(len as usize)?;
let capability_type = BgpCapabilityType::from(code);
macro_rules! parse_capability {
($parser:path, $variant:ident) => {
match $parser(Bytes::from(capability_data.clone())) {
Ok(parsed) => CapabilityValue::$variant(parsed),
Err(_) => CapabilityValue::Raw(capability_data),
}
};
}
let capability_value = match capability_type {
BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4 => {
parse_capability!(
MultiprotocolExtensionsCapability::parse,
MultiprotocolExtensions
)
}
BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4 => {
parse_capability!(RouteRefreshCapability::parse, RouteRefresh)
}
BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING => {
parse_capability!(ExtendedNextHopCapability::parse, ExtendedNextHop)
}
BgpCapabilityType::GRACEFUL_RESTART_CAPABILITY => {
parse_capability!(GracefulRestartCapability::parse, GracefulRestart)
}
BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY => {
parse_capability!(FourOctetAsCapability::parse, FourOctetAs)
}
BgpCapabilityType::ADD_PATH_CAPABILITY => {
parse_capability!(AddPathCapability::parse, AddPath)
}
BgpCapabilityType::BGP_ROLE => {
parse_capability!(BgpRoleCapability::parse, BgpRole)
}
BgpCapabilityType::BGP_EXTENDED_MESSAGE => {
parse_capability!(
BgpExtendedMessageCapability::parse,
BgpExtendedMessage
)
}
_ => CapabilityValue::Raw(capability_data),
};
capacities.push(Capability {
ty: capability_type,
value: capability_value,
});
}
ParamValue::Capacities(capacities)
}
_ => {
let bytes = input.read_n_bytes(param_len as usize)?;
ParamValue::Raw(bytes)
}
};
params.push(OptParam {
param_type,
param_len,
param_value,
});
}
Ok(BgpOpenMessage {
version,
asn,
hold_time,
bgp_identifier,
extended_length,
opt_params: params,
})
}
impl BgpOpenMessage {
pub fn encode(&self) -> Bytes {
let mut buf = BytesMut::new();
let raw_header = RawBgpOpenHeader {
version: self.version,
asn: U16::new(self.asn.into()),
hold_time: U16::new(self.hold_time),
bgp_identifier: U32::new(u32::from(self.bgp_identifier)),
opt_params_len: self.opt_params.len() as u8,
};
buf.extend_from_slice(raw_header.as_bytes());
for param in &self.opt_params {
buf.put_u8(param.param_type);
buf.put_u8(param.param_len as u8);
match ¶m.param_value {
ParamValue::Capacities(capacities) => {
for cap in capacities {
buf.put_u8(cap.ty.into());
let encoded_value = match &cap.value {
CapabilityValue::MultiprotocolExtensions(mp) => mp.encode(),
CapabilityValue::RouteRefresh(rr) => rr.encode(),
CapabilityValue::ExtendedNextHop(enh) => enh.encode(),
CapabilityValue::GracefulRestart(gr) => gr.encode(),
CapabilityValue::FourOctetAs(foa) => foa.encode(),
CapabilityValue::AddPath(ap) => ap.encode(),
CapabilityValue::BgpRole(br) => br.encode(),
CapabilityValue::BgpExtendedMessage(bem) => bem.encode(),
CapabilityValue::Raw(raw) => Bytes::from(raw.clone()),
};
buf.put_u8(encoded_value.len() as u8);
buf.extend(&encoded_value);
}
}
ParamValue::Raw(bytes) => {
buf.extend(bytes);
}
}
}
buf.freeze()
}
}
fn read_nlri(
mut input: Bytes,
afi: &Afi,
add_path: bool,
) -> Result<Vec<NetworkPrefix>, ParserError> {
let length = input.len();
if length == 0 {
return Ok(vec![]);
}
if length == 1 {
warn!("seeing strange one-byte NLRI field (parsing NLRI in BGP UPDATE message)");
input.advance(1); return Ok(vec![]);
}
parse_nlri_list(input, add_path, afi)
}
pub fn parse_bgp_update_message(
mut input: Bytes,
add_path: bool,
asn_len: &AsnLength,
) -> Result<BgpUpdateMessage, ParserError> {
let afi = Afi::Ipv4;
let withdrawn_bytes_length_raw = input.read_u16()?;
let withdrawn_bytes_length = withdrawn_bytes_length_raw as usize;
input.has_n_remaining(withdrawn_bytes_length)?;
let withdrawn_bytes = input.split_to(withdrawn_bytes_length);
let withdrawn_prefixes = read_nlri(withdrawn_bytes, &afi, add_path)?;
let attribute_length_raw = input.read_u16()?;
let attribute_length = attribute_length_raw as usize;
input.has_n_remaining(attribute_length)?;
let attr_data_slice = input.split_to(attribute_length);
let attributes = parse_attributes(attr_data_slice, asn_len, add_path, None, None, None)?;
let announced_prefixes = read_nlri(input, &afi, add_path)?;
Ok(BgpUpdateMessage {
withdrawn_prefixes,
attributes,
announced_prefixes,
})
}
impl BgpUpdateMessage {
pub fn encode(&self, asn_len: AsnLength) -> Bytes {
let mut bytes = BytesMut::new();
let withdrawn_bytes = encode_nlri_prefixes(&self.withdrawn_prefixes);
bytes.put_u16(withdrawn_bytes.len() as u16);
bytes.put_slice(&withdrawn_bytes);
let attr_bytes = self.attributes.encode(asn_len);
bytes.put_u16(attr_bytes.len() as u16);
bytes.put_slice(&attr_bytes);
bytes.extend(encode_nlri_prefixes(&self.announced_prefixes));
bytes.freeze()
}
pub fn is_end_of_rib(&self) -> bool {
if !self.announced_prefixes.is_empty() || !self.withdrawn_prefixes.is_empty() {
return false;
}
if self.attributes.inner.is_empty() {
return true;
}
if self.attributes.inner.len() > 1 {
return false;
}
if let AttributeValue::MpUnreachNlri(nlri) = &self.attributes.inner.first().unwrap().value {
if nlri.prefixes.is_empty() {
return true;
}
}
false
}
}
impl BgpMessage {
const MARKER: [u8; 16] = [0xFF; 16];
pub fn encode(&self, asn_len: AsnLength) -> Bytes {
let mut bytes = BytesMut::new();
bytes.put_slice(&Self::MARKER);
let (msg_type, msg_bytes) = match self {
BgpMessage::Open(msg) => (BgpMessageType::OPEN, msg.encode()),
BgpMessage::Update(msg) => (BgpMessageType::UPDATE, msg.encode(asn_len)),
BgpMessage::Notification(msg) => (BgpMessageType::NOTIFICATION, msg.encode()),
BgpMessage::KeepAlive => (BgpMessageType::KEEPALIVE, Bytes::new()),
};
bytes.put_u16(msg_bytes.len() as u16 + 16 + 2 + 1);
bytes.put_u8(msg_type as u8);
bytes.put_slice(&msg_bytes);
bytes.freeze()
}
}
impl From<&BgpElem> for BgpUpdateMessage {
fn from(elem: &BgpElem) -> Self {
BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: Attributes::from(elem),
announced_prefixes: vec![],
}
}
}
impl From<BgpUpdateMessage> for BgpMessage {
fn from(value: BgpUpdateMessage) -> Self {
BgpMessage::Update(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::Ipv4Addr;
use std::str::FromStr;
#[test]
fn test_end_of_rib() {
let attrs = Attributes::default();
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(msg.is_end_of_rib());
let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: None,
prefixes: vec![],
link_state_nlris: None,
flowspec_nlris: None,
})]);
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(msg.is_end_of_rib());
let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
let attrs = Attributes::default();
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![prefix],
};
assert!(!msg.is_end_of_rib());
let prefix = NetworkPrefix::from_str("192.168.1.0/24").unwrap();
let attrs = Attributes::default();
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![prefix],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(!msg.is_end_of_rib());
let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: None,
prefixes: vec![],
link_state_nlris: None,
flowspec_nlris: None,
})]);
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(!msg.is_end_of_rib());
let attrs = Attributes::from_iter(vec![AttributeValue::MpReachNlri(Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: None,
prefixes: vec![prefix],
link_state_nlris: None,
flowspec_nlris: None,
})]);
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(!msg.is_end_of_rib());
let attrs = Attributes::from_iter(vec![AttributeValue::MpUnreachNlri(Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: None,
prefixes: vec![prefix],
link_state_nlris: None,
flowspec_nlris: None,
})]);
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(!msg.is_end_of_rib());
let attrs = Attributes::from_iter(vec![
AttributeValue::MpUnreachNlri(Nlri {
afi: Afi::Ipv4,
safi: Safi::Unicast,
next_hop: None,
prefixes: vec![],
link_state_nlris: None,
flowspec_nlris: None,
}),
AttributeValue::AtomicAggregate,
]);
let msg = BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes: attrs,
announced_prefixes: vec![],
};
assert!(!msg.is_end_of_rib());
}
#[test]
fn test_invlaid_length() {
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ]);
let mut data = bytes.clone();
assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x05, ]);
let mut data = bytes.clone();
assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
}
#[test]
fn test_invlaid_type() {
let bytes = Bytes::from_static(&[
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x28, 0x05, ]);
let mut data = bytes.clone();
assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_err());
}
#[test]
fn test_bgp_message_length_underflow_protection() {
for len in [0u16, 1, 18] {
let bytes = Bytes::from(vec![
0xFF,
0xFF,
0xFF,
0xFF, 0xFF,
0xFF,
0xFF,
0xFF, 0xFF,
0xFF,
0xFF,
0xFF, 0xFF,
0xFF,
0xFF,
0xFF, (len >> 8) as u8,
(len & 0xFF) as u8, 0x01, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(
result.is_err(),
"Length {} should be rejected as invalid",
len
);
}
}
#[test]
fn test_bgp_marker_encoding_rfc4271() {
let msg = BgpMessage::KeepAlive;
let encoded = msg.encode(AsnLength::Bits16);
assert_eq!(
&encoded[..16],
&[0xFF; 16],
"BGP marker should be all 0xFF bytes"
);
}
#[test]
fn test_bgp_marker_validation() {
let valid_bytes = Bytes::from(vec![
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x13, 0x04, ]);
let mut data = valid_bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(result.is_ok(), "Valid marker should parse successfully");
let invalid_bytes = Bytes::from(vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x04, ]);
let mut data = invalid_bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(
result.is_ok(),
"Invalid marker should still parse (with warning)"
);
}
#[test]
fn test_attribute_length_overflow_protection() {
let update_bytes = Bytes::from(vec![
0x00, 0x00, 0xFF,
0xFF, ]);
let result = parse_bgp_update_message(update_bytes, false, &AsnLength::Bits16);
assert!(
result.is_err(),
"Should fail when attribute_length exceeds available data"
);
assert!(
matches!(result, Err(ParserError::TruncatedMsg(_))),
"Should fail with TruncatedMsg error"
);
let valid_update = Bytes::from(vec![
0x00, 0x00, 0x00,
0x00, ]);
let result = parse_bgp_update_message(valid_update, false, &AsnLength::Bits16);
assert!(result.is_ok(), "Should parse valid empty UPDATE");
}
#[test]
fn test_parse_bgp_notification_message() {
let bytes = Bytes::from_static(&[
0x01, 0x02, 0x00, 0x00, ]);
let msg = parse_bgp_notification_message(bytes).unwrap();
matches!(
msg.error,
BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH)
);
assert_eq!(msg.data, Bytes::from_static(&[0x00, 0x00]));
}
#[test]
fn test_encode_bgp_notification_messsage() {
let msg = BgpNotificationMessage {
error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
data: vec![0x00, 0x00],
};
let bytes = msg.encode();
assert_eq!(bytes, Bytes::from_static(&[0x01, 0x02, 0x00, 0x00]));
}
#[test]
fn test_parse_bgp_open_message() {
let bytes = Bytes::from_static(&[
0x04, 0x00, 0x01, 0x00, 0xb4, 0xc0, 0x00, 0x02, 0x01, 0x00, ]);
let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
assert_eq!(msg.version, 4);
assert_eq!(msg.asn, Asn::new_16bit(1));
assert_eq!(msg.hold_time, 180);
assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
assert!(!msg.extended_length);
assert_eq!(msg.opt_params.len(), 0);
}
#[test]
fn test_encode_bgp_open_message() {
let msg = BgpOpenMessage {
version: 4,
asn: Asn::new_16bit(1),
hold_time: 180,
bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
extended_length: false,
opt_params: vec![],
};
let bytes = msg.encode();
assert_eq!(
bytes,
Bytes::from_static(&[
0x04, 0x00, 0x01, 0x00, 0xb4, 0xc0, 0x00, 0x02, 0x01, 0x00, ])
);
}
#[test]
fn test_encode_bgp_notification_message() {
let bgp_message = BgpMessage::Notification(BgpNotificationMessage {
error: BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH),
data: vec![0x00, 0x00],
});
let bytes = bgp_message.encode(AsnLength::Bits16);
assert_eq!(
bytes,
Bytes::from_static(&[
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x17, 0x03, 0x01, 0x02, 0x00, 0x00 ])
);
}
#[test]
fn test_bgp_message_from_bgp_update_message() {
let msg = BgpMessage::from(BgpUpdateMessage::default());
assert!(matches!(msg, BgpMessage::Update(_)));
}
#[test]
fn test_parse_bgp_open_message_with_extended_next_hop_capability() {
use crate::models::{Afi, Safi};
let bytes = Bytes::from(vec![
0x04, 0xfd, 0xe9, 0x00, 0xb4, 0xc0, 0x00, 0x02, 0x01, 0x10, 0x02, 0x0e, 0x05, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x80, 0x00, 0x02, ]);
let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
assert_eq!(msg.version, 4);
assert_eq!(msg.asn, Asn::new_16bit(65001));
assert_eq!(msg.hold_time, 180);
assert_eq!(msg.bgp_identifier, Ipv4Addr::new(192, 0, 2, 1));
assert!(!msg.extended_length);
assert_eq!(msg.opt_params.len(), 1);
if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
assert_eq!(enh_cap.entries.len(), 2);
let entry1 = &enh_cap.entries[0];
assert_eq!(entry1.nlri_afi, Afi::Ipv4);
assert_eq!(entry1.nlri_safi, Safi::Unicast);
assert_eq!(entry1.nexthop_afi, Afi::Ipv6);
let entry2 = &enh_cap.entries[1];
assert_eq!(entry2.nlri_afi, Afi::Ipv4);
assert_eq!(entry2.nlri_safi, Safi::MplsVpn);
assert_eq!(entry2.nexthop_afi, Afi::Ipv6);
assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
assert!(!enh_cap.supports(Afi::Ipv4, Safi::Multicast, Afi::Ipv6));
} else {
panic!("Expected ExtendedNextHop capability value");
}
} else {
panic!("Expected capability parameter");
}
}
#[test]
fn test_rfc8654_extended_message_length_validation() {
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, ]);
let mut data = bytes.clone();
assert!(parse_bgp_message(&mut data, false, &AsnLength::Bits16).is_ok());
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(result.is_err());
if let Err(ParserError::ParseError(msg)) = result {
assert!(msg.contains("BGP OPEN message length"));
assert!(msg.contains("4096 bytes"));
}
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x04, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(result.is_err());
if let Err(ParserError::ParseError(msg)) = result {
assert!(msg.contains("BGP KEEPALIVE message length"));
assert!(msg.contains("4096 bytes"));
}
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x02, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
if let Err(ParserError::ParseError(msg)) = result {
assert!(!msg.contains("invalid BGP message length"));
}
}
#[test]
fn test_bgp_extended_message_capability_parsing() {
use crate::models::CapabilityValue;
let bytes = Bytes::from(vec![
0x04, 0x00, 0x01, 0x00, 0xb4, 0xc0, 0x00, 0x02, 0x01, 0x04, 0x02, 0x02, 0x06, 0x00, ]);
let msg = parse_bgp_open_message(&mut bytes.clone()).unwrap();
assert_eq!(msg.version, 4);
assert_eq!(msg.asn, Asn::new_16bit(1));
assert_eq!(msg.opt_params.len(), 1);
if let ParamValue::Capacities(cap) = &msg.opt_params[0].param_value {
assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
} else {
panic!("Expected BgpExtendedMessage capability value");
}
} else {
panic!("Expected capability parameter");
}
}
#[test]
fn test_rfc8654_edge_cases() {
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x06, 0x00, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
if let Err(ParserError::ParseError(msg)) = result {
assert!(!msg.contains("invalid BGP message length"));
assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
}
let open_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, ];
let bytes = Bytes::from(open_data);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
if let Err(ParserError::ParseError(msg)) = result {
assert!(!msg.contains("exceeds maximum allowed 4096 bytes"));
}
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x02, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
if let Err(ParserError::ParseError(msg)) = result {
assert!(!msg.contains("invalid BGP message length"));
}
}
#[test]
fn test_rfc8654_capability_encoding_path() {
use crate::models::capabilities::BgpExtendedMessageCapability;
let capability_value =
CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability::new());
let capability = Capability {
ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
value: capability_value,
};
let opt_param = OptParam {
param_type: 2, param_len: 2,
param_value: ParamValue::Capacities(vec![capability]),
};
let msg = BgpOpenMessage {
version: 4,
asn: Asn::new_16bit(65001),
hold_time: 180,
bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
extended_length: false,
opt_params: vec![opt_param],
};
let encoded = msg.encode();
assert!(!encoded.is_empty());
let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
assert_eq!(parsed.opt_params.len(), 1);
if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
}
}
#[test]
fn test_rfc8654_error_message_formatting() {
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(result.is_err());
if let Err(ParserError::ParseError(msg)) = result {
assert!(msg.contains("BGP OPEN message length"));
assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
}
let bytes = Bytes::from_static(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x04, ]);
let mut data = bytes.clone();
let result = parse_bgp_message(&mut data, false, &AsnLength::Bits16);
assert!(result.is_err());
if let Err(ParserError::ParseError(msg)) = result {
assert!(msg.contains("BGP KEEPALIVE message length"));
assert!(msg.contains("exceeds maximum allowed 4096 bytes"));
}
}
#[test]
fn test_encode_bgp_open_message_with_extended_message_capability() {
use crate::models::capabilities::BgpExtendedMessageCapability;
let extended_msg_capability = BgpExtendedMessageCapability::new();
let msg = BgpOpenMessage {
version: 4,
asn: Asn::new_16bit(65001),
hold_time: 180,
bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
extended_length: false,
opt_params: vec![OptParam {
param_type: 2, param_len: 2, param_value: ParamValue::Capacities(vec![Capability {
ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
value: CapabilityValue::BgpExtendedMessage(extended_msg_capability),
}]),
}],
};
let encoded = msg.encode();
let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
assert_eq!(parsed.version, msg.version);
assert_eq!(parsed.asn, msg.asn);
assert_eq!(parsed.hold_time, msg.hold_time);
assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
assert_eq!(parsed.opt_params.len(), 1);
if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
assert_eq!(cap[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
if let CapabilityValue::BgpExtendedMessage(_) = &cap[0].value {
} else {
panic!("Expected BgpExtendedMessage capability value after round trip");
}
} else {
panic!("Expected capability parameter after round trip");
}
}
#[test]
fn test_encode_bgp_open_message_with_extended_next_hop_capability() {
use crate::models::capabilities::{ExtendedNextHopCapability, ExtendedNextHopEntry};
use crate::models::{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 enh_capability = ExtendedNextHopCapability::new(entries);
let msg = BgpOpenMessage {
version: 4,
asn: Asn::new_16bit(65001),
hold_time: 180,
bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
extended_length: false,
opt_params: vec![OptParam {
param_type: 2, param_len: 14, param_value: ParamValue::Capacities(vec![Capability {
ty: BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING,
value: CapabilityValue::ExtendedNextHop(enh_capability),
}]),
}],
};
let encoded = msg.encode();
let parsed = parse_bgp_open_message(&mut encoded.clone()).unwrap();
assert_eq!(parsed.version, msg.version);
assert_eq!(parsed.asn, msg.asn);
assert_eq!(parsed.hold_time, msg.hold_time);
assert_eq!(parsed.bgp_identifier, msg.bgp_identifier);
assert_eq!(parsed.extended_length, msg.extended_length);
assert_eq!(parsed.opt_params.len(), 1);
if let ParamValue::Capacities(cap) = &parsed.opt_params[0].param_value {
assert_eq!(cap[0].ty, BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING);
if let CapabilityValue::ExtendedNextHop(enh_cap) = &cap[0].value {
assert_eq!(enh_cap.entries.len(), 2);
assert!(enh_cap.supports(Afi::Ipv4, Safi::Unicast, Afi::Ipv6));
assert!(enh_cap.supports(Afi::Ipv4, Safi::MplsVpn, Afi::Ipv6));
} else {
panic!("Expected ExtendedNextHop capability value after round trip");
}
} else {
panic!("Expected capability parameter after round trip");
}
}
#[test]
fn test_parse_bgp_open_message_with_multiple_capabilities() {
let extended_msg_cap = Capability {
ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
};
let route_refresh_cap = Capability {
ty: BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4,
value: CapabilityValue::RouteRefresh(RouteRefreshCapability {}),
};
let four_octet_as_cap = Capability {
ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
value: CapabilityValue::FourOctetAs(FourOctetAsCapability { asn: 65536 }),
};
let msg = BgpOpenMessage {
version: 4,
asn: Asn::new_32bit(65000),
hold_time: 180,
bgp_identifier: "10.0.0.1".parse().unwrap(),
extended_length: false,
opt_params: vec![OptParam {
param_type: 2, param_len: 10, param_value: ParamValue::Capacities(vec![
extended_msg_cap,
route_refresh_cap,
four_octet_as_cap,
]),
}],
};
let encoded = msg.encode();
let mut encoded_bytes = encoded.clone();
let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
assert_eq!(parsed.version, 4);
assert_eq!(parsed.asn, Asn::new_32bit(65000));
assert_eq!(parsed.hold_time, 180);
assert_eq!(
parsed.bgp_identifier,
"10.0.0.1".parse::<std::net::Ipv4Addr>().unwrap()
);
assert_eq!(parsed.opt_params.len(), 1);
if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
assert_eq!(caps.len(), 3, "Should have 3 capabilities");
assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
assert!(matches!(
caps[0].value,
CapabilityValue::BgpExtendedMessage(_)
));
assert_eq!(
caps[1].ty,
BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4
);
assert!(matches!(caps[1].value, CapabilityValue::RouteRefresh(_)));
assert_eq!(
caps[2].ty,
BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
);
if let CapabilityValue::FourOctetAs(foa) = &caps[2].value {
assert_eq!(foa.asn, 65536);
} else {
panic!("Expected FourOctetAs capability value");
}
} else {
panic!("Expected Capacities parameter");
}
}
#[test]
fn test_parse_bgp_open_message_with_multiple_capability_parameters() {
let msg = BgpOpenMessage {
version: 4,
asn: Asn::new_32bit(65001),
hold_time: 90,
bgp_identifier: "192.168.1.1".parse().unwrap(),
extended_length: false,
opt_params: vec![
OptParam {
param_type: 2, param_len: 2,
param_value: ParamValue::Capacities(vec![Capability {
ty: BgpCapabilityType::BGP_EXTENDED_MESSAGE,
value: CapabilityValue::BgpExtendedMessage(BgpExtendedMessageCapability {}),
}]),
},
OptParam {
param_type: 2, param_len: 6,
param_value: ParamValue::Capacities(vec![Capability {
ty: BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY,
value: CapabilityValue::FourOctetAs(FourOctetAsCapability {
asn: 4200000000,
}),
}]),
},
],
};
let encoded = msg.encode();
let mut encoded_bytes = encoded.clone();
let parsed = parse_bgp_open_message(&mut encoded_bytes).unwrap();
assert_eq!(parsed.opt_params.len(), 2);
if let ParamValue::Capacities(caps) = &parsed.opt_params[0].param_value {
assert_eq!(caps.len(), 1);
assert_eq!(caps[0].ty, BgpCapabilityType::BGP_EXTENDED_MESSAGE);
} else {
panic!("Expected Capacities in first parameter");
}
if let ParamValue::Capacities(caps) = &parsed.opt_params[1].param_value {
assert_eq!(caps.len(), 1);
assert_eq!(
caps[0].ty,
BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
);
if let CapabilityValue::FourOctetAs(foa) = &caps[0].value {
assert_eq!(foa.asn, 4200000000);
}
} else {
panic!("Expected Capacities in second parameter");
}
}
}