pub mod default;
mod diagnostic;
pub mod fallible;
mod raw;
mod recovery;
mod route;
mod update;
pub use default::{ElemIterator, RecordIterator};
pub use diagnostic::{
record_validation_warnings, span_record_warnings, DiagnosticEvent, DiagnosticIterator,
DissectedDiagnosticEvent, DissectingDiagnosticIterator,
};
pub use fallible::{FallibleElemIterator, FallibleRecordIterator};
pub use raw::{FilteredRawRecordIterator, RawRecordIterator};
pub use recovery::{
RecoveringElemIterator, RecoveringRecordIterator, RecoveryConfig, RecoveryError, RecoveryEvent,
RecoveryEvidence, RecoveryGap,
};
pub use route::{FallibleRouteIterator, RouteIterator};
pub use update::{
Bgp4MpUpdate, FallibleUpdateIterator, LegacyBgpUpdate, MrtUpdate, TableDumpV2Entry,
UpdateIterator,
};
use crate::error::{ParserError, ParserErrorWithBytes};
use crate::models::BgpElem;
use crate::models::{MrtMessage, MrtRecord, TableDumpV2Message};
use crate::parser::mrt::mrt_record::parse_chunked_record_with_zebra_compat;
use crate::parser::{chunk_mrt_record, BgpkitParser};
use crate::RawMrtRecord;
use crate::{Elementor, Filter, Filterable};
use log::{debug, error, warn};
use std::io::Read;
use std::path::Path;
fn next_fallible_raw_record(
reader: &mut impl Read,
finished: &mut bool,
) -> Option<Result<RawMrtRecord, ParserErrorWithBytes>> {
if *finished {
return None;
}
match chunk_mrt_record(reader) {
Ok(record) => Some(Ok(record)),
Err(error) if matches!(error.error, ParserError::EofExpected) => {
*finished = true;
None
}
Err(error) => {
match &error.error {
ParserError::IoError(io_error) | ParserError::EofError(io_error) => {
let nothing_consumed = error.bytes.as_ref().is_none_or(Vec::is_empty);
*finished = !(nothing_consumed
&& matches!(
io_error.kind(),
std::io::ErrorKind::Interrupted | std::io::ErrorKind::WouldBlock
));
}
ParserError::OversizedRecord { .. } => {
*finished = true;
}
_ => {}
}
Some(Err(error))
}
}
}
fn next_fallible_record<R: Read>(
parser: &mut BgpkitParser<R>,
finished: &mut bool,
) -> Option<Result<MrtRecord, ParserErrorWithBytes>> {
let raw_record = match next_fallible_raw_record(&mut parser.reader, finished)? {
Ok(record) => record,
Err(error) => return Some(Err(error)),
};
Some(match parse_chunked_record_with_zebra_compat(raw_record) {
Ok((record, used_zebra_compat)) => {
if used_zebra_compat {
parser.warn_zebra_compat_once();
}
Ok(record)
}
Err(error) => Err(error),
})
}
#[inline]
pub(crate) fn record_matches_filters(
record: &MrtRecord,
filters: &[Filter],
elementor: &mut Elementor,
) -> bool {
if filters.is_empty() {
return true;
}
if matches!(
&record.message,
MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(_))
) {
let _ = elementor.record_to_elems(record.clone());
return true;
}
let elems = elementor.record_to_elems(record.clone());
if elems.is_empty() {
debug!(
"filters active: record of type {:?} yields no elems and is dropped",
record.common_header.entry_type
);
return false;
}
elems.iter().any(|element| element.match_filters(filters))
}
pub(crate) fn handle_record_parse_error<R>(
parser: &mut crate::parser::BgpkitParser<R>,
error: ParserError,
bytes: Option<Vec<u8>>,
) -> bool {
match error {
ParserError::TruncatedMsg(err_str) | ParserError::Unsupported(err_str) => {
if parser.options.show_warnings {
warn!("parser warn: {}", err_str);
}
write_mrt_core_dump(parser.core_dump, bytes);
true
}
ParserError::ParseError(err_str) => {
parser.log_parse_error_once(&err_str);
write_mrt_core_dump(parser.core_dump, bytes);
!parser.core_dump
}
ParserError::EofExpected => {
false
}
ParserError::OversizedRecord { length } => {
error!(
"parser error: MRT message too large: {} bytes; stream position is unreliable, stopping",
length
);
write_mrt_core_dump(parser.core_dump, bytes);
false
}
ParserError::IoError(err) | ParserError::EofError(err) => {
error!("{:?}", err);
write_mrt_core_dump(parser.core_dump, bytes);
false
}
#[cfg(feature = "oneio")]
ParserError::OneIoError(_) => false,
ParserError::FilterError(_) => {
false
}
ParserError::InvalidLabeledNlriLength
| ParserError::TruncatedLabeledNlri
| ParserError::TruncatedPrefix
| ParserError::MaxLabelStackDepthExceeded
| ParserError::PeerMaxLabelsExceeded
| ParserError::InvalidPrefix => {
if parser.options.show_warnings {
warn!("parser warn: labeled NLRI parsing error: {:?}", error);
}
true
}
}
}
pub(crate) fn write_mrt_core_dump(enabled: bool, bytes: Option<Vec<u8>>) {
write_mrt_core_dump_to_path(enabled, bytes, "mrt_core_dump");
}
pub(crate) fn write_mrt_core_dump_to_path<P: AsRef<Path>>(
enabled: bool,
bytes: Option<Vec<u8>>,
path: P,
) {
if enabled {
if let Some(bytes) = bytes {
std::fs::write(path, bytes).expect("Unable to write to mrt_core_dump");
}
}
}
impl<R: Read> IntoIterator for BgpkitParser<R> {
type Item = BgpElem;
type IntoIter = ElemIterator<R>;
fn into_iter(self) -> Self::IntoIter {
ElemIterator::new(self)
}
}
impl<R> BgpkitParser<R> {
pub fn into_record_iter(self) -> RecordIterator<R> {
RecordIterator::new(self)
}
pub fn into_elem_iter(self) -> ElemIterator<R> {
ElemIterator::new(self)
}
pub fn into_raw_record_iter(self) -> RawRecordIterator<R> {
RawRecordIterator::new(self)
}
pub fn into_filtered_raw_record_iter(self) -> FilteredRawRecordIterator<R> {
FilteredRawRecordIterator::new(self)
}
pub fn into_recovering_record_iter(
self,
config: RecoveryConfig,
) -> RecoveringRecordIterator<R> {
RecoveringRecordIterator::new(self, config)
}
pub fn into_recovering_elem_iter(self, config: RecoveryConfig) -> RecoveringElemIterator<R> {
RecoveringElemIterator::new(self, config)
}
pub fn into_update_iter(self) -> UpdateIterator<R> {
UpdateIterator::new(self)
}
pub fn into_route_iter(self) -> RouteIterator<R> {
RouteIterator::new(self)
}
pub fn into_fallible_record_iter(self) -> FallibleRecordIterator<R> {
FallibleRecordIterator::new(self)
}
pub fn into_fallible_elem_iter(self) -> FallibleElemIterator<R> {
FallibleElemIterator::new(self)
}
pub fn into_fallible_update_iter(self) -> FallibleUpdateIterator<R> {
FallibleUpdateIterator::new(self)
}
pub fn into_fallible_route_iter(self) -> FallibleRouteIterator<R> {
FallibleRouteIterator::new(self)
}
pub fn into_diagnostic_iter(self) -> DiagnosticIterator<R> {
DiagnosticIterator::new(self)
}
pub fn into_elementor_and_raw_record_iter(
self,
) -> (Elementor, impl Iterator<Item = RawMrtRecord>)
where
R: Read,
{
let mut raw_iter = RawRecordIterator::new(self).peekable();
let elementor = match raw_iter.peek().cloned().and_then(|r| r.parse().ok()) {
Some(MrtRecord {
message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(pit)),
..
}) => {
raw_iter.next();
Elementor::with_peer_table(pit)
}
_ => Elementor::new(),
};
(elementor, raw_iter)
}
pub fn into_elementor_and_record_iter(self) -> (Elementor, impl Iterator<Item = MrtRecord>)
where
R: Read,
{
let mut record_iter = RecordIterator::new(self).peekable();
let elementor = match record_iter.peek().cloned() {
Some(MrtRecord {
message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(pit)),
..
}) => {
record_iter.next();
Elementor::with_peer_table(pit)
}
_ => Elementor::new(),
};
(elementor, record_iter)
}
}