bgpkit_parser/parser/mrt/
mrt_header.rs1use crate::models::{CommonHeader, EntryType};
2use crate::ParserError;
3use bytes::Bytes;
4use std::io::Read;
5use zerocopy::big_endian::{U16, U32};
6use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
7
8#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
10#[repr(C)]
11struct RawMrtCommonHeader {
12 timestamp: U32,
13 entry_type: U16,
14 entry_subtype: U16,
15 length: U32,
16}
17
18const _: () = assert!(size_of::<RawMrtCommonHeader>() == 12);
19
20#[derive(IntoBytes, FromBytes, KnownLayout, Immutable)]
22#[repr(C)]
23struct RawMrtEtCommonHeader {
24 timestamp: U32,
25 entry_type: U16,
26 entry_subtype: U16,
27 length: U32,
28 microseconds: U32,
29}
30
31const _: () = assert!(size_of::<RawMrtEtCommonHeader>() == 16);
32
33enum RawMrtHeader {
34 Standard(RawMrtCommonHeader),
35 Et(RawMrtEtCommonHeader),
36}
37
38impl From<&CommonHeader> for RawMrtHeader {
39 fn from(header: &CommonHeader) -> Self {
40 match header.microsecond_timestamp {
41 None => RawMrtHeader::Standard(RawMrtCommonHeader {
42 timestamp: U32::new(header.timestamp),
43 entry_type: U16::new(header.entry_type as u16),
44 entry_subtype: U16::new(header.entry_subtype),
45 length: U32::new(header.length),
46 }),
47 Some(microseconds) => RawMrtHeader::Et(RawMrtEtCommonHeader {
48 timestamp: U32::new(header.timestamp),
49 entry_type: U16::new(header.entry_type as u16),
50 entry_subtype: U16::new(header.entry_subtype),
51 length: U32::new(header.length + 4),
55 microseconds: U32::new(microseconds),
56 }),
57 }
58 }
59}
60
61impl RawMrtHeader {
62 fn as_bytes(&self) -> &[u8] {
63 match self {
64 RawMrtHeader::Standard(raw) => raw.as_bytes(),
65 RawMrtHeader::Et(raw) => raw.as_bytes(),
66 }
67 }
68}
69
70pub struct ParsedHeader {
72 pub header: CommonHeader,
73 pub raw_bytes: Bytes,
74}
75
76pub fn parse_common_header<T: Read>(input: &mut T) -> Result<CommonHeader, ParserError> {
113 Ok(parse_common_header_with_bytes(input)?.header)
114}
115
116pub fn parse_common_header_with_bytes<T: Read>(input: &mut T) -> Result<ParsedHeader, ParserError> {
121 let framed = frame_common_header(input)?;
122 Ok(ParsedHeader {
123 header: framed.header?,
124 raw_bytes: framed.raw_bytes,
125 })
126}
127
128pub(crate) struct FramedHeader {
131 pub(crate) header: Result<CommonHeader, ParserError>,
133 pub(crate) raw_bytes: Bytes,
135 pub(crate) body_length: u32,
137}
138
139pub(crate) fn frame_common_header<T: Read>(input: &mut T) -> std::io::Result<FramedHeader> {
142 let mut base_bytes = [0u8; 12];
143 input.read_exact(&mut base_bytes)?;
144
145 let raw = RawMrtCommonHeader::ref_from_bytes(&base_bytes)
147 .expect("base_bytes is exactly 12 bytes with no alignment requirement");
148
149 let timestamp = raw.timestamp.get();
150 let entry_subtype = raw.entry_subtype.get();
151 let length = raw.length.get();
153
154 let rejected = |error: ParserError| FramedHeader {
157 header: Err(error),
158 raw_bytes: Bytes::copy_from_slice(&base_bytes),
159 body_length: length,
160 };
161
162 let entry_type = match EntryType::try_from(raw.entry_type.get()) {
163 Ok(entry_type) => entry_type,
164 Err(error) => return Ok(rejected(error.into())),
165 };
166
167 let (microsecond_timestamp, raw_bytes, body_length) = match &entry_type {
168 EntryType::BGP4MP_ET => {
169 if length < 4 {
173 return Ok(rejected(ParserError::ParseError(
174 "invalid MRT header length for ET record: length < 4".into(),
175 )));
176 }
177 let mut combined = [0u8; 16];
178 combined[..12].copy_from_slice(&base_bytes);
179 input.read_exact(&mut combined[12..])?;
180 let microseconds = u32::from_be_bytes(combined[12..16].try_into().unwrap());
181 (
182 Some(microseconds),
183 Bytes::copy_from_slice(&combined),
184 length - 4,
185 )
186 }
187 _ => (None, Bytes::copy_from_slice(&base_bytes), length),
188 };
189
190 Ok(FramedHeader {
191 header: Ok(CommonHeader {
192 timestamp,
193 microsecond_timestamp,
194 entry_type,
195 entry_subtype,
196 length: body_length,
197 }),
198 raw_bytes,
199 body_length,
200 })
201}
202
203impl CommonHeader {
204 pub fn encode(&self) -> Bytes {
205 let raw = RawMrtHeader::from(self);
206 Bytes::copy_from_slice(raw.as_bytes())
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::models::EntryType;
214 use bytes::Buf;
215
216 #[test]
217 fn test_parse_common_header_with_bytes() {
218 let input = Bytes::from_static(&[
219 0, 0, 0, 1, 0, 16, 0, 4, 0, 0, 0, 5, ]);
224
225 let mut reader = input.clone().reader();
226 let result = parse_common_header_with_bytes(&mut reader).unwrap();
227
228 assert_eq!(result.header.timestamp, 1);
229 assert_eq!(result.header.entry_type, EntryType::BGP4MP);
230 assert_eq!(result.header.entry_subtype, 4);
231 assert_eq!(result.header.length, 5);
232 assert_eq!(result.raw_bytes, input);
233 }
234
235 #[test]
236 fn test_parse_common_header_with_bytes_et() {
237 let input = Bytes::from_static(&[
238 0, 0, 0, 1, 0, 17, 0, 4, 0, 0, 0, 9, 0, 3, 130, 112, ]);
244
245 let mut reader = input.clone().reader();
246 let result = parse_common_header_with_bytes(&mut reader).unwrap();
247
248 assert_eq!(result.header.timestamp, 1);
249 assert_eq!(result.header.entry_type, EntryType::BGP4MP_ET);
250 assert_eq!(result.header.entry_subtype, 4);
251 assert_eq!(result.header.length, 5); assert_eq!(result.header.microsecond_timestamp, Some(230_000));
253 assert_eq!(result.raw_bytes, input);
254 }
255
256 #[test]
258 fn test_encode_common_header() {
259 let header = CommonHeader {
260 timestamp: 1,
261 microsecond_timestamp: None,
262 entry_type: EntryType::BGP4MP,
263 entry_subtype: 4,
264 length: 5,
265 };
266
267 let expected = Bytes::from_static(&[
268 0, 0, 0, 1, 0, 16, 0, 4, 0, 0, 0, 5, ]);
273
274 let encoded = header.encode();
275 assert_eq!(encoded, expected);
276
277 let mut reader = expected.reader();
278 let parsed = parse_common_header(&mut reader).unwrap();
279 assert_eq!(parsed, header);
280 }
281
282 #[test]
284 fn test_encode_common_header_et() {
285 let header = CommonHeader {
286 timestamp: 1,
287 microsecond_timestamp: Some(230_000),
288 entry_type: EntryType::BGP4MP_ET,
289 entry_subtype: 4,
290 length: 5,
291 };
292
293 let expected = Bytes::from_static(&[
294 0, 0, 0, 1, 0, 17, 0, 4, 0, 0, 0, 9, 0, 3, 130, 112, ]);
300
301 let encoded = header.encode();
302 assert_eq!(encoded, expected);
303
304 let mut reader = expected.reader();
305 let parsed = parse_common_header(&mut reader).unwrap();
306 assert_eq!(parsed, header);
307 }
308
309 #[test]
311 fn test_parse_common_header_et_invalid_length() {
312 let bytes = Bytes::from_static(&[
314 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 3, ]);
319 let mut reader = bytes.reader();
320 let res = parse_common_header(&mut reader);
321 assert!(res.is_err());
322 }
323}