1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str;
use byteorder::{BigEndian, ReadBytesExt};
use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};

pub mod bgp;
pub mod processor;

pub trait Message<M> {
    fn parse<R: ReadBytesExt>(reader: &mut R, header: &MrtHeader) -> io::Result<M>;
    fn can_parse(typ: MrtType) -> bool;
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TableDump {
    AfiIpv4,
    AfiIpv6,
    Unknown(u16),
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TableDumpV2 {
    PeerIndex,
    RibIpv4Unicast,
    RibIpv6Unicast,
    Unknown(u16),
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MrtType {
    TableDump(TableDump),
    TableDumpV2(TableDumpV2),
    Unknown(u16),
}

#[derive(Debug)]
pub struct MrtHeader {
    pub timestamp: u32,
    pub typ: MrtType,
    pub length: u32,
}

pub struct Parser<R: ReadBytesExt> {
    reader: R,
}

impl<R: ReadBytesExt> Parser<R> {
    pub fn new(reader: R) -> Self {
        Self { reader }
    }

    pub fn read_header(&mut self) -> io::Result<Option<MrtHeader>> {
        let timestamp = match self.reader.read_u32::<BigEndian>() {
            Ok(timestamp) => timestamp,
            Err(e) => {
                return if e.kind() == io::ErrorKind::UnexpectedEof {
                    Ok(None)
                } else {
                    Err(e)
                }
            }
        };
        let typ = self.reader.read_u16::<BigEndian>()?;
        let subtype = self.reader.read_u16::<BigEndian>()?;
        let length = self.reader.read_u32::<BigEndian>()?;

        let typ = match typ {
            12 => MrtType::TableDump(match subtype {
                1 => TableDump::AfiIpv4,
                2 => TableDump::AfiIpv6,
                _ => TableDump::Unknown(subtype),
            }),
            13 => MrtType::TableDumpV2(match subtype {
                1 => TableDumpV2::PeerIndex,
                2 => TableDumpV2::RibIpv4Unicast,
                4 => TableDumpV2::RibIpv6Unicast,
                _ => TableDumpV2::Unknown(subtype),
            }),
            _ => MrtType::Unknown(typ),
        };

        Ok(Some(MrtHeader {
            timestamp,
            typ,
            length,
        }))
    }

    pub fn skip_message(&mut self, header: &MrtHeader) -> io::Result<()> {
        read_exact(&mut self.reader, header.length as usize)?;
        Ok(())
    }

    pub fn read_message<M: Message<M>>(&mut self, header: &MrtHeader) -> io::Result<M> {
        if !M::can_parse(header.typ) {
            panic!("This parser cannot parse {:?}", header.typ);
        }

        M::parse(&mut self.reader, header)
    }
}

#[derive(Debug)]
pub struct Afi {
    pub view_number: u16,
    pub sequence_number: u16,
    pub prefix: IpNetwork,
    pub status: u8,
    pub originated_time: u32,
    pub peer_ip: IpAddr,
    pub peer_as: u16,
    data: Vec<u8>,
}

impl Message<Afi> for Afi {
    fn parse<R: ReadBytesExt>(reader: &mut R, header: &MrtHeader) -> io::Result<Self> {
        let is_ipv6 = match header.typ {
            MrtType::TableDump(subtype) => match subtype {
                TableDump::AfiIpv4 => false,
                TableDump::AfiIpv6 => true,
                _ => panic!("Only AFI_IPv4 and AFI_IPv6 subtypes are supported"),
            },
            _ => panic!("Only TableDump types is supported"),
        };

        let view_number = reader.read_u16::<BigEndian>()?;
        let sequence_number = reader.read_u16::<BigEndian>()?;
        let prefix_ip = read_ip_addr(reader, is_ipv6)?;
        let prefix_length = reader.read_u8()?;
        let prefix = IpNetwork::new(prefix_ip, prefix_length).unwrap();
        let status = reader.read_u8()?;
        let originated_time = reader.read_u32::<BigEndian>()?;
        let peer_ip = read_ip_addr(reader, is_ipv6)?;
        let peer_as = reader.read_u16::<BigEndian>()?;
        let attribute_length = reader.read_u16::<BigEndian>()?;
        let data = read_exact(reader, attribute_length as usize)?;

        Ok(Afi {
            view_number,
            sequence_number,
            prefix,
            status,
            originated_time,
            peer_ip,
            peer_as,
            data,
        })
    }

    fn can_parse(typ: MrtType) -> bool {
        typ == MrtType::TableDump(TableDump::AfiIpv4)
            || typ == MrtType::TableDump(TableDump::AfiIpv6)
    }
}

impl Afi {
    pub fn get_bgp_attributes(&self) -> io::Result<Vec<bgp::Attribute>> {
        bgp::Attribute::parse_all(&self.data)
    }
}

#[derive(Debug)]
pub struct PeerIndexTable {
    pub collector_bgp_id: u32,
    pub view_name: String,
    pub peer_entries: Vec<PeerEntry>,
}

impl Message<PeerIndexTable> for PeerIndexTable {
    fn parse<R: ReadBytesExt>(reader: &mut R, _: &MrtHeader) -> io::Result<Self> {
        let collector_bgp_id = reader.read_u32::<BigEndian>()?;

        let view_name_length = reader.read_u16::<BigEndian>()?;
        let view_name_buffer = read_exact(reader, view_name_length as usize)?;
        let view_name = str::from_utf8(&view_name_buffer)
            .map(|x| x.to_string())
            .map_err(|_| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    "PeerIndexTable view name did not contain valid UTF-8",
                )
            })?;

        let peer_count = reader.read_u16::<BigEndian>()?;

        let mut peer_entries = Vec::with_capacity(peer_count as usize);
        for _ in 0..peer_count {
            peer_entries.push(PeerEntry::parse(reader)?);
        }

        Ok(PeerIndexTable {
            collector_bgp_id,
            view_name,
            peer_entries,
        })
    }

    fn can_parse(typ: MrtType) -> bool {
        typ == MrtType::TableDumpV2(TableDumpV2::PeerIndex)
    }
}

#[derive(Debug)]
pub struct PeerEntry {
    pub peer_bgp_id: u32,
    pub ip_addr: IpAddr,
    pub asn: u32,
}

impl PeerEntry {
    fn parse<R: ReadBytesExt>(rdr: &mut R) -> io::Result<Self> {
        let type_ = rdr.read_u8()?;
        let is_ipv6 = ((type_) & 0x1) == 1;
        let is_asn_32bit = ((type_ >> 1) & 0x1) == 1;

        let peer_bgp_id = rdr.read_u32::<BigEndian>()?;
        let ip_addr = read_ip_addr(rdr, is_ipv6)?;
        let asn = if is_asn_32bit {
            rdr.read_u32::<BigEndian>()?
        } else {
            rdr.read_u16::<BigEndian>()? as u32
        };

        Ok(Self {
            peer_bgp_id,
            ip_addr,
            asn,
        })
    }
}

#[derive(Debug)]
pub struct RibEntry {
    pub sequence_number: u32,
    pub prefix: IpNetwork,
    pub sub_entries: Vec<RibSubEntry>,
}

impl Message<RibEntry> for RibEntry {
    fn parse<R: ReadBytesExt>(reader: &mut R, header: &MrtHeader) -> io::Result<Self> {
        let sequence_number = reader.read_u32::<BigEndian>()?;

        let prefix_length = reader.read_u8()?;
        let prefix_bytes = ((prefix_length + 7) / 8) as usize;
        let prefix_buffer = read_exact(reader, prefix_bytes)?;

        let prefix = match header.typ {
            MrtType::TableDumpV2(subtype) => match subtype {
                TableDumpV2::RibIpv4Unicast => {
                    debug_assert!(prefix_length <= 32);
                    let mut parts: [u8; 4] = [0; 4];
                    parts[..prefix_bytes].copy_from_slice(prefix_buffer.as_slice());
                    let ip = Ipv4Addr::from(parts);
                    IpNetwork::V4(Ipv4Network::new(ip, prefix_length).unwrap())
                }
                TableDumpV2::RibIpv6Unicast => {
                    debug_assert!(prefix_length <= 128);
                    let mut parts: [u8; 16] = [0; 16];
                    parts[..prefix_bytes].copy_from_slice(prefix_buffer.as_slice());
                    let ip = Ipv6Addr::from(parts);
                    IpNetwork::V6(Ipv6Network::new(ip, prefix_length).unwrap())
                }
                _ => panic!("This parser cannot parse TableDumpV2 {:?} subtype", subtype),
            },
            _ => panic!("This parser cannot parse {:?} type", header.typ),
        };

        let entry_count = reader.read_u16::<BigEndian>()?;
        let mut sub_entries = Vec::with_capacity(entry_count as usize);
        for _ in 0..entry_count {
            sub_entries.push(RibSubEntry::parse(reader)?);
        }

        Ok(RibEntry {
            sequence_number,
            prefix,
            sub_entries,
        })
    }

