mrt_rs/
lib.rs

1#![deny(missing_docs)]
2
3//! The `mrt-rs` crate provides functionality to parse an MRT-formatted streams.
4//!
5//! # Examples
6//!
7//! ## Reading a MRT file containing BPG messages
8//! ```
9//! use std::fs::File;
10//! use mrt_rs::Record;
11//! use mrt_rs::bgp4mp::BGP4MP;
12//!
13//! // Open an MRT-formatted file.
14//! let mut file = File::open("res/bird-mrtdump_bgp").unwrap();
15//!
16//! // Keep reading (Header, Record) tuples till the end of the file has been reached.
17//! while let Some((header, record)) = mrt_rs::read(&mut file).unwrap() {
18//!     match record {
19//!         Record::BGP4MP(x) => match x {
20//!             BGP4MP::MESSAGE(y) => println!("{:?}", y),
21//!             BGP4MP::MESSAGE_AS4(y) => println!("{:?}", y),
22//!             _ => continue,
23//!         },
24//!         _ => continue,
25//!     }
26//! }
27//! ```
28
29use byteorder::{BigEndian, ReadBytesExt};
30use std::io::{Error, ErrorKind, Read};
31
32/// Contains the implementation of all MRT record types.
33pub mod records {
34
35    /// Contains all BGP subtypes.
36    pub mod bgp;
37
38    /// Contains all BGP4PLUS subtypes.
39    pub mod bgp4plus;
40
41    /// Contains all BGP4MP subtypes.
42    pub mod bgp4mp;
43
44    /// Contains all ISIS subtypes.
45    pub mod isis;
46
47    /// Contains all OSPF subtypes such as OSPFv2 and OSPFv3.
48    pub mod ospf;
49
50    /// Contains all RIP subtypes.
51    pub mod rip;
52
53    /// Contains all TABLE_DUMP subtypes such as TABLE_DUMP and TABLE_DUMP_V2.
54    pub mod tabledump;
55}
56
57// Re-exports to allow users more convenient access.
58pub use records::bgp;
59pub use records::bgp4mp;
60pub use records::bgp4plus;
61pub use records::isis;
62pub use records::ospf;
63pub use records::rip;
64pub use records::tabledump;
65
66/// Represents an Address Family Idenfitier. Currently only IPv4 and IPv6 are supported.
67#[derive(Debug)]
68#[repr(u16)]
69pub enum AFI {
70    /// Internet Protocol version 4 (32 bits)
71    IPV4 = 1,
72    /// Internet Protocol version 6 (128 bits)
73    IPV6 = 2,
74}
75
76impl AFI {
77    fn from(value: u16) -> Result<AFI, Error> {
78        match value {
79            1 => Ok(AFI::IPV4),
80            2 => Ok(AFI::IPV6),
81            _ => {
82                let msg = format!(
83                    "Number {} does not represent a valid address family.",
84                    value
85                );
86                Err(std::io::Error::new(std::io::ErrorKind::Other, msg))
87            }
88        }
89    }
90
91    /// Returns the size in bytes of the an instance of this address family type.
92    pub fn size(&self) -> u32 {
93        match self {
94            AFI::IPV4 => 4,
95            AFI::IPV6 => 16,
96        }
97    }
98}
99
100/// Represents the MRT header accompanying every MRT record.
101#[derive(Debug)]
102pub struct Header {
103    /// The time at which this message was generated. Represented in UNIX time.
104    pub timestamp: u32,
105
106    /// Microsecond resolution of the time on which this message was generated. Zero if not present.
107    pub extended: u32,
108
109    /// The main type of the MRT record.
110    pub record_type: u16,
111
112    /// The sub-type of the MRT record.
113    pub sub_type: u16,
114
115    /// The length in bytes of the MRT record excluding the MRT header.
116    pub length: u32,
117}
118
119/// Represents a single MRT record.
120#[derive(Debug)]
121#[allow(missing_docs)]
122#[allow(non_camel_case_types)]
123pub enum Record {
124    NULL,
125    START,
126    DIE,
127    I_AM_DEAD,
128    PEER_DOWN,
129    BGP(records::bgp::BGP),
130    RIP(records::rip::RIP),
131    IDRP,
132    RIPNG(records::rip::RIPNG),
133    BGP4PLUS(records::bgp4plus::BGP4PLUS),
134    BGP4PLUS_01(records::bgp4plus::BGP4PLUS),
135    OSPFv2(records::ospf::OSPFv2),
136    TABLE_DUMP(records::tabledump::TABLE_DUMP),
137    TABLE_DUMP_V2(records::tabledump::TABLE_DUMP_V2),
138    BGP4MP(records::bgp4mp::BGP4MP),
139    BGP4MP_ET(records::bgp4mp::BGP4MP),
140    ISIS(Vec<u8>),
141    ISIS_ET(Vec<u8>),
142    OSPFv3(records::ospf::OSPFv3),
143    OSPFv3_ET(records::ospf::OSPFv3),
144}
145
146///
147/// Reads the next MRT record in the stream.
148///
149/// # Panics
150/// This function does not panic.
151///
152/// # Errors
153/// Any IO error will be returned while reading from the stream.
154/// If an ill-formatted stream provided behavior will be undefined.
155///
156/// # Safety
157/// This function does not make use of unsafe code.
158///
159pub fn read(mut stream: &mut impl Read) -> Result<Option<(Header, Record)>, Error> {
160    let result = stream.read_u32::<BigEndian>();
161
162    // Check if an EOF has occurred at the beginning of the stream and return None
163    // if this is the case.
164    let timestamp = match result {
165        Err(ref e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(None),
166        Err(e) => return Err(e),
167        Ok(x) => x,
168    };
169
170    // Parse the MRTHeader
171    let mut header = Header {
172        timestamp,
173        extended: 0,
174        record_type: stream.read_u16::<BigEndian>()?,
175        sub_type: stream.read_u16::<BigEndian>()?,
176        length: stream.read_u32::<BigEndian>()?,
177    };
178
179    match header.record_type {
180        0 => Ok(Some((header, Record::NULL))),
181        1 => Ok(Some((header, Record::START))),
182        2 => Ok(Some((header, Record::DIE))),
183        3 => Ok(Some((header, Record::I_AM_DEAD))),
184        4 => Ok(Some((header, Record::PEER_DOWN))),
185        5 => {
186            let record = records::bgp::BGP::parse(&header, &mut stream)?;
187            Ok(Some((header, Record::BGP(record))))
188        }
189        6 => {
190            let record = records::rip::RIP::parse(&header, &mut stream)?;
191            Ok(Some((header, Record::RIP(record))))
192        }
193        7 => Ok(Some((header, Record::IDRP))),
194        8 => {
195            let record = records::rip::RIPNG::parse(&header, &mut stream)?;
196            Ok(Some((header, Record::RIPNG(record))))
197        }
198        9 => {
199            let record = records::bgp4plus::BGP4PLUS::parse(&header, &mut stream)?;
200            Ok(Some((header, Record::BGP4PLUS(record))))
201        }
202        10 => {
203            let record = records::bgp4plus::BGP4PLUS::parse(&header, &mut stream)?;
204            Ok(Some((header, Record::BGP4PLUS_01(record))))
205        }
206        11 => {
207            let record = records::ospf::OSPFv2::parse(&header, &mut stream)?;
208            Ok(Some((header, Record::OSPFv2(record))))
209        }
210        12 => {
211            let record = records::tabledump::TABLE_DUMP::parse(&header, &mut stream)?;
212            Ok(Some((header, Record::TABLE_DUMP(record))))
213        }
214        13 => {
215            let record = records::tabledump::TABLE_DUMP_V2::parse(&header, &mut stream)?;
216            Ok(Some((header, Record::TABLE_DUMP_V2(record))))
217        }
218        16 => {
219            let record = records::bgp4mp::BGP4MP::parse(&header, &mut stream)?;
220            Ok(Some((header, Record::BGP4MP(record))))
221        }
222        17 => {
223            header.extended = stream.read_u32::<BigEndian>()?;
224            let record = records::bgp4mp::BGP4MP::parse(&header, &mut stream)?;
225            Ok(Some((header, Record::BGP4MP_ET(record))))
226        }
227        32 => {
228            let record = records::isis::parse(&header, &mut stream)?;
229            Ok(Some((header, Record::ISIS(record))))
230        }
231        33 => {
232            header.extended = stream.read_u32::<BigEndian>()?;
233            let record = records::isis::parse(&header, &mut stream)?;
234            Ok(Some((header, Record::ISIS_ET(record))))
235        }
236        48 => {
237            let record = records::ospf::OSPFv3::parse(&header, &mut stream)?;
238            Ok(Some((header, Record::OSPFv3(record))))
239        }
240        49 => {
241            header.extended = stream.read_u32::<BigEndian>()?;
242            let record = records::ospf::OSPFv3::parse(&header, &mut stream)?;
243            Ok(Some((header, Record::OSPFv3_ET(record))))
244        }
245        x => Err(Error::new(
246            ErrorKind::Other,
247            format!("Unknown record type found in MRT header: {}", x),
248        )),
249    }
250}