1use bytes::{Buf, BufMut};
2
3use crate::capability::{Afi, Safi};
4use crate::constants::{HEADER_LEN, MARKER, message_type};
5use crate::error::{DecodeError, EncodeError};
6
7const BODY_LEN: usize = 4;
9
10const TOTAL_LEN: usize = HEADER_LEN + BODY_LEN;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum RouteRefreshSubtype {
16 Normal,
18 BoRR,
20 EoRR,
22 Unknown(
24 u8,
26 ),
27}
28
29impl RouteRefreshSubtype {
30 #[must_use]
32 pub fn from_u8(value: u8) -> Self {
33 match value {
34 0 => Self::Normal,
35 1 => Self::BoRR,
36 2 => Self::EoRR,
37 other => Self::Unknown(other),
38 }
39 }
40
41 #[must_use]
43 pub fn as_u8(self) -> u8 {
44 match self {
45 Self::Normal => 0,
46 Self::BoRR => 1,
47 Self::EoRR => 2,
48 Self::Unknown(value) => value,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct RouteRefreshMessage {
62 pub afi_raw: u16,
64 pub subtype_raw: u8,
66 pub safi_raw: u8,
68}
69
70impl RouteRefreshMessage {
71 #[must_use]
73 pub fn new(afi: Afi, safi: Safi) -> Self {
74 Self::new_with_subtype(afi, safi, RouteRefreshSubtype::Normal)
75 }
76
77 #[must_use]
79 pub fn new_with_subtype(afi: Afi, safi: Safi, subtype: RouteRefreshSubtype) -> Self {
80 Self {
81 afi_raw: afi as u16,
82 subtype_raw: subtype.as_u8(),
83 safi_raw: safi as u8,
84 }
85 }
86
87 #[must_use]
89 pub fn afi(&self) -> Option<Afi> {
90 Afi::from_u16(self.afi_raw)
91 }
92
93 #[must_use]
95 pub fn safi(&self) -> Option<Safi> {
96 Safi::from_u8(self.safi_raw)
97 }
98
99 #[must_use]
101 pub fn subtype(&self) -> RouteRefreshSubtype {
102 RouteRefreshSubtype::from_u8(self.subtype_raw)
103 }
104
105 pub fn decode(buf: &mut impl Buf, body_len: usize) -> Result<Self, DecodeError> {
112 if body_len != BODY_LEN {
113 return Err(DecodeError::InvalidLength {
114 length: u16::try_from(HEADER_LEN + body_len).unwrap_or(u16::MAX),
115 });
116 }
117 if buf.remaining() < BODY_LEN {
118 return Err(DecodeError::Incomplete {
119 needed: BODY_LEN,
120 available: buf.remaining(),
121 });
122 }
123
124 let afi_raw = buf.get_u16();
125 let subtype_raw = buf.get_u8();
126 let safi_raw = buf.get_u8();
127
128 Ok(Self {
129 afi_raw,
130 subtype_raw,
131 safi_raw,
132 })
133 }
134
135 pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
142 buf.put_slice(&MARKER);
143 #[expect(clippy::cast_possible_truncation)]
144 buf.put_u16(TOTAL_LEN as u16);
145 buf.put_u8(message_type::ROUTE_REFRESH);
146 buf.put_u16(self.afi_raw);
147 buf.put_u8(self.subtype_raw);
148 buf.put_u8(self.safi_raw);
149 Ok(())
150 }
151
152 #[must_use]
154 pub fn encoded_len(&self) -> usize {
155 TOTAL_LEN
156 }
157}
158
159impl std::fmt::Display for RouteRefreshMessage {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 let subtype = match self.subtype() {
162 RouteRefreshSubtype::Normal => "Normal".to_string(),
163 RouteRefreshSubtype::BoRR => "BoRR".to_string(),
164 RouteRefreshSubtype::EoRR => "EoRR".to_string(),
165 RouteRefreshSubtype::Unknown(value) => format!("Unknown({value})"),
166 };
167
168 match (self.afi(), self.safi()) {
169 (Some(afi), Some(safi)) => {
170 write!(
171 f,
172 "ROUTE-REFRESH subtype={subtype} AFI={afi:?} SAFI={safi:?}"
173 )
174 }
175 _ => write!(
176 f,
177 "ROUTE-REFRESH subtype={subtype} AFI={} SAFI={}",
178 self.afi_raw, self.safi_raw
179 ),
180 }
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use bytes::{Bytes, BytesMut};
187
188 use super::*;
189
190 #[test]
191 fn roundtrip_ipv4_unicast() {
192 let msg = RouteRefreshMessage::new(Afi::Ipv4, Safi::Unicast);
193 let mut buf = BytesMut::with_capacity(TOTAL_LEN);
194 msg.encode(&mut buf).unwrap();
195 assert_eq!(buf.len(), TOTAL_LEN);
196
197 let mut bytes = buf.freeze();
198 bytes.advance(HEADER_LEN);
199 let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
200 assert_eq!(decoded, msg);
201 assert_eq!(decoded.afi(), Some(Afi::Ipv4));
202 assert_eq!(decoded.safi(), Some(Safi::Unicast));
203 assert_eq!(decoded.subtype(), RouteRefreshSubtype::Normal);
204 }
205
206 #[test]
207 fn roundtrip_ipv6_unicast() {
208 let msg = RouteRefreshMessage::new(Afi::Ipv6, Safi::Unicast);
209 let mut buf = BytesMut::with_capacity(TOTAL_LEN);
210 msg.encode(&mut buf).unwrap();
211
212 let mut bytes = buf.freeze();
213 bytes.advance(HEADER_LEN);
214 let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
215 assert_eq!(decoded, msg);
216 assert_eq!(decoded.afi(), Some(Afi::Ipv6));
217 assert_eq!(decoded.safi(), Some(Safi::Unicast));
218 assert_eq!(decoded.subtype(), RouteRefreshSubtype::Normal);
219 }
220
221 #[test]
222 fn roundtrip_borr() {
223 let msg = RouteRefreshMessage::new_with_subtype(
224 Afi::Ipv4,
225 Safi::Unicast,
226 RouteRefreshSubtype::BoRR,
227 );
228 let mut buf = BytesMut::with_capacity(TOTAL_LEN);
229 msg.encode(&mut buf).unwrap();
230 let mut bytes = buf.freeze();
231 bytes.advance(HEADER_LEN);
232 let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
233 assert_eq!(decoded, msg);
234 assert_eq!(decoded.subtype(), RouteRefreshSubtype::BoRR);
235 }
236
237 #[test]
238 fn roundtrip_eorr() {
239 let msg = RouteRefreshMessage::new_with_subtype(
240 Afi::Ipv6,
241 Safi::Unicast,
242 RouteRefreshSubtype::EoRR,
243 );
244 let mut buf = BytesMut::with_capacity(TOTAL_LEN);
245 msg.encode(&mut buf).unwrap();
246 let mut bytes = buf.freeze();
247 bytes.advance(HEADER_LEN);
248 let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
249 assert_eq!(decoded, msg);
250 assert_eq!(decoded.subtype(), RouteRefreshSubtype::EoRR);
251 }
252
253 #[test]
254 fn reject_wrong_body_length() {
255 let data: &[u8] = &[0, 1, 0, 1, 0xFF];
256 let mut buf = Bytes::copy_from_slice(data);
257 assert!(RouteRefreshMessage::decode(&mut buf, 5).is_err());
258 }
259
260 #[test]
261 fn reject_body_length_three() {
262 let data: &[u8] = &[0, 1, 0];
263 let mut buf = Bytes::copy_from_slice(data);
264 assert!(RouteRefreshMessage::decode(&mut buf, 3).is_err());
265 }
266
267 #[test]
268 fn decode_unknown_afi_succeeds() {
269 let data: &[u8] = &[0, 99, 0, 1];
270 let mut buf = Bytes::copy_from_slice(data);
271 let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
272 assert_eq!(msg.afi_raw, 99);
273 assert_eq!(msg.afi(), None);
274 assert_eq!(msg.safi(), Some(Safi::Unicast));
275 assert_eq!(msg.subtype(), RouteRefreshSubtype::Normal);
276 }
277
278 #[test]
279 fn decode_unknown_safi_succeeds() {
280 let data: &[u8] = &[0, 1, 0, 99];
281 let mut buf = Bytes::copy_from_slice(data);
282 let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
283 assert_eq!(msg.safi_raw, 99);
284 assert_eq!(msg.safi(), None);
285 assert_eq!(msg.afi(), Some(Afi::Ipv4));
286 assert_eq!(msg.subtype(), RouteRefreshSubtype::Normal);
287 }
288
289 #[test]
290 fn decode_unknown_subtype_succeeds() {
291 let data: &[u8] = &[0, 1, 9, 1];
292 let mut buf = Bytes::copy_from_slice(data);
293 let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
294 assert_eq!(msg.subtype(), RouteRefreshSubtype::Unknown(9));
295 }
296
297 #[test]
298 fn encoded_len_is_23() {
299 let msg = RouteRefreshMessage::new(Afi::Ipv4, Safi::Unicast);
300 assert_eq!(msg.encoded_len(), 23);
301 }
302
303 #[test]
304 fn display_known_family() {
305 let msg = RouteRefreshMessage::new(Afi::Ipv6, Safi::Unicast);
306 let s = format!("{msg}");
307 assert!(s.contains("Ipv6"));
308 assert!(s.contains("Unicast"));
309 assert!(s.contains("Normal"));
310 }
311
312 #[test]
313 fn display_unknown_family() {
314 let msg = RouteRefreshMessage {
315 afi_raw: 99,
316 subtype_raw: 7,
317 safi_raw: 42,
318 };
319 let s = format!("{msg}");
320 assert!(s.contains("99"));
321 assert!(s.contains("42"));
322 assert!(s.contains("Unknown(7)"));
323 }
324}