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