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