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 mut base_bytes = [0u8; 12];
122 input.read_exact(&mut base_bytes)?;
123
124 let raw = RawMrtCommonHeader::ref_from_bytes(&base_bytes)
126 .expect("base_bytes is exactly 12 bytes with no alignment requirement");
127
128 let timestamp = raw.timestamp.get();
129 let entry_type = EntryType::try_from(raw.entry_type.get())?;
130 let entry_subtype = raw.entry_subtype.get();
131 let mut length = raw.length.get();
133
134 let (microsecond_timestamp, raw_bytes) = match &entry_type {
135 EntryType::BGP4MP_ET => {
136 if length < 4 {
140 return Err(ParserError::ParseError(
141 "invalid MRT header length for ET record: length < 4".into(),
142 ));
143 }
144 length -= 4;
145 let mut combined = [0u8; 16];
146 combined[..12].copy_from_slice(&base_bytes);
147 input.read_exact(&mut combined[12..])?;
148 let microseconds = u32::from_be_bytes(combined[12..16].try_into().unwrap());
149 (Some(microseconds), Bytes::copy_from_slice(&combined))
150 }
151 _ => (None, Bytes::copy_from_slice(&base_bytes)),
152 };
153
154 Ok(ParsedHeader {
155 header: CommonHeader {
156 timestamp,
157 microsecond_timestamp,
158 entry_type,
159 entry_subtype,
160 length,
161 },
162 raw_bytes,
163 })
164}
165
166impl CommonHeader {
167 pub fn encode(&self) -> Bytes {
168 let raw = RawMrtHeader::from(self);
169 Bytes::copy_from_slice(raw.as_bytes())
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::models::EntryType;
177 use bytes::Buf;
178
179 #[test]
180 fn test_parse_common_header_with_bytes() {
181 let input = Bytes::from_static(&[
182 0, 0, 0, 1, 0, 16, 0, 4, 0, 0, 0, 5, ]);
187
188 let mut reader = input.clone().reader();
189 let result = parse_common_header_with_bytes(&mut reader).unwrap();
190
191 assert_eq!(result.header.timestamp, 1);
192 assert_eq!(result.header.entry_type, EntryType::BGP4MP);
193 assert_eq!(result.header.entry_subtype, 4);
194 assert_eq!(result.header.length, 5);
195 assert_eq!(result.raw_bytes, input);
196 }
197
198 #[test]
199 fn test_parse_common_header_with_bytes_et() {
200 let input = Bytes::from_static(&[
201 0, 0, 0, 1, 0, 17, 0, 4, 0, 0, 0, 9, 0, 3, 130, 112, ]);
207
208 let mut reader = input.clone().reader();
209 let result = parse_common_header_with_bytes(&mut reader).unwrap();
210
211 assert_eq!(result.header.timestamp, 1);
212 assert_eq!(result.header.entry_type, EntryType::BGP4MP_ET);
213 assert_eq!(result.header.entry_subtype, 4);
214 assert_eq!(result.header.length, 5); assert_eq!(result.header.microsecond_timestamp, Some(230_000));
216 assert_eq!(result.raw_bytes, input);
217 }
218
219 #[test]
221 fn test_encode_common_header() {
222 let header = CommonHeader {
223 timestamp: 1,
224 microsecond_timestamp: None,
225 entry_type: EntryType::BGP4MP,
226 entry_subtype: 4,
227 length: 5,
228 };
229
230 let expected = Bytes::from_static(&[
231 0, 0, 0, 1, 0, 16, 0, 4, 0, 0, 0, 5, ]);
236
237 let encoded = header.encode();
238 assert_eq!(encoded, expected);
239
240 let mut reader = expected.reader();
241 let parsed = parse_common_header(&mut reader).unwrap();
242 assert_eq!(parsed, header);
243 }
244
245 #[test]
247 fn test_encode_common_header_et() {
248 let header = CommonHeader {
249 timestamp: 1,
250 microsecond_timestamp: Some(230_000),
251 entry_type: EntryType::BGP4MP_ET,
252 entry_subtype: 4,
253 length: 5,
254 };
255
256 let expected = Bytes::from_static(&[
257 0, 0, 0, 1, 0, 17, 0, 4, 0, 0, 0, 9, 0, 3, 130, 112, ]);
263
264 let encoded = header.encode();
265 assert_eq!(encoded, expected);
266
267 let mut reader = expected.reader();
268 let parsed = parse_common_header(&mut reader).unwrap();
269 assert_eq!(parsed, header);
270 }
271
272 #[test]
274 fn test_parse_common_header_et_invalid_length() {
275 let bytes = Bytes::from_static(&[
277 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 3, ]);
282 let mut reader = bytes.reader();
283 let res = parse_common_header(&mut reader);
284 assert!(res.is_err());
285 }
286}