Skip to main content

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}