bgpkit_parser/parser/mrt/messages/bgp4mp.rs
1use crate::error::ParserError;
2use crate::models::*;
3use crate::parser::bgp::messages::parse_bgp_message;
4use crate::parser::{encode_asn, encode_ipaddr, ReadUtils};
5use bytes::{Buf, BufMut, Bytes, BytesMut};
6use std::convert::TryFrom;
7
8/// Parse MRT BGP4MP type
9///
10/// RFC: <https://www.rfc-editor.org/rfc/rfc6396#section-4.4>
11///
12pub fn parse_bgp4mp(sub_type: u16, input: Bytes) -> Result<Bgp4MpEnum, ParserError> {
13 let bgp4mp_type: Bgp4MpType = Bgp4MpType::try_from(sub_type)?;
14 let msg: Bgp4MpEnum = match bgp4mp_type {
15 Bgp4MpType::StateChange => Bgp4MpEnum::StateChange(parse_bgp4mp_state_change(
16 input,
17 AsnLength::Bits16,
18 &bgp4mp_type,
19 )?),
20 Bgp4MpType::StateChangeAs4 => Bgp4MpEnum::StateChange(parse_bgp4mp_state_change(
21 input,
22 AsnLength::Bits32,
23 &bgp4mp_type,
24 )?),
25 Bgp4MpType::Message | Bgp4MpType::MessageLocal => Bgp4MpEnum::Message(
26 parse_bgp4mp_message(input, false, AsnLength::Bits16, &bgp4mp_type)?,
27 ),
28 Bgp4MpType::MessageAs4 | Bgp4MpType::MessageAs4Local => Bgp4MpEnum::Message(
29 parse_bgp4mp_message(input, false, AsnLength::Bits32, &bgp4mp_type)?,
30 ),
31 Bgp4MpType::MessageAddpath | Bgp4MpType::MessageLocalAddpath => Bgp4MpEnum::Message(
32 parse_bgp4mp_message(input, true, AsnLength::Bits16, &bgp4mp_type)?,
33 ),
34 Bgp4MpType::MessageAs4Addpath | Bgp4MpType::MessageLocalAs4Addpath => Bgp4MpEnum::Message(
35 parse_bgp4mp_message(input, true, AsnLength::Bits32, &bgp4mp_type)?,
36 ),
37 };
38
39 Ok(msg)
40}
41
42/// Return the embedded BGP message length in a BGP4MP message body.
43///
44/// The BGP4MP envelope is defined by RFC 6396 Section 4.4.2 for 16-bit ASNs
45/// and Section 4.4.3 for AS4 variants:
46/// <https://www.rfc-editor.org/rfc/rfc6396#section-4.4.2>
47/// <https://www.rfc-editor.org/rfc/rfc6396#section-4.4.3>
48///
49/// RFC 8050 Section 3 defines the ADDPATH BGP4MP subtypes that reuse the same
50/// envelope before the encapsulated BGP message:
51/// <https://www.rfc-editor.org/rfc/rfc8050#section-3>
52///
53/// `total_size` is the MRT message body length. Subtracting the peer/local ASNs,
54/// interface index, AFI, and peer/local IP addresses leaves the encapsulated BGP
55/// message length.
56/*
574.4.2. BGP4MP_MESSAGE Subtype:
58 0 1 2 3
59 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
60 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61 | Peer AS Number | Local AS Number |
62 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63 | Interface Index | Address Family |
64 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 | Peer IP Address (variable) |
66 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 | Local IP Address (variable) |
68 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69 | BGP Message... (variable)
70 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71
724.4.3. BGP4MP_MESSAGE_AS4 Subtype
73 0 1 2 3
74 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
75 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 | Peer AS Number |
77 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78 | Local AS Number |
79 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 | Interface Index | Address Family |
81 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82 | Peer IP Address (variable) |
83 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 | Local IP Address (variable) |
85 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 | BGP Message... (variable)
87 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88*/
89pub(crate) fn bgp4mp_message_payload_len(
90 afi: &Afi,
91 asn_len: &AsnLength,
92 total_size: usize,
93) -> usize {
94 let ip_size = match afi {
95 Afi::Ipv4 => 4 * 2,
96 Afi::Ipv6 => 16 * 2,
97 Afi::LinkState => 4 * 2, // Link-State uses IPv4 format for compatibility
98 };
99 let asn_size = match asn_len {
100 AsnLength::Bits16 => 2 * 2,
101 AsnLength::Bits32 => 2 * 4,
102 };
103 total_size - asn_size - 2 - 2 - ip_size
104}
105/*
106 0 1 2 3
107 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
108 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
109 | Peer AS Number | Local AS Number |
110 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
111 | Interface Index | Address Family |
112 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
113 | Peer IP Address (variable) |
114 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
115 | Local IP Address (variable) |
116 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
117 | BGP Message... (variable)
118 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119*/
120pub fn parse_bgp4mp_message(
121 mut data: Bytes,
122 add_path: bool,
123 asn_len: AsnLength,
124 msg_type: &Bgp4MpType,
125) -> Result<Bgp4MpMessage, ParserError> {
126 let total_size = data.len();
127
128 let peer_asn: Asn = data.read_asn(asn_len)?;
129 let local_asn: Asn = data.read_asn(asn_len)?;
130 let interface_index: u16 = data.read_u16()?;
131 let afi: Afi = data.read_afi()?;
132 let peer_ip = data.read_address(&afi)?;
133 let local_ip = data.read_address(&afi)?;
134
135 let should_read = bgp4mp_message_payload_len(&afi, &asn_len, total_size);
136 if should_read != data.remaining() {
137 return Err(ParserError::TruncatedMsg(format!(
138 "truncated bgp4mp message: should read {} bytes, have {} bytes available",
139 should_read,
140 data.remaining()
141 )));
142 }
143 let bgp_message: BgpMessage = parse_bgp_message(&mut data, add_path, &asn_len)?;
144
145 Ok(Bgp4MpMessage {
146 msg_type: *msg_type,
147 peer_asn,
148 local_asn,
149 interface_index,
150 peer_ip,
151 local_ip,
152 bgp_message,
153 })
154}
155
156impl Bgp4MpMessage {
157 pub fn encode(&self, asn_len: AsnLength) -> Bytes {
158 let mut bytes = BytesMut::new();
159 bytes.extend(encode_asn(&self.peer_asn, &asn_len));
160 bytes.extend(encode_asn(&self.local_asn, &asn_len));
161 bytes.put_u16(self.interface_index);
162 bytes.put_u16(address_family(&self.peer_ip));
163 bytes.extend(encode_ipaddr(&self.peer_ip));
164 bytes.extend(encode_ipaddr(&self.local_ip));
165 bytes.extend(&self.bgp_message.encode(asn_len));
166 bytes.freeze()
167 }
168}
169
170/*
171 0 1 2 3
172 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
173 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174 | Peer AS Number | Local AS Number |
175 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
176 | Interface Index | Address Family |
177 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
178 | Peer IP Address (variable) |
179 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
180 | Local IP Address (variable) |
181 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
182 | Old State | New State |
183 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
184
185 0 1 2 3
186 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
187 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
188 | Peer AS Number |
189 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
190 | Local AS Number |
191 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192 | Interface Index | Address Family |
193 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194 | Peer IP Address (variable) |
195 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
196 | Local IP Address (variable) |
197 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
198 | Old State | New State |
199 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200*/
201pub fn parse_bgp4mp_state_change(
202 mut input: Bytes,
203 asn_len: AsnLength,
204 msg_type: &Bgp4MpType,
205) -> Result<Bgp4MpStateChange, ParserError> {
206 let peer_asn: Asn = input.read_asn(asn_len)?;
207 let local_asn: Asn = input.read_asn(asn_len)?;
208 let interface_index: u16 = input.read_u16()?;
209 let address_family: Afi = input.read_afi()?;
210 let peer_ip = input.read_address(&address_family)?;
211 let local_addr = input.read_address(&address_family)?;
212 let old_state = BgpState::try_from(input.read_u16()?)?;
213 let new_state = BgpState::try_from(input.read_u16()?)?;
214 Ok(Bgp4MpStateChange {
215 msg_type: *msg_type,
216 peer_asn,
217 local_asn,
218 interface_index,
219 peer_ip,
220 local_addr,
221 old_state,
222 new_state,
223 })
224}
225
226impl Bgp4MpStateChange {
227 pub fn encode(&self, asn_len: AsnLength) -> Bytes {
228 let mut bytes = BytesMut::new();
229 bytes.extend(encode_asn(&self.peer_asn, &asn_len));
230 bytes.extend(encode_asn(&self.local_asn, &asn_len));
231 bytes.put_u16(self.interface_index);
232 bytes.put_u16(address_family(&self.peer_ip));
233 bytes.extend(encode_ipaddr(&self.peer_ip));
234 bytes.extend(encode_ipaddr(&self.local_addr));
235 bytes.put_u16(self.old_state as u16);
236 bytes.put_u16(self.new_state as u16);
237 bytes.freeze()
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use std::net::IpAddr;
245 use std::str::FromStr;
246
247 #[test]
248 fn test_bgp4mp_message_encode_uses_subtype_asn_width() {
249 let message = Bgp4MpMessage {
250 msg_type: Bgp4MpType::Message,
251 peer_asn: Asn::new_32bit(65000),
252 local_asn: Asn::new_32bit(65001),
253 interface_index: 1,
254 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
255 local_ip: IpAddr::from_str("10.0.0.2").unwrap(),
256 bgp_message: BgpMessage::KeepAlive,
257 };
258
259 let encoded = message.encode(AsnLength::Bits16);
260 let parsed = parse_bgp4mp(Bgp4MpType::Message as u16, encoded).unwrap();
261
262 match parsed {
263 Bgp4MpEnum::Message(parsed) => {
264 assert_eq!(parsed.peer_asn, Asn::new_16bit(65000));
265 assert_eq!(parsed.local_asn, Asn::new_16bit(65001));
266 assert_eq!(parsed.interface_index, 1);
267 assert_eq!(parsed.peer_ip, message.peer_ip);
268 assert_eq!(parsed.local_ip, message.local_ip);
269 assert_eq!(parsed.bgp_message, BgpMessage::KeepAlive);
270 }
271 other => panic!("unexpected BGP4MP message: {other:?}"),
272 }
273 }
274}