use bgp_models::bgp::*;
use bgp_models::network::*;
use byteorder::{ReadBytesExt, BE};
use num_traits::FromPrimitive;
use std::io::{Cursor, Seek, SeekFrom};
use crate::error::ParserError;
use crate::parser::{parse_nlri_list, AttributeParser, ReadUtils};
use log::warn;
pub fn parse_bgp_message(
data: &[u8],
add_path: bool,
asn_len: &AsnLength,
) -> Result<BgpMessage, ParserError> {
let total_size = data.len();
let mut input = Cursor::new(data);
input.read_32b()?;
input.read_32b()?;
input.read_32b()?;
input.read_32b()?;
let length = input.read_u16::<BE>()?;
if !(19..=4096).contains(&length) {
return Err(ParserError::ParseError(format!(
"invalid BGP message length {}",
length
)));
}
let bgp_msg_length = if (length as usize) > total_size {
(total_size - 19) as u64
} else {
(length - 19) as u64
};
let msg_type: BgpMessageType = match BgpMessageType::from_u8(input.read_8b()?) {
Some(t) => t,
None => {
return Err(ParserError::ParseError(
"Unknown BGP Message Type".to_string(),
))
}
};
Ok(match msg_type {
BgpMessageType::OPEN => BgpMessage::Open(parse_bgp_open_message(&mut input)?),
BgpMessageType::UPDATE => BgpMessage::Update(parse_bgp_update_message(
&mut input,
add_path,
asn_len,
bgp_msg_length,
)?),
BgpMessageType::NOTIFICATION => {
BgpMessage::Notification(parse_bgp_notification_message(&mut input, bgp_msg_length)?)
}
BgpMessageType::KEEPALIVE => BgpMessage::KeepAlive(BgpKeepAliveMessage {}),
})
}
pub fn parse_bgp_notification_message(
input: &mut Cursor<&[u8]>,
bgp_msg_length: u64,
) -> Result<BgpNotificationMessage, ParserError> {
let error_code = input.read_8b()?;
let error_subcode = input.read_8b()?;
let error_type = match parse_error_codes(&error_code, &error_subcode) {
Ok(t) => Some(t),
Err(e) => {
warn!("error parsing BGP notification error code: {}", e);
None
}
};
let data = input.read_n_bytes((bgp_msg_length - 2) as usize)?;
Ok(BgpNotificationMessage {
error_code,
error_subcode,
error_type,
data,
})
}
pub fn parse_bgp_open_message(input: &mut Cursor<&[u8]>) -> Result<BgpOpenMessage, ParserError> {
let version = input.read_8b()?;
let asn = Asn {
asn: input.read_u16::<BE>()? as u32,
len: AsnLength::Bits16,
};
let hold_time = input.read_u16::<BE>()?;
let sender_ip = input.read_ipv4_address()?;
let opt_params_len = input.read_8b()?;
let pos_end = input.position() + opt_params_len as u64;
let mut extended_length = false;
let mut first = true;
let mut params: Vec<OptParam> = vec![];
while input.position() < pos_end {
let param_type = input.read_8b()?;
if first {
if opt_params_len == 255 && param_type == 255 {
extended_length = true;
break;
} else {
first = false;
}
}
let parm_length = input.read_8b()?;
let param_value = match param_type {
2 => {
let code = input.read_8b()?;
let len = input.read_8b()?;
let value = input.read_n_bytes(len as usize)?;
let capability_type = match parse_capability(&code) {
Ok(t) => Some(t),
Err(e) => {
warn!("error parsing BGP capability code: {}", e.to_string());
None
}
};
ParamValue::Capability(Capability {
code,
len,
value,
capability_type,
})
}
_ => {
let bytes = input.read_n_bytes(parm_length as usize)?;
ParamValue::Raw(bytes)
}
};
params.push(OptParam {
param_type,
param_len: parm_length as u16,
param_value,
});
}
Ok(BgpOpenMessage {
version,
asn,
hold_time,
sender_ip,
extended_length,
opt_params: params,
})
}
fn read_nlri(
input: &mut Cursor<&[u8]>,
length: usize,
afi: &Afi,
add_path: bool,
) -> Result<Vec<NetworkPrefix>, ParserError> {
if length == 0 {
return Ok(vec![]);
}
if length == 1 {
warn!("seeing strange one-byte NLRI field");
input.read_8b().unwrap();
return Ok(vec![]);
}
parse_nlri_list(input, add_path, afi, length as u64)
}
pub fn parse_bgp_update_message(
input: &mut Cursor<&[u8]>,
add_path: bool,
asn_len: &AsnLength,
bgp_msg_length: u64,
) -> Result<BgpUpdateMessage, ParserError> {
let afi = Afi::Ipv4;
let withdrawn_length = input.read_u16::<BE>()? as u64;
let withdrawn_prefixes = read_nlri(input, withdrawn_length as usize, &afi, add_path)?;
let attribute_length = input.read_u16::<BE>()? as usize;
let attr_parser = AttributeParser::new(add_path);
let pos_start = input.position() as usize;
let pos_end = pos_start + attribute_length;
let attr_data_slice = &input.get_ref()[pos_start..pos_end];
let attributes = attr_parser.parse_attributes(attr_data_slice, asn_len, None, None, None)?;
input.seek(SeekFrom::Start(pos_end as u64))?;
let nlri_length = bgp_msg_length - 4 - withdrawn_length - attribute_length as u64;
let announced_prefixes = read_nlri(input, nlri_length as usize, &afi, add_path)?;
Ok(BgpUpdateMessage {
withdrawn_prefixes,
attributes,
announced_prefixes,
})
}