bgpkit_parser/parser/mrt/messages/
table_dump.rs

1use crate::error::*;
2use crate::models::*;
3use crate::parser::bgp::attributes::parse_attributes;
4use crate::parser::ReadUtils;
5use bytes::{BufMut, Bytes, BytesMut};
6use ipnet::IpNet;
7use std::net::IpAddr;
8
9/// Parse MRT TABLE_DUMP type message.
10///
11/// <https://www.rfc-editor.org/rfc/rfc6396#section-4.2>
12///
13/// ```text
14/// The TABLE_DUMP Type does not permit 4-byte Peer AS numbers, nor does
15//  it allow the AFI of the peer IP to differ from the AFI of the Prefix
16//  field.  The TABLE_DUMP_V2 Type MUST be used in these situations.
17/// ```
18///
19/// ```text
20///  0                   1                   2                   3
21///  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
22/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23/// |         View Number           |       Sequence Number         |
24/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25/// |                        Prefix (variable)                      |
26/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27/// | Prefix Length |    Status     |
28/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29/// |                         Originated Time                       |
30/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31/// |                    Peer IP Address (variable)                 |
32/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33/// |           Peer AS             |       Attribute Length        |
34/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35/// |                   BGP Attribute... (variable)
36/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37/// ```
38pub fn parse_table_dump_message(
39    sub_type: u16,
40    mut data: Bytes,
41) -> Result<TableDumpMessage, ParserError> {
42    // ####
43    // Step 0. prepare
44    //   - define AS number length
45    //   - determine address family
46    //   - create data slice reader cursor
47
48    // determine address family based on the sub_type value defined in the MRT [CommonHeader].
49    let afi = match sub_type {
50        1 => Afi::Ipv4,
51        2 => Afi::Ipv6,
52        _ => {
53            return Err(ParserError::ParseError(format!(
54                "Invalid subtype found for TABLE_DUMP (V1) message: {}",
55                sub_type
56            )))
57        }
58    };
59
60    // ####
61    // Step 1. read simple fields
62    //   - view number
63    //   - sequence number
64    //   - prefix
65    //   - prefix-length
66    //   - status
67    //   - originated time
68    //   - peer IP address
69    //   - peer ASN
70    //   - attribute length
71
72    let view_number = data.read_u16()?;
73    let sequence_number = data.read_u16()?;
74    let prefix = match &afi {
75        Afi::Ipv4 => data.read_ipv4_prefix().map(ipnet::IpNet::V4),
76        Afi::Ipv6 => data.read_ipv6_prefix().map(ipnet::IpNet::V6),
77    }?;
78
79    let status = data.read_u8()?;
80    let time = data.read_u32()? as u64;
81
82    let peer_address: IpAddr = data.read_address(&afi)?;
83    let peer_asn = Asn::new_16bit(data.read_u16()?);
84
85    let attribute_length = data.read_u16()? as usize;
86
87    // ####
88    // Step 2. read the attributes
89    //   - create subslice based on the cursor's current position
90    //   - pass the data into the parser function
91
92    data.has_n_remaining(attribute_length)?;
93    let attr_data_slice = data.split_to(attribute_length);
94
95    // for TABLE_DUMP type, the AS number length is always 2-byte.
96    let attributes =
97        parse_attributes(attr_data_slice, &AsnLength::Bits16, false, None, None, None)?;
98
99    Ok(TableDumpMessage {
100        view_number,
101        sequence_number,
102        prefix: NetworkPrefix::new(prefix, 0),
103        status,
104        originated_time: time,
105        peer_address,
106        peer_asn,
107        attributes,
108    })
109}
110
111impl TableDumpMessage {
112    pub fn encode(&self) -> Bytes {
113        let mut bytes = BytesMut::new();
114        bytes.put_u16(self.view_number);
115        bytes.put_u16(self.sequence_number);
116        match &self.prefix.prefix {
117            IpNet::V4(p) => {
118                bytes.put_u32(p.addr().into());
119                bytes.put_u8(p.prefix_len());
120            }
121            IpNet::V6(p) => {
122                bytes.put_u128(p.addr().into());
123                bytes.put_u8(p.prefix_len());
124            }
125        }
126        bytes.put_u8(self.status);
127        bytes.put_u32(self.originated_time as u32);
128
129        // peer address and peer asn
130        match self.peer_address {
131            IpAddr::V4(a) => {
132                bytes.put_u32(a.into());
133            }
134            IpAddr::V6(a) => {
135                bytes.put_u128(a.into());
136            }
137        }
138        bytes.put_u16(self.peer_asn.into());
139
140        // encode attributes
141        let mut attr_bytes = BytesMut::new();
142        for attr in &self.attributes.inner {
143            // add_path always false for v1 table dump
144            // asn_len always 16 bites
145            attr_bytes.extend(attr.encode(false, AsnLength::Bits16));
146        }
147
148        bytes.put_u16(attr_bytes.len() as u16);
149        bytes.put_slice(&attr_bytes);
150
151        bytes.freeze()
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use bytes::{BufMut, BytesMut};
159    use std::net::{Ipv4Addr, Ipv6Addr};
160
161    const VIEW_NUMBER: u16 = 0;
162    const SEQUENCE_NUMBER: u16 = 0;
163    const IPV4_PREFIX: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
164    const IPV6_PREFIX: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
165    const PREFIX_LEN: u8 = 0;
166    const STATUS: u8 = 0;
167    const TIME: u64 = 0;
168    const PEER_IPV4: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
169    const PEER_IPV6: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
170    const PEER_ASN_16BIT: u16 = 0;
171    const ATTRIBUTE_LENGTH: usize = 0;
172    const DUMMY_ATTRIBUTES: &[u8] = &[];
173
174    #[test]
175    fn test_parse_table_dump_message_ipv4() {
176        let mut bytes_mut = BytesMut::new();
177        // Populate the bytes_mut with the same sequence that parse_table_dump_message() expects to parse
178        bytes_mut.put_u16(VIEW_NUMBER);
179        bytes_mut.put_u16(SEQUENCE_NUMBER);
180        bytes_mut.put_u32(IPV4_PREFIX.into());
181        bytes_mut.put_u8(PREFIX_LEN);
182        bytes_mut.put_u8(STATUS);
183        bytes_mut.put_u32(TIME as u32);
184        bytes_mut.put_u32(PEER_IPV4.into());
185        bytes_mut.put_u16(PEER_ASN_16BIT);
186        bytes_mut.put_u16(ATTRIBUTE_LENGTH as u16);
187        bytes_mut.put_slice(DUMMY_ATTRIBUTES);
188
189        // Convert from BytesMut to Bytes
190        let bytes = bytes_mut.freeze();
191
192        let table_dump_message_res = parse_table_dump_message(1, bytes.clone());
193        assert!(
194            table_dump_message_res.is_ok(),
195            "Failed to parse TABLE_DUMP_V1 message"
196        );
197
198        let table_dump_message = table_dump_message_res.unwrap();
199        assert_eq!(
200            table_dump_message.view_number, VIEW_NUMBER,
201            "VIEW_NUMBER mismatch"
202        );
203        assert_eq!(
204            table_dump_message.sequence_number, SEQUENCE_NUMBER,
205            "SEQUENCE_NUMBER mismatch"
206        );
207        // Add more assertions here as per your actual requirements
208        let encoded = table_dump_message.encode();
209        assert_eq!(encoded, bytes);
210    }
211    #[test]
212    fn test_parse_table_dump_message_ipv6() {
213        let mut bytes_mut = BytesMut::new();
214        // Populate the bytes_mut with the same sequence that parse_table_dump_message() expects to parse
215        bytes_mut.put_u16(VIEW_NUMBER);
216        bytes_mut.put_u16(SEQUENCE_NUMBER);
217        bytes_mut.put_u128(IPV6_PREFIX.into());
218        bytes_mut.put_u8(PREFIX_LEN);
219        bytes_mut.put_u8(STATUS);
220        bytes_mut.put_u32(TIME as u32);
221        bytes_mut.put_u128(PEER_IPV6.into());
222        bytes_mut.put_u16(PEER_ASN_16BIT);
223        bytes_mut.put_u16(ATTRIBUTE_LENGTH as u16);
224        bytes_mut.put_slice(DUMMY_ATTRIBUTES);
225
226        // Convert from BytesMut to Bytes
227        let bytes = bytes_mut.freeze();
228
229        let table_dump_message_res = parse_table_dump_message(2, bytes.clone());
230        assert!(
231            table_dump_message_res.is_ok(),
232            "Failed to parse TABLE_DUMP_V1 message"
233        );
234
235        let table_dump_message = table_dump_message_res.unwrap();
236        assert_eq!(
237            table_dump_message.view_number, VIEW_NUMBER,
238            "VIEW_NUMBER mismatch"
239        );
240        assert_eq!(
241            table_dump_message.sequence_number, SEQUENCE_NUMBER,
242            "SEQUENCE_NUMBER mismatch"
243        );
244        // Add more assertions here as per your actual requirements
245
246        // test encoding
247        let encoded = table_dump_message.encode();
248        assert_eq!(encoded, bytes);
249    }
250}