bgpkit_parser/parser/mrt/messages/
legacy_bgp.rs1use 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
15pub 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
24pub 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(¬ification.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}