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
40pub fn parse_rib_afi_entries(
44 data: &mut Bytes,
45 rib_type: TableDumpV2Type,
46) -> Result<RibAfiEntries, ParserError> {
47 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type)?;
48
49 let is_add_path = matches!(
50 rib_type,
51 TableDumpV2Type::RibIpv4UnicastAddPath
52 | TableDumpV2Type::RibIpv4MulticastAddPath
53 | TableDumpV2Type::RibIpv6UnicastAddPath
54 | TableDumpV2Type::RibIpv6MulticastAddPath
55 );
56
57 let sequence_number = data.read_u32()?;
58
59 let prefix = data.read_nlri_prefix(&afi, false)?;
62
63 let entry_count = data.read_u16()?;
64 let min_entry_size =
66 2 + 4 + 2 + if is_add_path { 4 } else { 0 };
67 let max_possible = data.remaining() / min_entry_size;
68 let reserve = (entry_count as usize).min(max_possible).saturating_mul(2);
69 let mut rib_entries = Vec::with_capacity(reserve);
70
71 for _i in 0..entry_count {
75 let entry = match parse_rib_entry(data, is_add_path, &afi, &safi, prefix) {
76 Ok(entry) => entry,
77 Err(e) => {
78 warn!("early break due to error {}", e);
79 break;
80 }
81 };
82 rib_entries.push(entry);
83 }
84
85 Ok(RibAfiEntries {
86 rib_type,
87 sequence_number,
88 prefix,
89 rib_entries,
90 })
91}
92
93pub fn parse_rib_entry(
113 input: &mut Bytes,
114 is_add_path: bool,
115 afi: &Afi,
116 safi: &Safi,
117 prefix: NetworkPrefix,
118) -> Result<RibEntry, ParserError> {
119 if input.remaining() < 8 {
120 return Err(ParserError::TruncatedMsg("truncated msg".to_string()));
123 }
124
125 let peer_index = input.read_u16()?;
126 let originated_time = input.read_u32()?;
127
128 let path_id = match is_add_path {
129 true => Some(input.read_u32()?),
130 false => None,
131 };
132
133 let attribute_length = input.read_u16()? as usize;
134
135 input.has_n_remaining(attribute_length)?;
136 let attr_data_slice = input.split_to(attribute_length);
137 let attributes = parse_attributes(
138 attr_data_slice,
139 &AsnLength::Bits32,
140 is_add_path,
141 Some(*afi),
142 Some(*safi),
143 Some(&[prefix]),
144 )?;
145
146 Ok(RibEntry {
147 peer_index,
148 originated_time,
149 path_id,
150 attributes,
151 })
152}
153
154impl RibAfiEntries {
155 pub fn encode(&self) -> Bytes {
156 let mut bytes = BytesMut::new();
157
158 bytes.put_u32(self.sequence_number);
159 bytes.extend(self.prefix.encode());
160
161 let entry_count = self.rib_entries.len();
162 bytes.put_u16(entry_count as u16);
163
164 for entry in &self.rib_entries {
165 bytes.extend(entry.encode());
166 }
167
168 bytes.freeze()
169 }
170}
171
172impl RibEntry {
173 pub fn encode(&self) -> Bytes {
174 let mut bytes = BytesMut::new();
175 bytes.put_u16(self.peer_index);
176 bytes.put_u32(self.originated_time);
177 let attr_bytes = self.attributes.encode(AsnLength::Bits32);
178 bytes.put_u16(attr_bytes.len() as u16);
179 bytes.extend(attr_bytes);
180 bytes.freeze()
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_extract_afi_safi_from_rib_type() {
190 let rib_type = TableDumpV2Type::RibIpv4Unicast;
191 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
192 assert_eq!(afi, Afi::Ipv4);
193 assert_eq!(safi, Safi::Unicast);
194
195 let rib_type = TableDumpV2Type::RibIpv4Multicast;
196 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
197 assert_eq!(afi, Afi::Ipv4);
198 assert_eq!(safi, Safi::Multicast);
199
200 let rib_type = TableDumpV2Type::RibIpv6Unicast;
201 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
202 assert_eq!(afi, Afi::Ipv6);
203 assert_eq!(safi, Safi::Unicast);
204
205 let rib_type = TableDumpV2Type::RibIpv6Multicast;
206 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
207 assert_eq!(afi, Afi::Ipv6);
208 assert_eq!(safi, Safi::Multicast);
209
210 let rib_type = TableDumpV2Type::RibIpv4UnicastAddPath;
211 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
212 assert_eq!(afi, Afi::Ipv4);
213 assert_eq!(safi, Safi::Unicast);
214
215 let rib_type = TableDumpV2Type::RibIpv4MulticastAddPath;
216 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
217 assert_eq!(afi, Afi::Ipv4);
218 assert_eq!(safi, Safi::Multicast);
219
220 let rib_type = TableDumpV2Type::RibIpv6UnicastAddPath;
221 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
222 assert_eq!(afi, Afi::Ipv6);
223 assert_eq!(safi, Safi::Unicast);
224
225 let rib_type = TableDumpV2Type::RibIpv6MulticastAddPath;
226 let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
227 assert_eq!(afi, Afi::Ipv6);
228 assert_eq!(safi, Safi::Multicast);
229
230 let rib_type = TableDumpV2Type::RibGeneric;
231 let res = extract_afi_safi_from_rib_type(&rib_type);
232 assert!(res.is_err());
233 }
234
235 #[test]
236 fn test_rib_entry_encode() {
237 use crate::models::{AttributeValue, Attributes, Origin};
238
239 let mut attributes = Attributes::default();
240 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
241
242 let rib_entry = RibEntry {
243 peer_index: 1,
244 originated_time: 12345,
245 path_id: Some(42),
246 attributes,
247 };
248
249 let _encoded = rib_entry.encode();
251 }
252}