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: {sub_type}"
55            )))
56        }
57    };
58
59    // ####
60    // Step 1. read simple fields
61    //   - view number
62    //   - sequence number
63    //   - prefix
64    //   - prefix-length
65    //   - status
66    //   - originated time
67    //   - peer IP address
68    //   - peer ASN
69    //   - attribute length
70
71    let view_number = data.read_u16()?;
72    let sequence_number = data.read_u16()?;
73    let prefix = match &afi {
74        Afi::Ipv4 => data.read_ipv4_prefix().map(ipnet::IpNet::V4),
75        Afi::Ipv6 => data.read_ipv6_prefix().map(ipnet::IpNet::V6),
76        Afi::LinkState => {
77            // Link-State doesn't use traditional prefixes, but we need a placeholder
78            // Use 0.0.0.0/0 as a placeholder for now
79            Ok(ipnet::IpNet::V4(
80                ipnet::Ipv4Net::new(std::net::Ipv4Addr::new(0, 0, 0, 0), 0).unwrap(),
81            ))
82        }
83    }?;
84
85    let status = data.read_u8()?;
86    let time = data.read_u32()? as u64;
87
88    let peer_ip: IpAddr = data.read_address(&afi)?;
89    let peer_asn = Asn::new_16bit(data.read_u16()?);
90
91    let attribute_length = data.read_u16()? as usize;
92
93    // ####
94    // Step 2. read the attributes
95    //   - create subslice based on the cursor's current position
96    //   - pass the data into the parser function
97
98    data.has_n_remaining(attribute_length)?;
99    let attr_data_slice = data.split_to(attribute_length);
100
101    // for TABLE_DUMP type, the AS number length is always 2-byte.
102    let attributes =
103        parse_attributes(attr_data_slice, &AsnLength::Bits16, false, None, None, None)?;
104
105    Ok(TableDumpMessage {
106        view_number,
107        sequence_number,
108        prefix: NetworkPrefix::new(prefix, None),
109        status,
110        originated_time: time,
111        peer_ip,
112        peer_asn,
113        attributes,
114    })
115}
116
117impl TableDumpMessage {
118    pub fn encode(&self) -> Bytes {
119        let mut bytes = BytesMut::new();
120        bytes.put_u16(self.view_number);
121        bytes.put_u16(self.sequence_number);
122        match &self.prefix.prefix {
123            IpNet::V4(p) => {
124                bytes.put_u32(p.addr().into());
125                bytes.put_u8(p.prefix_len());
126            }
127            IpNet::V6(p) => {
128                bytes.put_u128(p.addr().into());
129                bytes.put_u8(p.prefix_len());
130            }
131        }
132        bytes.put_u8(self.status);
133        bytes.put_u32(self.originated_time as u32);
134
135        // peer address and peer asn
136        match self.peer_ip {
137            IpAddr::V4(a) => {
138                bytes.put_u32(a.into());
139            }
140            IpAddr::V6(a) => {
141                bytes.put_u128(a.into());
142            }
143        }
144        bytes.put_u16(self.peer_asn.into());
145
146        // encode attributes
147        let mut attr_bytes = BytesMut::new();
148        for attr in &self.attributes.inner {
149            // asn_len always 16 bites
150            attr_bytes.extend(attr.encode(AsnLength::Bits16));
151        }
152
153        bytes.put_u16(attr_bytes.len() as u16);
154        bytes.put_slice(&attr_bytes);
155
156        bytes.freeze()
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use bytes::{BufMut, BytesMut};
164    use std::net::{Ipv4Addr, Ipv6Addr};
165
166    const VIEW_NUMBER: u16 = 0;
167    const SEQUENCE_NUMBER: u16 = 0;
168    const IPV4_PREFIX: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
169    const IPV6_PREFIX: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
170    const PREFIX_LEN: u8 = 0;
171    const STATUS: u8 = 0;
172    const TIME: u64 = 0;
173    const PEER_IPV4: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
174    const PEER_IPV6: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
175    const PEER_ASN_16BIT: u16 = 0;
176    const ATTRIBUTE_LENGTH: usize = 0;
177    const DUMMY_ATTRIBUTES: &[u8] = &[];
178
179    #[test]
180    fn test_parse_table_dump_message_ipv4() {
181        let mut bytes_mut = BytesMut::new();
182        // Populate the bytes_mut with the same sequence that parse_table_dump_message() expects to parse
183        bytes_mut.put_u16(VIEW_NUMBER);
184        bytes_mut.put_u16(SEQUENCE_NUMBER);
185        bytes_mut.put_u32(IPV4_PREFIX.into());
186        bytes_mut.put_u8(PREFIX_LEN);
187        bytes_mut.put_u8(STATUS);
188        bytes_mut.put_u32(TIME as u32);
189        bytes_mut.put_u32(PEER_IPV4.into());
190        bytes_mut.put_u16(PEER_ASN_16BIT);
191        bytes_mut.put_u16(ATTRIBUTE_LENGTH as u16);
192        bytes_mut.put_slice(DUMMY_ATTRIBUTES);
193
194        // Convert from BytesMut to Bytes
195        let bytes = bytes_mut.freeze();
196
197        let table_dump_message_res = parse_table_dump_message(1, bytes.clone());
198        assert!(
199            table_dump_message_res.is_ok(),
200            "Failed to parse TABLE_DUMP_V1 message"
201        );
202
203        let table_dump_message = table_dump_message_res.unwrap();
204        assert_eq!(
205            table_dump_message.view_number, VIEW_NUMBER,
206            "VIEW_NUMBER mismatch"
207        );
208        assert_eq!(
209            table_dump_message.sequence_number, SEQUENCE_NUMBER,
210            "SEQUENCE_NUMBER mismatch"
211        );
212        // Add more assertions here as per your actual requirements
213        let encoded = table_dump_message.encode();
214        assert_eq!(encoded, bytes);
215    }
216    #[test]
217    fn test_parse_table_dump_message_ipv6() {
218        let mut bytes_mut = BytesMut::new();
219        // Populate the bytes_mut with the same sequence that parse_table_dump_message() expects to parse
220        bytes_mut.put_u16(VIEW_NUMBER);
221        bytes_mut.put_u16(SEQUENCE_NUMBER);
222        bytes_mut.put_u128(IPV6_PREFIX.into());
223        bytes_mut.put_u8(PREFIX_LEN);
224        bytes_mut.put_u8(STATUS);
225        bytes_mut.put_u32(TIME as u32);
226        bytes_mut.put_u128(PEER_IPV6.into());
227        bytes_mut.put_u16(PEER_ASN_16BIT);
228        bytes_mut.put_u16(ATTRIBUTE_LENGTH as u16);
229        bytes_mut.put_slice(DUMMY_ATTRIBUTES);
230
231        // Convert from BytesMut to Bytes
232        let bytes = bytes_mut.freeze();
233
234        let table_dump_message_res = parse_table_dump_message(2, bytes.clone());
235        assert!(
236            table_dump_message_res.is_ok(),
237            "Failed to parse TABLE_DUMP_V1 message"
238        );
239
240        let table_dump_message = table_dump_message_res.unwrap();
241        assert_eq!(
242            table_dump_message.view_number, VIEW_NUMBER,
243            "VIEW_NUMBER mismatch"
244        );
245        assert_eq!(
246            table_dump_message.sequence_number, SEQUENCE_NUMBER,
247            "SEQUENCE_NUMBER mismatch"
248        );
249        // Add more assertions here as per your actual requirements
250
251        // test encoding
252        let encoded = table_dump_message.encode();
253        assert_eq!(encoded, bytes);
254    }
255
256    #[test]
257    fn test_parse_table_dump_message_invalid_subtype() {
258        // Create a simple byte array for testing
259        let mut bytes_mut = BytesMut::new();
260        bytes_mut.put_u16(VIEW_NUMBER);
261        bytes_mut.put_u16(SEQUENCE_NUMBER);
262        let bytes = bytes_mut.freeze();
263
264        // Test with an invalid sub_type (not 1 or 2)
265        let result = parse_table_dump_message(0, bytes.clone());
266        assert!(result.is_err(), "Expected error for invalid sub_type");
267
268        if let Err(ParserError::ParseError(msg)) = result {
269            assert!(
270                msg.contains("Invalid subtype"),
271                "Expected error message to mention invalid subtype"
272            );
273        } else {
274            panic!("Expected ParseError for invalid sub_type");
275        }
276
277        // Test with another invalid sub_type
278        let result = parse_table_dump_message(3, bytes);
279        assert!(result.is_err(), "Expected error for invalid sub_type");
280
281        if let Err(ParserError::ParseError(msg)) = result {
282            assert!(
283                msg.contains("Invalid subtype"),
284                "Expected error message to mention invalid subtype"
285            );
286        } else {
287            panic!("Expected ParseError for invalid sub_type");
288        }
289    }
290
291    #[test]
292    fn test_table_dump_message_encode_with_attributes() {
293        use crate::models::{Asn, AttributeValue, Attributes, Origin};
294        use std::str::FromStr;
295
296        let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
297        let mut attributes = Attributes::default();
298        attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
299
300        let table_dump = TableDumpMessage {
301            view_number: 1,
302            sequence_number: 2,
303            prefix: NetworkPrefix::new(prefix, None),
304            status: 1,
305            originated_time: 12345,
306            peer_ip: IpAddr::V4("10.0.0.1".parse().unwrap()),
307            peer_asn: Asn::from(65000),
308            attributes,
309        };
310
311        // This should exercise the attr.encode(AsnLength::Bits16) line
312        let _encoded = table_dump.encode();
313    }
314}