bmp-protocol 0.1.3

Tokio-based BMP protocol decoder
Documentation
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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
use crate::{Error, Result};
use bytes::{
    Buf,
    buf::BufExt,
    BytesMut
};
// use serde_derive::Serialize;

use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

/// There are a few different types of BMP message, refer to RFC7xxx for details. This enum
/// encapsulates the different types
#[derive(Clone, Debug)]
pub enum MessageData {
    /// Used to represent a message type I haven't implemented yet
    Unimplemented,
    /// Initiation message, this is sent once at the start of a BMP session to advertise speaker
    /// information
    Initiation(Vec<InformationTlv>),
    /// PeerUp messages are sent in bulk when a session is initially established, then over the life
    /// of the session as peers change status
    PeerUp((PeerHeader, PeerUp)),
    /// PeerDown messages are sent when a peer disconnects
    PeerDown((PeerHeader, PeerDown)),
    /// RouteMonitoring messages are state-compressed BGP messages
    RouteMonitoring((PeerHeader, bgp_rs::Update)),
}

/// BMP Message Types (RFC7854 Section 10.1)
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum MessageKind {
    /// Route Monitoring
    RouteMonitoring = 0,
    /// Statistics Report (unimplemented)
    StatisticsReport = 1,
    /// Peer Down (unimplemented)
    PeerDown = 2,
    /// Peer Up
    PeerUp = 3,
    /// Initiation
    Initiation = 4,
    /// Termination (unimplemented)
    Termination = 5,
    /// Route Mirroring (unimplemented)
    RouteMirroring = 6,

    // __Invalid
}

impl TryFrom<u8> for MessageKind {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        match value {
            0 => Ok(MessageKind::RouteMonitoring),
            1 => Ok(MessageKind::StatisticsReport),
            2 => Ok(MessageKind::PeerDown),
            3 => Ok(MessageKind::PeerUp),
            4 => Ok(MessageKind::Initiation),
            5 => Ok(MessageKind::Termination),
            6 => Ok(MessageKind::RouteMirroring),

            v @ _ => Err(
                Error::decode(&format!("invalid value for BMP Message Type: {}", v))
            ),
        }
    }
}

impl fmt::Display for MessageKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MessageKind::RouteMonitoring => write!(f, "route_monitoring"),
            MessageKind::StatisticsReport => write!(f, "statistics_report"),
            MessageKind::PeerUp => write!(f, "peer_up"),
            MessageKind::PeerDown => write!(f, "peer_down"),
            MessageKind::Initiation => write!(f, "initiation"),
            MessageKind::Termination => write!(f, "termination"),
            MessageKind::RouteMirroring => write!(f, "route_mirroring"),
        }
    }
}

/// BMP Peer Types (RFC7854 Section 10.2)
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum PeerType {
    /// Global Instance Peer
    GlobalInstance = 0,
    /// RD Instance Peer
    RdInstance = 1,
    /// Local Instance Peer
    LocalInstance = 2,
}

impl TryFrom<u8> for PeerType {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        match value {
            0 => Ok(PeerType::GlobalInstance),
            1 => Ok(PeerType::RdInstance),
            2 => Ok(PeerType::LocalInstance),

            v @ _ => Err(
                Error::decode(&format!("invalid value for BMP Peer Type: {}", v))
            ),
        }
    }
}

impl fmt::Display for PeerType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            PeerType::GlobalInstance => write!(f, "global"),
            PeerType::RdInstance => write!(f, "rd"),
            PeerType::LocalInstance => write!(f, "local"),
        }
    }
}

/// BMP Peer Flags (RFC7854 Section 10.3)
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[allow(non_snake_case)]
pub struct PeerFlags {
    /// Indicates whether the Peer address is an IPv6 addr
    pub V: bool,
    /// Indicates whether the message reflects post-policy
    pub L: bool,
    /// Indicates whether the message is using 2-byte AS_PATH format
    pub A: bool,
    /// Indicated whether the message is Adj-RIB-In or Adj-RIB-Out
    pub O: bool,
}

#[allow(non_snake_case)]
impl From<u8> for PeerFlags {
    fn from(value: u8) -> Self {
        let V = value & 0b10000000 == 0b10000000;
        let L = value & 0b01000000 == 0b01000000;
        let A = value & 0b00100000 == 0b00100000;
        let O = value & 0b00010000 == 0b00010000;

        Self { V, L, A, O }
    }
}

