bgpkit_parser/encoder/
rib_encoder.rs1use 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
17fn rib_type_for_prefix(prefix: &IpNet, has_add_path: bool) -> TableDumpV2Type {
18 match (prefix.addr().is_ipv6(), has_add_path) {
19 (true, true) => TableDumpV2Type::RibIpv6UnicastAddPath,
20 (true, false) => TableDumpV2Type::RibIpv6Unicast,
21 (false, true) => TableDumpV2Type::RibIpv4UnicastAddPath,
22 (false, false) => TableDumpV2Type::RibIpv4Unicast,
23 }
24}
25
26#[derive(Default)]
27pub struct MrtRibEncoder {
28 index_table: PeerIndexTable,
29
30 per_prefix_entries_map: HashMap<IpNet, HashMap<u16, RibEntry>>,
31
32 timestamp: f64,
33}
34
35impl MrtRibEncoder {
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 pub fn reset(&mut self) {
41 self.index_table = PeerIndexTable::default();
42 self.per_prefix_entries_map = HashMap::default();
43 self.timestamp = 0.0;
44 }
45
46 pub fn process_elem(&mut self, elem: &BgpElem) {
52 if self.timestamp == 0.0 {
53 self.timestamp = elem.timestamp;
54 }
55 let bgp_identifier = match elem.peer_ip {
56 IpAddr::V4(ip) => ip,
57 IpAddr::V6(_ip) => Ipv4Addr::from(0),
58 };
59 let peer = Peer::new(bgp_identifier, elem.peer_ip, elem.peer_asn);
60 let peer_index = self.index_table.add_peer(peer);
61 let path_id = elem.prefix.path_id;
62 let prefix = elem.prefix.prefix;
63
64 let entries_map = self.per_prefix_entries_map.entry(prefix).or_default();
65 let entry = RibEntry {
66 peer_index,
67 path_id,
68 originated_time: elem.timestamp as u32,
69 attributes: Attributes::from(elem),
70 };
71 entries_map.insert(peer_index, entry);
72 }
73
74 pub fn export_bytes(&mut self) -> Bytes {
83 let mut bytes = BytesMut::new();
84
85 let mrt_message = MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(
87 self.index_table.clone(),
88 ));
89 let (seconds, _microseconds) = convert_timestamp(self.timestamp);
90 let subtype = TableDumpV2Type::PeerIndexTable as u16;
91 let data_bytes = mrt_message.encode(subtype);
92 let header = CommonHeader {
93 timestamp: seconds,
94 microsecond_timestamp: None,
95 entry_type: EntryType::TABLE_DUMP_V2,
96 entry_subtype: subtype,
97 length: data_bytes.len() as u32,
98 };
99 let header_bytes = header.encode();
100 bytes.extend(header_bytes);
101 bytes.extend(data_bytes);
102
103 for (entry_count, (prefix, entries_map)) in self.per_prefix_entries_map.iter().enumerate() {
105 let has_add_path = entries_map.values().any(|entry| entry.path_id.is_some());
106 let rib_type = rib_type_for_prefix(prefix, has_add_path);
107
108 let mut prefix_rib_entry = RibAfiEntries {
109 rib_type,
110 sequence_number: entry_count as u32,
111 prefix: NetworkPrefix::new(*prefix, None),
112 rib_entries: vec![],
113 };
114 for entry in entries_map.values() {
115 prefix_rib_entry.rib_entries.push(entry.clone());
116 }
117
118 let mrt_message =
119 MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(prefix_rib_entry));
120
121 let (seconds, _microseconds) = convert_timestamp(self.timestamp);
122 let subtype = rib_type as u16;
123 let data_bytes = mrt_message.encode(subtype);
124 let header_bytes = CommonHeader {
125 timestamp: seconds,
126 microsecond_timestamp: None,
127 entry_type: EntryType::TABLE_DUMP_V2,
128 entry_subtype: subtype,
129 length: data_bytes.len() as u32,
130 }
131 .encode();
132 bytes.extend(header_bytes);
133 bytes.extend(data_bytes);
134 }
135
136 self.reset();
137
138 bytes.freeze()
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::models::Asn;
146 use crate::parse_mrt_record;
147 use bytes::Buf;
148 use std::io::Cursor;
149
150 #[test]
151 fn test_encoding_rib() {
152 let mut encoder = MrtRibEncoder::new();
153 let mut elem = BgpElem {
154 peer_ip: IpAddr::V4("10.0.0.1".parse().unwrap()),
155 peer_asn: Asn::from(65000),
156 ..Default::default()
157 };
158 elem.prefix.prefix = "10.250.0.0/24".parse().unwrap();
159 encoder.process_elem(&elem);
160 elem.prefix.prefix = "10.251.0.0/24".parse().unwrap();
161 encoder.process_elem(&elem);
162 let bytes = encoder.export_bytes();
163
164 let mut cursor = Cursor::new(bytes.clone());
165 while cursor.has_remaining() {
166 let _parsed = parse_mrt_record(&mut cursor).unwrap();
167 }
168
169 let mut encoder = MrtRibEncoder::new();
171 let mut elem = BgpElem {
172 peer_ip: IpAddr::V6("::1".parse().unwrap()),
173 peer_asn: Asn::from(65000),
174 ..Default::default()
175 };
176 elem.prefix.prefix = "2001:db8::/32".parse().unwrap();
178 encoder.process_elem(&elem);
179 let bytes = encoder.export_bytes();
180
181 let mut cursor = Cursor::new(bytes.clone());
182 while cursor.has_remaining() {
183 let _parsed = parse_mrt_record(&mut cursor).unwrap();
184 }
185 }
186
187 #[test]
188 fn test_encoding_rib_with_add_path() {
189 let mut encoder = MrtRibEncoder::new();
190 let mut elem = BgpElem {
191 peer_ip: IpAddr::V4("10.0.0.1".parse().unwrap()),
192 peer_asn: Asn::from(65000),
193 ..Default::default()
194 };
195 elem.prefix = NetworkPrefix::new("10.250.0.0/24".parse().unwrap(), Some(42));
196 encoder.process_elem(&elem);
197
198 let bytes = encoder.export_bytes();
199 let mut cursor = Cursor::new(bytes);
200 let _peer_table = parse_mrt_record(&mut cursor).unwrap();
201 let parsed = parse_mrt_record(&mut cursor).unwrap();
202
203 match parsed.message {
204 MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(rib)) => {
205 assert_eq!(rib.rib_type, TableDumpV2Type::RibIpv4UnicastAddPath);
206 assert_eq!(rib.rib_entries.len(), 1);
207 assert_eq!(rib.rib_entries[0].path_id, Some(42));
208 }
209 other => panic!("unexpected MRT message: {other:?}"),
210 }
211 }
212}