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_id = self.index_table.add_peer(peer);
52        let prefix = elem.prefix.prefix;
53
54        let entries_map = self.per_prefix_entries_map.entry(prefix).or_default();
55        let entry = RibEntry {
56            peer_index: peer_id,
57            originated_time: elem.timestamp as u32,
58            attributes: Attributes::from(elem),
59        };
60        entries_map.insert(peer_id, entry);
61    }
62
63    /// Export the data stored in the struct to a byte array.
64    ///
65    /// The function first encodes the peer-index-table data into a `MrtMessage` and appends it to the `BytesMut` object.
66    /// Then, for each prefix in the `per_prefix_entries_map`, it creates a `RibAfiEntries` object and encodes it as a `MrtMessage`.
67    /// The resulting `BytesMut` object is then converted to an immutable `Bytes` object using `freeze()` and returned.
68    ///
69    /// # Return
70    /// Returns a `Bytes` object containing the exported data as a byte array.
71    pub fn export_bytes(&mut self) -> Bytes {
72        let mut bytes = BytesMut::new();
73
74        // encode peer-index-table
75        let mrt_message = MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(
76            self.index_table.clone(),
77        ));
78        let (seconds, _microseconds) = convert_timestamp(self.timestamp);
79        let subtype = TableDumpV2Type::PeerIndexTable as u16;
80        let data_bytes = mrt_message.encode(subtype);
81        let header = CommonHeader {
82            timestamp: seconds,
83            microsecond_timestamp: None,
84            entry_type: EntryType::TABLE_DUMP_V2,
85            entry_subtype: subtype,
86            length: data_bytes.len() as u32,
87        };
88        let header_bytes = header.encode();
89        bytes.extend(header_bytes);
90        bytes.extend(data_bytes);
91
92        // encode each RibAfiEntries
93        for (entry_count, (prefix, entries_map)) in self.per_prefix_entries_map.iter().enumerate() {
94            let rib_type = match prefix.addr().is_ipv6() {
95                true => TableDumpV2Type::RibIpv6Unicast,
96                false => TableDumpV2Type::RibIpv4Unicast,
97            };
98
99            let mut prefix_rib_entry = RibAfiEntries {
100                rib_type,
101                sequence_number: entry_count as u32,
102                prefix: NetworkPrefix::new(*prefix, 0),
103                rib_entries: vec![],
104            };
105            for entry in entries_map.values() {
106                prefix_rib_entry.rib_entries.push(entry.clone());
107            }
108
109            let mrt_message =
110                MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(prefix_rib_entry));
111
112            let (seconds, _microseconds) = convert_timestamp(self.timestamp);
113            let subtype = rib_type as u16;
114            let data_bytes = mrt_message.encode(subtype);
115            let header_bytes = CommonHeader {
116                timestamp: seconds,
117                microsecond_timestamp: None,
118                entry_type: EntryType::TABLE_DUMP_V2,
119                entry_subtype: subtype,
120                length: data_bytes.len() as u32,
121            }
122            .encode();
123            bytes.extend(header_bytes);
124            bytes.extend(data_bytes);
125        }
126
127        self.reset();
128
129        bytes.freeze()
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::models::Asn;
137    use crate::parse_mrt_record;
138    use bytes::Buf;
139    use std::io::Cursor;
140
141    #[test]
142    fn test_encoding_rib() {
143        let mut encoder = MrtRibEncoder::new();
144        let mut elem = BgpElem {
145            peer_ip: IpAddr::V4("10.0.0.1".parse().unwrap()),
146            peer_asn: Asn::from(65000),
147            ..Default::default()
148        };
149        elem.prefix.prefix = "10.250.0.0/24".parse().unwrap();
150        encoder.process_elem(&elem);
151        elem.prefix.prefix = "10.251.0.0/24".parse().unwrap();
152        encoder.process_elem(&elem);
153        let bytes = encoder.export_bytes();
154
155        let mut cursor = Cursor::new(bytes.clone());
156        while cursor.has_remaining() {
157            let _parsed = parse_mrt_record(&mut cursor).unwrap();
158        }
159
160        // v6
161        let mut encoder = MrtRibEncoder::new();
162        let mut elem = BgpElem {
163            peer_ip: IpAddr::V6("::1".parse().unwrap()),
164            peer_asn: Asn::from(65000),
165            ..Default::default()
166        };
167        // ipv6 prefix
168        elem.prefix.prefix = "2001:db8::/32".parse().unwrap();
169        encoder.process_elem(&elem);
170        let bytes = encoder.export_bytes();
171
172        let mut cursor = Cursor::new(bytes.clone());
173        while cursor.has_remaining() {
174            let _parsed = parse_mrt_record(&mut cursor).unwrap();
175        }
176    }
177}