Skip to main content

netgauze_pcap_decoder/handlers/
bmp.rs

1// Copyright (C) 2025-present The NetGauze Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12// implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::{decode_buffer, serialize_error, serialize_success};
17use crate::protocol_handler::{DecodeOutcome, ProtocolHandler};
18use bytes::BytesMut;
19use netgauze_bmp_pkt::BmpMessage;
20use netgauze_bmp_pkt::codec::{BmpCodec, BmpCodecDecoderError};
21use netgauze_pcap_reader::TransportProtocol;
22use std::collections::HashMap;
23use std::io;
24use std::net::IpAddr;
25
26pub struct BmpProtocolHandler {
27    ports: Vec<u16>,
28}
29
30impl BmpProtocolHandler {
31    pub fn new(ports: Vec<u16>) -> Self {
32        BmpProtocolHandler { ports }
33    }
34}
35
36impl ProtocolHandler<BmpMessage, BmpCodec, BmpCodecDecoderError> for BmpProtocolHandler {
37    fn decode(
38        &self,
39        flow_key: (IpAddr, u16, IpAddr, u16),
40        protocol: TransportProtocol,
41        packet_data: &[u8],
42        exporter_peers: &mut HashMap<(IpAddr, u16, IpAddr, u16), (BmpCodec, BytesMut)>,
43    ) -> Option<Vec<DecodeOutcome<BmpMessage, BmpCodecDecoderError>>> {
44        let dst_port: u16 = flow_key.3;
45
46        if protocol == TransportProtocol::TCP && self.ports.contains(&dst_port) {
47            let (codec, buffer) = exporter_peers
48                .entry(flow_key)
49                .or_insert((BmpCodec::default(), BytesMut::new()));
50            buffer.extend_from_slice(packet_data);
51
52            let mut results = Vec::new();
53            decode_buffer(buffer, codec, flow_key, &mut results);
54            if !results.is_empty() {
55                return Some(results);
56            }
57        }
58        None
59    }
60
61    fn serialize(
62        &self,
63        decode_outcome: DecodeOutcome<BmpMessage, BmpCodecDecoderError>,
64    ) -> io::Result<serde_json::Value> {
65        match decode_outcome {
66            DecodeOutcome::Success(m) => {
67                let (flow_key, bmp_message) = m;
68                serialize_success(flow_key, bmp_message)
69            }
70            DecodeOutcome::Error(m) => serialize_error(m),
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use netgauze_bmp_pkt::v3::{BmpMessageValue, InitiationInformation, InitiationMessage};
79    use netgauze_bmp_pkt::wire::deserializer::BmpMessageParsingError;
80    use serde_json::json;
81    use std::net::Ipv4Addr;
82
83    #[test]
84    fn test_bmp_handler_decode_success() {
85        let handler = BmpProtocolHandler::new(vec![1790]);
86        let flow_key = (
87            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
88            12345,
89            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
90            1790,
91        );
92        // A simple BMP Initiation message
93        let packet_data = [
94            0x03, 0x00, 0x00, 0x00, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x5b, 0x43, 0x69, 0x73, 0x63,
95            0x6f, 0x20, 0x49, 0x4f, 0x53, 0x20, 0x58, 0x52, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77,
96            0x61, 0x72, 0x65, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x35,
97            0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x31, 0x49, 0x5b, 0x44, 0x65, 0x66, 0x61, 0x75,
98            0x6c, 0x74, 0x5d, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20,
99            0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x34, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69,
100            0x73, 0x63, 0x6f, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49,
101            0x6e, 0x63, 0x2e, 0x00, 0x02, 0x00, 0x03, 0x78, 0x72, 0x33,
102        ];
103
104        let mut exporter_peers = HashMap::new();
105
106        let result = handler.decode(
107            flow_key,
108            TransportProtocol::TCP,
109            &packet_data,
110            &mut exporter_peers,
111        );
112
113        assert_eq!(
114            result,
115            Some(vec![DecodeOutcome::Success((
116                flow_key,
117                BmpMessage::V3(BmpMessageValue::Initiation(InitiationMessage::new(vec![
118                    InitiationInformation::SystemDescription(
119                        "Cisco IOS XR Software, Version 5.2.2.21I[Default]\nCopyright (c) 2014 by Cisco Systems, Inc.".to_string()
120                    ),
121                    InitiationInformation::SystemName("xr3".to_string())
122                ])))
123            ))]),
124        );
125        // Now we should have an empty buffer for this flow key
126        assert!(exporter_peers.get(&flow_key).unwrap().1.is_empty());
127    }
128
129    #[test]
130    fn test_bmp_handler_decode_fragmented_success() {
131        let handler = BmpProtocolHandler::new(vec![1790]);
132        let flow_key = (
133            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
134            12345,
135            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
136            1790,
137        );
138        let packet_data1 = [
139            0x03, 0x00, 0x00, 0x00, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x5b, 0x43, 0x69, 0x73, 0x63,
140            0x6f, 0x20,
141        ];
142        let packet_data2 = [
143            0x49, 0x4f, 0x53, 0x20, 0x58, 0x52, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
144            0x65, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x35, 0x2e, 0x32,
145            0x2e, 0x32, 0x2e, 0x32, 0x31, 0x49, 0x5b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
146            0x5d, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63,
147            0x29, 0x20, 0x32, 0x30, 0x31, 0x34, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63,
148            0x6f, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63,
149            0x2e, 0x00, 0x02, 0x00, 0x03, 0x78, 0x72, 0x33,
150        ];
151        let mut exporter_peers = HashMap::new();
152
153        let result1 = handler.decode(
154            flow_key,
155            TransportProtocol::TCP,
156            &packet_data1,
157            &mut exporter_peers,
158        );
159        assert!(result1.is_none());
160        // The buffer for this flow key should now contain the first part, so not empty
161        assert!(!exporter_peers.get(&flow_key).unwrap().1.is_empty());
162
163        let result2 = handler.decode(
164            flow_key,
165            TransportProtocol::TCP,
166            &packet_data2,
167            &mut exporter_peers,
168        );
169
170        assert_eq!(
171            result2,
172            Some(vec![DecodeOutcome::Success((
173                flow_key,
174                BmpMessage::V3(BmpMessageValue::Initiation(InitiationMessage::new(vec![
175                    InitiationInformation::SystemDescription(
176                        "Cisco IOS XR Software, Version 5.2.2.21I[Default]\nCopyright (c) 2014 by Cisco Systems, Inc.".to_string()
177                    ),
178                    InitiationInformation::SystemName("xr3".to_string())
179                ])))
180            ))]),
181        );
182        // Now we should have an empty buffer for this flow key
183        assert!(exporter_peers.get(&flow_key).unwrap().1.is_empty());
184    }
185
186    #[test]
187    fn test_bmp_decode_multiple_messages_success() {
188        let handler = BmpProtocolHandler::new(vec![1790]);
189        let flow_key = (
190            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
191            12345,
192            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
193            1790,
194        );
195        // Two simple BMP Initiation message
196        let packet_data = [
197            0x03, 0x00, 0x00, 0x00, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x5b, 0x43, 0x69, 0x73, 0x63,
198            0x6f, 0x20, 0x49, 0x4f, 0x53, 0x20, 0x58, 0x52, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77,
199            0x61, 0x72, 0x65, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x35,
200            0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x31, 0x49, 0x5b, 0x44, 0x65, 0x66, 0x61, 0x75,
201            0x6c, 0x74, 0x5d, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20,
202            0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x34, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69,
203            0x73, 0x63, 0x6f, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49,
204            0x6e, 0x63, 0x2e, 0x00, 0x02, 0x00, 0x03, 0x78, 0x72, 0x33, 0x03, 0x00, 0x00, 0x00,
205            0x6c, 0x04, 0x00, 0x01, 0x00, 0x5b, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20, 0x49, 0x4f,
206            0x53, 0x20, 0x58, 0x52, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c,
207            0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x35, 0x2e, 0x32, 0x2e, 0x32,
208            0x2e, 0x32, 0x31, 0x49, 0x5b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5d, 0x0a,
209            0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20,
210            0x32, 0x30, 0x31, 0x34, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20,
211            0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x00,
212            0x02, 0x00, 0x03, 0x78, 0x72, 0x33,
213        ];
214
215        let mut exporter_peers = HashMap::new();
216
217        let result = handler.decode(
218            flow_key,
219            TransportProtocol::TCP,
220            &packet_data,
221            &mut exporter_peers,
222        );
223
224        let expected_initiation = InitiationMessage::new(vec![
225            InitiationInformation::SystemDescription(
226                "Cisco IOS XR Software, Version 5.2.2.21I[Default]\nCopyright (c) 2014 by Cisco Systems, Inc.".to_string()
227            ),
228            InitiationInformation::SystemName("xr3".to_string())
229        ]);
230
231        assert_eq!(
232            result,
233            Some(vec![
234                DecodeOutcome::Success((
235                    flow_key,
236                    BmpMessage::V3(BmpMessageValue::Initiation(expected_initiation.clone()))
237                )),
238                DecodeOutcome::Success((
239                    flow_key,
240                    BmpMessage::V3(BmpMessageValue::Initiation(expected_initiation))
241                ))
242            ]),
243        );
244        // Now we should have an empty buffer for this flow key
245        assert!(exporter_peers.get(&flow_key).unwrap().1.is_empty());
246    }
247
248    #[test]
249    fn test_bmp_handler_decode_failure() {
250        let handler = BmpProtocolHandler::new(vec![1790]);
251        let flow_key = (
252            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
253            12345,
254            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
255            1790,
256        );
257        // A simple BMP Initiation message, but with wrong version
258        let packet_data = [
259            0x01, 0x00, 0x00, 0x00, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x5b, 0x43, 0x69, 0x73, 0x63,
260            0x6f, 0x20, // wrong version, first byte
261            0x49, 0x4f, 0x53, 0x20, 0x58, 0x52, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
262            0x65, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x35, 0x2e, 0x32,
263            0x2e, 0x32, 0x2e, 0x32, 0x31, 0x49, 0x5b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
264            0x5d, 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63,
265            0x29, 0x20, 0x32, 0x30, 0x31, 0x34, 0x20, 0x62, 0x79, 0x20, 0x43, 0x69, 0x73, 0x63,
266            0x6f, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63,
267            0x2e, 0x00, 0x02, 0x00, 0x03, 0x78, 0x72, 0x33,
268        ];
269        let mut exporter_peers = HashMap::new();
270
271        let result = handler.decode(
272            flow_key,
273            TransportProtocol::TCP,
274            &packet_data,
275            &mut exporter_peers,
276        );
277
278        assert_eq!(
279            result,
280            Some(vec![DecodeOutcome::Error(
281                BmpCodecDecoderError::BmpMessageParsingError(
282                    BmpMessageParsingError::UndefinedBmpVersion(
283                        netgauze_bmp_pkt::iana::UndefinedBmpVersion(1)
284                    )
285                )
286            )]),
287        );
288        // Now we should have an empty buffer for this flow key
289        assert!(exporter_peers.get(&flow_key).unwrap().1.is_empty());
290    }
291
292    #[test]
293    fn test_bmp_handler_decode_ignore_wrong_port() {
294        let handler = BmpProtocolHandler::new(vec![1790]);
295        let flow_key = (
296            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
297            12345,
298            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
299            123,
300        );
301        let packet_data = [0xff; 0xff];
302        let mut exporter_peers = HashMap::new();
303
304        let result = handler.decode(
305            flow_key,
306            TransportProtocol::TCP,
307            &packet_data,
308            &mut exporter_peers,
309        );
310
311        assert!(result.is_none());
312    }
313
314    #[test]
315    fn test_bmp_handler_decode_ignore_wrong_protocol() {
316        let handler = BmpProtocolHandler::new(vec![1790]);
317        let flow_key = (
318            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
319            12345,
320            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
321            1790,
322        );
323        let packet_data = [0xff; 0xff];
324        let mut exporter_peers = HashMap::new();
325
326        let result = handler.decode(
327            flow_key,
328            TransportProtocol::UDP,
329            &packet_data,
330            &mut exporter_peers,
331        );
332
333        assert!(result.is_none());
334    }
335
336    #[test]
337    fn test_bmp_handler_serialize_success() {
338        let handler = BmpProtocolHandler::new(vec![1790]);
339        let flow_key = (
340            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
341            12345,
342            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
343            1790,
344        );
345        let bmp_message =
346            BmpMessage::V3(BmpMessageValue::Initiation(InitiationMessage::new(vec![])));
347        let outcome = DecodeOutcome::Success((flow_key, bmp_message));
348        let result = handler.serialize(outcome);
349        assert!(result.is_ok());
350        let json = result.unwrap();
351        let expected = json!({
352          "source_address": "10.0.0.1:12345",
353          "destination_address": "10.0.0.2:1790",
354          "info": {
355            "V3": {
356              "Initiation": {
357                "information": []
358              }
359            }
360          }
361        });
362        assert_eq!(json, expected);
363    }
364
365    #[test]
366    fn test_bmp_handler_serialize_error() {
367        let handler = BmpProtocolHandler::new(vec![1790]);
368        let error = BmpCodecDecoderError::BmpMessageParsingError(
369            BmpMessageParsingError::InvalidBmpLength(10),
370        );
371        let outcome = DecodeOutcome::Error(error);
372        let result = handler.serialize(outcome);
373        assert!(result.is_ok());
374        let json = result.unwrap();
375        let expected = json!({
376            "BmpMessageParsingError": {
377                "InvalidBmpLength": 10
378            }
379        });
380        assert_eq!(json, expected);
381    }
382}