Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve BMP dx #165

Merged
merged 6 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to this project will be documented in this file.

## Unreleased

### BMP messages SDK improvements

* expose all pub structs and enums for BMP messages
* this allows users to access struct definitions and operate on them directly
* added `Copy` to `BmpMsgType`, `BmpPeerType` and `BmpPerPeerHeader`, `PerPeerFlags`, `BmpPeerType`
* implemented `Default`, `PartialEq`, `Eq` and `Hash` for `BmpPerPeerHeader`
* this allows users and compare and hash `BmpPerPeerHeader` structs
* also implemented `.strip_timestamp()` to remove the timestamp from the `BmpPerPeerHeader` struct for cases where
the timestamp is not needed
* rename `MessageBody` to `BmpMessageBody`
* derive `Clone`, `PartialEq` to `BmpMessage` and `MessageBody`

## v0.10.8 -2024-04-05

### Highlights
Expand Down
1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ ignore:
- "src/parser/iters.rs"
- "src/parser/bgp/attributes/mod.rs" # test covered in individual attribute
- "src/parser/mrt/mrt_elem.rs"
- "src/parser/bmp/mod.rs"
coverage:
status:
project:
Expand Down
4 changes: 2 additions & 2 deletions examples/bmp_listener.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bgpkit_parser::bmp::messages::MessageBody;
use bgpkit_parser::bmp::messages::BmpMessageBody;
use bgpkit_parser::parse_bmp_msg;
use bytes::{Buf, Bytes};
use std::io::Read;
Expand Down Expand Up @@ -27,7 +27,7 @@ fn handle_client(mut stream: TcpStream) {
let mut data = Bytes::from(buffer[..bytes_read].to_vec());
while data.remaining() > 0 {
let msg = parse_bmp_msg(&mut data).unwrap();
if let MessageBody::RouteMonitoring(mon_msg) = &msg.message_body {
if let BmpMessageBody::RouteMonitoring(mon_msg) = &msg.message_body {
if mon_msg.is_end_of_rib() {
dbg!("end of RIB");
}
Expand Down
4 changes: 2 additions & 2 deletions examples/real-time-routeviews-kafka-openbmp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bgpkit_parser::parser::bmp::messages::MessageBody;
use bgpkit_parser::parser::bmp::messages::BmpMessageBody;
use bgpkit_parser::Elementor;
pub use bgpkit_parser::{parse_bmp_msg, parse_openbmp_header};
use bytes::Bytes;
Expand Down Expand Up @@ -34,7 +34,7 @@ fn consume_and_print(group: String, topic: String, brokers: Vec<String>) -> Resu
match bmp_msg {
Ok(msg) => {
let per_peer_header = msg.per_peer_header.unwrap();
if let MessageBody::RouteMonitoring(m) = msg.message_body {
if let BmpMessageBody::RouteMonitoring(m) = msg.message_body {
for elem in Elementor::bgp_to_elems(
m.bgp_message,
header.timestamp,
Expand Down
114 changes: 109 additions & 5 deletions src/parser/bmp/messages/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use bitflags::bitflags;
use bytes::{Buf, Bytes};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::convert::TryFrom;
use std::hash::{Hash, Hasher};
use std::net::{IpAddr, Ipv4Addr};

/// BMP message type enum.
Expand All @@ -22,7 +23,8 @@ use std::net::{IpAddr, Ipv4Addr};
/// * Type = 5: Termination Message
/// * Type = 6: Route Mirroring Message
/// ```
#[derive(Debug, Clone, TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum BmpMsgType {
RouteMonitoring = 0,
Expand All @@ -48,7 +50,8 @@ pub enum BmpMsgType {
/// | Msg. Type |
/// +---------------+
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BmpCommonHeader {
pub version: u8,
pub msg_len: u32,
Expand All @@ -74,6 +77,14 @@ pub fn parse_bmp_common_header(data: &mut Bytes) -> Result<BmpCommonHeader, Pars

/// BMP Per-peer Header
///
/// Features:
/// * 42 bytes total size
/// * Hash and PartialEq implemented without considering the timestamp
/// * i.e., two headers are equal if all fields except the timestamp are equal
/// * implements Copy and Clone
///
/// <https://www.rfc-editor.org/rfc/rfc7854#section-4.2>
///
/// ```text
/// 0 1 2 3
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
Expand All @@ -95,7 +106,8 @@ pub fn parse_bmp_common_header(data: &mut Bytes) -> Result<BmpCommonHeader, Pars
/// | Timestamp (microseconds) |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BmpPerPeerHeader {
pub peer_type: BmpPeerType,
pub peer_flags: PerPeerFlags,
Expand All @@ -106,12 +118,64 @@ pub struct BmpPerPeerHeader {
pub timestamp: f64,
}

impl Default for BmpPerPeerHeader {
fn default() -> Self {
BmpPerPeerHeader {
peer_type: BmpPeerType::Global,
peer_flags: PerPeerFlags::PeerFlags(PeerFlags::empty()),
peer_distinguisher: 0,
peer_ip: IpAddr::V4(Ipv4Addr::from(0)),
peer_asn: Default::default(),
peer_bgp_id: Ipv4Addr::from(0),
timestamp: 0.0,
}
}
}

impl PartialEq for BmpPerPeerHeader {
fn eq(&self, other: &Self) -> bool {
self.peer_type == other.peer_type
&& self.peer_flags == other.peer_flags
&& self.peer_distinguisher == other.peer_distinguisher
&& self.peer_ip == other.peer_ip
&& self.peer_asn == other.peer_asn
&& self.peer_bgp_id == other.peer_bgp_id
&& self.timestamp == other.timestamp
}
}

impl Eq for BmpPerPeerHeader {}

impl Hash for BmpPerPeerHeader {
fn hash<H: Hasher>(&self, state: &mut H) {
self.peer_type.hash(state);
self.peer_flags.hash(state);
self.peer_distinguisher.hash(state);
self.peer_ip.hash(state);
self.peer_asn.hash(state);
self.peer_bgp_id.hash(state);
self.timestamp.to_bits().hash(state);
}
}

impl BmpPerPeerHeader {
/// Returns the AFI of the peer IP address
#[inline]
pub fn afi(&self) -> Afi {
Afi::from(self.peer_ip)
}

/// Strip the timestamp from the header.
///
/// This is useful when comparing two headers where the timestamp is not important.
pub fn strip_timestamp(&self) -> BmpPerPeerHeader {
BmpPerPeerHeader {
timestamp: 0.0,
..*self
}
}

/// Returns the ASN length based on the peer flags
pub fn asn_length(&self) -> AsnLength {
match self.peer_flags {
PerPeerFlags::PeerFlags(f) => f.asn_length(),
Expand All @@ -124,7 +188,8 @@ impl BmpPerPeerHeader {
///
/// - RFC7854: https://datatracker.ietf.org/doc/html/rfc7854#section-4.2
/// - RFC9069: https://datatracker.ietf.org/doc/html/rfc9069
#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Clone)]
#[derive(Debug, Copy, TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum BmpPeerType {
Global = 0,
Expand All @@ -133,7 +198,8 @@ pub enum BmpPeerType {
LocalRib = 3,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PerPeerFlags {
PeerFlags(PeerFlags),
LocalRibPeerFlags(LocalRibPeerFlags),
Expand Down Expand Up @@ -174,6 +240,11 @@ bitflags! {
}

impl PeerFlags {
/// Returns the address family for the `Peer` object.
///
/// # Returns
/// - `Afi::Ipv6` if the `PeerFlags` contains the `ADDRESS_FAMILY_IPV6` flag.
/// - `Afi::Ipv4` otherwise.
pub const fn address_family(&self) -> Afi {
if self.contains(PeerFlags::ADDRESS_FAMILY_IPV6) {
return Afi::Ipv6;
Expand All @@ -182,6 +253,12 @@ impl PeerFlags {
Afi::Ipv4
}

/// Determines the length of the ASN (Abstract Syntax Notation) based on peer flags.
///
/// # Returns
///
/// - `AsnLength::Bits16` if the `PeerFlags` contains the `AS_SIZE_16BIT` flag.
/// - `AsnLength::Bits32` otherwise.
pub const fn asn_length(&self) -> AsnLength {
if self.contains(PeerFlags::AS_SIZE_16BIT) {
return AsnLength::Bits16;
Expand Down Expand Up @@ -219,6 +296,17 @@ impl LocalRibPeerFlags {
}
}

/// Parses a BMP per-peer header from the provided byte data.
///
/// # Arguments
///
/// * `data` - A mutable reference to the byte data representing the per-peer header.
///
/// # Returns
///
/// * `Ok(BmpPerPeerHeader)` - If the parsing is successful, returns the parsed per-peer header.
/// * `Err(ParserBmpError)` - If an error occurs during parsing, returns the corresponding error.
///
pub fn parse_per_peer_header(data: &mut Bytes) -> Result<BmpPerPeerHeader, ParserBmpError> {
let peer_type = BmpPeerType::try_from(data.read_u8()?)?;

Expand Down Expand Up @@ -376,4 +464,20 @@ mod tests {
assert_eq!(header.peer_bgp_id, Ipv4Addr::new(192, 168, 1, 1));
assert_eq!(header.timestamp, 10.0001);
}

#[test]
#[allow(clippy::field_reassign_with_default)]
fn test_equality_hash() {
let header1 = BmpPerPeerHeader::default();

let mut header2 = BmpPerPeerHeader::default();
header2.timestamp = 1.0;

assert_ne!(header1, header2);
assert_eq!(header1.strip_timestamp(), header2.strip_timestamp());

let mut hashmap = std::collections::HashMap::new();
hashmap.insert(header1.strip_timestamp(), 1);
assert_eq!(hashmap.get(&header2.strip_timestamp()), Some(&1));
}
}
3 changes: 3 additions & 0 deletions src/parser/bmp/messages/initiation_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::convert::TryFrom;

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InitiationMessage {
pub tlvs: Vec<InitiationTlv>,
}

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InitiationTlv {
pub info_type: InitiationTlvType,
pub info_len: u16,
Expand All @@ -20,6 +22,7 @@ pub struct InitiationTlv {
///
/// https://www.iana.org/assignments/bmp-parameters/bmp-parameters.xhtml#initiation-peer-up-tlvs
#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum InitiationTlvType {
String = 0,
Expand Down
28 changes: 14 additions & 14 deletions src/parser/bmp/messages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
//!
//! <https://datatracker.ietf.org/doc/html/rfc7854>

pub use headers::{
parse_bmp_common_header, parse_per_peer_header, BmpCommonHeader, BmpMsgType, BmpPerPeerHeader,
};
pub use initiation_message::{parse_initiation_message, InitiationMessage};
pub use peer_down_notification::{parse_peer_down_notification, PeerDownNotification};
pub use peer_up_notification::{parse_peer_up_notification, PeerUpNotification};
pub use route_mirroring::{parse_route_mirroring, RouteMirroring};
pub use route_monitoring::{parse_route_monitoring, RouteMonitoring};
pub use stats_report::{parse_stats_report, StatsReport};
pub use termination_message::{parse_termination_message, TerminationMessage};
pub use headers::*;
pub use initiation_message::*;
pub use peer_down_notification::*;
pub use peer_up_notification::*;
pub use route_mirroring::*;
pub use route_monitoring::*;
pub use stats_report::*;
pub use termination_message::*;

pub(crate) mod headers;
pub(crate) mod initiation_message;
Expand All @@ -22,15 +20,17 @@ pub(crate) mod route_monitoring;
pub(crate) mod stats_report;
pub(crate) mod termination_message;

#[derive(Debug)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BmpMessage {
pub common_header: BmpCommonHeader,
pub per_peer_header: Option<BmpPerPeerHeader>,
pub message_body: MessageBody,
pub message_body: BmpMessageBody,
}

#[derive(Debug)]
pub enum MessageBody {
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BmpMessageBody {
PeerUpNotification(PeerUpNotification),
PeerDownNotification(PeerDownNotification),
InitiationMessage(InitiationMessage),
Expand Down
2 changes: 2 additions & 0 deletions src/parser/bmp/messages/peer_down_notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ use bytes::{Buf, Bytes};
use num_enum::{IntoPrimitive, TryFromPrimitive};

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PeerDownNotification {
pub reason: PeerDownReason,
pub data: Option<Vec<u8>>,
}

#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum PeerDownReason {
Reserved = 0,
Expand Down
3 changes: 3 additions & 0 deletions src/parser/bmp/messages/peer_up_notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::net::IpAddr;

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PeerUpNotification {
pub local_addr: IpAddr,
pub local_port: u16,
Expand All @@ -20,6 +21,7 @@ pub struct PeerUpNotification {
///
/// https://www.iana.org/assignments/bmp-parameters/bmp-parameters.xhtml#initiation-peer-up-tlvs
#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum PeerUpTlvType {
String = 0,
Expand All @@ -30,6 +32,7 @@ pub enum PeerUpTlvType {
}

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PeerUpNotificationTlv {
pub info_type: PeerUpTlvType,
pub info_len: u16,
Expand Down
4 changes: 4 additions & 0 deletions src/parser/bmp/messages/route_mirroring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::convert::TryFrom;

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RouteMirroring {
pub tlvs: Vec<RouteMirroringTlv>,
}

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RouteMirroringTlv {
pub info_len: u16,
pub value: RouteMirroringValue,
}

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RouteMirroringValue {
BgpMessage(BgpMessage),
Information(RouteMirroringInfo),
}

#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum RouteMirroringInfo {
ErroredPdu = 0,
Expand Down
Loading
Loading