bgpkit_parser/parser/mrt/messages/table_dump_v2/
rib_afi_entries.rs1use crate::bgp::attributes::parse_attributes;
2use crate::encoder::sink::with_u16_len;
3use crate::error::{check_max, EncodingError};
4use crate::models::{
5 Afi, AsnLength, NetworkPrefix, RibAfiEntries, RibEntry, Safi, TableDumpV2Type,
6};
7use crate::parser::ReadUtils;
8use crate::ParserError;
9use bytes::{Buf, BufMut, Bytes, BytesMut};
10use log::warn;
11
12fn extract_afi_safi_from_rib_type(rib_type: &TableDumpV2Type) -> Result<(Afi, Safi), ParserError> {
13 let afi: Afi;
14 let safi: Safi;
15 match rib_type {
16 TableDumpV2Type::RibIpv4Unicast | TableDumpV2Type::RibIpv4UnicastAddPath => {
17 afi = Afi::Ipv4;
18 safi = Safi::Unicast
19 }
20 TableDumpV2Type::RibIpv4Multicast | TableDumpV2Type::RibIpv4MulticastAddPath => {
21 afi = Afi::Ipv4;
22 safi = Safi::Multicast
23 }
24 TableDumpV2Type::RibIpv6Unicast | TableDumpV2Type::RibIpv6UnicastAddPath => {
25 afi = Afi::Ipv6;
26 safi = Safi::Unicast
27 }
28 TableDumpV2Type::RibIpv6Multicast | TableDumpV2Type::RibIpv6MulticastAddPath => {
29 afi = Afi::Ipv6;
30 safi = Safi::Multicast
31 }
32 _ => {
33 return Err(ParserError::ParseError(format!(
34 "wrong RIB type for parsing: {rib_type:?}"
35 )))
36 }
37 };
38
39 Ok((afi, safi))
40}
41
42fn is_add_path_rib_type(rib_type: TableDumpV2Type) -> bool {
43 matches!(
44 rib_type,
45 TableDumpV2Type::RibIpv4UnicastAddPath
46 | TableDumpV2Type::RibIpv4MulticastAddPath
47 | TableDumpV2Type::RibIpv6UnicastAddPath
48 | TableDumpV2Type::RibIpv6MulticastAddPath
49 )
50}
51
52pub(crate) const fn rib_entry_min_len(is_add_path: bool) -> usize {
53 2 + 4 + 2 + if is_add_path { 4 } else { 0 }
54}
55
56pub fn parse_rib_afi_entries(
60 data: &mut Bytes,
61 rib_type: TableDumpV2Type,
62) -> Result<RibAfiEntries, ParserError> {
63 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type)?;
64 let is_add_path = is_add_path_rib_type(rib_type);
65
66 let sequence_number = data.read_u32()?;
67
68 let prefix = data.read_nlri_prefix(&afi, false)?;
71
72 let entry_count = data.read_u16()?;
73 let min_entry_size = rib_entry_min_len(is_add_path);
75 let max_possible = data.remaining() / min_entry_size;
76 let reserve = (entry_count as usize).min(max_possible).saturating_mul(2);
77 let mut rib_entries = Vec::with_capacity(reserve);
78
79 for _i in 0..entry_count {
83 let entry = match parse_rib_entry(data, is_add_path, &afi, &safi, prefix) {
84 Ok(entry) => entry,
85 Err(e) => {
86 warn!(
87 "early break due to error {} while parsing RIB AFI entries",
88 e
89 );
90 break;
91 }
92 };
93 rib_entries.push(entry);
94 }
95
96 Ok(RibAfiEntries {
97 rib_type,
98 sequence_number,
99 prefix,
100 rib_entries,
101 })
102}
103
104pub fn parse_rib_entry(
124 input: &mut Bytes,
125 is_add_path: bool,
126 afi: &Afi,
127 safi: &Safi,
128 prefix: NetworkPrefix,
129) -> Result<RibEntry, ParserError> {
130 if input.remaining() < 8 {
131 return Err(ParserError::TruncatedMsg("truncated msg".to_string()));
134 }
135
136 let peer_index = input.read_u16()?;
137 let originated_time = input.read_u32()?;
138
139 let path_id = match is_add_path {
140 true => Some(input.read_u32()?),
141 false => None,
142 };
143
144 let attribute_length = input.read_u16()? as usize;
145
146 input.has_n_remaining(attribute_length)?;
147 let attr_data_slice = input.split_to(attribute_length);
148 let mut attributes = parse_attributes(
149 attr_data_slice,
150 &AsnLength::Bits32,
151 is_add_path,
152 Some(*afi),
153 Some(*safi),
154 Some(&[prefix]),
155 )?;
156
157 attributes.check_mandatory_attributes(true, *afi == Afi::Ipv4);
159
160 Ok(RibEntry {
161 peer_index,
162 originated_time,
163 path_id,
164 attributes,
165 })
166}
167
168impl RibAfiEntries {
169 pub fn encode(&self) -> Result<Bytes, EncodingError> {
170 let mut bytes = BytesMut::new();
171 let is_add_path = is_add_path_rib_type(self.rib_type);
172
173 bytes.put_u32(self.sequence_number);
174 bytes.extend(self.prefix.encode());
175
176 let entry_count = self.rib_entries.len();
177 check_max("RIB entry count", entry_count, u16::MAX as usize)?;
178 bytes.put_u16(entry_count as u16);
179
180 for entry in &self.rib_entries {
181 entry.encode_for_rib_type(is_add_path, &mut bytes)?;
182 }
183
184 Ok(bytes.freeze())
185 }
186}
187
188impl RibEntry {
189 pub fn encode(&self) -> Result<Bytes, EncodingError> {
190 let mut bytes = BytesMut::new();
191 self.encode_for_rib_type(self.path_id.is_some(), &mut bytes)?;
192 Ok(bytes.freeze())
193 }
194
195 fn encode_for_rib_type(
196 &self,
197 include_path_id: bool,
198 bytes: &mut BytesMut,
199 ) -> Result<(), EncodingError> {
200 bytes.put_u16(self.peer_index);
201 bytes.put_u32(self.originated_time);
202 if include_path_id {
203 if let Some(path_id) = self.path_id {
204 bytes.put_u32(path_id);
205 }
206 }
207 with_u16_len(bytes, "RIB entry attribute length", |b| {
208 self.attributes.encode_to(AsnLength::Bits32, b)
209 })
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use bytes::Buf;
217 use std::str::FromStr;
218
219 #[test]
220 fn test_extract_afi_safi_from_rib_type() {
221 let rib_type = TableDumpV2Type::RibIpv4Unicast;
222 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
223 assert_eq!(afi, Afi::Ipv4);
224 assert_eq!(safi, Safi::Unicast);
225
226 let rib_type = TableDumpV2Type::RibIpv4Multicast;
227 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
228 assert_eq!(afi, Afi::Ipv4);
229 assert_eq!(safi, Safi::Multicast);
230
231 let rib_type = TableDumpV2Type::RibIpv6Unicast;
232 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
233 assert_eq!(afi, Afi::Ipv6);
234 assert_eq!(safi, Safi::Unicast);
235
236 let rib_type = TableDumpV2Type::RibIpv6Multicast;
237 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
238 assert_eq!(afi, Afi::Ipv6);
239 assert_eq!(safi, Safi::Multicast);
240
241 let rib_type = TableDumpV2Type::RibIpv4UnicastAddPath;
242 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
243 assert_eq!(afi, Afi::Ipv4);
244 assert_eq!(safi, Safi::Unicast);
245
246 let rib_type = TableDumpV2Type::RibIpv4MulticastAddPath;
247 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
248 assert_eq!(afi, Afi::Ipv4);
249 assert_eq!(safi, Safi::Multicast);
250
251 let rib_type = TableDumpV2Type::RibIpv6UnicastAddPath;
252 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
253 assert_eq!(afi, Afi::Ipv6);
254 assert_eq!(safi, Safi::Unicast);
255
256 let rib_type = TableDumpV2Type::RibIpv6MulticastAddPath;
257 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
258 assert_eq!(afi, Afi::Ipv6);
259 assert_eq!(safi, Safi::Multicast);
260
261 let rib_type = TableDumpV2Type::RibGeneric;
262 let res = extract_afi_safi_from_rib_type(&rib_type);
263 assert!(res.is_err());
264 }
265
266 #[test]
267 fn test_rib_entry_encode() {
268 use crate::models::{AttributeValue, Attributes, Origin};
269
270 let mut attributes = Attributes::default();
271 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
272
273 let rib_entry = RibEntry {
274 peer_index: 1,
275 originated_time: 12345,
276 path_id: Some(42),
277 attributes,
278 };
279
280 let mut encoded = rib_entry.encode().unwrap();
281 assert_eq!(encoded.read_u16().unwrap(), 1);
282 assert_eq!(encoded.read_u32().unwrap(), 12345);
283 assert_eq!(encoded.read_u32().unwrap(), 42);
284 let attr_len = encoded.read_u16().unwrap() as usize;
285 assert_eq!(encoded.remaining(), attr_len);
286 }
287
288 #[test]
289 fn test_rib_afi_entries_encode_roundtrip_add_path() {
290 use crate::models::{AttributeValue, Attributes, Origin};
291
292 let mut attributes = Attributes::default();
293 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
294
295 let rib = RibAfiEntries {
296 rib_type: TableDumpV2Type::RibIpv4UnicastAddPath,
297 sequence_number: 7,
298 prefix: NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
299 rib_entries: vec![RibEntry {
300 peer_index: 3,
301 originated_time: 12345,
302 path_id: Some(42),
303 attributes,
304 }],
305 };
306
307 let encoded = rib.encode().unwrap();
308 let parsed = parse_rib_afi_entries(&mut encoded.clone(), rib.rib_type).unwrap();
309 assert_eq!(parsed.rib_type, rib.rib_type);
310 assert_eq!(parsed.sequence_number, rib.sequence_number);
311 assert_eq!(parsed.prefix, rib.prefix);
312 assert_eq!(parsed.rib_entries.len(), 1);
313 assert_eq!(parsed.rib_entries[0].peer_index, 3);
314 assert_eq!(parsed.rib_entries[0].originated_time, 12345);
315 assert_eq!(parsed.rib_entries[0].path_id, Some(42));
316 assert_eq!(
317 parsed.rib_entries[0].attributes.inner,
318 rib.rib_entries[0].attributes.inner
319 );
320 }
321}