use crate::{Error, Result};
use bytes::{
Buf,
buf::BufExt,
BytesMut
};
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[derive(Clone, Debug)]
pub enum MessageData {
Unimplemented,
Initiation(Vec<InformationTlv>),
PeerUp((PeerHeader, PeerUp)),
PeerDown((PeerHeader, PeerDown)),
RouteMonitoring((PeerHeader, bgp_rs::Update)),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum MessageKind {
RouteMonitoring = 0,
StatisticsReport = 1,
PeerDown = 2,
PeerUp = 3,
Initiation = 4,
Termination = 5,
RouteMirroring = 6,
}
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"),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum PeerType {
GlobalInstance = 0,
RdInstance = 1,
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"),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[allow(non_snake_case)]
pub struct PeerFlags {
pub V: bool,
pub L: bool,
pub A: bool,
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 }
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum InformationType {
String,
SysDescr,
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"),
}
}
}
#[derive(Clone, Debug)]
pub struct BmpMessage {
pub version: u8,
pub kind: MessageKind,
pub message: MessageData,
}
#[derive(Copy, Clone, Debug)]
pub struct PeerHeader {
pub peer_type: PeerType,
pub peer_flags: PeerFlags,
pub peer_distinguisher: (u32, u32), pub peer_addr: IpAddr,
pub peer_asn: u32,
pub peer_bgp_id: Ipv4Addr,
pub timestamp: u32,
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 {
false => {
buf.advance(12);
IpAddr::V4( Ipv4Addr::from(buf.get_u32()) )
},
true => {
IpAddr::V6( Ipv6Addr::from(buf.get_u128()) )
}
};
let peer_asn = match peer_flags.A {
true => {
buf.advance(2);
u32::from( buf.get_u16() )
},
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,
})
}
}
#[derive(Clone, Debug)]
pub struct InformationTlv {
pub information_type: InformationType,
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 })
}
}
#[derive(Clone, Debug)]
pub struct PeerUp {
pub local_addr: IpAddr,
pub local_port: u16,
pub remote_port: u16,
pub sent_open: Option<bgp_rs::Open>,
pub recv_open: Option<bgp_rs::Open>,
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 {
false => {
buf.advance(12);
IpAddr::V4( Ipv4Addr::from(buf.get_u32()) )
},
true => {
IpAddr::V6( Ipv6Addr::from(buf.get_u128()) )
}
};
let local_port = buf.get_u16();
let remote_port = buf.get_u16();
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
})
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum PeerDown {
LocalShutdown(bgp_rs::Notification),
LocalTerminate(u16),
RemoteShutdown(bgp_rs::Notification),
RemoteTerminate,
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))
},
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))
},
4 => Ok(Self::RemoteTerminate),
5 => Ok(Self::ConfigurationChange),
v @ _ => Err(Error::decode(&format!("invalid value for BMP Peer Down reason: {}", v)))
}
}
}