use std::{ffi::{OsStr, OsString}, fmt::{self, Display}, io::Cursor, ops::{Div, Mul}, time}; use bytemuck::NoUninit; use byteorder::{BigEndian, WriteBytesExt}; use nom::{bytes::complete::take, multi::many_m_n, number::complete::be_u8, IResult}; use nom_derive::{NomBE, Parse}; use proc_bitfield::*; #[repr(u8)] #[derive(ConvRaw, Debug, NomBE, PartialEq, Eq, Clone, Copy, NoUninit)] pub enum BfdDiagnostic { None = 0, TimeExpired = 1, EchoFailed = 2, NeighborDown = 3, FwdPlaneReset = 4, PathDown = 5, ConcatPathDown = 6, AdminDown = 7, RevConcatPathDown = 8, Reserved, } impl Display for BfdDiagnostic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { Self::None => "None", Self::TimeExpired => "TimeExpired", Self::EchoFailed => "EchoFailed", Self::NeighborDown => "NeighborDown", Self::FwdPlaneReset => "FwdPlaneReset", Self::PathDown => "PathDown", Self::ConcatPathDown => "ConcatPathDown", Self::AdminDown => "AdminDown", Self::RevConcatPathDown => "RevConcatPathDown", Self::Reserved => "Reserved", }) } } #[repr(u8)] #[derive(ConvRaw, Debug, NomBE, PartialEq, Eq, Default, Clone, Copy, NoUninit)] pub enum BfdState { AdminDown = 0, #[default] Down = 1, Init = 2, Up = 3, } impl Display for BfdState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { Self::AdminDown => "AdminDown", Self::Down => "Down", Self::Init => "Init", Self::Up => "Up", }) } } impl Into<&OsStr> for BfdState { fn into(self) -> &'static OsStr { match self { Self::AdminDown => OsStr::new("AdminDown"), Self::Init => OsStr::new("Init"), Self::Down => OsStr::new("Down"), Self::Up => OsStr::new("Up"), } } } #[repr(u8)] #[derive(ConvRaw, Debug, NomBE, PartialEq, Eq, Clone, Copy, NoUninit)] pub enum BfdAuthType { None = 0, SimplePassword = 1, KeyedMD5 = 2, MetKeyedMD5 = 3, KeyedSHA1 = 4, MetKeyedSHA1 = 5, Reserved, } #[repr(transparent)] #[derive(Debug, NomBE, PartialEq, Eq, Clone, Copy, NoUninit, Hash)] pub struct BfdDiscriminator(pub u32); impl Display for BfdDiscriminator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } #[repr(transparent)] #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, NoUninit, NomBE)] /// All intervals in BFD are specified in microseconds pub struct BfdInterval(u32); impl BfdInterval { pub fn from_micros(micros: u32) -> Self { Self(micros) } pub fn from_millis(millis: u32) -> Self { Self(millis * 1000) } pub fn from_secs(secs: u32) -> Self { Self(secs * 1000000) } pub fn from_secs_f32(secs: f32) -> Self { Self((secs * 1000000.0) as u32) } } impl From for time::Duration { fn from(value: BfdInterval) -> Self { time::Duration::from_micros(value.0 as u64) } } impl From for BfdInterval { fn from(value: time::Duration) -> Self { BfdInterval(value.as_micros() as u32) } } impl Display for BfdInterval { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}us", self.0) } } impl Mul for BfdInterval { type Output = BfdInterval; fn mul(self, rhs: u32) -> Self::Output { BfdInterval(self.0 * rhs) } } impl Mul for u32 { type Output = BfdInterval; fn mul(self, rhs: BfdInterval) -> Self::Output { BfdInterval(self * rhs.0) } } impl Div for BfdInterval { type Output = BfdInterval; fn div(self, rhs: u32) -> Self::Output { BfdInterval(self.0 / rhs) } } impl Div for u32 { type Output = BfdInterval; fn div(self, rhs: BfdInterval) -> Self::Output { BfdInterval(self / rhs.0) } } bitfield! { #[derive(NomBE)] pub struct BfdFlags(pub u32): Debug { pub vers: u8 @ 29..=31, pub diag: u8 [try_get BfdDiagnostic] @ 24..=28, pub state: u8 [try_get BfdState] @ 22..=23, pub poll: bool @ 21, pub final_: bool @ 20, pub cpi: bool @ 19, pub auth_present: bool @ 18, pub demand: bool @ 17, pub multipoint: bool @ 16, pub detect_mult: u8 @ 8..=15, pub length: u8 @ 0..=7 } } #[derive(Debug)] pub struct BfdAuthSimplePassword(Vec); impl<'a> Parse<&'a [u8]> for BfdAuthSimplePassword { fn parse(i: &'a [u8]) -> IResult<&'a [u8], Self, nom::error::Error<&'a [u8]>> { let (i, res) = many_m_n(1, 16, be_u8)(i)?; Ok((i, Self(res))) } } #[derive(Debug, NomBE)] pub struct BfdAuthKeyedMD5 { key_id: u8, _reserved: u8, seq: u32, digest: [u8; 16], } #[derive(Debug, NomBE)] pub struct BfdAuthKeyedSHA1 { key_id: u8, _reserved: u8, seq: u32, hash: [u8; 20], } #[derive(Debug, NomBE)] #[nom(Selector = "BfdAuthType", Complete)] pub enum BfdAuthData { #[nom(Selector = "BfdAuthType::SimplePassword")] SimplePassword(BfdAuthSimplePassword), #[nom(Selector = "BfdAuthType::KeyedMD5")] KeyedMD5(BfdAuthKeyedMD5), #[nom(Selector = "BfdAuthType::MetKeyedMD5")] MetKeyedMD5(BfdAuthKeyedMD5), #[nom(Selector = "BfdAuthType::KeyedSHA1")] KeyedSHA1(BfdAuthKeyedSHA1), #[nom(Selector = "BfdAuthType::MetKeyedSHA1")] MetKeyedSHA1(BfdAuthKeyedSHA1), } impl BfdAuthData { fn parse_be_with_length( i: &[u8], auth_type: BfdAuthType, auth_len: u8, ) -> IResult<&[u8], Self> { let (new_i, data) = take(auth_len)(i)?; let (_leftovers, retval) = BfdAuthData::parse_be(data, auth_type)?; Ok((new_i, retval)) } } #[derive(Debug, NomBE)] pub struct BfdAuth { auth_type: BfdAuthType, auth_len: u8, #[nom(Parse = "{ |i| BfdAuthData::parse_be_with_length(i, auth_type, auth_len) }")] auth_data: BfdAuthData, } #[derive(Debug, NomBE)] pub struct BfdPacket { pub flags: BfdFlags, pub my_disc: BfdDiscriminator, pub your_disc: BfdDiscriminator, pub desired_min_tx: BfdInterval, pub required_min_rx: BfdInterval, pub required_min_echo_rx: BfdInterval, #[nom(Cond = "flags.auth_present()")] pub auth: Option, } impl BfdPacket { pub fn serialize(&self) -> Result, std::io::Error> { // TODO: serialize auth let buf = [0u8; 24]; let mut wtr = Cursor::new(buf); wtr.write_u32::(self.flags.0)?; wtr.write_u32::(self.my_disc.0)?; wtr.write_u32::(self.your_disc.0)?; wtr.write_u32::(self.desired_min_tx.0)?; wtr.write_u32::(self.required_min_rx.0)?; wtr.write_u32::(self.required_min_echo_rx.0)?; Ok(Box::new(wtr.into_inner())) } }