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.has_n_remaining(12)?;
322 data.advance(12);
323 IpAddr::V4(data.read_ipv4_address()?)
324 }
325 Afi::Ipv6 => IpAddr::V6(data.read_ipv6_address()?),
326 Afi::LinkState => {
327 data.has_n_remaining(12)?;
330 data.advance(12);
331 IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0))
332 }
333 };
334
335 let peer_asn = match peer_flags.asn_length() {
336 AsnLength::Bits16 => {
337 data.has_n_remaining(2)?;
338 data.advance(2);
339 Asn::new_16bit(data.read_u16()?)
340 }
341 AsnLength::Bits32 => Asn::new_32bit(data.read_u32()?),
342 };
343
344 let peer_bgp_id = data.read_ipv4_address()?;
345
346 let t_sec = data.read_u32()?;
347 let t_usec = data.read_u32()?;
348 let timestamp = t_sec as f64 + (t_usec as f64) / 1_000_000.0;
349
350 Ok(BmpPerPeerHeader {
351 peer_type,
352 peer_flags: PerPeerFlags::PeerFlags(peer_flags),
353 peer_distinguisher,
354 peer_ip,
355 peer_asn,
356 peer_bgp_id,
357 timestamp,
358 })
359 }
360 BmpPeerType::LocalRib => {
361 let local_rib_peer_flags = LocalRibPeerFlags::from_bits_retain(data.read_u8()?);
362
363 let peer_distinguisher = data.read_u64()?;
364
365 let peer_addr_bytes: [u8; 16] = {
367 let mut bytes = [0u8; 16];
368 data.has_n_remaining(16)?;
369 data.copy_to_slice(&mut bytes);
370 bytes
371 };
372
373 if peer_addr_bytes != [0u8; 16] {
375 warn!("RFC 9069 violation: Local RIB peer address MUST be zero-filled, but found non-zero bytes");
376 }
377
378 let peer_ip = IpAddr::V4(Ipv4Addr::from(0));
380
381 let peer_asn = Asn::new_32bit(data.read_u32()?);
383
384 let peer_bgp_id = data.read_ipv4_address()?;
385
386 if peer_bgp_id == Ipv4Addr::from(0) && peer_distinguisher != 0 {
388 warn!("RFC 9069: Local RIB peer BGP ID should be set to VRF instance router-id for non-global instances");
389 }
390
391 let t_sec = data.read_u32()?;
392 let t_usec = data.read_u32()?;
393 let timestamp = t_sec as f64 + (t_usec as f64) / 1_000_000.0;
394
395 Ok(BmpPerPeerHeader {
396 peer_type,
397 peer_flags: PerPeerFlags::LocalRibPeerFlags(local_rib_peer_flags),
398 peer_distinguisher,
399 peer_ip,
400 peer_asn,
401 peer_bgp_id,
402 timestamp,
403 })
404 }
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411
412 #[test]
413 fn test_header_error() {
414 let mut data = Bytes::from(vec![0, 0, 0, 0, 0]);
415 assert!(parse_bmp_common_header(&mut data).is_err(),);
416 assert_eq!(
417 parse_bmp_common_header(&mut data).unwrap_err(),
418 ParserBmpError::CorruptedBmpMessage
419 );
420 }
421
422 #[test]
423 fn test_bmp_per_peer_header_basics() {
424 let per_peer_header = BmpPerPeerHeader {
426 peer_type: BmpPeerType::Global,
427 peer_flags: PerPeerFlags::LocalRibPeerFlags(LocalRibPeerFlags::empty()),
428 peer_distinguisher: 0,
429 peer_ip: IpAddr::V4(Ipv4Addr::from(0)),
430 peer_asn: Default::default(),
431 peer_bgp_id: Ipv4Addr::from(0),
432 timestamp: 0.0,
433 };
434 assert_eq!(per_peer_header.afi(), Afi::Ipv4);
435
436 assert_eq!(per_peer_header.asn_length(), AsnLength::Bits32);
438 }
439
440 #[test]
441 fn test_peer_flags() {
442 let mut flags = PeerFlags::empty();
443 assert_eq!(flags.address_family(), Afi::Ipv4);
444 assert_eq!(flags.asn_length(), AsnLength::Bits32);
445 assert!(!flags.is_adj_rib_out());
446 assert!(!flags.is_post_policy());
447
448 flags |= PeerFlags::ADDRESS_FAMILY_IPV6;
449 assert_eq!(flags.address_family(), Afi::Ipv6);
450
451 flags |= PeerFlags::AS_SIZE_16BIT;
452 assert_eq!(flags.asn_length(), AsnLength::Bits16);
453
454 flags |= PeerFlags::IS_ADJ_RIB_OUT;
455 assert!(flags.is_adj_rib_out());
456
457 flags |= PeerFlags::IS_POST_POLICY;
458 assert!(flags.is_post_policy());
459 }
460
461 #[test]
462 fn test_local_rib_peer_flags() {
463 let mut flags = LocalRibPeerFlags::empty();
464 assert!(!flags.is_filtered());
465
466 flags |= LocalRibPeerFlags::IS_FILTERED;
467 assert!(flags.is_filtered());
468 }
469
470 #[test]
471 fn test_parsing_local_rib_per_peer_header() {
472 let input_data = vec![
473 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, ];
482
483 let mut bytes = Bytes::from(input_data);
484 let header =
485 parse_per_peer_header(&mut bytes).expect("Failed to parse local rib per peer header");
486
487 assert_eq!(header.peer_type, BmpPeerType::LocalRib);
488 assert_eq!(
489 header.peer_flags,
490 PerPeerFlags::LocalRibPeerFlags(LocalRibPeerFlags::empty())
491 );
492 assert_eq!(header.peer_asn, Asn::new_32bit(1));
493 assert_eq!(header.peer_bgp_id, Ipv4Addr::new(192, 168, 1, 1));
494 assert_eq!(header.timestamp, 10.0001);
495 }
496
497 #[test]
498 #[allow(clippy::field_reassign_with_default)]
499 fn test_equality_hash() {
500 let header1 = BmpPerPeerHeader::default();
501
502 let mut header2 = BmpPerPeerHeader::default();
503 header2.timestamp = 1.0;
504
505 assert_ne!(header1, header2);
506 assert_eq!(header1.strip_timestamp(), header2.strip_timestamp());
507
508 let mut hashmap = std::collections::HashMap::new();
509 hashmap.insert(header1.strip_timestamp(), 1);
510 assert_eq!(hashmap.get(&header2.strip_timestamp()), Some(&1));
511 }
512}