bgpkit_parser/parser/mrt/messages/
table_dump.rs1use crate::encoder::sink::with_u16_len;
2use crate::error::*;
3use crate::models::*;
4use crate::parser::bgp::attributes::parse_attributes;
5use crate::parser::ReadUtils;
6use bytes::{BufMut, Bytes, BytesMut};
7use ipnet::IpNet;
8use std::net::IpAddr;
9
10pub fn parse_table_dump_message(
40 sub_type: u16,
41 data: Bytes,
42) -> Result<TableDumpMessage, ParserError> {
43 let mut messages = parse_table_dump_messages(sub_type, data)?;
44 if messages.len() != 1 {
45 return Err(ParserError::ParseError(format!(
46 "expected one TABLE_DUMP entry, found {}",
47 messages.len()
48 )));
49 }
50 Ok(messages.remove(0))
51}
52
53pub fn parse_table_dump_messages(
58 sub_type: u16,
59 mut data: Bytes,
60) -> Result<Vec<TableDumpMessage>, ParserError> {
61 let afi = match sub_type {
69 1 => Afi::Ipv4,
70 2 => Afi::Ipv6,
71 _ => {
72 return Err(ParserError::ParseError(format!(
73 "Invalid subtype found for TABLE_DUMP (V1) message: {sub_type}"
74 )))
75 }
76 };
77
78 let view_number = data.read_u16()?;
91 let sequence_number = data.read_u16()?;
92 let mut messages = Vec::new();
93
94 while !data.is_empty() {
95 messages.push(parse_table_dump_entry(
96 &mut data,
97 &afi,
98 view_number,
99 sequence_number,
100 )?);
101 }
102
103 if messages.is_empty() {
104 return Err(ParserError::TruncatedMsg(
105 "TABLE_DUMP record contains no entries".to_string(),
106 ));
107 }
108
109 Ok(messages)
110}
111
112fn parse_table_dump_entry(
113 data: &mut Bytes,
114 afi: &Afi,
115 view_number: u16,
116 sequence_number: u16,
117) -> Result<TableDumpMessage, ParserError> {
118 let prefix = match afi {
119 Afi::Ipv4 => data.read_ipv4_prefix().map(ipnet::IpNet::V4),
120 Afi::Ipv6 => data.read_ipv6_prefix().map(ipnet::IpNet::V6),
121 Afi::LinkState => {
122 Ok(ipnet::IpNet::V4(
125 ipnet::Ipv4Net::new(std::net::Ipv4Addr::new(0, 0, 0, 0), 0).unwrap(),
126 ))
127 }
128 }?;
129
130 let status = data.read_u8()?;
131 let time = data.read_u32()? as u64;
132
133 let peer_ip: IpAddr = data.read_address(afi)?;
134 let peer_asn = Asn::new_16bit(data.read_u16()?);
135
136 let attribute_length = data.read_u16()? as usize;
137
138 data.has_n_remaining(attribute_length)?;
144 let attr_data_slice = data.split_to(attribute_length);
145
146 let mut attributes =
148 parse_attributes(attr_data_slice, &AsnLength::Bits16, false, None, None, None)?;
149
150 attributes.check_mandatory_attributes(true, *afi == Afi::Ipv4);
152
153 Ok(TableDumpMessage {
154 view_number,
155 sequence_number,
156 prefix: NetworkPrefix::new(prefix, None),
157 status,
158 originated_time: time,
159 peer_ip,
160 peer_asn,
161 attributes,
162 })
163}
164
165impl TableDumpMessage {
166 pub fn encode(&self) -> Result<Bytes, EncodingError> {
167 let mut bytes = BytesMut::new();
168 bytes.put_u16(self.view_number);
169 bytes.put_u16(self.sequence_number);
170 self.encode_entry_to(&mut bytes)?;
171 Ok(bytes.freeze())
172 }
173
174 fn encode_entry_to(&self, bytes: &mut BytesMut) -> Result<(), EncodingError> {
175 match &self.prefix.prefix {
176 IpNet::V4(p) => {
177 bytes.put_u32(p.addr().into());
178 bytes.put_u8(p.prefix_len());
179 }
180 IpNet::V6(p) => {
181 bytes.put_u128(p.addr().into());
182 bytes.put_u8(p.prefix_len());
183 }
184 }
185 bytes.put_u8(self.status);
186 bytes.put_u32(self.originated_time as u32);
187
188 match self.peer_ip {
190 IpAddr::V4(a) => {
191 bytes.put_u32(a.into());
192 }
193 IpAddr::V6(a) => {
194 bytes.put_u128(a.into());
195 }
196 }
197 bytes.put_u16(self.peer_asn.into());
198
199 with_u16_len(bytes, "TABLE_DUMP attribute length", |b| {
201 self.attributes.encode_to(AsnLength::Bits16, b)
202 })?;
203 Ok(())
204 }
205}
206
207pub(crate) fn encode_table_dump_batch(
208 messages: &[TableDumpMessage],
209 sub_type: u16,
210) -> Result<Bytes, EncodingError> {
211 let Some(first) = messages.first() else {
212 return Err(EncodingError::unencodable(
213 "TABLE_DUMP batch",
214 "batch is empty",
215 ));
216 };
217
218 let expected_ipv4 = match sub_type {
219 1 => true,
220 2 => false,
221 _ => {
222 return Err(EncodingError::unencodable(
223 "TABLE_DUMP batch",
224 format!("invalid subtype {sub_type}"),
225 ));
226 }
227 };
228
229 let mut bytes = BytesMut::new();
230 bytes.put_u16(first.view_number);
231 bytes.put_u16(first.sequence_number);
232 for message in messages {
233 if message.view_number != first.view_number
234 || message.sequence_number != first.sequence_number
235 {
236 return Err(EncodingError::unencodable(
237 "TABLE_DUMP batch",
238 "all entries must have the same view and sequence numbers",
239 ));
240 }
241 if matches!(message.prefix.prefix, IpNet::V4(_)) != expected_ipv4
242 || matches!(message.peer_ip, IpAddr::V4(_)) != expected_ipv4
243 {
244 return Err(EncodingError::unencodable(
245 "TABLE_DUMP batch",
246 "entry address family does not match the MRT subtype",
247 ));
248 }
249 message.encode_entry_to(&mut bytes)?;
250 }
251 Ok(bytes.freeze())
252}
253
254pub(crate) fn needs_legacy_length_correction(sub_type: u16, data: &[u8]) -> bool {
264 let address_len = match sub_type {
265 1 => 4usize,
266 2 => 16usize,
267 _ => return false,
268 };
269 if data.len() < 4 {
270 return false;
271 }
272
273 let fixed_entry_len = address_len * 2 + 10;
276 let mut offset = 4usize;
277 while offset < data.len() {
278 let Some(attr_len_offset) = offset.checked_add(fixed_entry_len - 2) else {
279 return false;
280 };
281 if attr_len_offset + 2 > data.len() {
282 return false;
283 }
284 let attr_len =
285 u16::from_be_bytes([data[attr_len_offset], data[attr_len_offset + 1]]) as usize;
286 let Some(entry_end) = offset
287 .checked_add(fixed_entry_len)
288 .and_then(|value| value.checked_add(attr_len))
289 else {
290 return false;
291 };
292 if entry_end == data.len() + 4 {
293 return true;
294 }
295 if entry_end <= offset || entry_end > data.len() {
296 return false;
297 }
298 offset = entry_end;
299 }
300 false
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306 use bytes::{BufMut, BytesMut};
307 use std::net::{Ipv4Addr, Ipv6Addr};
308
309 const VIEW_NUMBER: u16 = 0;
310 const SEQUENCE_NUMBER: u16 = 0;
311 const IPV4_PREFIX: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
312 const IPV6_PREFIX: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
313 const PREFIX_LEN: u8 = 0;
314 const STATUS: u8 = 0;
315 const TIME: u64 = 0;
316 const PEER_IPV4: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
317 const PEER_IPV6: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
318 const PEER_ASN_16BIT: u16 = 0;
319 const ATTRIBUTE_LENGTH: usize = 0;
320 const DUMMY_ATTRIBUTES: &[u8] = &[];
321
322 #[test]
323 fn test_parse_table_dump_message_ipv4() {
324 let mut bytes_mut = BytesMut::new();
325 bytes_mut.put_u16(VIEW_NUMBER);
327 bytes_mut.put_u16(SEQUENCE_NUMBER);
328 bytes_mut.put_u32(IPV4_PREFIX.into());
329 bytes_mut.put_u8(PREFIX_LEN);
330 bytes_mut.put_u8(STATUS);
331 bytes_mut.put_u32(TIME as u32);
332 bytes_mut.put_u32(PEER_IPV4.into());
333 bytes_mut.put_u16(PEER_ASN_16BIT);
334 bytes_mut.put_u16(ATTRIBUTE_LENGTH as u16);
335 bytes_mut.put_slice(DUMMY_ATTRIBUTES);
336
337 let bytes = bytes_mut.freeze();
339
340 let table_dump_message_res = parse_table_dump_message(1, bytes.clone());
341 assert!(
342 table_dump_message_res.is_ok(),
343 "Failed to parse TABLE_DUMP_V1 message"
344 );
345
346 let table_dump_message = table_dump_message_res.unwrap();
347 assert_eq!(
348 table_dump_message.view_number, VIEW_NUMBER,
349 "VIEW_NUMBER mismatch"
350 );
351 assert_eq!(
352 table_dump_message.sequence_number, SEQUENCE_NUMBER,
353 "SEQUENCE_NUMBER mismatch"
354 );
355 let encoded = table_dump_message.encode().unwrap();
357 assert_eq!(encoded, bytes);
358 }
359 #[test]
360 fn test_parse_table_dump_message_ipv6() {
361 let mut bytes_mut = BytesMut::new();
362 bytes_mut.put_u16(VIEW_NUMBER);
364 bytes_mut.put_u16(SEQUENCE_NUMBER);
365 bytes_mut.put_u128(IPV6_PREFIX.into());
366 bytes_mut.put_u8(PREFIX_LEN);
367 bytes_mut.put_u8(STATUS);
368 bytes_mut.put_u32(TIME as u32);
369 bytes_mut.put_u128(PEER_IPV6.into());
370 bytes_mut.put_u16(PEER_ASN_16BIT);
371 bytes_mut.put_u16(ATTRIBUTE_LENGTH as u16);
372 bytes_mut.put_slice(DUMMY_ATTRIBUTES);
373
374 let bytes = bytes_mut.freeze();
376
377 let table_dump_message_res = parse_table_dump_message(2, bytes.clone());
378 assert!(
379 table_dump_message_res.is_ok(),
380 "Failed to parse TABLE_DUMP_V1 message"
381 );
382
383 let table_dump_message = table_dump_message_res.unwrap();
384 assert_eq!(
385 table_dump_message.view_number, VIEW_NUMBER,
386 "VIEW_NUMBER mismatch"
387 );
388 assert_eq!(
389 table_dump_message.sequence_number, SEQUENCE_NUMBER,
390 "SEQUENCE_NUMBER mismatch"
391 );
392 let encoded = table_dump_message.encode().unwrap();
396 assert_eq!(encoded, bytes);
397 }
398
399 #[test]
400 fn test_parse_table_dump_message_invalid_subtype() {
401 let mut bytes_mut = BytesMut::new();
403 bytes_mut.put_u16(VIEW_NUMBER);
404 bytes_mut.put_u16(SEQUENCE_NUMBER);
405 let bytes = bytes_mut.freeze();
406
407 let result = parse_table_dump_message(0, bytes.clone());
409 assert!(result.is_err(), "Expected error for invalid sub_type");
410
411 if let Err(ParserError::ParseError(msg)) = result {
412 assert!(
413 msg.contains("Invalid subtype"),
414 "Expected error message to mention invalid subtype"
415 );
416 } else {
417 panic!("Expected ParseError for invalid sub_type");
418 }
419
420 let result = parse_table_dump_message(3, bytes);
422 assert!(result.is_err(), "Expected error for invalid sub_type");
423
424 if let Err(ParserError::ParseError(msg)) = result {
425 assert!(
426 msg.contains("Invalid subtype"),
427 "Expected error message to mention invalid subtype"
428 );
429 } else {
430 panic!("Expected ParseError for invalid sub_type");
431 }
432 }
433
434 #[test]
435 fn test_table_dump_message_encode_with_attributes() {
436 use crate::models::{Asn, AttributeValue, Attributes, Origin};
437 use std::str::FromStr;
438
439 let prefix = IpNet::from_str("192.168.0.0/24").unwrap();
440 let mut attributes = Attributes::default();
441 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
442
443 let table_dump = TableDumpMessage {
444 view_number: 1,
445 sequence_number: 2,
446 prefix: NetworkPrefix::new(prefix, None),
447 status: 1,
448 originated_time: 12345,
449 peer_ip: IpAddr::V4("10.0.0.1".parse().unwrap()),
450 peer_asn: Asn::from(65000),
451 attributes,
452 };
453
454 let _encoded = table_dump.encode().unwrap();
456 }
457
458 #[test]
459 fn parses_and_encodes_batched_table_dump_messages() {
460 use crate::models::{AttributeValue, Origin};
461 use std::str::FromStr;
462
463 let mut attributes = Attributes::default();
464 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
465 let first = TableDumpMessage {
466 view_number: 7,
467 sequence_number: 9,
468 prefix: NetworkPrefix::from_str("192.0.2.0/24").unwrap(),
469 status: 1,
470 originated_time: 12345,
471 peer_ip: IpAddr::V4(Ipv4Addr::new(198, 51, 100, 1)),
472 peer_asn: Asn::new_16bit(64512),
473 attributes: attributes.clone(),
474 };
475 let second = TableDumpMessage {
476 prefix: NetworkPrefix::from_str("198.51.100.0/24").unwrap(),
477 ..first.clone()
478 };
479
480 let wire = encode_table_dump_batch(&[first.clone(), second.clone()], 1).unwrap();
481 let parsed = parse_table_dump_messages(1, wire.clone()).unwrap();
482 assert_eq!(parsed.len(), 2);
483 assert_eq!(parsed[0].prefix, first.prefix);
484 assert_eq!(parsed[1].prefix, second.prefix);
485 assert!(!needs_legacy_length_correction(1, &wire));
486 assert!(needs_legacy_length_correction(1, &wire[..wire.len() - 4]));
487 assert!(!needs_legacy_length_correction(1, &wire[..wire.len() - 3]));
488 }
489}