bgpkit_parser/encoder/
rib_encoder.rs

1//! MRT Encoder module
2//!
3//! `mrt_encoder` module handles serializing BGP/MRT messages back to MRT binary files. The main
4//! difficulty part of this process is the handling of TableDumpV2 RIB dumps, which requires
5//! reconstructing the peer index table before encoding all other contents.
6
7use crate::models::{
8    Attributes, BgpElem, CommonHeader, EntryType, MrtMessage, NetworkPrefix, Peer, PeerIndexTable,
9    RibAfiEntries, RibEntry, TableDumpV2Message, TableDumpV2Type,
10};
11use crate::utils::convert_timestamp;
12use bytes::{Bytes, BytesMut};
13use ipnet::IpNet;
14use std::collections::HashMap;
15use std::net::{IpAddr, Ipv4Addr};
16
17#[derive(Default)]
18pub struct MrtRibEncoder {
19    index_table: PeerIndexTable,
20
21    per_prefix_entries_map: HashMap<IpNet, HashMap<u16, RibEntry>>,
22
23    timestamp: f64,
24}
25
26impl MrtRibEncoder {
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    pub fn reset(&mut self) {
32        self.index_table = PeerIndexTable::default();
33        self.per_prefix_entries_map = HashMap::default();
34        self.timestamp = 0.0;
35    }
36
37    /// Processes a BgpElem and updates the internal data structures.
38    ///
39    /// # Arguments
40    ///
41    /// * `elem` - A reference to a BgpElem that contains the information to be processed.
42    pub fn process_elem(&mut self, elem: &BgpElem) {
43        if self.timestamp == 0.0 {
44            self.timestamp = elem.timestamp;
45        }
46        let bgp_identifier = match elem.peer_ip {
47            IpAddr::V4(ip) => ip,
48            IpAddr::V6(_ip) => Ipv4Addr::from(0),
49        };
50        let peer = Peer::new(bgp_identifier, elem.peer_ip, elem.peer_asn);
51        let peer_index = self.index_table.add_peer(peer);
52        let path_id = elem.prefix.path_id;
53        let prefix = elem.prefix.prefix;
54
55        let entries_map = self.per_prefix_entries_map.entry(prefix).or_default();
56        let entry = RibEntry {
57            peer_index,
58            path_id,
59            originated_time: elem.timestamp as u32,
60            attributes: Attributes::from(elem),
61        };
62        entries_map.insert(peer_index, entry);
63    }
64
65    /// Export the data stored in the struct to a byte array.
66    ///
67    /// The function first encodes the peer-index-table data into a `MrtMessage` and appends it to the `BytesMut` object.
68    /// Then, for each prefix in the `per_prefix_entries_map`, it creates a `RibAfiEntries` object and encodes it as a `MrtMessage`.
69    /// The resulting `BytesMut` object is then converted to an immutable `Bytes` object using `freeze()` and returned.
70    ///
71    /// # Return
72    /// Returns a `Bytes` object containing the exported data as a byte array.
73    pub fn export_bytes(&mut self) -> Bytes {
74        let mut bytes = BytesMut::new();
75
76        // encode peer-index-table
77        let mrt_message = MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(
78            self.index_table.clone(),
79        ));
80        let (seconds, _microseconds) = convert_timestamp(self.timestamp);
81        let subtype = TableDumpV2Type::PeerIndexTable as u16;
82        let data_bytes = mrt_message.encode(subtype);
83        let header = CommonHeader {
84            timestamp: seconds,
85            microsecond_timestamp: None,
86            entry_type: EntryType::TABLE_DUMP_V2,
87            entry_subtype: subtype,
88            length: data_bytes.len() as u32,
89        };
90        let header_bytes = header.encode();
91        bytes.extend(header_bytes);
92        bytes.extend(data_bytes);
93
94        // encode each RibAfiEntries
95        for (entry_count, (prefix, entries_map)) in self.per_prefix_entries_map.iter().enumerate() {
96            let rib_type = match prefix.addr().is_ipv6() {
97                true => TableDumpV2Type::RibIpv6Unicast,
98                false => TableDumpV2Type::RibIpv4Unicast,
99            };
100
101            let mut prefix_rib_entry = RibAfiEntries {
102                rib_type,
103                sequence_number: entry_count as u32,
104                prefix: NetworkPrefix::new(*prefix, None),
105                rib_entries: vec![],
106            };
107            for entry in entries_map.values() {
108                prefix_rib_entry.rib_entries.push(entry.clone());
109            }
110
111            let mrt_message =
112                MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(prefix_rib_entry));
113
114            let (seconds, _microseconds) = convert_timestamp(self.timestamp);
115            let subtype = rib_type as u16;
116            let data_bytes = mrt_message.encode(subtype);
117            let header_bytes = CommonHeader {
118                timestamp: seconds,
119                microsecond_timestamp: None,
120                entry_type: EntryType::TABLE_DUMP_V2,
121                entry_subtype: subtype,
122                length: data_bytes.len() as u32,
123            }
124            .encode();
125            bytes.extend(header_bytes);
126            bytes.extend(data_bytes);
127        }
128
129        self.reset();
130
131        bytes.freeze()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use crate::models::Asn;
139    use crate::parse_mrt_record;
140    use bytes::Buf;
141    use std::io::Cursor;
142
143    #[test]
144    fn test_encoding_rib() {
145        let mut encoder = MrtRibEncoder::new();
146        let mut elem = BgpElem {
147            peer_ip: IpAddr::V4("10.0.0.1".parse().unwrap()),
148            peer_asn: Asn::from(65000),
149            ..Default::default()
150        };
151        elem.prefix.prefix = "10.250.0.0/24".parse().unwrap();
152        encoder.process_elem(&elem);
153        elem.prefix.prefix = "10.251.0.0/24".parse().unwrap();
154        encoder.process_elem(&elem);
155        let bytes = encoder.export_bytes();
156
157        let mut cursor = Cursor::new(bytes.clone());
158        while cursor.has_remaining() {
159            let _parsed = parse_mrt_record(&mut cursor).unwrap();
160        }
161
162        // v6
163        let mut encoder = MrtRibEncoder::new();
164        let mut elem = BgpElem {
165            peer_ip: IpAddr::V6("::1".parse().unwrap()),
166            peer_asn: Asn::from(65000),
167            ..Default::default()
168        };
169        // ipv6 prefix
170        elem.prefix.prefix = "2001:db8::/32".parse().unwrap();
171        encoder.process_elem(&elem);
172        let bytes = encoder.export_bytes();
173
174        let mut cursor = Cursor::new(bytes.clone());
175        while cursor.has_remaining() {
176            let _parsed = parse_mrt_record(&mut cursor).unwrap();
177        }
178    }
179}