bgpkit_parser/parser/mrt/messages/table_dump_v2/
rib_afi_entries.rs

1use 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
40/// RIB AFI-specific entries
41///
42/// https://tools.ietf.org/html/rfc6396#section-4.3
43pub 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    // NOTE: here we parse the prefix as only length and prefix, the path identifier for add_path
60    //       entry is not handled here. We follow RFC6396 here https://www.rfc-editor.org/rfc/rfc6396.html#section-4.3.2
61    let prefix = data.read_nlri_prefix(&afi, false)?;
62
63    let entry_count = data.read_u16()?;
64    let mut rib_entries = Vec::with_capacity((entry_count * 2) as usize);
65
66    // get the u8 slice of the rest of the data
67    // let attr_data_slice = &input.into_inner()[(input.position() as usize)..];
68
69    for _i in 0..entry_count {
70        let entry = match parse_rib_entry(data, is_add_path, &afi, &safi, prefix) {
71            Ok(entry) => entry,
72            Err(e) => {
73                warn!("early break due to error {}", e);
74                break;
75            }
76        };
77        rib_entries.push(entry);
78    }
79
80    Ok(RibAfiEntries {
81        rib_type,
82        sequence_number,
83        prefix,
84        rib_entries,
85    })
86}
87
88/// RIB entry: one prefix per entry
89///
90///
91/// https://datatracker.ietf.org/doc/html/rfc6396#section-4.3.4
92/// ```text
93///         0                   1                   2                   3
94///         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
95///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96///        |         Peer Index            |
97///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98///        |                         Originated Time                       |
99///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100///        |      Attribute Length         |
101///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102///        |                    BGP Attributes... (variable)
103///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104///
105///                           Figure 10: RIB Entries
106/// ```
107pub fn parse_rib_entry(
108    input: &mut Bytes,
109    is_add_path: bool,
110    afi: &Afi,
111    safi: &Safi,
112    prefix: NetworkPrefix,
113) -> Result<RibEntry, ParserError> {
114    if input.remaining() < 8 {
115        // total length - current position less than 16 --
116        // meaning less than 16 bytes available to read
117        return Err(ParserError::TruncatedMsg("truncated msg".to_string()));
118    }
119
120    let peer_index = input.read_u16()?;
121    let originated_time = input.read_u32()?;
122
123    let path_id = match is_add_path {
124        true => Some(input.read_u32()?),
125        false => None,
126    };
127
128    let attribute_length = input.read_u16()? as usize;
129
130    input.has_n_remaining(attribute_length)?;
131    let attr_data_slice = input.split_to(attribute_length);
132    let attributes = parse_attributes(
133        attr_data_slice,
134        &AsnLength::Bits32,
135        is_add_path,
136        Some(*afi),
137        Some(*safi),
138        Some(&[prefix]),
139    )?;
140
141    Ok(RibEntry {
142        peer_index,
143        originated_time,
144        path_id,
145        attributes,
146    })
147}
148
149impl RibAfiEntries {
150    pub fn encode(&self) -> Bytes {
151        let mut bytes = BytesMut::new();
152
153        bytes.put_u32(self.sequence_number);
154        bytes.extend(self.prefix.encode());
155
156        let entry_count = self.rib_entries.len();
157        bytes.put_u16(entry_count as u16);
158
159        for entry in &self.rib_entries {
160            bytes.extend(entry.encode());
161        }
162
163        bytes.freeze()
164    }
165}
166
167impl RibEntry {
168    pub fn encode(&self) -> Bytes {
169        let mut bytes = BytesMut::new();
170        bytes.put_u16(self.peer_index);
171        bytes.put_u32(self.originated_time);
172        let attr_bytes = self.attributes.encode(AsnLength::Bits32);
173        bytes.put_u16(attr_bytes.len() as u16);
174        bytes.extend(attr_bytes);
175        bytes.freeze()
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    #[test]
184    fn test_extract_afi_safi_from_rib_type() {
185        let rib_type = TableDumpV2Type::RibIpv4Unicast;
186        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
187        assert_eq!(afi, Afi::Ipv4);
188        assert_eq!(safi, Safi::Unicast);
189
190        let rib_type = TableDumpV2Type::RibIpv4Multicast;
191        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
192        assert_eq!(afi, Afi::Ipv4);
193        assert_eq!(safi, Safi::Multicast);
194
195        let rib_type = TableDumpV2Type::RibIpv6Unicast;
196        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
197        assert_eq!(afi, Afi::Ipv6);
198        assert_eq!(safi, Safi::Unicast);
199
200        let rib_type = TableDumpV2Type::RibIpv6Multicast;
201        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
202        assert_eq!(afi, Afi::Ipv6);
203        assert_eq!(safi, Safi::Multicast);
204
205        let rib_type = TableDumpV2Type::RibIpv4UnicastAddPath;
206        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
207        assert_eq!(afi, Afi::Ipv4);
208        assert_eq!(safi, Safi::Unicast);
209
210        let rib_type = TableDumpV2Type::RibIpv4MulticastAddPath;
211        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
212        assert_eq!(afi, Afi::Ipv4);
213        assert_eq!(safi, Safi::Multicast);
214
215        let rib_type = TableDumpV2Type::RibIpv6UnicastAddPath;
216        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
217        assert_eq!(afi, Afi::Ipv6);
218        assert_eq!(safi, Safi::Unicast);
219
220        let rib_type = TableDumpV2Type::RibIpv6MulticastAddPath;
221        let (afi, safi) = extract_afi_safi_from_rib_type(&rib_type).unwrap();
222        assert_eq!(afi, Afi::Ipv6);
223        assert_eq!(safi, Safi::Multicast);
224
225        let rib_type = TableDumpV2Type::RibGeneric;
226        let res = extract_afi_safi_from_rib_type(&rib_type);
227        assert!(res.is_err());
228    }
229
230    #[test]
231    fn test_rib_entry_encode() {
232        use crate::models::{AttributeValue, Attributes, Origin};
233
234        let mut attributes = Attributes::default();
235        attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
236
237        let rib_entry = RibEntry {
238            peer_index: 1,
239            originated_time: 12345,
240            path_id: Some(42),
241            attributes,
242        };
243
244        // This should exercise the self.attributes.encode(AsnLength::Bits32) line
245        let _encoded = rib_entry.encode();
246    }
247}