    fn can_parse(typ: MrtType) -> bool {
        typ == MrtType::TableDumpV2(TableDumpV2::RibIpv4Unicast)
            || typ == MrtType::TableDumpV2(TableDumpV2::RibIpv6Unicast)
    }
}

#[derive(Debug)]
pub struct RibSubEntry {
    pub peer_index: u16,
    pub originated_time: u32,
    data: Vec<u8>,
}

impl RibSubEntry {
    fn parse<R: ReadBytesExt>(rdr: &mut R) -> io::Result<RibSubEntry> {
        let peer_index = rdr.read_u16::<BigEndian>()?;
        let originated_time = rdr.read_u32::<BigEndian>()?;
        let attribute_length = rdr.read_u16::<BigEndian>()?;
        let data = read_exact(rdr, attribute_length as usize)?;

        Ok(RibSubEntry {
            peer_index,
            originated_time,
            data,
        })
    }

    pub fn get_bgp_attributes(&self) -> io::Result<Vec<bgp::Attribute>> {
        bgp::Attribute::parse_all(&self.data)
    }
}

fn read_ip_addr<R: ReadBytesExt>(rdr: &mut R, is_ipv6: bool) -> io::Result<IpAddr> {
    if is_ipv6 {
        let mut buffer = [0; 16];
        rdr.read_exact(&mut buffer)?;
        Ok(IpAddr::V6(Ipv6Addr::from(buffer)))
    } else {
        Ok(IpAddr::V4(Ipv4Addr::from(rdr.read_u32::<BigEndian>()?)))
    }
}

#[inline]
fn read_exact<R: ReadBytesExt>(rdr: &mut R, length: usize) -> io::Result<Vec<u8>> {
    let mut buffer = vec![0; length as usize];
    rdr.read_exact(buffer.as_mut_slice())?;
    Ok(buffer)
}