bmp_protocol/
types.rs

1use crate::{Error, Result};
2use bytes::{
3    Buf,
4    buf::BufExt,
5    BytesMut
6};
7// use serde_derive::Serialize;
8
9use std::convert::{TryFrom, TryInto};
10use std::fmt;
11use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
12
13/// There are a few different types of BMP message, refer to RFC7xxx for details. This enum
14/// encapsulates the different types
15#[derive(Clone, Debug)]
16pub enum MessageData {
17    /// Used to represent a message type I haven't implemented yet
18    Unimplemented,
19    /// Initiation message, this is sent once at the start of a BMP session to advertise speaker
20    /// information
21    Initiation(Vec<InformationTlv>),
22    /// PeerUp messages are sent in bulk when a session is initially established, then over the life
23    /// of the session as peers change status
24    PeerUp((PeerHeader, PeerUp)),
25    /// PeerDown messages are sent when a peer disconnects
26    PeerDown((PeerHeader, PeerDown)),
27    /// RouteMonitoring messages are state-compressed BGP messages
28    RouteMonitoring((PeerHeader, bgp_rs::Update)),
29}
30
31/// BMP Message Types (RFC7854 Section 10.1)
32#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
33#[repr(u8)]
34pub enum MessageKind {
35    /// Route Monitoring
36    RouteMonitoring = 0,
37    /// Statistics Report (unimplemented)
38    StatisticsReport = 1,
39    /// Peer Down (unimplemented)
40    PeerDown = 2,
41    /// Peer Up
42    PeerUp = 3,
43    /// Initiation
44    Initiation = 4,
45    /// Termination (unimplemented)
46    Termination = 5,
47    /// Route Mirroring (unimplemented)
48    RouteMirroring = 6,
49
50    // __Invalid
51}
52
53impl TryFrom<u8> for MessageKind {
54    type Error = Error;
55
56    fn try_from(value: u8) -> Result<Self> {
57        match value {
58            0 => Ok(MessageKind::RouteMonitoring),
59            1 => Ok(MessageKind::StatisticsReport),
60            2 => Ok(MessageKind::PeerDown),
61            3 => Ok(MessageKind::PeerUp),
62            4 => Ok(MessageKind::Initiation),
63            5 => Ok(MessageKind::Termination),
64            6 => Ok(MessageKind::RouteMirroring),
65
66            v @ _ => Err(
67                Error::decode(&format!("invalid value for BMP Message Type: {}", v))
68            ),
69        }
70    }
71}
72
73impl fmt::Display for MessageKind {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        match self {
76            MessageKind::RouteMonitoring => write!(f, "route_monitoring"),
77            MessageKind::StatisticsReport => write!(f, "statistics_report"),
78            MessageKind::PeerUp => write!(f, "peer_up"),
79            MessageKind::PeerDown => write!(f, "peer_down"),
80            MessageKind::Initiation => write!(f, "initiation"),
81            MessageKind::Termination => write!(f, "termination"),
82            MessageKind::RouteMirroring => write!(f, "route_mirroring"),
83        }
84    }
85}
86
87/// BMP Peer Types (RFC7854 Section 10.2)
88#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
89#[repr(u8)]
90pub enum PeerType {
91    /// Global Instance Peer
92    GlobalInstance = 0,
93    /// RD Instance Peer
94    RdInstance = 1,
95    /// Local Instance Peer
96    LocalInstance = 2,
97}
98
99impl TryFrom<u8> for PeerType {
100    type Error = Error;
101
102    fn try_from(value: u8) -> Result<Self> {
103        match value {
104            0 => Ok(PeerType::GlobalInstance),
105            1 => Ok(PeerType::RdInstance),
106            2 => Ok(PeerType::LocalInstance),
107
108            v @ _ => Err(
109                Error::decode(&format!("invalid value for BMP Peer Type: {}", v))
110            ),
111        }
112    }
113}
114
115impl fmt::Display for PeerType {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        match self {
118            PeerType::GlobalInstance => write!(f, "global"),
119            PeerType::RdInstance => write!(f, "rd"),
120            PeerType::LocalInstance => write!(f, "local"),
121        }
122    }
123}
124
125/// BMP Peer Flags (RFC7854 Section 10.3)
126#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
127#[allow(non_snake_case)]
128pub struct PeerFlags {
129    /// Indicates whether the Peer address is an IPv6 addr
130    pub V: bool,
131    /// Indicates whether the message reflects post-policy
132    pub L: bool,
133    /// Indicates whether the message is using 2-byte AS_PATH format
134    pub A: bool,
135    /// Indicated whether the message is Adj-RIB-In or Adj-RIB-Out
136    pub O: bool,
137}
138
139#[allow(non_snake_case)]
140impl From<u8> for PeerFlags {
141    fn from(value: u8) -> Self {
142        let V = value & 0b10000000 == 0b10000000;
143        let L = value & 0b01000000 == 0b01000000;
144        let A = value & 0b00100000 == 0b00100000;
145        let O = value & 0b00010000 == 0b00010000;
146
147        Self { V, L, A, O }
148    }
149}
150
151/// BMP Initiation Message TLVs (RFC7854 Section 10.5)
152#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
153pub enum InformationType {
154    /// Generic String
155    String,
156    /// sysDescr
157    SysDescr,
158    /// sysName
159    SysName,
160}
161
162impl TryFrom<u16> for InformationType {
163    type Error = Error;
164
165    fn try_from(value: u16) -> Result<Self> {
166        match value {
167            0 => Ok(InformationType::String),
168            1 => Ok(InformationType::SysDescr),
169            2 => Ok(InformationType::SysName),
170
171            v @ _ => Err(
172                Error::decode(&format!("invalid value for BMP Information Type: {}", v))
173            ),
174        }
175    }
176}
177
178impl fmt::Display for InformationType {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        match self {
181            InformationType::String => write!(f, "string"),
182            InformationType::SysDescr => write!(f, "sys_descr"),
183            InformationType::SysName => write!(f, "sys_name"),
184        }
185    }
186}
187
188/// Message contaner
189#[derive(Clone, Debug)]
190pub struct BmpMessage {
191    /// BMP version (should be 3)
192    pub version: u8,
193    /// Message type 
194    pub kind: MessageKind,
195    // pub peer_header: decoder::PeerHeader,
196
197    /// Message data
198    pub message: MessageData,
199}
200
201/// Per-Peer Header
202///
203/// The per-peer header follows the common header for most BMP messages.
204/// The rest of the data in a BMP message is dependent on the MessageKind
205/// field in the common header.
206#[derive(Copy, Clone, Debug)]
207pub struct PeerHeader {
208    /// Peer Type
209    pub peer_type: PeerType,
210    /// Peer Flags
211    pub peer_flags: PeerFlags,
212    /// Peer Distinguisher
213    pub peer_distinguisher: (u32, u32),        // depends on PeerType, see RFC7854 for details
214    /// Peer address (TCP address used in BGP session)
215    pub peer_addr: IpAddr,
216    /// Peer ASN
217    pub peer_asn: u32,
218    /// Peer BGP Router ID
219    pub peer_bgp_id: Ipv4Addr,
220    /// Timestamp (seconds since epoch)
221    pub timestamp: u32,
222    /// Optional milliseconds, to be added to `timestamp`
223    pub timestamp_ms: u32,
224}
225
226impl PeerHeader {
227    pub(super) fn decode(buf: &mut BytesMut) -> Result<Self> {
228        let peer_type: PeerType = buf.get_u8().try_into()?;
229        let peer_flags: PeerFlags = buf.get_u8().into();
230        let peer_distinguisher = (buf.get_u32(), buf.get_u32());
231
232        let peer_addr = match peer_flags.V {
233            // IPv4
234            false => {
235                // Throw away 12 bytes
236                buf.advance(12);
237                IpAddr::V4( Ipv4Addr::from(buf.get_u32()) )
238            },
239            // IPv6
240            true => {
241                IpAddr::V6( Ipv6Addr::from(buf.get_u128()) )
242            }
243        };
244
245        let peer_asn = match peer_flags.A {
246            // 2 byte ASNs
247            true => {
248                // Throw away 2 bytes
249                buf.advance(2);
250                u32::from( buf.get_u16() )
251            },
252            // 4 byte ASNs
253            false => buf.get_u32()
254        };
255
256        let peer_bgp_id = Ipv4Addr::from( buf.get_u32() );
257
258        let timestamp = buf.get_u32();
259        let timestamp_ms = buf.get_u32();
260
261        Ok(Self {
262            peer_type,
263            peer_flags,
264            peer_distinguisher,
265            peer_addr,
266            peer_asn,
267            peer_bgp_id,
268            timestamp,
269            timestamp_ms,
270        })
271    }
272}
273
274/// Information TLV
275///
276/// The Information TLV is used by the Initiation and Peer Up messages.
277#[derive(Clone, Debug)]
278pub struct InformationTlv {
279    /// TLV message type
280    pub information_type: InformationType,
281    /// TLV message value 
282    pub value: String,
283}
284
285impl InformationTlv {
286    pub(super) fn decode(kind: u16, buf: &mut BytesMut) -> Result<Self> {
287        let information_type = InformationType::try_from(kind)?;
288        let len = buf.get_u16() as usize;
289
290        let value = String::from_utf8((buf.bytes())[..len].to_vec()).unwrap();
291
292        Ok(Self { information_type, value })
293    }
294}
295
296/// Peer Up Notification
297///
298/// The Peer Up message is used to indicate that a peering session has
299/// come up (i.e., has transitioned into the Established state).
300#[derive(Clone, Debug)]
301pub struct PeerUp {
302    /// Local IP address used in BGP TCP session
303    pub local_addr: IpAddr,
304    /// Local TCP port
305    pub local_port: u16,
306    /// Remote TCP port
307    pub remote_port: u16,
308    /// BGP OPEN message sent by the BMP speaker
309    pub sent_open: Option<bgp_rs::Open>,
310    /// BGP OPEN message received by the BMP speaker
311    pub recv_open: Option<bgp_rs::Open>,
312    /// Information TLVs
313    pub information: Vec<InformationTlv>,
314}
315
316impl PeerUp {
317    pub(super) fn decode(peer_flags: &PeerFlags, buf: &mut BytesMut) -> Result<Self> {
318        let local_addr = match peer_flags.V {
319            // IPv4
320            false => {
321                // Throw away 12 bytes
322                buf.advance(12);
323                IpAddr::V4( Ipv4Addr::from(buf.get_u32()) )
324            },
325            // IPv6
326            true => {
327                IpAddr::V6( Ipv6Addr::from(buf.get_u128()) )
328            }
329        };
330
331        let local_port = buf.get_u16();
332        let remote_port = buf.get_u16();
333
334        // For at least some routers (ie adm-b1) the PeerUp messages are missing the
335        // OPENs. Short-circuit here until I can figure out whats going on
336        if buf.remaining() == 0 {
337            return Ok(PeerUp {
338                local_addr,
339                local_port,
340                remote_port,
341                sent_open: None,
342                recv_open: None,
343                information: vec![]
344            });
345        }
346
347        let mut rdr = buf.reader();
348
349        let sent_hdr = bgp_rs::Header::parse(&mut rdr)?;
350        assert!(sent_hdr.record_type == 1);
351        let sent_open = Some(bgp_rs::Open::parse(&mut rdr)?);
352
353        let recv_hdr = bgp_rs::Header::parse(&mut rdr)?;
354        assert!(recv_hdr.record_type == 1);
355        let recv_open = Some(bgp_rs::Open::parse(&mut rdr)?);
356
357        let mut information = vec![];
358        while buf.remaining() > 0 {
359            let kind = buf.get_u16();
360            information.push( InformationTlv::decode(kind, buf)? );
361        }
362
363        Ok(PeerUp {
364            local_addr,
365            local_port,
366            remote_port,
367            sent_open,
368            recv_open,
369            information
370        })
371    }
372}
373
374/// Peer Down
375///
376/// The Peer Down message is used to indicate that the collector will no longer be receiving updates
377/// for a given neighbour, including the reason for the change
378#[derive(Clone, Debug)]
379#[non_exhaustive]
380pub enum PeerDown {
381    // LocalShutdown(bgp_rs::Notification),
382    /// The session was cleanly shutdown
383    LocalShutdown(bgp_rs::Notification),
384    /// The session was terminated because the underlying transport session was terminated,
385    /// no NOTIFICATION was sent or received
386    LocalTerminate(u16),
387    /// The session was cleanly shutdown by the remote peer
388    RemoteShutdown(bgp_rs::Notification),
389    /// The session was terminated because the underlying transport session was terminated,
390    /// no NOTIFICATION was sent or received
391    RemoteTerminate,
392    /// The session hasn't necessarily been torn down, but a configuration change on the BMP
393    /// speaker means the collector will no longer receive updates for the session
394    ConfigurationChange,
395}
396
397impl PeerDown {
398    pub(super) fn decode(buf: &mut BytesMut) -> Result<Self> {
399        let reason = buf.get_u8();
400
401        match reason {
402            1 => {
403                let mut rdr = buf.reader();
404                let header = bgp_rs::Header::parse(&mut rdr)?;
405                let notification = bgp_rs::Notification::parse(&header, &mut rdr)?;
406
407                Ok(Self::LocalShutdown(notification))
408                // Ok(Self::LocalShutdown)
409            },
410            2 => Ok(Self::LocalTerminate(buf.get_u16())),
411            3 => {
412                let mut rdr = buf.reader();
413                let header = bgp_rs::Header::parse(&mut rdr)?;
414                let notification = bgp_rs::Notification::parse(&header, &mut rdr)?;
415
416                Ok(Self::RemoteShutdown(notification))
417                // Ok(Self::RemoteShutdown)
418            },
419            4 => Ok(Self::RemoteTerminate),
420            5 => Ok(Self::ConfigurationChange),
421
422            v @ _ => Err(Error::decode(&format!("invalid value for BMP Peer Down reason: {}", v)))
423        }
424    }
425}