Skip to main content

rustbgpd_wire/
route_refresh.rs

1use bytes::{Buf, BufMut};
2
3use crate::capability::{Afi, Safi};
4use crate::constants::{HEADER_LEN, MARKER, message_type};
5use crate::error::{DecodeError, EncodeError};
6
7/// ROUTE-REFRESH message body length (AFI u16 + subtype u8 + SAFI u8).
8const BODY_LEN: usize = 4;
9
10/// Total wire length of a ROUTE-REFRESH message (header + body).
11const TOTAL_LEN: usize = HEADER_LEN + BODY_LEN;
12
13/// ROUTE-REFRESH demarcation subtype (RFC 7313).
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum RouteRefreshSubtype {
16    /// Normal route refresh request (subtype 0).
17    Normal,
18    /// Beginning of Route Refresh (subtype 1, RFC 7313).
19    BoRR,
20    /// End of Route Refresh (subtype 2, RFC 7313).
21    EoRR,
22    /// Unrecognized subtype value.
23    Unknown(
24        /// The raw subtype byte.
25        u8,
26    ),
27}
28
29impl RouteRefreshSubtype {
30    /// Create from a raw subtype byte.
31    #[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    /// Return the raw byte value for this subtype.
42    #[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/// BGP ROUTE-REFRESH message (RFC 2918 + RFC 7313).
54///
55/// Requests a peer to re-advertise its Adj-RIB-Out for the specified
56/// address family. RFC 7313 reuses the third octet as a demarcation subtype
57/// (BoRR/EoRR). Raw wire values are stored so that unknown AFI/SAFI or
58/// subtype values can be decoded without error — the transport layer decides
59/// whether to act on or ignore them.
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct RouteRefreshMessage {
62    /// Raw AFI value from the wire.
63    pub afi_raw: u16,
64    /// Raw demarcation subtype byte from the wire.
65    pub subtype_raw: u8,
66    /// Raw SAFI value from the wire.
67    pub safi_raw: u8,
68}
69
70impl RouteRefreshMessage {
71    /// Create a normal (subtype 0) ROUTE-REFRESH from typed AFI/SAFI values.
72    #[must_use]
73    pub fn new(afi: Afi, safi: Safi) -> Self {
74        Self::new_with_subtype(afi, safi, RouteRefreshSubtype::Normal)
75    }
76
77    /// Create a ROUTE-REFRESH with an explicit subtype.
78    #[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    /// Try to interpret the raw AFI as a known address family.
88    #[must_use]
89    pub fn afi(&self) -> Option<Afi> {
90        Afi::from_u16(self.afi_raw)
91    }
92
93    /// Try to interpret the raw SAFI as a known sub-address family.
94    #[must_use]
95    pub fn safi(&self) -> Option<Safi> {
96        Safi::from_u8(self.safi_raw)
97    }
98
99    /// Decode the demarcation subtype.
100    #[must_use]
101    pub fn subtype(&self) -> RouteRefreshSubtype {
102        RouteRefreshSubtype::from_u8(self.subtype_raw)
103    }
104
105    /// Decode a ROUTE-REFRESH message body from a buffer.
106    ///
107    /// # Errors
108    ///
109    /// Returns [`DecodeError`] if the body length is not exactly 4.
110    /// Unknown AFI/SAFI values and unknown subtypes are preserved.
111    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    /// Encode a complete ROUTE-REFRESH message (header + body) into a buffer.
136    ///
137    /// # Errors
138    ///
139    /// This encoding is infallible for valid values, but returns
140    /// [`EncodeError`] for API consistency.
141    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    /// Total encoded size on the wire (header + body).
153    #[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}