From c901f28c303e31f540e8bb9d3083ea3ceb2a8d7d Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Thu, 25 May 2023 21:09:23 +0200 Subject: [PATCH 01/20] Derive PartialEq for SessionConfig. --- src/bgp/message/update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index e1bb0d9b..977f6b65 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -519,7 +519,7 @@ impl UpdateMessage { /// available in the UPDATE messages themselves, but are only exchanged in the /// BGP OPEN messages when the session was established. /// -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct SessionConfig { pub four_octet_asn: FourOctetAsn, pub add_path: AddPath, From 06eb15e26f83838cb4d8724a2eedc99f34a734ec Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Thu, 25 May 2023 21:13:07 +0200 Subject: [PATCH 02/20] Clippy. --- src/bgp/message/update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 977f6b65..f42e09b5 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -519,7 +519,7 @@ impl UpdateMessage { /// available in the UPDATE messages themselves, but are only exchanged in the /// BGP OPEN messages when the session was established. /// -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SessionConfig { pub four_octet_asn: FourOctetAsn, pub add_path: AddPath, From d3594c58b8fec50b8f9cad5bbab70ad31b085758 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 1 Jun 2023 16:12:22 +0200 Subject: [PATCH 03/20] die already record --- src/record.rs | 202 -------------------------------------------------- 1 file changed, 202 deletions(-) delete mode 100644 src/record.rs diff --git a/src/record.rs b/src/record.rs deleted file mode 100644 index 4becfb1d..00000000 --- a/src/record.rs +++ /dev/null @@ -1,202 +0,0 @@ -//! Generic record types -//! -//! Records hold snippets of information that are contained in a -//! BGP packet. The type of the key is variable: it can be an NLRI, or the -//! NLRI can be disassembled into several records with the prefixes contained -//! in the NLRI as key. -//! -//! A record can be turned into a message (MessageRecord trait) to be ready -//! to be send to other units, or external systems. A message has no -//! references (anymore). -//! -//! example: -//! BGP packet → disassemble into records → turn into message → send to other -//! units -//! -//! See ```bgp``` module for records specific to BGP. -use std::borrow::Cow; -use std::fmt; - -//------------ Traits for Record -------------------------------------------- - -/// Trait for types that act as keys for records. -/// -/// These traits must be implemented by all record types. - -/// Trait that describes the record key, a key can be a NLRI (or a part of -/// that), but it can also be other TLVs originating from a BGP packet. - -/// Key of a record -pub trait Key {} -impl Key for (u64, u32) where Self: Copy + Sized {} - -/// Trait for a type to act as the identity of the message sender -pub trait SenderId -where - Self: Copy + Sized, -{ -} - -impl SenderId for u32 {} - -/// Sender ID type -pub type SenderIdInt = u32; - -/// Lamport Timestamp. Used to order messages between units/systems. -pub type LogicalTime = u64; - -/// Generic Record trait -/// -/// The Record trait describes any type that has a key and metadata, and that -/// it is not a message (yet). The type should accomodate both holding -/// metadata as a reference, as well as storing the metadata inside itself. -/// -/// Types implementing this trait should be used to disassemble BGP packets -/// into storable data points. -pub trait Record<'a> -where - Self: Clone, -{ - type Key: crate::record::Key; - type Meta: crate::record::Meta; - - fn new(key: Self::Key, meta: &'a Self::Meta) -> Self; - fn new_with_local_meta(key: Self::Key, local_meta: Self::Meta) -> Self; - fn key(&'a self) -> Self::Key; - fn meta(&'a self) -> Cow<'a, Self::Meta>; -} - -/// Record as a stand-alone message -/// -/// The MessageRecord trait describes a record turned message. It should have -/// at least two fields, `sender_id` and `ltime` (or be able to synthesize -/// those). -/// -/// A generic record type (that implements the Record trait) should be able -/// to be turned into a message record. The message should own all the data -/// it holds so it can be cut loose and send off to other systems through -/// cloning and/or serialization. -/// -/// The MessageRecord type should be used to send messages to other units or -/// external systems. -pub trait MessageRecord<'a> -where - Self: Clone + Record<'a>, -{ - type SenderId: crate::record::SenderId; - - fn new( - key: >::Key, - meta: >::Meta, - sender_id: Self::SenderId, - // Logical Time of this message (Lamport timestamp) - ltime: u64, - ) -> Self; - fn new_from_record( - record: Self, - sender_id: Self::SenderId, - ltime: u64, - ) -> Self; - #[must_use] - fn into_message(self, sender_id: Self::SenderId, ltime: u64) -> Self; - fn sender_id(&self) -> Self::SenderId; - fn key(&'a self) -> >::Key { - ::key(self) - } - fn meta(&'a self) -> Cow<>::Meta> { - Cow::Owned(::meta(self).into_owned()) - } - fn ltime(&self) -> u64; - fn set_ltime(&mut self, ltime: u64) -> u64; - fn inc_ltime(&mut self) -> u64 { - let ltime = self.ltime(); - self.set_ltime(ltime + 1) - } - fn timestamp(&self) -> u64; -} - -//----------------------- meta-data traits/types----------------------------- - -/// Trait that describes how an existing record gets merged -/// -/// MergeUpdate must be implemented by a type that implements Meta if it -/// wants to be able to be stored. It should describe how the metadata for an -/// existing record should be merged with newly arriving records for the same -/// key. -pub trait MergeUpdate { - fn merge_update( - &mut self, - update_meta: Self, - ) -> Result<(), Box>; - - // This is part of the Read-Copy-Update pattern for updating a record - // concurrently. The Read part should be done by the caller and then - // the result should be passed in into this function together with - // the new meta-data that updates it. This function will then create - // a copy (in the pattern lingo, but in Rust that would be a Clone, - // since we're not requiring Copy for Meta) and update that with a - // copy of the new meta-data. It then returns the result of that merge. - // The caller should then proceed to insert that as a new entry - // in the global store. - fn clone_merge_update( - &self, - update_meta: &Self, - ) -> Result> - where - Self: std::marker::Sized; -} - -/// Trait for types that can be used as metadata of a record -pub trait Meta -where - Self: fmt::Debug + Sized + fmt::Display + Clone + MergeUpdate, -{ - fn summary(&self) -> String; -} - -impl Meta for T -where - T: fmt::Debug + fmt::Display + Clone + MergeUpdate, -{ - fn summary(&self) -> String { - format!("{}", self) - } -} - -/// Tree-wide empty meta-data type -/// -/// A special type that indicates that there's no metadata in the tree -/// storing the prefixes. Note that this is different from a tree with -/// optional meta-data. -#[derive(Clone, Copy, Hash)] -pub enum NoMeta { - Empty, -} - -impl fmt::Debug for NoMeta { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } -} - -impl fmt::Display for NoMeta { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } -} - -impl MergeUpdate for NoMeta { - fn merge_update( - &mut self, - _: NoMeta, - ) -> Result<(), Box> { - Ok(()) - } - - fn clone_merge_update( - &self, - _: &NoMeta, - ) -> Result> { - Ok(NoMeta::Empty) - } -} From e40b45f1b025c83e325597786681a89976770eed Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 1 Jun 2023 16:14:02 +0200 Subject: [PATCH 04/20] add lib --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ac4aeafa..28273170 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ pub mod bgpsec; pub mod bmp; #[cfg(feature = "bgp")] pub mod flowspec; -pub mod record; #[cfg(feature = "bmp")] pub use octseq::Octets; From ac23197a2cfbcd9fec5f4d346afdabff103fa399 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 14 Jun 2023 09:29:11 +0200 Subject: [PATCH 05/20] Add derives on UpdateMessage --- src/bgp/message/update.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index f42e09b5..66ac66ed 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -26,7 +26,7 @@ use crate::bgp::communities::{ const COFF: usize = 19; // XXX replace this with .skip()'s? /// BGP UPDATE message, variant of the [`Message`] enum. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, Hash, PartialEq)] pub struct UpdateMessage { octets: Octs, session_config: SessionConfig, @@ -519,7 +519,7 @@ impl UpdateMessage { /// available in the UPDATE messages themselves, but are only exchanged in the /// BGP OPEN messages when the session was established. /// -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct SessionConfig { pub four_octet_asn: FourOctetAsn, pub add_path: AddPath, @@ -587,14 +587,14 @@ impl SessionConfig { } /// Indicates whether this session is Four Octet capable. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum FourOctetAsn { Enabled, Disabled, } /// Indicates whether AddPath is enabled for this session. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum AddPath { Enabled, Disabled, From fb2f598fdcf9b262050624003881c33b236a6bca Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 14 Jun 2023 09:34:51 +0200 Subject: [PATCH 06/20] Also add Ord/PartialOrd derives on UpdateMessage --- src/bgp/message/update.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 66ac66ed..653456f5 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -26,7 +26,7 @@ use crate::bgp::communities::{ const COFF: usize = 19; // XXX replace this with .skip()'s? /// BGP UPDATE message, variant of the [`Message`] enum. -#[derive(Debug, Clone, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct UpdateMessage { octets: Octs, session_config: SessionConfig, @@ -519,7 +519,7 @@ impl UpdateMessage { /// available in the UPDATE messages themselves, but are only exchanged in the /// BGP OPEN messages when the session was established. /// -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SessionConfig { pub four_octet_asn: FourOctetAsn, pub add_path: AddPath, @@ -587,14 +587,14 @@ impl SessionConfig { } /// Indicates whether this session is Four Octet capable. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum FourOctetAsn { Enabled, Disabled, } /// Indicates whether AddPath is enabled for this session. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum AddPath { Enabled, Disabled, From 3a1d53d9d920c0ac63f4147ea026adcbdabfd8d2 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 22 Jun 2023 16:23:45 +0200 Subject: [PATCH 07/20] hash everywhere --- src/bgp/aspath.rs | 14 +++++++++++++- src/bgp/message/update.rs | 2 +- src/bgp/types.rs | 6 +++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/bgp/aspath.rs b/src/bgp/aspath.rs index 9d547e4b..80e98cbf 100644 --- a/src/bgp/aspath.rs +++ b/src/bgp/aspath.rs @@ -40,7 +40,7 @@ pub type OwnedHop = Hop>; /// /// ```Hop(AS10), Hop(AS20), Hop(AS30), Hop(Set(AS40, AS50))``` /// -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)] pub struct HopPath { /// The hops in this HopPath. hops: Vec, @@ -975,6 +975,18 @@ pub enum Hop { Segment(Segment), } +impl Hash for Hop { + fn hash(&self, state: &mut H) { + todo!() + } + + fn hash_slice(data: &[Self], state: &mut H) + where + Self: Sized, { + todo!() + } +} + impl Hop { /// Tries to convert the `Hop` into an [`Asn`]. This returns an error if /// `Hop` is not of the [`Hop::Asn`] variant. diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 653456f5..a71991dd 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -1261,7 +1261,7 @@ impl<'a, Ref: Octets> IntoIterator for PathAttributes<'a, Ref> { //--- Aggregator ------------------------------------------------------------- /// Path Attribute (7). -#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] pub struct Aggregator { asn: Asn, speaker: Ipv4Addr, diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 84a2dacc..7da96988 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -29,7 +29,7 @@ typeenum!( ); /// BGP Origin types as used in BGP UPDATE messages. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum OriginType { Igp, Egp, @@ -81,7 +81,7 @@ typeenum!( ); /// Wrapper for the 4 byte Multi-Exit Discriminator in path attributes. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct MultiExitDisc(pub u32); impl std::fmt::Display for MultiExitDisc { @@ -91,7 +91,7 @@ impl std::fmt::Display for MultiExitDisc { } /// Wrapper for the 4 byte Local Preference value in path attributes. -#[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] pub struct LocalPref(pub u32); impl std::fmt::Display for LocalPref { From 74791370dd5a2a1ca3a89afba1c40fc8c82ff14e Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:37:40 +0200 Subject: [PATCH 08/20] Add some missing Serde Serialize derives (#30) * Derive Serialize for Communities. * Derive Serialize for Hop and related types. * Derive Serialize for UpdateMessage and related types. * Derive Serialize for OriginType. * Derive Serialize for LocalPref. * Derive Serialize for MultiExitDisc. * Derive Serialize for Aggregator. --- src/bgp/aspath.rs | 3 +++ src/bgp/communities.rs | 7 ++++++- src/bgp/message/update.rs | 5 +++++ src/bgp/types.rs | 3 +++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/bgp/aspath.rs b/src/bgp/aspath.rs index 9d547e4b..563c3ba1 100644 --- a/src/bgp/aspath.rs +++ b/src/bgp/aspath.rs @@ -41,6 +41,7 @@ pub type OwnedHop = Hop>; /// ```Hop(AS10), Hop(AS20), Hop(AS30), Hop(Set(AS40, AS50))``` /// #[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct HopPath { /// The hops in this HopPath. hops: Vec, @@ -691,6 +692,7 @@ impl<'a, Octs: Octets> Iterator for PathSegments<'a, Octs> { /// AS_PATH Segment generic over [`Octets`]. #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Segment { stype: SegmentType, four_byte_asns: bool, @@ -970,6 +972,7 @@ impl fmt::Display for SegmentType { /// variant `Segment`, which contain the entire segment and thus (possibly) /// multiple ASNs. #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum Hop { Asn(Asn), Segment(Segment), diff --git a/src/bgp/communities.rs b/src/bgp/communities.rs index 85df6fb0..330436af 100644 --- a/src/bgp/communities.rs +++ b/src/bgp/communities.rs @@ -98,7 +98,8 @@ use crate::asn::{Asn, Asn16, ParseAsnError}; //--- Community -------------------------------------------------------------- /// Standard and Extended/Large Communities variants. -#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd )] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, )] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum Community { Standard(StandardCommunity), Extended(ExtendedCommunity), @@ -349,6 +350,7 @@ wellknown!(Wellknown, /// Conventional, RFC1997 4-byte community. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd )] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct StandardCommunity([u8; 4]); impl StandardCommunity { @@ -523,6 +525,7 @@ impl Display for Tag { /// Extended Community as defined in RFC4360. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd )] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct ExtendedCommunity([u8; 8]); impl ExtendedCommunity { @@ -907,6 +910,7 @@ impl Display for ExtendedCommunity { /// IPv6 Extended Community as defined in RFC5701. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd )] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Ipv6ExtendedCommunity([u8; 20]); @@ -1023,6 +1027,7 @@ impl Display for Ipv6ExtendedCommunity { /// Large Community as defined in RFC8092. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd )] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct LargeCommunity([u8; 12]); impl LargeCommunity { diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 653456f5..47d07d5e 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -27,6 +27,7 @@ const COFF: usize = 19; // XXX replace this with .skip()'s? /// BGP UPDATE message, variant of the [`Message`] enum. #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct UpdateMessage { octets: Octs, session_config: SessionConfig, @@ -520,6 +521,7 @@ impl UpdateMessage { /// BGP OPEN messages when the session was established. /// #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct SessionConfig { pub four_octet_asn: FourOctetAsn, pub add_path: AddPath, @@ -588,6 +590,7 @@ impl SessionConfig { /// Indicates whether this session is Four Octet capable. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum FourOctetAsn { Enabled, Disabled, @@ -595,6 +598,7 @@ pub enum FourOctetAsn { /// Indicates whether AddPath is enabled for this session. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum AddPath { Enabled, Disabled, @@ -1262,6 +1266,7 @@ impl<'a, Ref: Octets> IntoIterator for PathAttributes<'a, Ref> { //--- Aggregator ------------------------------------------------------------- /// Path Attribute (7). #[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Aggregator { asn: Asn, speaker: Ipv4Addr, diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 84a2dacc..f73095d5 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -30,6 +30,7 @@ typeenum!( /// BGP Origin types as used in BGP UPDATE messages. #[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum OriginType { Igp, Egp, @@ -82,6 +83,7 @@ typeenum!( /// Wrapper for the 4 byte Multi-Exit Discriminator in path attributes. #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct MultiExitDisc(pub u32); impl std::fmt::Display for MultiExitDisc { @@ -92,6 +94,7 @@ impl std::fmt::Display for MultiExitDisc { /// Wrapper for the 4 byte Local Preference value in path attributes. #[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct LocalPref(pub u32); impl std::fmt::Display for LocalPref { From bcf943d6231249956bd46ec5c0c7de000e2cb91c Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 22 Jun 2023 17:12:52 +0200 Subject: [PATCH 09/20] Impl Hash for Hop. --- src/bgp/aspath.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/bgp/aspath.rs b/src/bgp/aspath.rs index 80e98cbf..aecaac5c 100644 --- a/src/bgp/aspath.rs +++ b/src/bgp/aspath.rs @@ -975,15 +975,18 @@ pub enum Hop { Segment(Segment), } -impl Hash for Hop { +impl Hash for Hop { fn hash(&self, state: &mut H) { - todo!() - } - - fn hash_slice(data: &[Self], state: &mut H) - where - Self: Sized, { - todo!() + match self { + Hop::Asn(asn) => { + state.write_u8(0); + asn.hash(state); + } + Hop::Segment(segment) => { + state.write_u8(1); + segment.hash(state); + } + } } } From d8f8eade2c9b2b971a03247cce120e6e172d083e Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Fri, 23 Jun 2023 12:43:35 +0200 Subject: [PATCH 10/20] Add Serialize --- src/bgp/types.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 9e0dfaaa..e29c0f25 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -83,6 +83,7 @@ typeenum!( /// Wrapper for the 4 byte Multi-Exit Discriminator in path attributes. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MultiExitDisc(pub u32); impl std::fmt::Display for MultiExitDisc { @@ -93,6 +94,7 @@ impl std::fmt::Display for MultiExitDisc { /// Wrapper for the 4 byte Local Preference value in path attributes. #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LocalPref(pub u32); impl std::fmt::Display for LocalPref { From 36ce7181ccd758024056c8047cd1d79665384c21 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 5 Jul 2023 14:49:20 +0200 Subject: [PATCH 11/20] type enums everywhere --- src/bmp/message.rs | 76 ++++++++++++++++++--------------------- src/util/macros.rs | 89 ++++++++++++++++++++++++++++++---------------- 2 files changed, 92 insertions(+), 73 deletions(-) diff --git a/src/bmp/message.rs b/src/bmp/message.rs index a3b00d8f..d782a255 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -373,12 +373,7 @@ impl> PerPeerHeader { /// Returns the peer type as defined in /// [RFC7854](https://datatracker.ietf.org/doc/html/rfc7854#section-10.2). pub fn peer_type(&self) -> PeerType { - match self.octets.as_ref()[0] { - 0 => PeerType::GlobalInstance, - 1 => PeerType::RdInstance, - 2 => PeerType::LocalInstance, - _ => PeerType::Undefined, - } + self.octets.as_ref()[0].into() } // 0 1 2 3 4 5 6 7 @@ -511,22 +506,26 @@ impl> PartialEq for PerPeerHeader { } } -/// The three peer types as defined in -/// [RFC7854](https://datatracker.ietf.org/doc/html/rfc7854#section-4.2). -#[derive(Debug, Hash, Eq, PartialEq)] -pub enum PeerType { - GlobalInstance, - RdInstance, - LocalInstance, - Undefined, -} +typeenum!( + /// The peer types as defined in + /// https://www.iana.org/assignments/bmp-parameters/bmp-parameters.xhtml#peer-types + PeerType, u8, + 0 => GlobalInstance, + 1 => RdInstance, + 2 => LocalInstance, + 3 => LocalRibInstance; + 4..=250 => Unassigned; + 251..=254 => Experimental; + 255 => Reserved +); -/// Specify which RIB the contents of a message originated from. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] -pub enum RibType { - AdjRibIn, - AdjRibOut, -} + +typeenum!( + /// Specify which RIB the contents of a message originated from. + RibType, u8, + 0 => AdjRibIn, + 1 => AdjRibOut +); //--- Specific Message types ------------------------------------------------- @@ -1068,14 +1067,7 @@ impl<'a> InformationTlv<'a> { /// Returns the `InformationTlvType` for this TLV. pub fn typ(&self) -> InformationTlvType { - match u16::from_be_bytes(self.octets[0..=1].try_into().unwrap()) { - 0 => InformationTlvType::String, - 1 => InformationTlvType::SysDesc, - 2 => InformationTlvType::SysName, - 3 => InformationTlvType::VrfTableName, - 4 => InformationTlvType::AdminLabel, - u => InformationTlvType::Undefined(u) - } + InformationTlvType::from(u16::from_be_bytes(self.octets[0..=1].try_into().unwrap())) } /// Returns the length of the value. @@ -1105,19 +1097,19 @@ impl<'a> Display for InformationTlv<'a> { } -/// Types of Information TLVs. -/// -/// See also -/// -#[derive(Debug, Eq, PartialEq)] -pub enum InformationTlvType { - String, // type 0 - SysDesc, // type 1 - SysName, // type 2 - VrfTableName, // type 3, RFC 9069 - AdminLabel, // type 4, RFC 8671 - Undefined(u16), -} +typeenum!( + /// Types of Information TLVs. + /// + /// See also + /// + InformationTlvType, u16, + 0 => String, + 1 => SysDesc, + 2 => SysName, + 3 => VrfTableName, + 4 => AdminLabel; + 5.. => Undefined +); /// Iterator over `InformationTlv`'s. pub struct InformationTlvIter<'a> { diff --git a/src/util/macros.rs b/src/util/macros.rs index 6251c5ee..32aa4f9f 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -18,45 +18,72 @@ /// and `L2Vpn`. On this enum, the [`From`] (for conversion between the /// variants and `u16`) and [`std::fmt::Display`] traits are implemented. /// +/// You can also specify ranges as like so: +/// +/// ```rust +/// typeenum!( +/// PeerType, u8, +/// 0 => GlobalInstance, +/// 1 => RdInstance, +/// 2 => LocalInstance, +/// 3 => LocalRibInstance; +/// 4..=250 => Unassigned; +/// 251..=254 => Experimental; +/// 255 => Reserved +/// ); +/// ``` +/// Note that match lines with ranges are separated by semi-colons, rather +/// than by commas. Range variants have a data field with the specified value. +/// Specifying an half-open range to the right or specifying the matches +/// exhaustively will disable the default `Unimplemented` variant. #[macro_export] macro_rules! typeenum { - ($(#[$attr:meta])* $name:ident, $ty:ty, $($x:expr => $y:ident),+ $(,)*) => { - $(#[$attr])* - #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub enum $name { - $($y),+, - Unimplemented($ty), - } + ($(#[$attr:meta])* $name:ident, + $ty:ty, + $($x:expr => $y:ident),+ $(,)* + $(;$x1:pat => $y1:ident)*) => { + $(#[$attr])* + #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum $name { + $($y),+, + $($y1($ty),)? + Unimplemented($ty), + } - impl From<$ty> for $name { - fn from(f: $ty) -> $name { - match f { - $($x => $name::$y,)+ - u => $name::Unimplemented(u), + impl From<$ty> for $name { + #[allow(unreachable_patterns)] + fn from(f: $ty) -> $name { + match f { + $($x => $name::$y,)+ + $($x1 => $name::$y1(f),)? + u => $name::Unimplemented(u), + } } } - } - impl From<$name> for $ty { - fn from(s: $name) -> $ty { - match s { - $($name::$y => $x,)+ - $name::Unimplemented(u) => u, + impl From<$name> for $ty { + fn from(s: $name) -> $ty { + match s { + $($name::$y => $x,)+ + $($name::$y1(u) => u,)? + $name::Unimplemented(u) => u, + } } + } + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter) + -> Result<(), std::fmt::Error> + { + match self { + $($name::$y => write!(f, stringify!($y))),+, + $($name::$y1(u) => write!(f, "{} ({})", stringify!($u), u),)? + $name::Unimplemented(u) => + write!(f, "unknown-{}-{}", stringify!($name), u) + } + } + } } - impl std::fmt::Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter) - -> Result<(), std::fmt::Error> - { - match self { - $($name::$y => write!(f, stringify!($y))),+, - $name::Unimplemented(u) => - write!(f, "unknown-{}-{}", stringify!($name), u) - } - } - } - } } From c54911c47712b5b732779167d3a8ab9c4d16663a Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 5 Jul 2023 16:18:55 +0200 Subject: [PATCH 12/20] modify macro syntax + affected invocations --- src/bgp/message/mod.rs | 14 ++++---- src/bgp/message/open.rs | 58 ++++++++++++++++-------------- src/bgp/types.rs | 78 ++++++++++++++++++++++------------------- src/bmp/message.rs | 38 ++++++++++++-------- src/util/macros.rs | 51 +++++++++++++++++---------- 5 files changed, 136 insertions(+), 103 deletions(-) diff --git a/src/bgp/message/mod.rs b/src/bgp/message/mod.rs index ea8af1d7..5d26ae1a 100644 --- a/src/bgp/message/mod.rs +++ b/src/bgp/message/mod.rs @@ -89,12 +89,14 @@ impl Message { typeenum!( /// BGP Message types. MsgType, u8, - 1 => Open, - 2 => Update, - 3 => Notification, - 4 => Keepalive, - 5 => RouteRefresh, // RFC2918 - //6 => //Capability, // draft-ietf-idr-dynamic-cap + { + 1 => Open, + 2 => Update, + 3 => Notification, + 4 => Keepalive, + 5 => RouteRefresh, // RFC2918 + //6 => //Capability, // draft-ietf-idr-dynamic-cap + } ); impl Message { diff --git a/src/bgp/message/open.rs b/src/bgp/message/open.rs index a58ac124..519e5e04 100644 --- a/src/bgp/message/open.rs +++ b/src/bgp/message/open.rs @@ -635,38 +635,44 @@ typeenum!( /// BGP Capability type, as per /// . CapabilityType, u8, - 0 => Reserved, - 1 => MultiProtocol, - 2 => RouteRefresh, - 3 => OutboundRouteFiltering, - 5 => ExtendedNextHop, - 6 => ExtendedMessage, - 8 => MultipleLabels, - 9 => BgpRole, - //10..=63 => Unassigned, - 64 => GracefulRestart, - 65 => FourOctetAsn, - 66 => DeprecatedDynamicCapability, - 67 => DynamicCapability, - 68 => Multisession, - 69 => AddPath, - 70 => EnhancedRouteRefresh, - 71 => LongLivedGracefulRestart, - 73 => FQDN, - 128 => PrestandardRouteRefresh, - 130 => PrestandardOutboundRouteFiltering, - 131 => PrestandardMultisession, + { + 0 => Reserved, + 1 => MultiProtocol, + 2 => RouteRefresh, + 3 => OutboundRouteFiltering, + 5 => ExtendedNextHop, + 6 => ExtendedMessage, + 8 => MultipleLabels, + 9 => BgpRole, + //10..=63 => Unassigned, + 64 => GracefulRestart, + 65 => FourOctetAsn, + 66 => DeprecatedDynamicCapability, + 67 => DynamicCapability, + 68 => Multisession, + 69 => AddPath, + 70 => EnhancedRouteRefresh, + 71 => LongLivedGracefulRestart, + 73 => FQDN, + 128 => PrestandardRouteRefresh, + 130 => PrestandardOutboundRouteFiltering, + 131 => PrestandardMultisession, + } ); typeenum!( /// BGP OPEN Optional Parameter type, as per /// . OptionalParameterType, u8, - 0 => Reserved, - 1 => Authentication, - 2 => Capabilities, - //3..=254 => Unassigned, - 255 => ExtendedLength + { + 0 => Reserved, + 1 => Authentication, + 2 => Capabilities, + 255 => ExtendedLength + }, + { + 3..=254 => Unassigned, + } ); diff --git a/src/bgp/types.rs b/src/bgp/types.rs index e29c0f25..7e0470d1 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -8,25 +8,27 @@ use serde::{Serialize, Deserialize}; typeenum!( /// AFI as used in BGP OPEN and UPDATE messages. - AFI, u16, - 1 => Ipv4, - 2 => Ipv6, - 25 => L2Vpn, -); + AFI, u16, + { + 1 => Ipv4, + 2 => Ipv6, + 25 => L2Vpn + }); typeenum!( /// SAFI as used in BGP OPEN and UPDATE messages. SAFI, u8, - 1 => Unicast, - 2 => Multicast, - 4 => MplsUnicast, - 65 => Vpls, - 70 => Evpn, - 128 => MplsVpnUnicast, + { + 1 => Unicast, + 2 => Multicast, + 4 => MplsUnicast, + 65 => Vpls, + 70 => Evpn, + 128 => MplsVpnUnicast, 132 => RouteTarget, 133 => FlowSpec, - 134 => FlowSpecVpn, -); + 134 => FlowSpecVpn + }); /// BGP Origin types as used in BGP UPDATE messages. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -55,30 +57,32 @@ typeenum!( /// As per: /// PathAttributeType, u8, - 0 => Reserved, - 1 => Origin, - 2 => AsPath, - 3 => NextHop, - 4 => MultiExitDisc, - 5 => LocalPref, - 6 => AtomicAggregate, - 7 => Aggregator, - 8 => Communities, - 9 => OriginatorId, - 10 => ClusterList, - 14 => MpReachNlri, - 15 => MpUnreachNlri, - 16 => ExtendedCommunities, - 17 => As4Path, - 18 => As4Aggregator, - 20 => Connector, - 21 => AsPathLimit, - 22 => PmsiTunnel, - 25 => Ipv6ExtendedCommunities, - 32 => LargeCommunities, - 33 => BgpsecAsPath, - 128 => AttrSet, - 255 => RsrvdDevelopment, + { + 0 => Reserved, + 1 => Origin, + 2 => AsPath, + 3 => NextHop, + 4 => MultiExitDisc, + 5 => LocalPref, + 6 => AtomicAggregate, + 7 => Aggregator, + 8 => Communities, + 9 => OriginatorId, + 10 => ClusterList, + 14 => MpReachNlri, + 15 => MpUnreachNlri, + 16 => ExtendedCommunities, + 17 => As4Path, + 18 => As4Aggregator, + 20 => Connector, + 21 => AsPathLimit, + 22 => PmsiTunnel, + 25 => Ipv6ExtendedCommunities, + 32 => LargeCommunities, + 33 => BgpsecAsPath, + 128 => AttrSet, + 255 => RsrvdDevelopment + } ); /// Wrapper for the 4 byte Multi-Exit Discriminator in path attributes. diff --git a/src/bmp/message.rs b/src/bmp/message.rs index d782a255..652e0bb4 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -510,22 +510,26 @@ typeenum!( /// The peer types as defined in /// https://www.iana.org/assignments/bmp-parameters/bmp-parameters.xhtml#peer-types PeerType, u8, - 0 => GlobalInstance, - 1 => RdInstance, - 2 => LocalInstance, - 3 => LocalRibInstance; - 4..=250 => Unassigned; - 251..=254 => Experimental; - 255 => Reserved + { + 0 => GlobalInstance, + 1 => RdInstance, + 2 => LocalInstance, + 3 => LocalRibInstance, + 255 => Reserved + }, + { + 4..=250 => Unassigned, + 251..=254 => Experimental, + } ); typeenum!( /// Specify which RIB the contents of a message originated from. RibType, u8, - 0 => AdjRibIn, + {0 => AdjRibIn, 1 => AdjRibOut -); +}); //--- Specific Message types ------------------------------------------------- @@ -1103,12 +1107,16 @@ typeenum!( /// See also /// InformationTlvType, u16, - 0 => String, - 1 => SysDesc, - 2 => SysName, - 3 => VrfTableName, - 4 => AdminLabel; - 5.. => Undefined + { + 0 => String, + 1 => SysDesc, + 2 => SysName, + 3 => VrfTableName, + 4 => AdminLabel, + }, + { + 5.. => Undefined, + } ); /// Iterator over `InformationTlv`'s. diff --git a/src/util/macros.rs b/src/util/macros.rs index 32aa4f9f..df956547 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -7,10 +7,13 @@ /// # use serde::{Serialize, Deserialize}; /// # #[macro_use] extern crate routecore; /// # fn main() { -/// typeenum!(AFI, u16, -/// 1 => Ipv4, -/// 2 => Ipv6, -/// 25 => L2Vpn, +/// typeenum!( +/// AFI, u16, +/// { +/// 1 => Ipv4, +/// 2 => Ipv6, +/// 25 => L2Vpn +/// } /// ); /// # } /// ``` @@ -23,13 +26,17 @@ /// ```rust /// typeenum!( /// PeerType, u8, -/// 0 => GlobalInstance, -/// 1 => RdInstance, -/// 2 => LocalInstance, -/// 3 => LocalRibInstance; -/// 4..=250 => Unassigned; -/// 251..=254 => Experimental; -/// 255 => Reserved +/// { +/// 0 => GlobalInstance, +/// 1 => RdInstance, +/// 2 => LocalInstance, +/// 3 => LocalRibInstance, +/// } +/// { +/// 4..=250 => Unassigned, +/// 251..=254 => Experimental, +/// 255 => Reserved +/// } /// ); /// ``` /// Note that match lines with ranges are separated by semi-colons, rather @@ -38,16 +45,22 @@ /// exhaustively will disable the default `Unimplemented` variant. #[macro_export] macro_rules! typeenum { - ($(#[$attr:meta])* $name:ident, - $ty:ty, - $($x:expr => $y:ident),+ $(,)* - $(;$x1:pat => $y1:ident)*) => { + ( $(#[$attr:meta])* + $name:ident, + $ty:ty, + { + $($x:expr => $y:ident),+ $(,)* + } + $(,{ + $( $x1:pat => $y1:ident ),* $(,)* + })? + ) => { $(#[$attr])* #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum $name { $($y),+, - $($y1($ty),)? + $($($y1($ty),)?)? Unimplemented($ty), } @@ -56,7 +69,7 @@ macro_rules! typeenum { fn from(f: $ty) -> $name { match f { $($x => $name::$y,)+ - $($x1 => $name::$y1(f),)? + $($($x1 => $name::$y1(f),)?)? u => $name::Unimplemented(u), } } @@ -66,7 +79,7 @@ macro_rules! typeenum { fn from(s: $name) -> $ty { match s { $($name::$y => $x,)+ - $($name::$y1(u) => u,)? + $($($name::$y1(u) => u,)?)? $name::Unimplemented(u) => u, } } @@ -79,7 +92,7 @@ macro_rules! typeenum { { match self { $($name::$y => write!(f, stringify!($y))),+, - $($name::$y1(u) => write!(f, "{} ({})", stringify!($u), u),)? + $($($name::$y1(u) => write!(f, "{} ({})", stringify!($u), u),)?)? $name::Unimplemented(u) => write!(f, "unknown-{}-{}", stringify!($name), u) } From 21fcdae5c83e1ee64ffac0fca91d9f10e3317b98 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 5 Jul 2023 16:26:28 +0200 Subject: [PATCH 13/20] update docstring --- src/util/macros.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/util/macros.rs b/src/util/macros.rs index df956547..ec2aa539 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -39,10 +39,11 @@ /// } /// ); /// ``` -/// Note that match lines with ranges are separated by semi-colons, rather -/// than by commas. Range variants have a data field with the specified value. -/// Specifying an half-open range to the right or specifying the matches -/// exhaustively will disable the default `Unimplemented` variant. +/// Note that match lines with ranges are come in a separate block after +/// the block with single selector values. Range variants have a data +/// field with the specified value. Specifying an half-open range to the +/// right or specifying the matches exhaustively will disable the default +/// `Unimplemented` variant. #[macro_export] macro_rules! typeenum { ( $(#[$attr:meta])* From a782af6b3ae042fd94ab06768f92914551366ec8 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 11 Jul 2023 10:00:51 +0200 Subject: [PATCH 14/20] typeenum leftover --- src/bmp/message.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/bmp/message.rs b/src/bmp/message.rs index 652e0bb4..e3769fc2 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -75,14 +75,16 @@ typeenum!( /// Types of BMP messages as defined in /// [RFC7854](https://datatracker.ietf.org/doc/html/rfc7854). MessageType, u8, - 0 => RouteMonitoring, - 1 => StatisticsReport, - 2 => PeerDownNotification, - 3 => PeerUpNotification, - 4 => InitiationMessage, - 5 => TerminationMessage, - 6 => RouteMirroring, -); + { + 0 => RouteMonitoring, + 1 => StatisticsReport, + 2 => PeerDownNotification, + 3 => PeerUpNotification, + 4 => InitiationMessage, + 5 => TerminationMessage, + 6 => RouteMirroring, + } + ); impl> AsRef<[u8]> for InitiationMessage { From b88a18c1bdfb95c8d8e405bf8b88fd4681256abd Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 11 Jul 2023 10:07:45 +0200 Subject: [PATCH 15/20] correct indentation --- src/bmp/message.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bmp/message.rs b/src/bmp/message.rs index e3769fc2..a1bebdbd 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -529,9 +529,11 @@ typeenum!( typeenum!( /// Specify which RIB the contents of a message originated from. RibType, u8, - {0 => AdjRibIn, - 1 => AdjRibOut -}); + { + 0 => AdjRibIn, + 1 => AdjRibOut + } +); //--- Specific Message types ------------------------------------------------- From 6d3122b192299bd3c867bf127e59297dc68b3f4f Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 11 Jul 2023 16:44:44 +0200 Subject: [PATCH 16/20] fix doc --- src/util/macros.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/util/macros.rs b/src/util/macros.rs index ec2aa539..a1b64421 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -24,6 +24,10 @@ /// You can also specify ranges as like so: /// /// ```rust +/// # #[cfg(feature = "serde")] +/// # use serde::{Serialize, Deserialize}; +/// # #[macro_use] extern crate routecore; +/// # fn main() { /// typeenum!( /// PeerType, u8, /// { @@ -31,13 +35,14 @@ /// 1 => RdInstance, /// 2 => LocalInstance, /// 3 => LocalRibInstance, -/// } +/// }, /// { /// 4..=250 => Unassigned, /// 251..=254 => Experimental, -/// 255 => Reserved +/// 255 => Reserved, /// } /// ); +/// # } /// ``` /// Note that match lines with ranges are come in a separate block after /// the block with single selector values. Range variants have a data From 92dc00798badb0718e930d288ab43e13bfdfd021 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Fri, 14 Jul 2023 13:06:30 +0200 Subject: [PATCH 17/20] some impls on bmp message --- src/bmp/message.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/bmp/message.rs b/src/bmp/message.rs index a1bebdbd..6ddf68fe 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -60,6 +60,7 @@ impl Error for MessageError { } /// additional payload. The payload often comprises one or multiple /// [`bgp::Message`](crate::bgp::Message)s. +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum Message> { RouteMonitoring(RouteMonitoring), StatisticsReport(StatisticsReport), @@ -70,6 +71,11 @@ pub enum Message> { RouteMirroring(RouteMirroring), } +impl, OtherOcts: AsRef<[u8]>> PartialEq> for Message { + fn eq(&self, other: &Message) -> bool { + self.as_ref().eq(other.as_ref()) + } +} typeenum!( /// Types of BMP messages as defined in @@ -83,8 +89,8 @@ typeenum!( 4 => InitiationMessage, 5 => TerminationMessage, 6 => RouteMirroring, - } - ); + } +); impl> AsRef<[u8]> for InitiationMessage { @@ -540,6 +546,9 @@ typeenum!( /// Route Monitoring message. + +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug)] pub struct RouteMonitoring> { octets: Octets @@ -589,6 +598,8 @@ impl RouteMonitoring { } /// Statistics Report message. + +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct StatisticsReport { octets: Octs, } @@ -649,7 +660,9 @@ impl Debug for StatisticsReport { } -/// Peer Down Notification. +/// Peer Down Notification. +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug)] pub struct PeerDownNotification> { octets: Octets, } @@ -757,6 +770,8 @@ impl PeerDownNotification { /// Peer Up Notification. +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug)] pub struct PeerUpNotification> { octets: Octets, } @@ -939,6 +954,8 @@ impl PeerUpNotification { /// Initiation Message. +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug)] pub struct InitiationMessage> { octets: Octets, } @@ -978,6 +995,7 @@ impl InitiationMessage { /// Termination message. +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct TerminationMessage> { octets: Octets, } @@ -1022,6 +1040,7 @@ impl TerminationMessage { /// RouteMirroring. /// /// NB: Not well tested/supported at this moment! +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct RouteMirroring { octets: Octs, } From 795106953a656a9181813868c35b91c77104e0dc Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 17 Jul 2023 15:10:59 +0200 Subject: [PATCH 18/20] Fix Display for Unrecognized Wellknown communities --- src/bgp/communities.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bgp/communities.rs b/src/bgp/communities.rs index 330436af..611da1cf 100644 --- a/src/bgp/communities.rs +++ b/src/bgp/communities.rs @@ -310,7 +310,7 @@ macro_rules! wellknown { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match self { $($name::$var => write!(f, $pprim),)+ - $name::Unrecognized(n) => write!(f, "0x{:08X}", n) + $name::Unrecognized(n) => write!(f, "0xFFFF{:04X}", n) } } } @@ -1410,4 +1410,12 @@ mod tests { assert!(>::try_from(0xffff0001).is_ok()); assert!(>::try_from(0x0fff0001).is_err()); } + + #[test] + fn to_string_and_back() { + let c: Community = [0xFF, 0xFF, 0xFF, 0x05].into(); + let s = c.to_string(); + let c2 = Community::from_str(&s).unwrap(); + assert_eq!(c, c2); + } } From 756ea3f17f2ecd67943cec7bc181c8a5c0321cf5 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 19 Jul 2023 13:09:31 +0200 Subject: [PATCH 19/20] Enhanced BGP NOTIFICATION support (#35) - (Better and more complete) parsing and creation of NOTIFICATION PDUs, which now includes all the error (sub)codes; - Adds a method `contains` on Prefix, to check whether an `IpAddr` lies within a certain Prefix. --- src/addr.rs | 37 ++++ src/bgp/message/nlri.rs | 8 +- src/bgp/message/notification.rs | 365 ++++++++++++++++++++++++++++++-- src/bgp/message/open.rs | 2 +- src/bgp/message/update.rs | 6 +- src/bmp/message.rs | 8 +- 6 files changed, 399 insertions(+), 27 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 36eb58dd..bf96312a 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -404,6 +404,11 @@ impl Prefix { self.bits.into_int() == other.bits.into_int() & !(u128::MAX >> self.len()) } + + /// Returns `true` if the prefix contains `addr`. + pub fn contains(self, addr: IpAddr) -> bool { + self.min_addr() <= addr && addr <= self.max_addr() + } } //--- PartialOrd and Ord @@ -1383,4 +1388,36 @@ mod test { fn clear_host_of_zero_len_prefix() { assert_eq!(Bits(0), Bits(12345).clear_host(0)); } + + #[test] + fn prefix_contains() { + fn test(prefix: &str, addr: &str, expected: bool) { + let p = Prefix::from_str(prefix).unwrap(); + let a = IpAddr::from_str(addr).unwrap(); + assert_eq!(p.contains(a), expected); + } + + for i in [ + ("10.0.0.0/8", "10.0.0.0", true), + ("10.0.0.0/8", "10.1.1.1", true), + ("10.0.0.0/8", "10.255.255.255", true), + ("10.0.0.0/32", "10.0.0.0", true), + ("10.0.0.0/8", "192.168.1.1", false), + ("10.0.0.0/8", "2001:0db8::1", false), + + ("2001:0db8::/32", "2001:0db8::0", true), + ("2001:0db8::/32", "2001:0db8::1", true), + ("2001:0db8::/32", "10.0.0.1", false), + + ("::0/120", "0.0.0.10", false), + ("0.0.0.0/24", "::1", false), + + ("0.0.0.1/32", "::1", false), + ("0.0.0.1/32", "0.0.0.1", true), + ("::1/128", "0.0.0.1", false), + ("::1/128", "::1", true), + ] { + test(i.0, i.1, i.2); + } + } } diff --git a/src/bgp/message/nlri.rs b/src/bgp/message/nlri.rs index 7ba551f4..368a1446 100644 --- a/src/bgp/message/nlri.rs +++ b/src/bgp/message/nlri.rs @@ -24,13 +24,13 @@ impl NextHop { (16, AFI::Ipv6, SAFI::Unicast | SAFI::MplsUnicast) => //NextHop::Ipv6 parser.advance(16)?, - (32, AFI::Ipv6, SAFI::Unicast) => + (32, AFI::Ipv6, SAFI::Unicast | SAFI::Multicast) => //NextHop::Ipv6LL parser.advance(16 + 16)?, (24, AFI::Ipv6, SAFI::MplsVpnUnicast) => //NextHop::Ipv6MplsVpnUnicast parser.advance(8 + 16)?, - (4, AFI::Ipv4, SAFI::Unicast | SAFI::MplsUnicast ) => + (4, AFI::Ipv4, SAFI::Unicast | SAFI::Multicast | SAFI::MplsUnicast ) => //NextHop::Ipv4 parser.advance(4)?, (12, AFI::Ipv4, SAFI::MplsVpnUnicast) => @@ -63,7 +63,7 @@ impl NextHop { let res = match (len, afi, safi) { (16, AFI::Ipv6, SAFI::Unicast | SAFI::MplsUnicast) => NextHop::Ipv6(parse_ipv6addr(parser)?), - (32, AFI::Ipv6, SAFI::Unicast) => + (32, AFI::Ipv6, SAFI::Unicast | SAFI::Multicast) => NextHop::Ipv6LL( parse_ipv6addr(parser)?, parse_ipv6addr(parser)? @@ -73,7 +73,7 @@ impl NextHop { RouteDistinguisher::parse(parser)?, parse_ipv6addr(parser)? ), - (4, AFI::Ipv4, SAFI::Unicast | SAFI::MplsUnicast ) => + (4, AFI::Ipv4, SAFI::Unicast | SAFI::Multicast | SAFI::MplsUnicast ) => NextHop::Ipv4(parse_ipv4addr(parser)?), (12, AFI::Ipv4, SAFI::MplsVpnUnicast) => NextHop::Ipv4MplsVpnUnicast( diff --git a/src/bgp/message/notification.rs b/src/bgp/message/notification.rs index 66f38cb4..4ab0dc94 100644 --- a/src/bgp/message/notification.rs +++ b/src/bgp/message/notification.rs @@ -1,6 +1,13 @@ -use crate::bgp::message::Header; +use log::warn; +use octseq::{Octets, OctetsBuilder, Parser}; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; // for typeenum! macro + +use crate::bgp::message::{Header, MsgType}; +use crate::typeenum; use crate::util::parser::ParseError; -use octseq::{Octets, Parser}; + +use std::fmt; const COFF: usize = 19; // XXX replace this with .skip()'s? @@ -29,13 +36,6 @@ impl AsRef<[u8]> for NotificationMessage { } -// to properly enumify the codes, check: -// RFCs -// 4271 -// 4486 -// 8203 -// 9003 - /// BGP NOTIFICATION Message. /// /// @@ -49,12 +49,31 @@ impl NotificationMessage { Self { octets: s } } - pub fn code(&self) -> u8 { - self.octets.as_ref()[COFF] + pub fn code(&self) -> ErrorCode { + self.octets.as_ref()[COFF].into() } - pub fn subcode(&self) -> u8 { - self.octets.as_ref()[COFF+1] + /// Get the (sub)code and optional data for this Notification message. + pub fn details(&self) -> Details { + let subraw = self.octets.as_ref()[COFF+1]; + use ErrorCode as E; + use Details as S; + match self.code() { + E::Reserved => S::Reserved, + E::MessageHeaderError => S::MessageHeaderError(subraw.into()), + E::OpenMessageError => S::OpenMessageError(subraw.into()), + E::UpdateMessageError => S::UpdateMessageError(subraw.into()), + E::HoldTimerExpired => S::HoldTimerExpired, + E::FiniteStateMachineError => { + S::FiniteStateMachineError(subraw.into()) + } + E::Cease => S::Cease(subraw.into()), + E::RouteRefreshMessageError => { + S::RouteRefreshMessageError(subraw.into()) + } + E::Unimplemented(code) => S::Unimplemented(code, subraw) + } + } pub fn data(&self) -> Option<&[u8]> { @@ -82,11 +101,9 @@ impl NotificationMessage { let pos = parser.pos(); let hdr = Header::parse(parser)?; - // TODO implement enums for codes/subcodes let _code = parser.parse_u8()?; let _subcode = parser.parse_u8()?; - // Now, their might be variable length data from the current position // to the end of the message. There is no length field. // The data depends on the code/subscode. @@ -99,6 +116,283 @@ impl NotificationMessage { } } +//------------ Builder ------------------------------------------------------- + +pub struct NotificationBuilder { + _target: Target, +} + +use core::convert::Infallible; +impl NotificationBuilder +where + Infallible: From<::AppendError>, +{ + pub fn from_target>( + mut target: Target, + subcode: S, + data: Option + ) -> Result + where S: Into
+ { + let mut h = Header::<&[u8]>::new(); + h.set_length(21 + + u16::try_from( + data.as_ref().map_or(0, |d| d.as_ref().len()) + ).map_err(|_| NotificationBuildError::LargePdu)? + ); + h.set_type(MsgType::Notification); + + let _ = target.append_slice(h.as_ref()); + // XXX or do we want + // target.append_slice( + // h.as_ref()).map_err(|_| NotificationBuildError::ShortBuf)?; + + let _ = target.append_slice(&subcode.into().raw()); + + if let Some(data) = data { + let _ = target.append_slice(data.as_ref()); + } + + Ok(target) + } + +} + +impl NotificationBuilder> { + pub fn new_vec>( + subcode: S, + data: Option + ) -> Result, NotificationBuildError> + where S: Into
+ { + Self::from_target(Vec::with_capacity(21), /*code,*/ subcode, data) + } + + pub fn new_vec_nodata(subcode: S) -> Vec + where S: Into
+ { + // Without data (of arbitrary length) this is infallible for Vecs + // so we simply unwrap + Self::from_target( + Vec::with_capacity(21), + subcode, + Option::>::None + ).unwrap() + } +} + +#[derive(Debug)] +pub enum NotificationBuildError { + ShortBuf, + LargePdu, +} + +impl From for NotificationBuildError { + fn from(_: octseq::ShortBuf) -> Self { + NotificationBuildError::ShortBuf + } +} + +impl fmt::Display for NotificationBuildError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + NotificationBuildError::ShortBuf => octseq::ShortBuf.fmt(f), + NotificationBuildError::LargePdu => { + f.write_str("PDU size exceeded") + } + } + } +} + + + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Details { + Reserved, + MessageHeaderError(MessageHeaderSubcode), + OpenMessageError(OpenMessageSubcode), + UpdateMessageError(UpdateMessageSubcode), + HoldTimerExpired, // no subcodes, should be 0 + FiniteStateMachineError(FiniteStateMachineSubcode), + Cease(CeaseSubcode), + RouteRefreshMessageError(RouteRefreshMessageSubcode), + Unimplemented(u8, u8), +} + +impl Details { + pub fn raw(&self) -> [u8; 2] { + use Details as S; + use ErrorCode as E; + match self { + S::Reserved => [0, 0], + S::MessageHeaderError(sub) => { + [E::MessageHeaderError.into(), (*sub).into()] + } + S::OpenMessageError(sub) => { + [E::OpenMessageError.into(), (*sub).into()] + } + S::UpdateMessageError(sub) => { + [E::UpdateMessageError.into(), (*sub).into()] + } + S::HoldTimerExpired => { + [E::HoldTimerExpired.into(), 0] + } + S::FiniteStateMachineError(sub) => { + [E::FiniteStateMachineError.into(), (*sub).into()] + } + S::Cease(sub) => { + [E::Cease.into(), (*sub).into()] + } + S::RouteRefreshMessageError(sub) => { + [E::RouteRefreshMessageError.into(), (*sub).into()] + } + S::Unimplemented(code, subcode) => { + warn!("serializing unimplemented \ + Notification code/subcode {}/{}", + code, subcode); + [*code, *subcode] + } + } + } + +} + +//------------ Codes and Subcodes -------------------------------------------- + +// See RFCs 4271 4486 8203 9003 + +typeenum!( + ErrorCode, u8, + { + 0 => Reserved, + 1 => MessageHeaderError, + 2 => OpenMessageError, + 3 => UpdateMessageError, + 4 => HoldTimerExpired, + 5 => FiniteStateMachineError, + 6 => Cease, + 7 => RouteRefreshMessageError, + } +); + +typeenum!( + MessageHeaderSubcode, u8, + { + 0 => Unspecific, + 1 => ConnectionNotSynchronized, + 2 => BadMessageLength, // data: u16 bad length + 3 => BadMessageType, // data: u8 bad type + } +); + +impl From for Details { + fn from(s: MessageHeaderSubcode) -> Self { + Details::MessageHeaderError(s) + } +} + +typeenum!( + OpenMessageSubcode, u8, + { + 0 => Unspecific, + 1 => UnsupportedVersionNumber, // only one with data: u16 version number + 2 => BadPeerAs, + 3 => BadBgpIdentifier, + 4 => UnsupportedOptionalParameter, + 5 => Deprecated5, // was: authentication failure + 6 => UnacceptableHoldTime, + 7 => UnsupportedCapability, + 8 => Deprecated8, // 8-10 deprecated because of 'improper use', rfc9234 + 9 => Deprecated9, + 10 => Deprecated10, + 11 => RoleMismatch, + } +); + +impl From for Details { + fn from(s: OpenMessageSubcode) -> Self { + Details::OpenMessageError(s) + } +} + +typeenum!( + UpdateMessageSubcode, u8, + { + 0 => Unspecific, + 1 => MalformedAttributeList, // no data + 2 => UnrecognizedWellknownAttribute, // data: unrecognized attribute + 3 => MissingWellknownAttribute, // data: typecode of missing attribute + 4 => AttributeFlagsError, // data: erroneous attribute (t, l, v) + 5 => AttributeLengthError, // data: erroneous attribute (t, l, v) + 6 => InvalidOriginAttribute, // data: erroneous attribute (t, l, v) + 7 => Deprecated7, // was: AS routing loop, rfc1771 + 8 => InvalidNextHopAttribute, // data: erroneous attribute (t, l, v) + 9 => OptionalAttributeError, // data: erroneous attribute (t, l, v) + 10 => InvalidNetworkField, // no data + 11 => MalformedAsPath, // no data + } +); + +impl From for Details { + fn from(s: UpdateMessageSubcode) -> Self { + Details::UpdateMessageError(s) + } +} + +typeenum!( + FiniteStateMachineSubcode, u8, + { + 0 => UnspecifiedError, + 1 => UnexpectedMessageInOpenSentState, // data: u8 of message type + 2 => UnexpectedMessageInOpenConfirmState, // data: u8 of message type + 3 => UnexpectedMessageInEstablishedState, // data: u8 of message type + } +); + +impl From for Details { + fn from(s: FiniteStateMachineSubcode) -> Self { + Details::FiniteStateMachineError(s) + } +} + +typeenum!( + CeaseSubcode, u8, + { + 0 => Reserved, + 1 => MaximumPrefixesReached, + 2 => AdministrativeShutdown, + 3 => PeerDeconfigured, + 4 => AdministrativeReset, + 5 => ConnectionRejected, + 6 => OtherConfigurationChange, + 7 => ConnectionCollisionResolution, + 8 => OutOfResources, + 9 => HardReset, + 10 => BfdDown, + } +); + +impl From for Details { + fn from(s: CeaseSubcode) -> Self { + Details::Cease(s) + } +} + + +typeenum!( + RouteRefreshMessageSubcode, u8, + { + 0 => Reserved, + 1 => InvalidMessageLength, // data: complete RouteRefresh message + } +); + +impl From for Details { + fn from(s: RouteRefreshMessageSubcode) -> Self { + Details::RouteRefreshMessageError(s) + } +} + //--- Tests ------------------------------------------------------------------ #[cfg(test)] @@ -117,9 +411,46 @@ mod tests { Message::from_octets(&buf, None).unwrap().try_into().unwrap(); assert_eq!(notification.length(), 21); - assert_eq!(notification.code(), 6); - assert_eq!(notification.subcode(), 4); + assert_eq!(notification.code(), ErrorCode::Cease); + assert_eq!( + notification.details(), + Details::Cease(CeaseSubcode::AdministrativeReset) + ); assert_eq!(notification.data(), None); } + + #[test] + fn build_nodata() { + let msg = NotificationBuilder::new_vec( + CeaseSubcode::OtherConfigurationChange, + Some(Vec::new()) + ).unwrap(); + + let parsed = NotificationMessage::from_octets(&msg).unwrap(); + assert_eq!(parsed.code(), ErrorCode::Cease); + assert_eq!( + parsed.details(), + CeaseSubcode::OtherConfigurationChange.into() + ); + assert_eq!(parsed.length(), 21); + assert_eq!(parsed.as_ref().len(), 21); + + let msg2 = NotificationBuilder::new_vec_nodata( + CeaseSubcode::OtherConfigurationChange + ); + assert_eq!(msg, msg2); + } + + #[test] + fn build_with_data() { + let msg = NotificationBuilder::new_vec( + MessageHeaderSubcode::BadMessageType, Some([12]) + ).unwrap(); + + assert_eq!(msg.len(), 22); + + let parsed = NotificationMessage::from_octets(&msg).unwrap(); + assert_eq!(parsed.length(), 22); + } } diff --git a/src/bgp/message/open.rs b/src/bgp/message/open.rs index 519e5e04..1356b396 100644 --- a/src/bgp/message/open.rs +++ b/src/bgp/message/open.rs @@ -507,7 +507,7 @@ impl Capability { // Also see // -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Capability { octets: Octs, } diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 466c8144..646d57f7 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -811,8 +811,8 @@ impl<'a> PathAttribute<'a, [u8]> { } }, PathAttributeType::Aggregator => { - let mut pp = parser.parse_parser(len)?; - Aggregator::check(&mut pp, config)?; + let pp = parser.parse_parser(len)?; + Aggregator::check(&pp, config)?; }, PathAttributeType::Communities => { let mut pp = parser.parse_parser(len)?; @@ -1392,7 +1392,7 @@ impl Iterator for LargeCommunityIter { } impl Aggregator { - fn check(parser: &mut Parser<[u8]>, config: SessionConfig) + fn check(parser: &Parser<[u8]>, config: SessionConfig) -> Result<(), ParseError> { let len = parser.remaining(); // XXX is this always correct? diff --git a/src/bmp/message.rs b/src/bmp/message.rs index 6ddf68fe..af6393d2 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -1820,6 +1820,8 @@ mod tests { #[test] fn peer_down_notification() { + use crate::bgp::message::notification::CeaseSubcode; + // BMP PeerDownNotification type 3, containing a BGP NOTIFICATION. let buf = vec![ 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x00, 0x80, @@ -1838,8 +1840,10 @@ mod tests { assert_eq!(bmp.fsm(), None); let bgp_notification = bmp.notification().unwrap(); - assert_eq!(bgp_notification.code(), 6); - assert_eq!(bgp_notification.subcode(), 2); + assert_eq!( + bgp_notification.details(), + CeaseSubcode::AdministrativeShutdown.into() + ); } From 3c55f4e0a21cf373df40b9a40a8cdde1a4f273d9 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 19 Jul 2023 13:13:23 +0200 Subject: [PATCH 20/20] Update Changelog --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index ed72f9f1..1ebe65e0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -34,6 +34,10 @@ New reason about than the wireformat. `HopPath` replaces `AsPathBuilder`. ([#23]) +* Added `fn contains` to check whether an `std::net::IpAddr` lies within a + `addr::Prefix`. ([#35]) + +* Better parsing and creation of BGP NOTIFICATION messages. ([#35]) Bug fixes @@ -47,6 +51,7 @@ Other changes [#22]: https://github.com/NLnetLabs/routecore/pull/22 [#23]: https://github.com/NLnetLabs/routecore/pull/23 [#24]: https://github.com/NLnetLabs/routecore/pull/24 +[#35]: https://github.com/NLnetLabs/routecore/pull/35 ## 0.2.0