bgpkit_parser/parser/bmp/messages/
peer_down_notification.rs

1use crate::parser::bmp::error::ParserBmpError;
2use crate::parser::ReadUtils;
3use bytes::{Buf, Bytes};
4use num_enum::{IntoPrimitive, TryFromPrimitive};
5
6#[derive(Debug, PartialEq, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct PeerDownNotification {
9    pub reason: PeerDownReason,
10    pub data: Option<Vec<u8>>,
11}
12
13#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[repr(u8)]
16pub enum PeerDownReason {
17    Reserved = 0,
18    LocalSystemClosedNotificationPduFollows = 1,
19    LocalSystemClosedFsmEvenFollows = 2,
20    RemoteSystemClosedNotificationPduFollows = 3,
21    RemoteSystemsClosedNoData,
22    PeerDeConfigured = 5,
23    LocalSystemClosedTlvDataFollows = 6,
24}
25
26pub fn parse_peer_down_notification(
27    data: &mut Bytes,
28) -> Result<PeerDownNotification, ParserBmpError> {
29    let reason = PeerDownReason::try_from(data.read_u8()?)?;
30    let bytes_left = data.remaining();
31    let data: Option<Vec<u8>> = match reason {
32        PeerDownReason::Reserved => match bytes_left {
33            0 => None,
34            _ => Some(data.read_n_bytes(bytes_left)?),
35        },
36        PeerDownReason::LocalSystemClosedNotificationPduFollows => {
37            /*
38            The local system closed the session.  Following the
39            Reason is a BGP PDU containing a BGP NOTIFICATION message that
40            would have been sent to the peer.
41            */
42            Some(data.read_n_bytes(bytes_left)?)
43        }
44        PeerDownReason::LocalSystemClosedFsmEvenFollows => {
45            /*
46            The local system closed the session.  No notification
47            message was sent.  Following the reason code is a 2-byte field
48            containing the code corresponding to the Finite State Machine
49            (FSM) Event that caused the system to close the session (see
50            Section 8.1 of [RFC4271]).  Two bytes both set to 0 are used to
51            indicate that no relevant Event code is defined.
52             */
53            Some(data.read_n_bytes(bytes_left)?)
54        }
55        PeerDownReason::RemoteSystemClosedNotificationPduFollows => {
56            /*
57            The remote system closed the session with a notification
58            message.  Following the Reason is a BGP PDU containing the BGP
59            NOTIFICATION message as received from the peer.
60             */
61            Some(data.read_n_bytes(bytes_left)?)
62        }
63        PeerDownReason::RemoteSystemsClosedNoData => {
64            /*
65            The remote system closed the session without a
66            notification message.  This includes any unexpected termination of
67            the transport session, so in some cases both the local and remote
68            systems might consider this to apply.
69             */
70            None
71        }
72        PeerDownReason::PeerDeConfigured => {
73            /*
74            Information for this peer will no longer be sent to the
75            monitoring station for configuration reasons.  This does not,
76            strictly speaking, indicate that the peer has gone down, but it
77            does indicate that the monitoring station will not receive updates
78            for the peer.
79             */
80            None
81        }
82        PeerDownReason::LocalSystemClosedTlvDataFollows => {
83            /*
84            https://www.rfc-editor.org/rfc/rfc9069.html#name-peer-down-notification
85            The Peer Down notification MUST use reason code 6. Following the reason
86            is data in TLV format. The following Peer Down Information TLV type is defined:
87            */
88            Some(data.read_n_bytes(bytes_left)?)
89        }
90    };
91    Ok(PeerDownNotification { reason, data })
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use bytes::BufMut;
98
99    #[test]
100    fn test_parse_peer_down_notification() {
101        // Test with reason `1`
102        let mut data = bytes::BytesMut::new();
103        data.put_u8(1);
104        data.put_slice(&[0u8; 10]);
105        let mut data = data.freeze();
106        let result = parse_peer_down_notification(&mut data);
107        assert!(result.is_ok());
108        let peer_down_notification = result.unwrap();
109        assert_eq!(
110            peer_down_notification.reason,
111            PeerDownReason::LocalSystemClosedNotificationPduFollows
112        );
113        assert_eq!(peer_down_notification.data.unwrap(), vec![0u8; 10]);
114
115        // Test with reason `2`
116        let mut data = bytes::BytesMut::new();
117        data.put_u8(2);
118        data.put_slice(&[0u8; 10]);
119        let mut data = data.freeze();
120        let result = parse_peer_down_notification(&mut data);
121        assert!(result.is_ok());
122        let peer_down_notification = result.unwrap();
123        assert_eq!(
124            peer_down_notification.reason,
125            PeerDownReason::LocalSystemClosedFsmEvenFollows
126        );
127        assert_eq!(peer_down_notification.data.unwrap(), vec![0u8; 10]);
128
129        // Test with reason `3`
130        let mut data = bytes::BytesMut::new();
131        data.put_u8(3);
132        data.put_slice(&[0u8; 10]);
133        let mut data = data.freeze();
134        let result = parse_peer_down_notification(&mut data);
135        assert!(result.is_ok());
136        let peer_down_notification = result.unwrap();
137        assert_eq!(
138            peer_down_notification.reason,
139            PeerDownReason::RemoteSystemClosedNotificationPduFollows
140        );
141        assert_eq!(peer_down_notification.data.unwrap(), vec![0u8; 10]);
142
143        // Test with reason `4`
144        let mut data = bytes::BytesMut::new();
145        data.put_u8(4);
146        let mut data = data.freeze();
147        let result = parse_peer_down_notification(&mut data);
148        assert!(result.is_ok());
149        let peer_down_notification = result.unwrap();
150        assert_eq!(
151            peer_down_notification.reason,
152            PeerDownReason::RemoteSystemsClosedNoData
153        );
154        assert!(peer_down_notification.data.is_none());
155
156        // Test with reason `5`
157        let mut data = bytes::BytesMut::new();
158        data.put_u8(5);
159        let mut data = data.freeze();
160        let result = parse_peer_down_notification(&mut data);
161        assert!(result.is_ok());
162        let peer_down_notification = result.unwrap();
163        assert_eq!(
164            peer_down_notification.reason,
165            PeerDownReason::PeerDeConfigured
166        );
167        assert!(peer_down_notification.data.is_none());
168
169        // Test with reason `5`
170        let mut data = bytes::BytesMut::new();
171        data.put_u8(6);
172        data.put_slice(&[0u8; 10]);
173        let mut data = data.freeze();
174        let result = parse_peer_down_notification(&mut data);
175        assert!(result.is_ok());
176        let peer_down_notification = result.unwrap();
177        assert_eq!(
178            peer_down_notification.reason,
179            PeerDownReason::LocalSystemClosedTlvDataFollows
180        );
181        assert_eq!(peer_down_notification.data.unwrap(), vec![0u8; 10]);
182
183        // Test with invalid reason
184        let mut data = bytes::BytesMut::new();
185        data.put_u8(7);
186        let mut data = data.freeze();
187        let result = parse_peer_down_notification(&mut data);
188        assert!(result.is_err());
189    }
190}