Skip to main content

rustbgpd_wire/
error.rs

1use bytes::{BufMut, Bytes, BytesMut};
2use thiserror::Error;
3
4use crate::notification::NotificationCode;
5
6/// Errors encountered while decoding a BGP message from bytes.
7#[derive(Error, Debug, Clone, PartialEq, Eq)]
8pub enum DecodeError {
9    /// Not enough bytes are available to decode the message.
10    #[error("incomplete message: need {needed} bytes, have {available}")]
11    Incomplete {
12        /// Minimum bytes required.
13        needed: usize,
14        /// Bytes currently available.
15        available: usize,
16    },
17
18    /// The 16-byte header marker is not all `0xFF`.
19    #[error("invalid header marker")]
20    InvalidMarker,
21
22    /// Message length field is outside the valid range.
23    #[error("invalid message length {length} (must be 19..=4096)")]
24    InvalidLength {
25        /// The invalid length value from the wire.
26        length: u16,
27    },
28
29    /// Message type byte is not a known BGP message type.
30    #[error("unknown message type {0}")]
31    UnknownMessageType(
32        /// The unrecognized type byte.
33        u8,
34    ),
35
36    /// BGP version in OPEN is not 4.
37    #[error("unsupported BGP version {version} (expected 4)")]
38    UnsupportedVersion {
39        /// The version number received.
40        version: u8,
41    },
42
43    /// KEEPALIVE message has an invalid length (must be exactly 19).
44    #[error("invalid keepalive length {length} (expected 19)")]
45    InvalidKeepaliveLength {
46        /// The invalid length value.
47        length: u16,
48    },
49
50    /// A field within the message body is structurally invalid.
51    #[error("{message_type}: {detail}")]
52    MalformedField {
53        /// Which message type contained the error.
54        message_type: &'static str,
55        /// Human-readable description of the malformation.
56        detail: String,
57    },
58
59    /// An optional parameter in OPEN is malformed.
60    #[error("malformed optional parameter at offset {offset}: {detail}")]
61    MalformedOptionalParameter {
62        /// Byte offset within the optional parameters.
63        offset: usize,
64        /// Human-readable description of the error.
65        detail: String,
66    },
67
68    /// UPDATE withdrawn/attribute/NLRI length fields are inconsistent.
69    #[error("UPDATE length mismatch: {detail}")]
70    UpdateLengthMismatch {
71        /// Human-readable description of the mismatch.
72        detail: String,
73    },
74
75    /// UPDATE attribute fails RFC 4271 §6.3 validation.
76    #[error("UPDATE attribute error (subcode {subcode}): {detail}")]
77    UpdateAttributeError {
78        /// NOTIFICATION subcode for this error.
79        subcode: u8,
80        /// Raw attribute bytes for the NOTIFICATION data field.
81        data: Vec<u8>,
82        /// Human-readable description of the error.
83        detail: String,
84    },
85
86    /// NLRI prefix encoding is invalid (RFC 4271 §4.3).
87    #[error("UPDATE invalid network field: {detail}")]
88    InvalidNetworkField {
89        /// Human-readable description of the error.
90        detail: String,
91        /// Raw NLRI bytes for the NOTIFICATION data field.
92        data: Vec<u8>,
93    },
94}
95
96/// Errors encountered while encoding a BGP message to bytes.
97#[derive(Error, Debug, Clone, PartialEq, Eq)]
98pub enum EncodeError {
99    /// Encoded message exceeds the maximum BGP message size.
100    #[error("message exceeds maximum size: {size} bytes (max 4096)")]
101    MessageTooLong {
102        /// Total encoded size in bytes.
103        size: usize,
104    },
105
106    /// A field value is outside its valid range for encoding.
107    #[error("{field}: value {value} out of range")]
108    ValueOutOfRange {
109        /// Name of the field that is out of range.
110        field: &'static str,
111        /// String representation of the invalid value.
112        value: String,
113    },
114}
115
116impl DecodeError {
117    /// Returns the NOTIFICATION (code, subcode, data) that should be sent
118    /// to the peer when this decode error is encountered.
119    #[must_use]
120    pub fn to_notification(&self) -> (NotificationCode, u8, Bytes) {
121        match self {
122            Self::InvalidMarker => (
123                NotificationCode::MessageHeader,
124                1, // Connection Not Synchronized
125                Bytes::new(),
126            ),
127            Self::InvalidLength { length } => {
128                let mut data = BytesMut::with_capacity(2);
129                data.put_u16(*length);
130                (
131                    NotificationCode::MessageHeader,
132                    2, // Bad Message Length
133                    data.freeze(),
134                )
135            }
136            Self::UnknownMessageType(t) => (
137                NotificationCode::MessageHeader,
138                3, // Bad Message Type
139                Bytes::copy_from_slice(&[*t]),
140            ),
141            Self::UnsupportedVersion { .. } => {
142                let mut data = BytesMut::with_capacity(2);
143                data.put_u16(4); // supported version
144                (
145                    NotificationCode::OpenMessage,
146                    1, // Unsupported Version Number
147                    data.freeze(),
148                )
149            }
150            Self::InvalidKeepaliveLength { .. } | Self::Incomplete { .. } => (
151                NotificationCode::MessageHeader,
152                2, // Bad Message Length
153                Bytes::new(),
154            ),
155            Self::MalformedField { message_type, .. } if *message_type == "UPDATE" => (
156                NotificationCode::UpdateMessage,
157                1, // Malformed Attribute List
158                Bytes::new(),
159            ),
160            Self::MalformedField { .. } | Self::MalformedOptionalParameter { .. } => {
161                (NotificationCode::OpenMessage, 0, Bytes::new())
162            }
163            Self::UpdateLengthMismatch { .. } => (
164                NotificationCode::UpdateMessage,
165                1, // Malformed Attribute List
166                Bytes::new(),
167            ),
168            Self::UpdateAttributeError { subcode, data, .. } => (
169                NotificationCode::UpdateMessage,
170                *subcode,
171                Bytes::from(data.clone()),
172            ),
173            Self::InvalidNetworkField { data, .. } => (
174                NotificationCode::UpdateMessage,
175                10, // Invalid Network Field
176                Bytes::from(data.clone()),
177            ),
178        }
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn invalid_marker_maps_to_header_error() {
188        let (code, subcode, _) = DecodeError::InvalidMarker.to_notification();
189        assert_eq!(code, NotificationCode::MessageHeader);
190        assert_eq!(subcode, 1);
191    }
192
193    #[test]
194    fn invalid_length_maps_to_bad_message_length() {
195        let err = DecodeError::InvalidLength { length: 5000 };
196        let (code, subcode, data) = err.to_notification();
197        assert_eq!(code, NotificationCode::MessageHeader);
198        assert_eq!(subcode, 2);
199        assert_eq!(data.as_ref(), &5000u16.to_be_bytes());
200    }
201
202    #[test]
203    fn unknown_type_maps_to_bad_message_type() {
204        let err = DecodeError::UnknownMessageType(99);
205        let (code, subcode, data) = err.to_notification();
206        assert_eq!(code, NotificationCode::MessageHeader);
207        assert_eq!(subcode, 3);
208        assert_eq!(data.as_ref(), &[99]);
209    }
210
211    #[test]
212    fn unsupported_version_maps_to_open_error() {
213        let err = DecodeError::UnsupportedVersion { version: 3 };
214        let (code, subcode, data) = err.to_notification();
215        assert_eq!(code, NotificationCode::OpenMessage);
216        assert_eq!(subcode, 1);
217        assert_eq!(data.as_ref(), &4u16.to_be_bytes());
218    }
219
220    #[test]
221    fn update_length_mismatch_maps_to_update_error() {
222        let err = DecodeError::UpdateLengthMismatch {
223            detail: "test".into(),
224        };
225        let (code, subcode, _) = err.to_notification();
226        assert_eq!(code, NotificationCode::UpdateMessage);
227        assert_eq!(subcode, 1);
228    }
229
230    #[test]
231    fn update_malformed_field_maps_to_update_error() {
232        let err = DecodeError::MalformedField {
233            message_type: "UPDATE",
234            detail: "invalid ORIGIN value 5".into(),
235        };
236        let (code, subcode, _) = err.to_notification();
237        assert_eq!(code, NotificationCode::UpdateMessage);
238        assert_eq!(subcode, 1);
239    }
240
241    #[test]
242    fn update_attribute_error_maps_correctly() {
243        let err = DecodeError::UpdateAttributeError {
244            subcode: 6,
245            data: vec![0x40, 0x01, 0x01, 0x05],
246            detail: "invalid ORIGIN value 5".into(),
247        };
248        let (code, subcode, data) = err.to_notification();
249        assert_eq!(code, NotificationCode::UpdateMessage);
250        assert_eq!(subcode, 6);
251        assert_eq!(data.as_ref(), &[0x40, 0x01, 0x01, 0x05]);
252    }
253
254    #[test]
255    fn invalid_network_field_maps_to_subcode_10() {
256        let err = DecodeError::InvalidNetworkField {
257            detail: "NLRI prefix length 33 exceeds 32".into(),
258            data: vec![33, 10, 0, 0, 0],
259        };
260        let (code, subcode, data) = err.to_notification();
261        assert_eq!(code, NotificationCode::UpdateMessage);
262        assert_eq!(subcode, 10);
263        // Data includes the offending length byte + address bytes
264        assert_eq!(data.as_ref(), &[33, 10, 0, 0, 0]);
265    }
266
267    #[test]
268    fn open_malformed_field_maps_to_open_error() {
269        let err = DecodeError::MalformedField {
270            message_type: "OPEN",
271            detail: "test".into(),
272        };
273        let (code, subcode, _) = err.to_notification();
274        assert_eq!(code, NotificationCode::OpenMessage);
275        assert_eq!(subcode, 0);
276    }
277}