use crate::error::ParserError;
use crate::parser::{AttributeParser, ReadUtils};
use bgp_models::mrt::tabledump::{
Peer, PeerIndexTable, RibAfiEntries, RibEntry, TableDumpV2Message, TableDumpV2Type,
};
use bgp_models::network::*;
use byteorder::{ReadBytesExt, BE};
use log::warn;
use num_traits::FromPrimitive;
use std::collections::HashMap;
use std::io::{Cursor, Seek, SeekFrom};
use std::net::{IpAddr, Ipv4Addr};
pub fn parse_table_dump_v2_message(
sub_type: u16,
input: &[u8],
) -> Result<TableDumpV2Message, ParserError> {
let v2_type: TableDumpV2Type = match TableDumpV2Type::from_u16(sub_type) {
Some(t) => t,
None => {
return Err(ParserError::ParseError(format!(
"cannot parse table dump v2 type: {}",
sub_type
)))
}
};
let msg: TableDumpV2Message = match v2_type {
TableDumpV2Type::PeerIndexTable => {
TableDumpV2Message::PeerIndexTable(parse_peer_index_table(input)?)
}
TableDumpV2Type::RibIpv4Unicast
| TableDumpV2Type::RibIpv4Multicast
| TableDumpV2Type::RibIpv6Unicast
| TableDumpV2Type::RibIpv6Multicast
| TableDumpV2Type::RibIpv4UnicastAddPath
| TableDumpV2Type::RibIpv4MulticastAddPath
| TableDumpV2Type::RibIpv6UnicastAddPath
| TableDumpV2Type::RibIpv6MulticastAddPath => {
TableDumpV2Message::RibAfiEntries(parse_rib_afi_entries(input, v2_type)?)
}
TableDumpV2Type::RibGeneric
| TableDumpV2Type::RibGenericAddPath
| TableDumpV2Type::GeoPeerTable => {
return Err(ParserError::Unsupported(
"TableDumpV2 RibGeneric and GeoPeerTable is not currently supported".to_string(),
))
}
};
Ok(msg)
}
pub fn parse_peer_index_table(data: &[u8]) -> Result<PeerIndexTable, ParserError> {
let mut input = Cursor::new(data);
let collector_bgp_id = Ipv4Addr::from(input.read_32b()?);
let view_name_length = input.read_u16::<BE>()?;
input.seek(SeekFrom::Current(view_name_length as i64))?;
let peer_count = input.read_u16::<BE>()?;
let mut peers = vec![];
for _index in 0..peer_count {
let peer_type = input.read_8b()?;
let afi = match peer_type & 1 {
1 => Afi::Ipv6,
_ => Afi::Ipv4,
};
let asn_len = match peer_type & 2 {
2 => AsnLength::Bits32,
_ => AsnLength::Bits16,
};
let peer_bgp_id = Ipv4Addr::from(input.read_32b()?);
let peer_address: IpAddr = input.read_address(&afi)?;
let peer_asn = input.read_asn(&asn_len)?;
peers.push(Peer {
peer_type,
peer_bgp_id,
peer_address,
peer_asn,
})
}
let mut peers_map = HashMap::new();
for (id, p) in peers.into_iter().enumerate() {
peers_map.insert(id as u32, p);
}
Ok(PeerIndexTable {
collector_bgp_id,
view_name_length,
view_name: "".to_owned(),
peer_count,
peers_map,
})
}
pub fn parse_rib_afi_entries(
data: &[u8],
rib_type: TableDumpV2Type,
) -> Result<RibAfiEntries, ParserError> {
let mut input = Cursor::new(data);
let afi: Afi;
let safi: Safi;
match rib_type {
TableDumpV2Type::RibIpv4Unicast | TableDumpV2Type::RibIpv4UnicastAddPath => {
afi = Afi::Ipv4;
safi = Safi::Unicast
}
TableDumpV2Type::RibIpv4Multicast | TableDumpV2Type::RibIpv4MulticastAddPath => {
afi = Afi::Ipv4;
safi = Safi::Multicast
}
TableDumpV2Type::RibIpv6Unicast | TableDumpV2Type::RibIpv6UnicastAddPath => {
afi = Afi::Ipv6;
safi = Safi::Unicast
}
TableDumpV2Type::RibIpv6Multicast | TableDumpV2Type::RibIpv6MulticastAddPath => {
afi = Afi::Ipv6;
safi = Safi::Multicast
}
_ => {
return Err(ParserError::ParseError(format!(
"wrong RIB type for parsing: {:?}",
rib_type
)))
}
};
let add_path = matches!(
rib_type,
TableDumpV2Type::RibIpv4UnicastAddPath
| TableDumpV2Type::RibIpv4MulticastAddPath
| TableDumpV2Type::RibIpv6UnicastAddPath
| TableDumpV2Type::RibIpv6MulticastAddPath
);
let sequence_number = input.read_32b()?;
let prefix = input.read_nlri_prefix(&afi, false)?;
let prefixes = vec![prefix];
let entry_count = input.read_u16::<BE>()?;
let mut rib_entries = Vec::with_capacity((entry_count * 2) as usize);
for _i in 0..entry_count {
let entry = match parse_rib_entry(&mut input, add_path, &afi, &safi, &prefixes) {
Ok(entry) => entry,
Err(e) => {
warn!("early break due to error {}", e.to_string());
break;
}
};
rib_entries.push(entry);
}
Ok(RibAfiEntries {
rib_type,
sequence_number,
prefix,
rib_entries,
})
}
pub fn parse_rib_entry(
input: &mut Cursor<&[u8]>,
add_path: bool,
afi: &Afi,
safi: &Safi,
prefixes: &[NetworkPrefix],
) -> Result<RibEntry, ParserError> {
let mut total_bytes_left = input.get_ref().len() - (input.position() as usize);
if total_bytes_left < 8 {
return Err(ParserError::TruncatedMsg("truncated msg".to_string()));
}
let peer_index = input.read_u16::<BE>()?;
let originated_time = input.read_32b()?;
total_bytes_left -= 6;
if add_path {
let _path_id = input.read_32b()?;
total_bytes_left -= 4;
}
let attribute_length = input.read_u16::<BE>()? as usize;
total_bytes_left -= 2;
if total_bytes_left < attribute_length {
return Err(ParserError::TruncatedMsg("truncated msg".to_string()));
}
let attr_parser = AttributeParser::new(add_path);
let pos = input.position() as usize;
let pos_end = pos + attribute_length;
let attr_data_slice = &input.get_ref()[pos..pos_end];
let attributes = attr_parser.parse_attributes(
attr_data_slice,
&AsnLength::Bits32,
Some(*afi),
Some(*safi),
Some(prefixes),
)?;
input.seek(SeekFrom::Start(pos_end as u64))?;
Ok(RibEntry {
peer_index,
originated_time,
attributes,
})
}