Skip to main content

bgpkit_parser/parser/mrt/messages/
legacy_bgp.rs

1//! Parser and encoder for the deprecated MRT Type 5 BGP format.
2
3use crate::error::{EncodingError, ParserError};
4use crate::models::{
5    Asn, AsnLength, BgpMessage, BgpState, LegacyBgp, LegacyBgpMessage, LegacyBgpStateChange,
6};
7use crate::parser::bgp::messages::{
8    parse_bgp_notification_message, parse_bgp_open_message, parse_bgp_update_message,
9};
10use crate::parser::ReadUtils;
11use bytes::{Buf, BufMut, Bytes, BytesMut};
12use std::convert::TryFrom;
13use std::net::{IpAddr, Ipv4Addr};
14
15// Deprecated BGP subtype codes for MRT type 5 records, from the IANA registry:
16//
17// https://www.iana.org/assignments/mrt/mrt.xhtml#bgp-subtype-codes
18pub const BGP_UPDATE: u16 = 1;
19pub const BGP_STATE_CHANGE: u16 = 3;
20pub const BGP_OPEN: u16 = 5;
21pub const BGP_NOTIFY: u16 = 6;
22pub const BGP_KEEPALIVE: u16 = 7;
23
24/// Parse a deprecated MRT Type 5 BGP message as defined in
25/// [RFC 6396, Appendix B.2.1].
26///
27/// [RFC 6396, Appendix B.2.1]: https://www.rfc-editor.org/rfc/rfc6396.html#appendix-B.2.1
28pub fn parse_legacy_bgp(sub_type: u16, mut data: Bytes) -> Result<LegacyBgp, ParserError> {
29    match sub_type {
30        BGP_UPDATE | BGP_OPEN | BGP_NOTIFY | BGP_KEEPALIVE => {
31            let peer_asn = Asn::new_16bit(data.read_u16()?);
32            let peer_ip = IpAddr::V4(data.read_ipv4_address()?);
33            let local_asn = Asn::new_16bit(data.read_u16()?);
34            let local_ip = IpAddr::V4(data.read_ipv4_address()?);
35
36            let bgp_message = match sub_type {
37                BGP_UPDATE => {
38                    BgpMessage::Update(parse_bgp_update_message(data, false, &AsnLength::Bits16)?)
39                }
40                BGP_OPEN => BgpMessage::Open(parse_bgp_open_message(&mut data)?),
41                BGP_NOTIFY => BgpMessage::Notification(parse_bgp_notification_message(data)?),
42                BGP_KEEPALIVE => {
43                    if data.has_remaining() {
44                        return Err(ParserError::ParseError(format!(
45                            "legacy BGP KEEPALIVE has {} trailing bytes",
46                            data.remaining()
47                        )));
48                    }
49                    BgpMessage::KeepAlive
50                }
51                _ => unreachable!("matched legacy BGP message subtype"),
52            };
53
54            Ok(LegacyBgp::Message(LegacyBgpMessage {
55                peer_asn,
56                peer_ip,
57                local_asn,
58                local_ip,
59                bgp_message,
60            }))
61        }
62        BGP_STATE_CHANGE => {
63            let peer_asn = Asn::new_16bit(data.read_u16()?);
64            let peer_ip = IpAddr::V4(data.read_ipv4_address()?);
65            let old_state = BgpState::try_from(data.read_u16()?)?;
66            let new_state = BgpState::try_from(data.read_u16()?)?;
67            if data.has_remaining() {
68                return Err(ParserError::ParseError(format!(
69                    "legacy BGP STATE_CHANGE has {} trailing bytes",
70                    data.remaining()
71                )));
72            }
73            Ok(LegacyBgp::StateChange(LegacyBgpStateChange {
74                peer_asn,
75                peer_ip,
76                old_state,
77                new_state,
78            }))
79        }
80        _ => Err(ParserError::Unsupported(format!(
81            "unsupported legacy BGP subtype: {sub_type}"
82        ))),
83    }
84}
85
86pub fn encode_legacy_bgp(message: &LegacyBgp, sub_type: u16) -> Result<Bytes, EncodingError> {
87    let mut bytes = BytesMut::new();
88    match (sub_type, message) {
89        (BGP_UPDATE, LegacyBgp::Message(message)) => {
90            encode_peer_envelope(message, &mut bytes)?;
91            match &message.bgp_message {
92                BgpMessage::Update(update) => {
93                    bytes.put_slice(&update.encode(AsnLength::Bits16)?);
94                }
95                _ => {
96                    return Err(EncodingError::unencodable(
97                        "legacy BGP UPDATE",
98                        "message payload is not an UPDATE",
99                    ));
100                }
101            }
102        }
103        (BGP_OPEN, LegacyBgp::Message(message)) => {
104            encode_peer_envelope(message, &mut bytes)?;
105            match &message.bgp_message {
106                BgpMessage::Open(open) => bytes.put_slice(&open.encode()?),
107                _ => {
108                    return Err(EncodingError::unencodable(
109                        "legacy BGP OPEN",
110                        "message payload is not an OPEN",
111                    ));
112                }
113            }
114        }
115        (BGP_NOTIFY, LegacyBgp::Message(message)) => {
116            encode_peer_envelope(message, &mut bytes)?;
117            match &message.bgp_message {
118                BgpMessage::Notification(notification) => {
119                    bytes.put_slice(&notification.encode());
120                }
121                _ => {
122                    return Err(EncodingError::unencodable(
123                        "legacy BGP NOTIFY",
124                        "message payload is not a NOTIFICATION",
125                    ));
126                }
127            }
128        }
129        (BGP_KEEPALIVE, LegacyBgp::Message(message)) => {
130            encode_peer_envelope(message, &mut bytes)?;
131            if !matches!(message.bgp_message, BgpMessage::KeepAlive) {
132                return Err(EncodingError::unencodable(
133                    "legacy BGP KEEPALIVE",
134                    "message payload is not a KEEPALIVE",
135                ));
136            }
137        }
138        (BGP_STATE_CHANGE, LegacyBgp::StateChange(change)) => {
139            bytes.put_u16(asn16(&change.peer_asn, "legacy BGP peer ASN")?);
140            bytes.put_u32(ipv4(&change.peer_ip, "legacy BGP peer IP")?.into());
141            bytes.put_u16(change.old_state as u16);
142            bytes.put_u16(change.new_state as u16);
143        }
144        _ => {
145            return Err(EncodingError::unencodable(
146                "legacy BGP message",
147                format!("message does not match subtype {sub_type}"),
148            ));
149        }
150    }
151    Ok(bytes.freeze())
152}
153
154fn encode_peer_envelope(
155    message: &LegacyBgpMessage,
156    bytes: &mut BytesMut,
157) -> Result<(), EncodingError> {
158    bytes.put_u16(asn16(&message.peer_asn, "legacy BGP peer ASN")?);
159    bytes.put_u32(ipv4(&message.peer_ip, "legacy BGP peer IP")?.into());
160    bytes.put_u16(asn16(&message.local_asn, "legacy BGP local ASN")?);
161    bytes.put_u32(ipv4(&message.local_ip, "legacy BGP local IP")?.into());
162    Ok(())
163}
164
165fn asn16(asn: &Asn, field: &'static str) -> Result<u16, EncodingError> {
166    let value: u32 = (*asn).into();
167    u16::try_from(value)
168        .map_err(|_| EncodingError::too_large(field, value as usize, u16::MAX as usize))
169}
170
171fn ipv4(ip: &IpAddr, field: &'static str) -> Result<Ipv4Addr, EncodingError> {
172    match ip {
173        IpAddr::V4(ip) => Ok(*ip),
174        IpAddr::V6(_) => Err(EncodingError::unencodable(
175            field,
176            "deprecated MRT Type 5 supports IPv4 only",
177        )),
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184
185    fn peer_envelope() -> BytesMut {
186        let mut bytes = BytesMut::new();
187        bytes.put_u16(64512);
188        bytes.put_u32(u32::from(Ipv4Addr::new(192, 0, 2, 1)));
189        bytes.put_u16(64513);
190        bytes.put_u32(u32::from(Ipv4Addr::new(192, 0, 2, 2)));
191        bytes
192    }
193
194    #[test]
195    fn legacy_update_round_trips() {
196        let mut bytes = peer_envelope();
197        bytes.put_u16(0);
198        bytes.put_u16(0);
199        let wire = bytes.freeze();
200        let message = parse_legacy_bgp(BGP_UPDATE, wire.clone()).unwrap();
201        assert!(matches!(
202            &message,
203            LegacyBgp::Message(LegacyBgpMessage {
204                bgp_message: BgpMessage::Update(_),
205                ..
206            })
207        ));
208        assert_eq!(encode_legacy_bgp(&message, BGP_UPDATE).unwrap(), wire);
209    }
210
211    #[test]
212    fn legacy_keepalive_round_trips() {
213        let wire = peer_envelope().freeze();
214        let message = parse_legacy_bgp(BGP_KEEPALIVE, wire.clone()).unwrap();
215        assert!(matches!(
216            &message,
217            LegacyBgp::Message(LegacyBgpMessage {
218                bgp_message: BgpMessage::KeepAlive,
219                ..
220            })
221        ));
222        assert_eq!(encode_legacy_bgp(&message, BGP_KEEPALIVE).unwrap(), wire);
223    }
224
225    #[test]
226    fn legacy_open_round_trips() {
227        let mut bytes = peer_envelope();
228        bytes.put_u8(4);
229        bytes.put_u16(64512);
230        bytes.put_u16(180);
231        bytes.put_u32(u32::from(Ipv4Addr::new(192, 0, 2, 1)));
232        bytes.put_u8(0);
233        let wire = bytes.freeze();
234
235        let message = parse_legacy_bgp(BGP_OPEN, wire.clone()).unwrap();
236        assert!(matches!(
237            &message,
238            LegacyBgp::Message(LegacyBgpMessage {
239                bgp_message: BgpMessage::Open(_),
240                ..
241            })
242        ));
243        assert_eq!(encode_legacy_bgp(&message, BGP_OPEN).unwrap(), wire);
244    }
245
246    #[test]
247    fn legacy_notify_round_trips() {
248        let mut bytes = peer_envelope();
249        bytes.put_u8(4);
250        bytes.put_u8(0);
251        bytes.put_slice(&[1, 2, 3]);
252        let wire = bytes.freeze();
253
254        let message = parse_legacy_bgp(BGP_NOTIFY, wire.clone()).unwrap();
255        assert!(matches!(
256            &message,
257            LegacyBgp::Message(LegacyBgpMessage {
258                bgp_message: BgpMessage::Notification(_),
259                ..
260            })
261        ));
262        assert_eq!(encode_legacy_bgp(&message, BGP_NOTIFY).unwrap(), wire);
263    }
264
265    #[test]
266    fn legacy_state_change_round_trips() {
267        let mut bytes = BytesMut::new();
268        bytes.put_u16(64512);
269        bytes.put_u32(u32::from(Ipv4Addr::new(192, 0, 2, 1)));
270        bytes.put_u16(BgpState::Active as u16);
271        bytes.put_u16(BgpState::Connect as u16);
272        let wire = bytes.freeze();
273        let message = parse_legacy_bgp(BGP_STATE_CHANGE, wire.clone()).unwrap();
274        assert!(matches!(&message, LegacyBgp::StateChange(_)));
275        assert_eq!(encode_legacy_bgp(&message, BGP_STATE_CHANGE).unwrap(), wire);
276    }
277
278    #[test]
279    fn rejects_trailing_keepalive_bytes_and_unknown_subtypes() {
280        let mut bytes = peer_envelope();
281        bytes.put_u8(0);
282        assert!(parse_legacy_bgp(BGP_KEEPALIVE, bytes.freeze()).is_err());
283        assert!(matches!(
284            parse_legacy_bgp(2, Bytes::new()),
285            Err(ParserError::Unsupported(_))
286        ));
287
288        let keepalive = parse_legacy_bgp(BGP_KEEPALIVE, peer_envelope().freeze()).unwrap();
289        assert!(encode_legacy_bgp(&keepalive, BGP_OPEN).is_err());
290        assert!(encode_legacy_bgp(&keepalive, BGP_NOTIFY).is_err());
291    }
292}