1use crate::models::*;
2use crate::parser::bmp::error::ParserBmpError;
3use crate::parser::ReadUtils;
4use bitflags::bitflags;
5use bytes::{Buf, Bytes};
6use log::warn;
7use num_enum::{IntoPrimitive, TryFromPrimitive};
8use std::convert::TryFrom;
9use std::hash::{Hash, Hasher};
10use std::net::{IpAddr, Ipv4Addr};
11
12#[derive(Debug, Clone, TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[repr(u8)]
30pub enum BmpMsgType {
31 RouteMonitoring = 0,
32 StatisticsReport = 1,
33 PeerDownNotification = 2,
34 PeerUpNotification = 3,
35 InitiationMessage = 4,
36 TerminationMessage = 5,
37 RouteMirroringMessage = 6,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct BmpCommonHeader {
57 pub version: u8,
58 pub msg_len: u32,
59 pub msg_type: BmpMsgType,
60}
61
62pub fn parse_bmp_common_header(data: &mut Bytes) -> Result<BmpCommonHeader, ParserBmpError> {
63 let version = data.read_u8()?;
64 if version != 3 {
65 return Err(ParserBmpError::CorruptedBmpMessage);
67 }
68
69 let msg_len = data.read_u32()?;
70
71 let msg_type = BmpMsgType::try_from(data.read_u8()?)?;
72 Ok(BmpCommonHeader {
73 version,
74 msg_len,
75 msg_type,
76 })
77}
78
79#[derive(Debug, Copy, Clone)]
111#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
112pub struct BmpPerPeerHeader {
113 pub peer_type: BmpPeerType,
114 pub peer_flags: PerPeerFlags,
115 pub peer_distinguisher: u64,
116 pub peer_ip: IpAddr,
117 pub peer_asn: Asn,
118 pub peer_bgp_id: BgpIdentifier,
119 pub timestamp: f64,
120}
121
122impl Default for BmpPerPeerHeader {
123 fn default() -> Self {
124 BmpPerPeerHeader {
125 peer_type: BmpPeerType::Global,
126 peer_flags: PerPeerFlags::PeerFlags(PeerFlags::empty()),
127 peer_distinguisher: 0,
128 peer_ip: IpAddr::V4(Ipv4Addr::from(0)),
129 peer_asn: Default::default(),
130 peer_bgp_id: Ipv4Addr::from(0),
131 timestamp: 0.0,
132 }
133 }
134}
135
136impl PartialEq for BmpPerPeerHeader {
137 fn eq(&self, other: &Self) -> bool {
138 self.peer_type == other.peer_type
139 && self.peer_flags == other.peer_flags
140 && self.peer_distinguisher == other.peer_distinguisher
141 && self.peer_ip == other.peer_ip
142 && self.peer_asn == other.peer_asn
143 && self.peer_bgp_id == other.peer_bgp_id
144 && self.timestamp == other.timestamp
145 }
146}
147
148impl Eq for BmpPerPeerHeader {}
149
150impl Hash for BmpPerPeerHeader {
151 fn hash<H: Hasher>(&self, state: &mut H) {
152 self.peer_type.hash(state);
153 self.peer_flags.hash(state);
154 self.peer_distinguisher.hash(state);
155 self.peer_ip.hash(state);
156 self.peer_asn.hash(state);
157 self.peer_bgp_id.hash(state);
158 self.timestamp.to_bits().hash(state);
159 }
160}
161
162impl BmpPerPeerHeader {
163 #[inline]
165 pub fn afi(&self) -> Afi {
166 Afi::from(self.peer_ip)
167 }
168
169 pub fn strip_timestamp(&self) -> BmpPerPeerHeader {
173 BmpPerPeerHeader {
174 timestamp: 0.0,
175 ..*self
176 }
177 }
178
179 pub fn asn_length(&self) -> AsnLength {
181 match self.peer_flags {
182 PerPeerFlags::PeerFlags(f) => f.asn_length(),
183 PerPeerFlags::LocalRibPeerFlags(_) => AsnLength::Bits32,
184 }
185 }
186}
187
188#[derive(Debug, Copy, TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Clone)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
194#[repr(u8)]
195pub enum BmpPeerType {
196 Global = 0,
197 RD = 1,
198 Local = 2,
199 LocalRib = 3,
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
204pub enum PerPeerFlags {
205 PeerFlags(PeerFlags),
206 LocalRibPeerFlags(LocalRibPeerFlags),
207}
208
209bitflags! {
210 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
234 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
235 pub struct PeerFlags: u8 {
236 const ADDRESS_FAMILY_IPV6 = 0b1000_0000;
237 const IS_POST_POLICY = 0b0100_0000;
238 const AS_SIZE_16BIT = 0b0010_0000;
239 const IS_ADJ_RIB_OUT = 0b0001_0000;
240 }
241}
242
243impl PeerFlags {
244 pub const fn address_family(&self) -> Afi {
250 if self.contains(PeerFlags::ADDRESS_FAMILY_IPV6) {
251 return Afi::Ipv6;
252 }
253
254 Afi::Ipv4
255 }
256
257 pub const fn asn_length(&self) -> AsnLength {
264 if self.contains(PeerFlags::AS_SIZE_16BIT) {
265 return AsnLength::Bits16;
266 }
267
268 AsnLength::Bits32
269 }
270
271 pub const fn is_adj_rib_out(&self) -> bool {
273 self.contains(PeerFlags::IS_ADJ_RIB_OUT)
274 }
275
276 pub const fn is_post_policy(&self) -> bool {
278 self.contains(PeerFlags::IS_POST_POLICY)
279 }
280}
281
282bitflags! {
283 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
288 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
289 pub struct LocalRibPeerFlags: u8 {
290 const IS_FILTERED = 0b1000_0000;
291 }
292}
293
294impl LocalRibPeerFlags {
295 pub const fn is_filtered(&self) -> bool {
296 self.contains(LocalRibPeerFlags::IS_FILTERED)
297 }
298}
299
300pub fn parse_per_peer_header(data: &mut Bytes) -> Result<BmpPerPeerHeader, ParserBmpError> {
312 let peer_type = BmpPeerType::try_from(data.read_u8()?)?;
313
314 match peer_type {
315 BmpPeerType::Global | BmpPeerType::RD | BmpPeerType::Local => {
316 let peer_flags = PeerFlags::from_bits_retain(data.read_u8()?);
317
318 let peer_distinguisher = data.read_u64()?;
319 let peer_ip = match peer_flags.address_family() {
320 Afi::Ipv4 => {
321 data.advance(12);
322 IpAddr::V4(data.read_ipv4_address()?)
323 }
324 Afi::Ipv6 => IpAddr::V6(data.read_ipv6_address()?),
325 Afi::LinkState => {
326 data.advance(12);
329 IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0))
330 }
331 };
332
333 let peer_asn = match peer_flags.asn_length() {
334 AsnLength::Bits16 => {
335 data.advance(2);
336 Asn::new_16bit(data.read_u16()?)
337 }
338 AsnLength::Bits32 => Asn::new_32bit(data.read_u32()?),
339 };
340
341 let peer_bgp_id = data.read_ipv4_address()?;
342
343 let t_sec = data.read_u32()?;
344 let t_usec = data.read_u32()?;
345 let timestamp = t_sec as f64 + (t_usec as f64) / 1_000_000.0;
346
347 Ok(BmpPerPeerHeader {
348 peer_type,
349 peer_flags: PerPeerFlags::PeerFlags(peer_flags),
350 peer_distinguisher,
351 peer_ip,
352 peer_asn,
353 peer_bgp_id,
354 timestamp,
355 })
356 }
357 BmpPeerType::LocalRib => {
358 let local_rib_peer_flags = LocalRibPeerFlags::from_bits_retain(data.read_u8()?);
359
360 let peer_distinguisher = data.read_u64()?;
361
362 let peer_addr_bytes: [u8; 16] = {
364 let mut bytes = [0u8; 16];
365 data.copy_to_slice(&mut bytes);
366 bytes
367 };
368
369 if peer_addr_bytes != [0u8; 16] {
371 warn!("RFC 9069 violation: Local RIB peer address MUST be zero-filled, but found non-zero bytes");
372 }
373
374 let peer_ip = IpAddr::V4(Ipv4Addr::from(0));
376
377 let peer_asn = Asn::new_32bit(data.read_u32()?);
379
380 let peer_bgp_id = data.read_ipv4_address()?;
381
382 if peer_bgp_id == Ipv4Addr::from(0) && peer_distinguisher != 0 {
384 warn!("RFC 9069: Local RIB peer BGP ID should be set to VRF instance router-id for non-global instances");
385 }
386
387 let t_sec = data.read_u32()?;
388 let t_usec = data.read_u32()?;
389 let timestamp = t_sec as f64 + (t_usec as f64) / 1_000_000.0;
390
391 Ok(BmpPerPeerHeader {
392 peer_type,
393 peer_flags: PerPeerFlags::LocalRibPeerFlags(local_rib_peer_flags),
394 peer_distinguisher,
395 peer_ip,
396 peer_asn,
397 peer_bgp_id,
398 timestamp,
399 })
400 }
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 #[test]
409 fn test_header_error() {
410 let mut data = Bytes::from(vec![0, 0, 0, 0, 0]);
411 assert!(parse_bmp_common_header(&mut data).is_err(),);
412 assert_eq!(
413 parse_bmp_common_header(&mut data).unwrap_err(),
414 ParserBmpError::CorruptedBmpMessage
415 );
416 }
417
418 #[test]
419 fn test_bmp_per_peer_header_basics() {
420 let per_peer_header = BmpPerPeerHeader {
422 peer_type: BmpPeerType::Global,
423 peer_flags: PerPeerFlags::LocalRibPeerFlags(LocalRibPeerFlags::empty()),
424 peer_distinguisher: 0,
425 peer_ip: IpAddr::V4(Ipv4Addr::from(0)),
426 peer_asn: Default::default(),
427 peer_bgp_id: Ipv4Addr::from(0),
428 timestamp: 0.0,
429 };
430 assert_eq!(per_peer_header.afi(), Afi::Ipv4);
431
432 assert_eq!(per_peer_header.asn_length(), AsnLength::Bits32);
434 }
435
436 #[test]
437 fn test_peer_flags() {
438 let mut flags = PeerFlags::empty();
439 assert_eq!(flags.address_family(), Afi::Ipv4);
440 assert_eq!(flags.asn_length(), AsnLength::Bits32);
441 assert!(!flags.is_adj_rib_out());
442 assert!(!flags.is_post_policy());
443
444 flags |= PeerFlags::ADDRESS_FAMILY_IPV6;
445 assert_eq!(flags.address_family(), Afi::Ipv6);
446
447 flags |= PeerFlags::AS_SIZE_16BIT;
448 assert_eq!(flags.asn_length(), AsnLength::Bits16);
449
450 flags |= PeerFlags::IS_ADJ_RIB_OUT;
451 assert!(flags.is_adj_rib_out());
452
453 flags |= PeerFlags::IS_POST_POLICY;
454 assert!(flags.is_post_policy());
455 }
456
457 #[test]
458 fn test_local_rib_peer_flags() {
459 let mut flags = LocalRibPeerFlags::empty();
460 assert!(!flags.is_filtered());
461
462 flags |= LocalRibPeerFlags::IS_FILTERED;
463 assert!(flags.is_filtered());
464 }
465
466 #[test]
467 fn test_parsing_local_rib_per_peer_header() {
468 let input_data = vec![
469 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 192, 168, 1, 1, 0, 0, 0, 10, 0, 0, 0, 100, ];
478
479 let mut bytes = Bytes::from(input_data);
480 let header =
481 parse_per_peer_header(&mut bytes).expect("Failed to parse local rib per peer header");
482
483 assert_eq!(header.peer_type, BmpPeerType::LocalRib);
484 assert_eq!(
485 header.peer_flags,
486 PerPeerFlags::LocalRibPeerFlags(LocalRibPeerFlags::empty())
487 );
488 assert_eq!(header.peer_asn, Asn::new_32bit(1));
489 assert_eq!(header.peer_bgp_id, Ipv4Addr::new(192, 168, 1, 1));
490 assert_eq!(header.timestamp, 10.0001);
491 }
492
493 #[test]
494 #[allow(clippy::field_reassign_with_default)]
495 fn test_equality_hash() {
496 let header1 = BmpPerPeerHeader::default();
497
498 let mut header2 = BmpPerPeerHeader::default();
499 header2.timestamp = 1.0;
500
501 assert_ne!(header1, header2);
502 assert_eq!(header1.strip_timestamp(), header2.strip_timestamp());
503
504 let mut hashmap = std::collections::HashMap::new();
505 hashmap.insert(header1.strip_timestamp(), 1);
506 assert_eq!(hashmap.get(&header2.strip_timestamp()), Some(&1));
507 }
508}