/// BMP Initiation Message TLVs (RFC7854 Section 10.5)
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum InformationType {
    /// Generic String
    String,
    /// sysDescr
    SysDescr,
    /// sysName
    SysName,
}

impl TryFrom<u16> for InformationType {
    type Error = Error;

    fn try_from(value: u16) -> Result<Self> {
        match value {
            0 => Ok(InformationType::String),
            1 => Ok(InformationType::SysDescr),
            2 => Ok(InformationType::SysName),

            v @ _ => Err(
                Error::decode(&format!("invalid value for BMP Information Type: {}", v))
            ),
        }
    }
}

impl fmt::Display for InformationType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            InformationType::String => write!(f, "string"),
            InformationType::SysDescr => write!(f, "sys_descr"),
            InformationType::SysName => write!(f, "sys_name"),
        }
    }
}

/// Message contaner
#[derive(Clone, Debug)]
pub struct BmpMessage {
    /// BMP version (should be 3)
    pub version: u8,
    /// Message type 
    pub kind: MessageKind,
    // pub peer_header: decoder::PeerHeader,

    /// Message data
    pub message: MessageData,
}

/// Per-Peer Header
///
/// The per-peer header follows the common header for most BMP messages.
/// The rest of the data in a BMP message is dependent on the MessageKind
/// field in the common header.
#[derive(Copy, Clone, Debug)]
pub struct PeerHeader {
    /// Peer Type
    pub peer_type: PeerType,
    /// Peer Flags
    pub peer_flags: PeerFlags,
    /// Peer Distinguisher
    pub peer_distinguisher: (u32, u32),        // depends on PeerType, see RFC7854 for details
    /// Peer address (TCP address used in BGP session)
    pub peer_addr: IpAddr,
    /// Peer ASN
    pub peer_asn: u32,
    /// Peer BGP Router ID
    pub peer_bgp_id: Ipv4Addr,
    /// Timestamp (seconds since epoch)
    pub timestamp: u32,
    /// Optional milliseconds, to be added to `timestamp`
    pub timestamp_ms: u32,
}

impl PeerHeader {
    pub(super) fn decode(buf: &mut BytesMut) -> Result<Self> {
        let peer_type: PeerType = buf.get_u8().try_into()?;
        let peer_flags: PeerFlags = buf.get_u8().into();
        let peer_distinguisher = (buf.get_u32(), buf.get_u32());

        let peer_addr = match peer_flags.V {
            // IPv4
            false => {
                // Throw away 12 bytes
                buf.advance(12);
                IpAddr::V4( Ipv4Addr::from(buf.get_u32()) )
            },
            // IPv6
            true => {
                IpAddr::V6( Ipv6Addr::from(buf.get_u128()) )
            }
        };

        let peer_asn = match peer_flags.A {
            // 2 byte ASNs
            true => {
                // Throw away 2 bytes
                buf.advance(2);
                u32::from( buf.get_u16() )
            },
            // 4 byte ASNs
            false => buf.get_u32()
        };

        let peer_bgp_id = Ipv4Addr::from( buf.get_u32() );

        let timestamp = buf.get_u32();
        let timestamp_ms = buf.get_u32();

        Ok(Self {
            peer_type,
            peer_flags,
            peer_distinguisher,
            peer_addr,
            peer_asn,
            peer_bgp_id,
            timestamp,
            timestamp_ms,
        })
    }
}

/// Information TLV
///
/// The Information TLV is used by the Initiation and Peer Up messages.
#[derive(Clone, Debug)]
pub struct InformationTlv {
    /// TLV message type
    pub information_type: InformationType,
    /// TLV message value 
    pub value: String,
}

impl InformationTlv {
    pub(super) fn decode(kind: u16, buf: &mut BytesMut) -> Result<Self> {
        let information_type = InformationType::try_from(kind)?;
        let len = buf.get_u16() as usize;

        let value = String::from_utf8((buf.bytes())[..len].to_vec()).unwrap();

        Ok(Self { information_type, value })
    }
}

