bgpkit_parser/parser/bmp/messages/
route_monitoring.rs

1use crate::models::*;
2use crate::parser::bgp::messages::parse_bgp_message;
3use crate::parser::bmp::error::ParserBmpError;
4use crate::parser::bmp::messages::BmpPeerType;
5use bytes::Bytes;
6use log::warn;
7
8#[derive(Debug, PartialEq, Clone)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct RouteMonitoring {
11    pub bgp_message: BgpMessage,
12}
13
14pub fn parse_route_monitoring(
15    data: &mut Bytes,
16    asn_len: &AsnLength,
17    peer_type: Option<&BmpPeerType>,
18) -> Result<RouteMonitoring, ParserBmpError> {
19    // RFC 9069: Local RIB MUST use 4-byte ASN encoding
20    if let Some(BmpPeerType::LocalRib) = peer_type {
21        if *asn_len != AsnLength::Bits32 {
22            warn!("RFC 9069 violation: Local RIB route monitoring MUST use 4-byte ASN encoding");
23        }
24    }
25
26    let bgp_update = parse_bgp_message(data, false, asn_len)?;
27    Ok(RouteMonitoring {
28        bgp_message: bgp_update,
29    })
30}
31
32impl RouteMonitoring {
33    /// Check if the BMP route-monitoring message is an End-of-RIB marker.
34    pub fn is_end_of_rib(&self) -> bool {
35        if let BgpMessage::Update(u) = &self.bgp_message {
36            u.is_end_of_rib()
37        } else {
38            false
39        }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn test_end_of_rib() {
49        let msg = BgpUpdateMessage {
50            withdrawn_prefixes: vec![],
51            attributes: Attributes::default(),
52            announced_prefixes: vec![],
53        };
54        assert!(msg.is_end_of_rib());
55
56        let mon_msg = RouteMonitoring {
57            bgp_message: BgpMessage::Update(msg),
58        };
59        assert!(mon_msg.is_end_of_rib());
60
61        let mon_msg = RouteMonitoring {
62            bgp_message: BgpMessage::KeepAlive,
63        };
64        assert!(!mon_msg.is_end_of_rib());
65    }
66
67    #[test]
68    fn test_debug() {
69        let msg = BgpUpdateMessage {
70            withdrawn_prefixes: vec![],
71            attributes: Attributes::default(),
72            announced_prefixes: vec![],
73        };
74        let mon_msg = RouteMonitoring {
75            bgp_message: BgpMessage::Update(msg),
76        };
77        #[cfg(feature = "parser")]
78        let expected = "RouteMonitoring { bgp_message: Update(BgpUpdateMessage { withdrawn_prefixes: [], attributes: Attributes { inner: [], validation_warnings: [] }, announced_prefixes: [] }) }";
79        #[cfg(not(feature = "parser"))]
80        let expected = "RouteMonitoring { bgp_message: Update(BgpUpdateMessage { withdrawn_prefixes: [], attributes: Attributes { inner: [] }, announced_prefixes: [] }) }";
81
82        assert_eq!(format!("{mon_msg:?}"), expected);
83    }
84
85    // Note: These tests verify that the parser handles LocalRib ASN validation correctly.
86    // The actual warning messages are logged via the `log` crate and would appear
87    // in production use. For testing purposes, we verify that parsing succeeds
88    // and the code paths are exercised.
89
90    #[test]
91    fn test_local_rib_with_16bit_asn() {
92        // This test verifies that LocalRib route monitoring with 16-bit ASN
93        // is parsed successfully. In production, a warning would be logged.
94
95        // Create a simple BGP UPDATE message (End-of-RIB)
96        let bgp_update = BgpMessage::Update(BgpUpdateMessage {
97            withdrawn_prefixes: vec![],
98            attributes: Attributes::default(),
99            announced_prefixes: vec![],
100        });
101        let bgp_bytes = bgp_update.encode(AsnLength::Bits16);
102
103        let mut data = bgp_bytes;
104        let asn_len = AsnLength::Bits16; // RFC 9069 violation
105        let peer_type = crate::parser::bmp::messages::BmpPeerType::LocalRib;
106
107        let result = parse_route_monitoring(&mut data, &asn_len, Some(&peer_type));
108
109        assert!(result.is_ok(), "Parsing should succeed");
110        // In production, a warning would be logged about ASN encoding violation
111    }
112
113    #[test]
114    fn test_local_rib_with_32bit_asn() {
115        // This test verifies that LocalRib route monitoring with 32-bit ASN
116        // is parsed successfully without warnings.
117
118        // Create a simple BGP UPDATE message (End-of-RIB)
119        let bgp_update = BgpMessage::Update(BgpUpdateMessage {
120            withdrawn_prefixes: vec![],
121            attributes: Attributes::default(),
122            announced_prefixes: vec![],
123        });
124        let bgp_bytes = bgp_update.encode(AsnLength::Bits32);
125
126        let mut data = bgp_bytes;
127        let asn_len = AsnLength::Bits32; // RFC 9069 compliant
128        let peer_type = crate::parser::bmp::messages::BmpPeerType::LocalRib;
129
130        let result = parse_route_monitoring(&mut data, &asn_len, Some(&peer_type));
131
132        assert!(result.is_ok(), "Parsing should succeed");
133        // No warning should be logged when using 32-bit ASN
134    }
135
136    #[test]
137    fn test_non_local_rib_with_16bit_asn() {
138        // This test verifies that non-LocalRib route monitoring doesn't trigger
139        // LocalRib-specific ASN validation.
140
141        // Create a simple BGP UPDATE message (End-of-RIB)
142        let bgp_update = BgpMessage::Update(BgpUpdateMessage {
143            withdrawn_prefixes: vec![],
144            attributes: Attributes::default(),
145            announced_prefixes: vec![],
146        });
147        let bgp_bytes = bgp_update.encode(AsnLength::Bits16);
148
149        let mut data = bgp_bytes;
150        let asn_len = AsnLength::Bits16;
151        let peer_type = crate::parser::bmp::messages::BmpPeerType::Global; // Not LocalRib
152
153        let result = parse_route_monitoring(&mut data, &asn_len, Some(&peer_type));
154
155        assert!(result.is_ok(), "Parsing should succeed");
156        // No warnings should be logged for non-LocalRib peer types
157    }
158}