rustbgpd_wire/
notification_msg.rs1use bytes::{Buf, BufMut, Bytes};
2
3use crate::constants::HEADER_LEN;
4use crate::error::{DecodeError, EncodeError};
5use crate::header::{BgpHeader, MessageType};
6use crate::notification::NotificationCode;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct NotificationMessage {
11 pub code: NotificationCode,
13 pub subcode: u8,
15 pub data: Bytes,
17}
18
19impl NotificationMessage {
20 pub fn decode(buf: &mut impl Buf, body_len: usize) -> Result<Self, DecodeError> {
29 if body_len < 2 {
30 return Err(DecodeError::MalformedField {
31 message_type: "NOTIFICATION",
32 detail: format!("body too short: {body_len} bytes (need at least 2)"),
33 });
34 }
35
36 if buf.remaining() < body_len {
37 return Err(DecodeError::Incomplete {
38 needed: body_len,
39 available: buf.remaining(),
40 });
41 }
42
43 let code_byte = buf.get_u8();
44 let code = NotificationCode::from_u8(code_byte);
45
46 let subcode = buf.get_u8();
47
48 let data_len = body_len - 2;
49 let data = buf.copy_to_bytes(data_len);
50
51 Ok(Self {
52 code,
53 subcode,
54 data,
55 })
56 }
57
58 pub fn encode_body(&self, buf: &mut impl BufMut) {
61 buf.put_u8(self.code.as_u8());
62 buf.put_u8(self.subcode);
63 buf.put_slice(&self.data);
64 }
65
66 pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
73 let total_len = HEADER_LEN + 2 + self.data.len();
74 if total_len > usize::from(crate::constants::MAX_MESSAGE_LEN) {
75 return Err(EncodeError::MessageTooLong { size: total_len });
76 }
77
78 let header = BgpHeader {
79 #[expect(clippy::cast_possible_truncation)]
80 length: total_len as u16,
81 message_type: MessageType::Notification,
82 };
83 header.encode(buf);
84 self.encode_body(buf);
85 Ok(())
86 }
87
88 #[must_use]
90 pub fn encoded_len(&self) -> usize {
91 HEADER_LEN + 2 + self.data.len()
92 }
93
94 #[must_use]
96 pub fn new(code: NotificationCode, subcode: u8, data: Bytes) -> Self {
97 Self {
98 code,
99 subcode,
100 data,
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use bytes::BytesMut;
108
109 use super::*;
110 use crate::constants::MAX_MESSAGE_LEN;
111
112 #[test]
113 fn decode_notification_no_data() {
114 let body: &[u8] = &[6, 2];
116 let mut buf = Bytes::copy_from_slice(body);
117 let msg = NotificationMessage::decode(&mut buf, 2).unwrap();
118 assert_eq!(msg.code, NotificationCode::Cease);
119 assert_eq!(msg.subcode, 2);
120 assert!(msg.data.is_empty());
121 }
122
123 #[test]
124 fn decode_notification_with_data() {
125 let body: &[u8] = &[1, 2, 0x00, 0x0F]; let mut buf = Bytes::copy_from_slice(body);
127 let msg = NotificationMessage::decode(&mut buf, 4).unwrap();
128 assert_eq!(msg.code, NotificationCode::MessageHeader);
129 assert_eq!(msg.subcode, 2);
130 assert_eq!(msg.data.as_ref(), &[0x00, 0x0F]);
131 }
132
133 #[test]
134 fn reject_body_too_short() {
135 let body: &[u8] = &[1];
136 let mut buf = Bytes::copy_from_slice(body);
137 assert!(NotificationMessage::decode(&mut buf, 1).is_err());
138 }
139
140 #[test]
141 fn encode_decode_roundtrip() {
142 let original = NotificationMessage::new(
143 NotificationCode::OpenMessage,
144 6, Bytes::from_static(&[0x00, 0x02]),
146 );
147
148 let mut encoded = BytesMut::with_capacity(original.encoded_len());
149 original.encode(&mut encoded).unwrap();
150
151 let mut bytes = encoded.freeze();
153 let header = BgpHeader::decode(&mut bytes, MAX_MESSAGE_LEN).unwrap();
154 assert_eq!(header.message_type, MessageType::Notification);
155
156 let decoded =
157 NotificationMessage::decode(&mut bytes, usize::from(header.length) - HEADER_LEN)
158 .unwrap();
159 assert_eq!(original, decoded);
160 }
161
162 #[test]
163 fn reject_message_too_long() {
164 let msg = NotificationMessage::new(
165 NotificationCode::Cease,
166 0,
167 Bytes::from(vec![0u8; 4096]), );
169 let mut buf = BytesMut::with_capacity(5000);
170 assert!(matches!(
171 msg.encode(&mut buf),
172 Err(EncodeError::MessageTooLong { .. })
173 ));
174 }
175}