/// Peer Up Notification
///
/// The Peer Up message is used to indicate that a peering session has
/// come up (i.e., has transitioned into the Established state).
#[derive(Clone, Debug)]
pub struct PeerUp {
    /// Local IP address used in BGP TCP session
    pub local_addr: IpAddr,
    /// Local TCP port
    pub local_port: u16,
    /// Remote TCP port
    pub remote_port: u16,
    /// BGP OPEN message sent by the BMP speaker
    pub sent_open: Option<bgp_rs::Open>,
    /// BGP OPEN message received by the BMP speaker
    pub recv_open: Option<bgp_rs::Open>,
    /// Information TLVs
    pub information: Vec<InformationTlv>,
}

impl PeerUp {
    pub(super) fn decode(peer_flags: &PeerFlags, buf: &mut BytesMut) -> Result<Self> {
        let local_addr = match peer_flags.V {
            // IPv4
            false => {
                // Throw away 12 bytes
                buf.advance(12);
                IpAddr::V4( Ipv4Addr::from(buf.get_u32()) )
            },
            // IPv6
            true => {
                IpAddr::V6( Ipv6Addr::from(buf.get_u128()) )
            }
        };

        let local_port = buf.get_u16();
        let remote_port = buf.get_u16();

        // For at least some routers (ie adm-b1) the PeerUp messages are missing the
        // OPENs. Short-circuit here until I can figure out whats going on
        if buf.remaining() == 0 {
            return Ok(PeerUp {
                local_addr,
                local_port,
                remote_port,
                sent_open: None,
                recv_open: None,
                information: vec![]
            });
        }

        let mut rdr = buf.reader();

        let sent_hdr = bgp_rs::Header::parse(&mut rdr)?;
        assert!(sent_hdr.record_type == 1);
        let sent_open = Some(bgp_rs::Open::parse(&mut rdr)?);

        let recv_hdr = bgp_rs::Header::parse(&mut rdr)?;
        assert!(recv_hdr.record_type == 1);
        let recv_open = Some(bgp_rs::Open::parse(&mut rdr)?);

        let mut information = vec![];
        while buf.remaining() > 0 {
            let kind = buf.get_u16();
            information.push( InformationTlv::decode(kind, buf)? );
        }

        Ok(PeerUp {
            local_addr,
            local_port,
            remote_port,
            sent_open,
            recv_open,
            information
        })
    }
}

/// Peer Down
///
/// The Peer Down message is used to indicate that the collector will no longer be receiving updates
/// for a given neighbour, including the reason for the change
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum PeerDown {
    // LocalShutdown(bgp_rs::Notification),
    /// The session was cleanly shutdown
    LocalShutdown(bgp_rs::Notification),
    /// The session was terminated because the underlying transport session was terminated,
    /// no NOTIFICATION was sent or received
    LocalTerminate(u16),
    /// The session was cleanly shutdown by the remote peer
    RemoteShutdown(bgp_rs::Notification),
    /// The session was terminated because the underlying transport session was terminated,
    /// no NOTIFICATION was sent or received
    RemoteTerminate,
    /// The session hasn't necessarily been torn down, but a configuration change on the BMP
    /// speaker means the collector will no longer receive updates for the session
    ConfigurationChange,
}

impl PeerDown {
    pub(super) fn decode(buf: &mut BytesMut) -> Result<Self> {
        let reason = buf.get_u8();

        match reason {
            1 => {
                let mut rdr = buf.reader();
                let header = bgp_rs::Header::parse(&mut rdr)?;
                let notification = bgp_rs::Notification::parse(&header, &mut rdr)?;

                Ok(Self::LocalShutdown(notification))
                // Ok(Self::LocalShutdown)
            },
            2 => Ok(Self::LocalTerminate(buf.get_u16())),
            3 => {
                let mut rdr = buf.reader();
                let header = bgp_rs::Header::parse(&mut rdr)?;
                let notification = bgp_rs::Notification::parse(&header, &mut rdr)?;

                Ok(Self::RemoteShutdown(notification))
                // Ok(Self::RemoteShutdown)
            },
            4 => Ok(Self::RemoteTerminate),
            5 => Ok(Self::ConfigurationChange),

            v @ _ => Err(Error::decode(&format!("invalid value for BMP Peer Down reason: {}", v)))
        }
    }
}