bgpkit_parser/models/mrt/
mod.rs

1//! MRT message and relevant structs.
2
3pub mod bgp4mp;
4pub mod table_dump;
5pub mod table_dump_v2;
6
7pub use bgp4mp::*;
8use num_enum::{IntoPrimitive, TryFromPrimitive};
9pub use table_dump::*;
10pub use table_dump_v2::*;
11
12/// MrtRecord is a wrapper struct that contains a header and a message.
13///
14/// A MRT record is constructed as the following:
15/// ```text
16///  0                   1                   2                   3
17///  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
18/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19/// |                      Header... (variable)                     |
20/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21/// |                      Message... (variable)
22/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23/// ```
24///
25/// See [CommonHeader] for the content in header, and [MrtMessage] for the
26/// message format.
27#[derive(Debug, PartialEq, Clone, Eq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct MrtRecord {
30    pub common_header: CommonHeader,
31    pub message: MrtMessage,
32}
33
34/// MRT common header.
35///
36/// A CommonHeader ([RFC6396 section 2][header-link]) is constructed as the following:
37/// ```text
38///  0                   1                   2                   3
39///  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
40/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41/// |                           Timestamp                           |
42/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43/// |             Type              |            Subtype            |
44/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45/// |                             Length                            |
46/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47/// ```
48///
49/// Or with extended timestamp:
50/// ```text
51///  0                   1                   2                   3
52///  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
53/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54/// |                           Timestamp                           |
55/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56/// |             Type              |            Subtype            |
57/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
58/// |                             Length                            |
59/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60/// |                      Microsecond Timestamp                    |
61/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62/// ```
63///
64/// The headers include the following:
65/// - timestamp: 32 bits
66/// - entry_type: [EntryType] enum
67/// - entry_subtype: entry subtype
68/// - length: length of the message in octets
69/// - (`ET` type only) microsecond_timestamp: microsecond part of the timestamp.
70///   only applicable to the MRT message type with `_ET` suffix, such as
71///   `BGP4MP_ET`
72///
73/// [header-link]: https://datatracker.ietf.org/doc/html/rfc6396#section-2
74#[derive(Debug, Copy, Clone, Eq)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76pub struct CommonHeader {
77    pub timestamp: u32,
78    pub microsecond_timestamp: Option<u32>,
79    pub entry_type: EntryType,
80    pub entry_subtype: u16,
81    pub length: u32,
82}
83
84impl PartialEq for CommonHeader {
85    fn eq(&self, other: &Self) -> bool {
86        self.timestamp == other.timestamp
87            && self.microsecond_timestamp == other.microsecond_timestamp
88            && self.entry_type == other.entry_type
89            && self.entry_subtype == other.entry_subtype
90        // && self.length == other.length
91        // relax the length check as it might be different due to incorrect encoding
92    }
93}
94
95#[derive(Debug, PartialEq, Clone, Eq)]
96#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
97pub enum MrtMessage {
98    TableDumpMessage(TableDumpMessage),
99    TableDumpV2Message(TableDumpV2Message),
100    Bgp4Mp(Bgp4MpEnum),
101}
102
103/// MRT entry type.
104///
105/// EntryType indicates the type of the current MRT record. Type 0 to 10 are deprecated.
106///
107/// Excerpt from [RFC6396 section 4](https://datatracker.ietf.org/doc/html/rfc6396#section-4):
108/// ```text
109/// The following MRT Types are currently defined for the MRT format.
110/// The MRT Types that contain the "_ET" suffix in their names identify
111/// those types that use an Extended Timestamp MRT Header.  The Subtype
112/// and Message fields in these types remain as defined for the MRT Types
113/// of the same name without the "_ET" suffix.
114///
115///     11   OSPFv2
116///     12   TABLE_DUMP
117///     13   TABLE_DUMP_V2
118///     16   BGP4MP
119///     17   BGP4MP_ET
120///     32   ISIS
121///     33   ISIS_ET
122///     48   OSPFv3
123///     49   OSPFv3_ET
124/// ```
125#[derive(Debug, TryFromPrimitive, IntoPrimitive, Copy, Clone, PartialEq, Eq, Hash)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127#[allow(non_camel_case_types)]
128#[repr(u16)]
129pub enum EntryType {
130    // START DEPRECATED
131    NULL = 0,
132    START = 1,
133    DIE = 2,
134    I_AM_DEAD = 3,
135    PEER_DOWN = 4,
136    BGP = 5,
137    RIP = 6,
138    IDRP = 7,
139    RIPNG = 8,
140    BGP4PLUS = 9,
141    BGP4PLUS_01 = 10,
142    // END DEPRECATED
143    OSPFv2 = 11,
144    TABLE_DUMP = 12,
145    TABLE_DUMP_V2 = 13,
146    BGP4MP = 16,
147    BGP4MP_ET = 17,
148    ISIS = 32,
149    ISIS_ET = 33,
150    OSPFv3 = 48,
151    OSPFv3_ET = 49,
152}
153
154#[cfg(test)]
155mod tests {
156
157    #[test]
158    #[cfg(feature = "serde")]
159    fn test_entry_type_serialize_and_deserialize() {
160        use super::*;
161        let types = vec![
162            EntryType::NULL,
163            EntryType::START,
164            EntryType::DIE,
165            EntryType::I_AM_DEAD,
166            EntryType::PEER_DOWN,
167            EntryType::BGP,
168            EntryType::RIP,
169            EntryType::IDRP,
170            EntryType::RIPNG,
171            EntryType::BGP4PLUS,
172            EntryType::BGP4PLUS_01,
173            EntryType::OSPFv2,
174            EntryType::TABLE_DUMP,
175            EntryType::TABLE_DUMP_V2,
176            EntryType::BGP4MP,
177            EntryType::BGP4MP_ET,
178            EntryType::ISIS,
179            EntryType::ISIS_ET,
180            EntryType::OSPFv3,
181            EntryType::OSPFv3_ET,
182        ];
183
184        for entry_type in types {
185            let serialized = serde_json::to_string(&entry_type).unwrap();
186            let deserialized: EntryType = serde_json::from_str(&serialized).unwrap();
187
188            assert_eq!(entry_type, deserialized);
189        }
190    }
191
192    #[test]
193    #[cfg(feature = "serde")]
194    fn test_serialization() {
195        use super::*;
196        use serde_json;
197        use std::net::IpAddr;
198        use std::str::FromStr;
199
200        let mrt_record = MrtRecord {
201            common_header: CommonHeader {
202                timestamp: 0,
203                microsecond_timestamp: None,
204                entry_type: EntryType::BGP4MP,
205                entry_subtype: 0,
206                length: 0,
207            },
208            message: MrtMessage::Bgp4Mp(Bgp4MpEnum::StateChange(Bgp4MpStateChange {
209                msg_type: Bgp4MpType::StateChange,
210                peer_asn: crate::models::Asn::new_32bit(0),
211                local_asn: crate::models::Asn::new_32bit(0),
212                interface_index: 1,
213                peer_ip: IpAddr::from_str("10.0.0.0").unwrap(),
214                local_addr: IpAddr::from_str("10.0.0.0").unwrap(),
215                old_state: BgpState::Idle,
216                new_state: BgpState::Connect,
217            })),
218        };
219
220        let serialized = serde_json::to_string(&mrt_record).unwrap();
221        let deserialized: MrtRecord = serde_json::from_str(&serialized).unwrap();
222        assert_eq!(mrt_record, deserialized);
223    }
224}