use byteorder::{BigEndian, ReadBytesExt};
use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str;
pub mod bgp;
pub mod processor;
pub trait Message<M> {
fn parse<R: ReadBytesExt>(reader: &mut R, header: &MrtHeader) -> io::Result<M>;
fn can_parse(typ: MrtType) -> bool;
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TableDump {
AfiIpv4,
AfiIpv6,
Unknown(u16),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TableDumpV2 {
PeerIndex,
RibIpv4Unicast,
RibIpv6Unicast,
Unknown(u16),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MrtType {
TableDump(TableDump),
TableDumpV2(TableDumpV2),
Unknown(u16),
}
#[derive(Debug)]
pub struct MrtHeader {
pub timestamp: u32,
pub typ: MrtType,
pub length: u32,
}
pub struct Parser<R: ReadBytesExt> {
reader: R,
}
impl<R: ReadBytesExt> Parser<R> {
pub fn new(reader: R) -> Self {
Self { reader }
}
pub fn read_header(&mut self) -> io::Result<Option<MrtHeader>> {
let timestamp = match self.reader.read_u32::<BigEndian>() {
Ok(timestamp) => timestamp,
Err(e) => {
return if e.kind() == io::ErrorKind::UnexpectedEof {
Ok(None)
} else {
Err(e)
}
}
};
let typ = self.reader.read_u16::<BigEndian>()?;
let subtype = self.reader.read_u16::<BigEndian>()?;
let length = self.reader.read_u32::<BigEndian>()?;
let typ = match typ {
12 => MrtType::TableDump(match subtype {
1 => TableDump::AfiIpv4,
2 => TableDump::AfiIpv6,
_ => TableDump::Unknown(subtype),
}),
13 => MrtType::TableDumpV2(match subtype {
1 => TableDumpV2::PeerIndex,
2 => TableDumpV2::RibIpv4Unicast,
4 => TableDumpV2::RibIpv6Unicast,
_ => TableDumpV2::Unknown(subtype),
}),
_ => MrtType::Unknown(typ),
};
Ok(Some(MrtHeader {
timestamp,
typ,
length,
}))
}
pub fn skip_message(&mut self, header: &MrtHeader) -> io::Result<()> {
read_exact(&mut self.reader, header.length as usize)?;
Ok(())
}
pub fn read_message<M: Message<M>>(&mut self, header: &MrtHeader) -> io::Result<M> {
if !M::can_parse(header.typ) {
panic!("This parser cannot parse {:?}", header.typ);
}
M::parse(&mut self.reader, header)
}
}
#[derive(Debug)]
pub struct Afi {
pub view_number: u16,
pub sequence_number: u16,
pub prefix: IpNetwork,
pub status: u8,
pub originated_time: u32,
pub peer_ip: IpAddr,
pub peer_as: u16,
data: Vec<u8>,
}
impl Message<Afi> for Afi {
fn parse<R: ReadBytesExt>(reader: &mut R, header: &MrtHeader) -> io::Result<Self> {
let is_ipv6 = match header.typ {
MrtType::TableDump(subtype) => match subtype {
TableDump::AfiIpv4 => false,
TableDump::AfiIpv6 => true,
_ => panic!("Only AFI_IPv4 and AFI_IPv6 subtypes are supported"),
},
_ => panic!("Only TableDump types is supported"),
};
let view_number = reader.read_u16::<BigEndian>()?;
let sequence_number = reader.read_u16::<BigEndian>()?;
let prefix_ip = read_ip_addr(reader, is_ipv6)?;
let prefix_length = reader.read_u8()?;
let prefix = IpNetwork::new(prefix_ip, prefix_length).unwrap();
let status = reader.read_u8()?;
let originated_time = reader.read_u32::<BigEndian>()?;
let peer_ip = read_ip_addr(reader, is_ipv6)?;
let peer_as = reader.read_u16::<BigEndian>()?;
let attribute_length = reader.read_u16::<BigEndian>()?;
let data = read_exact(reader, attribute_length as usize)?;
Ok(Afi {
view_number,
sequence_number,
prefix,
status,
originated_time,
peer_ip,
peer_as,
data,
})
}
fn can_parse(typ: MrtType) -> bool {
typ == MrtType::TableDump(TableDump::AfiIpv4)
|| typ == MrtType::TableDump(TableDump::AfiIpv6)
}
}
impl Afi {
pub fn get_bgp_attributes(&self) -> io::Result<Vec<bgp::Attribute>> {
bgp::Attribute::parse_all(&self.data)
}
}
#[derive(Debug)]
pub struct PeerIndexTable {
pub collector_bgp_id: u32,
pub view_name: String,
pub peer_entries: Vec<PeerEntry>,
}
impl Message<PeerIndexTable> for PeerIndexTable {
fn parse<R: ReadBytesExt>(reader: &mut R, _: &MrtHeader) -> io::Result<Self> {
let collector_bgp_id = reader.read_u32::<BigEndian>()?;
let view_name_length = reader.read_u16::<BigEndian>()?;
let view_name_buffer = read_exact(reader, view_name_length as usize)?;
let view_name = str::from_utf8(&view_name_buffer)
.map(|x| x.to_string())
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"PeerIndexTable view name did not contain valid UTF-8",
)
})?;
let peer_count = reader.read_u16::<BigEndian>()?;
let mut peer_entries = Vec::with_capacity(peer_count as usize);
for _ in 0..peer_count {
peer_entries.push(PeerEntry::parse(reader)?);
}
Ok(PeerIndexTable {
collector_bgp_id,
view_name,
peer_entries,
})
}
fn can_parse(typ: MrtType) -> bool {
typ == MrtType::TableDumpV2(TableDumpV2::PeerIndex)
}
}
#[derive(Debug)]
pub struct PeerEntry {
pub peer_bgp_id: u32,
pub ip_addr: IpAddr,
pub asn: u32,
}
impl PeerEntry {
fn parse<R: ReadBytesExt>(rdr: &mut R) -> io::Result<Self> {
let type_ = rdr.read_u8()?;
let is_ipv6 = ((type_) & 0x1) == 1;
let is_asn_32bit = ((type_ >> 1) & 0x1) == 1;
let peer_bgp_id = rdr.read_u32::<BigEndian>()?;
let ip_addr = read_ip_addr(rdr, is_ipv6)?;
let asn = if is_asn_32bit {
rdr.read_u32::<BigEndian>()?
} else {
rdr.read_u16::<BigEndian>()? as u32
};
Ok(Self {
peer_bgp_id,
ip_addr,
asn,
})
}
}
#[derive(Debug)]
pub struct RibEntry {
pub sequence_number: u32,
pub prefix: IpNetwork,
pub sub_entries: Vec<RibSubEntry>,
}
impl Message<RibEntry> for RibEntry {
fn parse<R: ReadBytesExt>(reader: &mut R, header: &MrtHeader) -> io::Result<Self> {
let sequence_number = reader.read_u32::<BigEndian>()?;
let prefix_length = reader.read_u8()?;
let prefix_bytes = ((prefix_length + 7) / 8) as usize;
let prefix_buffer = read_exact(reader, prefix_bytes)?;
let prefix = match header.typ {
MrtType::TableDumpV2(subtype) => match subtype {
TableDumpV2::RibIpv4Unicast => {
debug_assert!(prefix_length <= 32);
let mut parts: [u8; 4] = [0; 4];
parts[..prefix_bytes].copy_from_slice(prefix_buffer.as_slice());
let ip = Ipv4Addr::from(parts);
IpNetwork::V4(Ipv4Network::new(ip, prefix_length).unwrap())
}
TableDumpV2::RibIpv6Unicast => {
debug_assert!(prefix_length <= 128);
let mut parts: [u8; 16] = [0; 16];
parts[..prefix_bytes].copy_from_slice(prefix_buffer.as_slice());
let ip = Ipv6Addr::from(parts);
IpNetwork::V6(Ipv6Network::new(ip, prefix_length).unwrap())
}
_ => panic!("This parser cannot parse TableDumpV2 {:?} subtype", subtype),
},
_ => panic!("This parser cannot parse {:?} type", header.typ),
};
let entry_count = reader.read_u16::<BigEndian>()?;
let mut sub_entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
sub_entries.push(RibSubEntry::parse(reader)?);
}
Ok(RibEntry {
sequence_number,
prefix,
sub_entries,
})
}
fn can_parse(typ: MrtType) -> bool {
typ == MrtType::TableDumpV2(TableDumpV2::RibIpv4Unicast)
|| typ == MrtType::TableDumpV2(TableDumpV2::RibIpv6Unicast)
}
}
#[derive(Debug)]
pub struct RibSubEntry {
pub peer_index: u16,
pub originated_time: u32,
data: Vec<u8>,
}
impl RibSubEntry {
fn parse<R: ReadBytesExt>(rdr: &mut R) -> io::Result<RibSubEntry> {
let peer_index = rdr.read_u16::<BigEndian>()?;
let originated_time = rdr.read_u32::<BigEndian>()?;
let attribute_length = rdr.read_u16::<BigEndian>()?;
let data = read_exact(rdr, attribute_length as usize)?;
Ok(RibSubEntry {
peer_index,
originated_time,
data,
})
}
pub fn get_bgp_attributes(&self) -> io::Result<Vec<bgp::Attribute>> {
bgp::Attribute::parse_all(&self.data)
}
}
fn read_ip_addr<R: ReadBytesExt>(rdr: &mut R, is_ipv6: bool) -> io::Result<IpAddr> {
if is_ipv6 {
let mut buffer = [0; 16];
rdr.read_exact(&mut buffer)?;
Ok(IpAddr::V6(Ipv6Addr::from(buffer)))
} else {
Ok(IpAddr::V4(Ipv4Addr::from(rdr.read_u32::<BigEndian>()?)))
}
}
#[inline]
fn read_exact<R: ReadBytesExt>(rdr: &mut R, length: usize) -> io::Result<Vec<u8>> {
let mut buffer = vec![0; length as usize];
rdr.read_exact(buffer.as_mut_slice())?;
Ok(buffer)
}