bgpkit_parser/models/mrt/
table_dump_v2.rs

1//! MRT table dump version 2 structs
2use crate::models::*;
3use bitflags::bitflags;
4use num_enum::{IntoPrimitive, TryFromPrimitive};
5use std::collections::HashMap;
6use std::net::{IpAddr, Ipv4Addr};
7use std::str::FromStr;
8
9/// TableDump message version 2 enum
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum TableDumpV2Message {
13    PeerIndexTable(PeerIndexTable),
14    RibAfi(RibAfiEntries),
15    /// Currently unsupported
16    RibGeneric(RibGenericEntries),
17}
18
19impl TableDumpV2Message {
20    pub const fn dump_type(&self) -> TableDumpV2Type {
21        match self {
22            TableDumpV2Message::PeerIndexTable(_) => TableDumpV2Type::PeerIndexTable,
23            TableDumpV2Message::RibAfi(x) => x.rib_type,
24            TableDumpV2Message::RibGeneric(_) => TableDumpV2Type::RibGeneric,
25        }
26    }
27}
28
29/// TableDump version 2 subtypes.
30///
31/// <https://www.iana.org/assignments/mrt/mrt.xhtml#subtype-codes>
32#[derive(Debug, TryFromPrimitive, IntoPrimitive, Copy, Clone, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[repr(u16)]
35pub enum TableDumpV2Type {
36    PeerIndexTable = 1,
37    RibIpv4Unicast = 2,
38    RibIpv4Multicast = 3,
39    RibIpv6Unicast = 4,
40    RibIpv6Multicast = 5,
41    RibGeneric = 6,
42    GeoPeerTable = 7,
43    RibIpv4UnicastAddPath = 8,
44    RibIpv4MulticastAddPath = 9,
45    RibIpv6UnicastAddPath = 10,
46    RibIpv6MulticastAddPath = 11,
47    RibGenericAddPath = 12,
48}
49
50/// AFI/SAFI-Specific RIB Subtypes.
51///
52/// ```text
53///    The AFI/SAFI-specific RIB Subtypes consist of the RIB_IPV4_UNICAST,
54///    RIB_IPV4_MULTICAST, RIB_IPV6_UNICAST, and RIB_IPV6_MULTICAST
55///    Subtypes.  These specific RIB table entries are given their own MRT
56///    TABLE_DUMP_V2 subtypes as they are the most common type of RIB table
57///    instances, and providing specific MRT subtypes for them permits more
58///    compact encodings.  These subtypes permit a single MRT record to
59///    encode multiple RIB table entries for a single prefix.  The Prefix
60///    Length and Prefix fields are encoded in the same manner as the BGP
61///    NLRI encoding for IPv4 and IPv6 prefixes.  Namely, the Prefix field
62///    contains address prefixes followed by enough trailing bits to make
63///    the end of the field fall on an octet boundary.  The value of
64///    trailing bits is irrelevant.
65///
66///         0                   1                   2                   3
67///         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
68///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69///        |                         Sequence Number                       |
70///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71///        | Prefix Length |
72///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73///        |                        Prefix (variable)                      |
74///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
75///        |         Entry Count           |  RIB Entries (variable)
76///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77/// ```
78#[derive(Debug, Clone, PartialEq, Eq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub struct RibAfiEntries {
81    pub rib_type: TableDumpV2Type,
82    pub sequence_number: u32,
83    pub prefix: NetworkPrefix,
84    pub rib_entries: Vec<RibEntry>,
85}
86
87/// RIB generic entries subtype.
88///
89/// ```text
90/// The RIB_GENERIC header is shown below.  It is used to cover RIB
91/// entries that do not fall under the common case entries defined above.
92/// It consists of an AFI, Subsequent AFI (SAFI), and a single NLRI
93/// entry.  The NLRI information is specific to the AFI and SAFI values.
94/// An implementation that does not recognize particular AFI and SAFI
95/// values SHOULD discard the remainder of the MRT record.
96///         0                   1                   2                   3
97///         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
98///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99///        |                         Sequence Number                       |
100///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101///        |    Address Family Identifier  |Subsequent AFI |
102///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
103///        |     Network Layer Reachability Information (variable)         |
104///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
105///        |         Entry Count           |  RIB Entries (variable)
106///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
107/// ```
108#[derive(Debug, Clone, PartialEq, Eq)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub struct RibGenericEntries {
111    pub sequence_number: u32,
112    pub afi: Afi,
113    pub safi: Safi,
114    pub nlri: NetworkPrefix,
115    pub rib_entries: Vec<RibEntry>,
116}
117
118/// RIB entry.
119///
120/// ```text
121///    The RIB Entries are repeated Entry Count times.  These entries share
122///    a common format as shown below.  They include a Peer Index from the
123///    PEER_INDEX_TABLE MRT record, an originated time for the RIB Entry,
124///    and the BGP path attribute length and attributes.  All AS numbers in
125///    the AS_PATH attribute MUST be encoded as 4-byte AS numbers.
126///
127///         0                   1                   2                   3
128///         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
129///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130///        |         Peer Index            |
131///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
132///        |                         Originated Time                       |
133///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
134///        |      Attribute Length         |
135///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
136///        |                    BGP Attributes... (variable)
137///        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138/// ```
139#[derive(Debug, Clone, PartialEq, Eq)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141pub struct RibEntry {
142    pub peer_index: u16,
143    pub originated_time: u32,
144    pub attributes: Attributes,
145}
146
147/// peer index table.
148///
149/// ```text
150///    An initial PEER_INDEX_TABLE MRT record provides the BGP ID of the
151///    collector, an OPTIONAL view name, and a list of indexed peers.
152///    Following the PEER_INDEX_TABLE MRT record, a series of MRT records is
153///    used to encode RIB table entries.  This series of MRT records uses
154///    subtypes 2-6 and is separate from the PEER_INDEX_TABLE MRT record
155///    itself and includes full MRT record headers.  The RIB entry MRT
156///    records MUST immediately follow the PEER_INDEX_TABLE MRT record.
157/// ```
158#[derive(Debug, Clone, PartialEq, Eq)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160pub struct PeerIndexTable {
161    pub collector_bgp_id: BgpIdentifier,
162    pub view_name: String,
163    pub id_peer_map: HashMap<u16, Peer>,
164    pub peer_addr_id_map: HashMap<IpAddr, u16>,
165}
166
167impl Default for PeerIndexTable {
168    fn default() -> Self {
169        PeerIndexTable {
170            collector_bgp_id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
171            view_name: "".to_string(),
172            id_peer_map: HashMap::new(),
173            peer_addr_id_map: HashMap::new(),
174        }
175    }
176}
177
178bitflags! {
179    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
180    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181    pub struct PeerType: u8 {
182        const AS_SIZE_32BIT = 0x2;
183        const ADDRESS_FAMILY_IPV6 = 0x1;
184    }
185}
186
187/// Peer struct.
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct Peer {
191    pub peer_type: PeerType,
192    pub peer_bgp_id: BgpIdentifier,
193    pub peer_address: IpAddr,
194    pub peer_asn: Asn,
195}
196
197impl Peer {
198    pub fn new(peer_bgp_id: BgpIdentifier, peer_address: IpAddr, peer_asn: Asn) -> Self {
199        let mut peer_type = PeerType::empty();
200
201        if peer_asn.is_four_byte() {
202            peer_type.insert(PeerType::AS_SIZE_32BIT);
203        }
204
205        if peer_address.is_ipv6() {
206            peer_type.insert(PeerType::ADDRESS_FAMILY_IPV6);
207        }
208
209        Peer {
210            peer_type,
211            peer_bgp_id,
212            peer_address,
213            peer_asn,
214        }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    // Create a helper function to initialize Peer structure
223    fn create_peer() -> Peer {
224        let bgp_id = Ipv4Addr::from_str("1.1.1.1").unwrap();
225        let peer_address: IpAddr = Ipv4Addr::from_str("2.2.2.2").unwrap().into();
226        // Assuming Asn::new(u32) is defined.
227        let asn = Asn::new_32bit(65000);
228        Peer::new(bgp_id, peer_address, asn)
229    }
230
231    #[test]
232    fn test_peer_new() {
233        let peer = create_peer();
234        assert_eq!(peer.peer_type, PeerType::AS_SIZE_32BIT);
235        assert_eq!(peer.peer_bgp_id, Ipv4Addr::from_str("1.1.1.1").unwrap());
236        assert_eq!(
237            peer.peer_address,
238            IpAddr::V4(Ipv4Addr::from_str("2.2.2.2").unwrap())
239        );
240        assert_eq!(peer.peer_asn, Asn::new_32bit(65000));
241    }
242
243    #[test]
244    fn test_default_peer_index_table() {
245        let peer_index_table = PeerIndexTable::default();
246        assert_eq!(
247            peer_index_table.collector_bgp_id,
248            Ipv4Addr::from_str("0.0.0.0").unwrap()
249        );
250        assert_eq!(peer_index_table.view_name, "".to_string());
251        assert_eq!(peer_index_table.id_peer_map, HashMap::new());
252        assert_eq!(peer_index_table.peer_addr_id_map, HashMap::new());
253    }
254
255    #[test]
256    fn test_peer_type_flags() {
257        let mut peer_type = PeerType::empty();
258        assert_eq!(peer_type, PeerType::empty());
259
260        peer_type.insert(PeerType::AS_SIZE_32BIT);
261        assert_eq!(peer_type, PeerType::AS_SIZE_32BIT);
262
263        peer_type.insert(PeerType::ADDRESS_FAMILY_IPV6);
264        assert_eq!(
265            peer_type,
266            PeerType::AS_SIZE_32BIT | PeerType::ADDRESS_FAMILY_IPV6
267        );
268
269        peer_type.remove(PeerType::AS_SIZE_32BIT);
270        assert_eq!(peer_type, PeerType::ADDRESS_FAMILY_IPV6);
271
272        peer_type.remove(PeerType::ADDRESS_FAMILY_IPV6);
273        assert_eq!(peer_type, PeerType::empty());
274    }
275
276    #[test]
277    fn test_dump_type() {
278        let peer_index_table = TableDumpV2Message::PeerIndexTable(PeerIndexTable::default());
279        assert_eq!(
280            peer_index_table.dump_type(),
281            TableDumpV2Type::PeerIndexTable
282        );
283
284        let rib_afi = TableDumpV2Message::RibAfi(RibAfiEntries {
285            rib_type: TableDumpV2Type::RibIpv4Unicast,
286            sequence_number: 1,
287            prefix: NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
288            rib_entries: vec![],
289        });
290        assert_eq!(rib_afi.dump_type(), TableDumpV2Type::RibIpv4Unicast);
291
292        let rib_generic = TableDumpV2Message::RibGeneric(RibGenericEntries {
293            sequence_number: 1,
294            afi: Afi::Ipv4,
295            safi: Safi::Unicast,
296            nlri: NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
297            rib_entries: vec![],
298        });
299        assert_eq!(rib_generic.dump_type(), TableDumpV2Type::RibGeneric);
300    }
301
302    #[test]
303    #[cfg(feature = "serde")]
304    fn test_serialization() {
305        let peer_index_table = TableDumpV2Message::PeerIndexTable(PeerIndexTable::default());
306        let serialized = serde_json::to_string(&peer_index_table).unwrap();
307        let deserialized: TableDumpV2Message = serde_json::from_str(&serialized).unwrap();
308        assert_eq!(deserialized, peer_index_table);
309
310        let rib_entry = RibEntry {
311            peer_index: 1,
312            originated_time: 1,
313            attributes: Attributes::default(),
314        };
315        let rib_afi = TableDumpV2Message::RibAfi(RibAfiEntries {
316            rib_type: TableDumpV2Type::RibIpv4Unicast,
317            sequence_number: 1,
318            prefix: NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
319            rib_entries: vec![rib_entry],
320        });
321        let serialized = serde_json::to_string(&rib_afi).unwrap();
322        let deserialized: TableDumpV2Message = serde_json::from_str(&serialized).unwrap();
323        assert_eq!(deserialized, rib_afi);
324
325        let rib_generic = TableDumpV2Message::RibGeneric(RibGenericEntries {
326            sequence_number: 1,
327            afi: Afi::Ipv4,
328            safi: Safi::Unicast,
329            nlri: NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
330            rib_entries: vec![],
331        });
332        let serialized = serde_json::to_string(&rib_generic).unwrap();
333        let deserialized: TableDumpV2Message = serde_json::from_str(&serialized).unwrap();
334        assert_eq!(deserialized, rib_generic);
335    }
336}