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