From d16d5cc98ef2ddc2947cc804bca6dd4f7ee4adcf Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 6 Feb 2024 10:04:55 +0100 Subject: [PATCH 01/96] WIP PathAttributesBuilder --- src/bgp/message/update_builder.rs | 15 ++- src/bgp/path_attributes.rs | 149 +++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 602e5029..04ff8553 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -25,7 +25,7 @@ use crate::bgp::path_attributes::{ MultiExitDisc, NextHop as NextHopAttribute, Origin, - PathAttribute, PathAttributeType + PathAttribute, PathAttributesBuilder, PathAttributeType }; //------------ UpdateBuilder ------------------------------------------------- @@ -65,6 +65,14 @@ where Target: octseq::Truncate }) } + pub fn from_attributes_builder(attributes: PathAttributesBuilder) + -> UpdateBuilder> + { + let mut res = UpdateBuilder::>::new_vec(); + res.attributes = attributes.into_inner(); + res + } + /// Creates an UpdateBuilder with Path Attributes from an UpdateMessage /// /// @@ -595,6 +603,11 @@ impl UpdateBuilder )?) } + pub fn into_attributes(self) -> BTreeMap { + self.attributes + } + + // Check whether the combination of NLRI and attributes would produce a // valid UPDATE pdu. fn is_valid(&self) -> Result<(), ComposeError> { diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index cbe9522f..4b48335b 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::fmt::{self, Display}; use std::net::Ipv4Addr; @@ -7,8 +8,10 @@ use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; use crate::asn::Asn; use crate::bgp::aspath::HopPath; use crate::bgp::message::{ + SessionConfig, + UpdateMessage, nlri::FixedNlriIter, - SessionConfig + update_builder::{ComposeError, UpdateBuilder}, }; use crate::bgp::message::update_builder::{ MpReachNlriBuilder, @@ -433,6 +436,150 @@ macro_rules! path_attributes { } } +/* +//------------ MaterializedPathAttributes ------------------------------------ + + paste! { + #[derive(Clone, Debug, Eq, Hash, PartialEq)] + pub struct MaterializedPathAttributes { + $( pub [<$name:snake>] : Option<$name> ),+, + pub unknown_transitives: Vec, + } + } + + impl MaterializedPathAttributes { + + pub fn from_message(msg: super::message::UpdateMessage<()>) -> Self { + todo!() + } + } + + impl From for MaterializedPathAttributes + where I: Iterator + { + fn from(_iter: I) -> Self { + todo!() + } + } +*/ + +type AttributeMap = BTreeMap; + +pub struct PathAttributesBuilder { + attributes: AttributeMap, +} + +impl PathAttributesBuilder { + + pub fn empty() -> Self { + Self { + attributes: BTreeMap::new() + } + } + + pub fn from_update_pdu(pdu: &UpdateMessage) + -> Result + where + for<'a> Vec: OctetsFrom> + { + let mut res = Self::empty(); + for pa in pdu.path_attributes()? { + if let Ok(pa) = pa { + if pa.type_code() != PathAttributeType::MpReachNlri + && pa.type_code() != PathAttributeType::MpUnreachNlri + { + if let PathAttributeType::Invalid(n) = pa.type_code() { + warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); + } + res.add_attribute(pa.to_owned()?)?; + } + } else { + return Err(ComposeError::InvalidAttribute); + } + } + Ok(res) + } + + pub fn merge(&mut self, other: &Self) -> Result<(), ComposeError> { + for val in other.attributes.values().cloned() { + self.add_attribute(val)?; + } + Ok(()) + } + + pub fn into_inner(self) -> AttributeMap { + self.attributes + } + + pub fn into_update_builder(self) -> UpdateBuilder> { + UpdateBuilder::>::from_attributes_builder(self) + } + + pub fn from_update_builder(builder: UpdateBuilder) -> Self { + Self { + attributes: builder.into_attributes() + } + } + + pub fn add_attribute(&mut self, pa: PathAttribute) + -> Result<(), ComposeError> + { + if let PathAttribute::Invalid(..) = pa { + warn!( + "adding Invalid attribute to UpdateBuilder: {}", + &pa.type_code() + ); + } + if let Some(existing_pa) = self.attributes.get_mut(&pa.type_code()) { + *existing_pa = pa; + } else { + self.attributes.insert(pa.type_code(), pa); + } + + Ok(()) + } + + //-------- Specific path attribute methods ------------------------------- + // + pub fn set_origin(&mut self, origin: OriginType) + -> Result<(), ComposeError> + { + self.add_attribute(Origin::new(origin).into()) + } + + pub fn set_aspath(&mut self , aspath: HopPath) + -> Result<(), ComposeError> + { + // XXX there should be a HopPath::compose_len really, instead of + // relying on .to_as_path() first. + if let Ok(wireformat) = aspath.to_as_path::>() { + if wireformat.compose_len() > u16::MAX.into() { + return Err(ComposeError::AttributeTooLarge( + PathAttributeType::AsPath, + wireformat.compose_len() + )); + } + } else { + return Err(ComposeError::InvalidAttribute) + } + + self.add_attribute(AsPath::new(aspath).into()) + } + + pub fn set_multi_exit_disc(&mut self, med: MultiExitDisc) + -> Result<(), ComposeError> + { + self.add_attribute(med.into()) + } + + pub fn set_local_pref(&mut self, local_pref: LocalPref) + -> Result<(), ComposeError> + { + self.add_attribute(local_pref.into()) + } +} + + //------------ PathAttributeType --------------------------------------------- #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] From 3adb5855e536d521e62ed76b155c2e4199406309 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 6 Feb 2024 10:12:25 +0100 Subject: [PATCH 02/96] Use type alias AttributesMap everywhere --- src/bgp/message/update_builder.rs | 9 ++++----- src/bgp/path_attributes.rs | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 04ff8553..11e98bb8 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -1,6 +1,5 @@ use std::fmt; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use std::collections::BTreeMap; use bytes::BytesMut; use octseq::{FreezeBuilder, Octets, OctetsBuilder, OctetsFrom, OctetsInto, ShortBuf}; @@ -25,7 +24,7 @@ use crate::bgp::path_attributes::{ MultiExitDisc, NextHop as NextHopAttribute, Origin, - PathAttribute, PathAttributesBuilder, PathAttributeType + AttributesMap, PathAttribute, PathAttributesBuilder, PathAttributeType }; //------------ UpdateBuilder ------------------------------------------------- @@ -38,7 +37,7 @@ pub struct UpdateBuilder { withdrawals: Vec>>, addpath_enabled: Option, // for conventional nlri (unicast v4) // attributes: - attributes: BTreeMap, + attributes: AttributesMap } impl UpdateBuilder { @@ -61,7 +60,7 @@ where Target: octseq::Truncate announcements: Vec::new(), withdrawals: Vec::new(), addpath_enabled: None, - attributes: BTreeMap::new(), + attributes: AttributesMap::new() }) } @@ -603,7 +602,7 @@ impl UpdateBuilder )?) } - pub fn into_attributes(self) -> BTreeMap { + pub fn into_attributes(self) -> AttributesMap { self.attributes } diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 4b48335b..6c1f36bb 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -463,10 +463,10 @@ macro_rules! path_attributes { } */ -type AttributeMap = BTreeMap; +pub type AttributesMap = BTreeMap; pub struct PathAttributesBuilder { - attributes: AttributeMap, + attributes: AttributesMap, } impl PathAttributesBuilder { @@ -507,7 +507,7 @@ impl PathAttributesBuilder { Ok(()) } - pub fn into_inner(self) -> AttributeMap { + pub fn into_inner(self) -> AttributesMap { self.attributes } From 125948ff6a2a73338e9d1d888007f7d23461e5b6 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 6 Feb 2024 10:15:02 +0100 Subject: [PATCH 03/96] Add fn remove_attribute --- src/bgp/path_attributes.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 6c1f36bb..0ec0fb4b 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -539,6 +539,12 @@ impl PathAttributesBuilder { Ok(()) } + pub fn remove_attribute(&mut self, pat: PathAttributeType) + -> Option + { + self.attributes.remove(&pat) + } + //-------- Specific path attribute methods ------------------------------- // pub fn set_origin(&mut self, origin: OriginType) From 96364126ad2a56a2a35fd764a7a44a66b8a6bae7 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Mon, 12 Feb 2024 17:52:37 +0100 Subject: [PATCH 04/96] rough outline RouteWorkshop --- src/bgp/aspath.rs | 4 + src/bgp/communities.rs | 2 +- src/bgp/message/update.rs | 2 +- src/bgp/message/update_builder.rs | 28 +- src/bgp/mod.rs | 3 + src/bgp/path_attributes.rs | 511 ++++++++++++++++------ src/bgp/workshop/afisafi_nlri.rs | 123 ++++++ src/bgp/workshop/mod.rs | 2 + src/bgp/workshop/route.rs | 694 ++++++++++++++++++++++++++++++ 9 files changed, 1216 insertions(+), 153 deletions(-) create mode 100644 src/bgp/workshop/afisafi_nlri.rs create mode 100644 src/bgp/workshop/mod.rs create mode 100644 src/bgp/workshop/route.rs diff --git a/src/bgp/aspath.rs b/src/bgp/aspath.rs index 95a7247d..e7561c22 100644 --- a/src/bgp/aspath.rs +++ b/src/bgp/aspath.rs @@ -83,6 +83,10 @@ impl HopPath { self.hops.iter().any(|h| h == hop) } + pub fn get_hop(&self, index: usize) -> Option<&OwnedHop> { + self.hops.get(index) + } + /// Returns the number of [`Hop`]s in this HopPath. /// /// Note that this counts AS_SETs, AS_CONFED_SEQUENCEs and AS_CONFED_SETs diff --git a/src/bgp/communities.rs b/src/bgp/communities.rs index 4784e5b0..5e73f412 100644 --- a/src/bgp/communities.rs +++ b/src/bgp/communities.rs @@ -483,7 +483,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]); +pub struct StandardCommunity(pub(crate) [u8; 4]); impl StandardCommunity { pub fn new(asn: Asn16, tag: Tag) -> StandardCommunity { diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 169aaebe..9985007d 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -750,7 +750,7 @@ impl UpdateMessage { fn _communities>(&self) -> Result, T>>, ParseError> { - if let Some(WireformatPathAttribute::Communities(epa)) = self.path_attributes()?.get(PathAttributeType::Communities) { + if let Some(WireformatPathAttribute::StandardCommunities(epa)) = self.path_attributes()?.get(PathAttributeType::StandardCommunities) { let mut p = epa.value_into_parser(); Ok(Some(CommunityIter::new(p.parse_octets(p.remaining())?))) } else { diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 11e98bb8..1cc310f1 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -17,7 +17,7 @@ use crate::util::parser::ParseError; use crate::bgp::path_attributes::{ AsPath, - Communities, + StandardCommunities, LocalPref, MpReachNlri, MpUnreachNlri, @@ -396,15 +396,15 @@ where Target: octseq::Truncate pub fn add_community(&mut self, community: StandardCommunity) -> Result<(), ComposeError> { - if !self.attributes.contains_key(&PathAttributeType::Communities) { - self.add_attribute(Communities::new( + if !self.attributes.contains_key(&PathAttributeType::StandardCommunities) { + self.add_attribute(StandardCommunities::new( StandardCommunitiesBuilder::new() ).into())?; } - let pa = self.attributes.get_mut(&PathAttributeType::Communities) + let pa = self.attributes.get_mut(&PathAttributeType::StandardCommunities) .unwrap(); // Just added it, so we know it is there. - if let PathAttribute::Communities(ref mut pa) = pa { + if let PathAttribute::StandardCommunities(ref mut pa) = pa { let builder = pa.as_mut(); builder.add_community(community); Ok(()) @@ -606,6 +606,16 @@ impl UpdateBuilder self.attributes } + pub fn attributes(&self) -> &AttributesMap { + &self.attributes + } + + pub fn from_map(&self, map: &mut AttributesMap) -> PathAttributesBuilder { + let mut pab = PathAttributesBuilder::empty(); + pab.append(map); + pab + } + // Check whether the combination of NLRI and attributes would produce a // valid UPDATE pdu. @@ -1337,7 +1347,7 @@ impl StandardCommunitiesBuilder { } } - pub(crate) fn communities(&self) -> &Vec { + pub fn communities(&self) -> &Vec { &self.communities } @@ -1349,7 +1359,7 @@ impl StandardCommunitiesBuilder { // } //} - pub(crate) fn add_community(&mut self, community: StandardCommunity) { + pub fn add_community(&mut self, community: StandardCommunity) { if !self.extended && self.len + 4 > 255 { self.extended = true; } @@ -1743,7 +1753,7 @@ mod tests { // .unwrap() ).unwrap(); } - builder.set_local_pref(LocalPref::new(123)).unwrap(); + builder.set_local_pref(LocalPref::new(crate::bgp::types::LocalPref(123))).unwrap(); builder.set_multi_exit_disc(MultiExitDisc::new(123)).unwrap(); (1..=300).for_each(|n| { builder.add_community(StandardCommunity::new(n.into(), Tag::new(123))).unwrap(); @@ -2109,7 +2119,7 @@ mod tests { builder.set_aspath(path).unwrap(); builder.set_multi_exit_disc(MultiExitDisc::new(1234)).unwrap(); - builder.set_local_pref(LocalPref::new(9876)).unwrap(); + builder.set_local_pref(LocalPref::new(crate::bgp::types::LocalPref(9876))).unwrap(); let msg = builder.into_message().unwrap(); msg.print_pcap(); diff --git a/src/bgp/mod.rs b/src/bgp/mod.rs index 036bd11b..66bc1267 100644 --- a/src/bgp/mod.rs +++ b/src/bgp/mod.rs @@ -6,4 +6,7 @@ pub mod path_attributes; pub mod types; pub mod message; + +pub mod workshop; + pub use crate::util::parser::ParseError; diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 0ec0fb4b..65a43005 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; use std::fmt::{self, Display}; -use std::net::Ipv4Addr; +use std::net::{IpAddr, Ipv4Addr}; use log::{debug, warn}; use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; @@ -18,11 +18,12 @@ use crate::bgp::message::update_builder::{ MpUnreachNlriBuilder, StandardCommunitiesBuilder }; +use crate::bgp::communities::StandardCommunity; use crate::bgp::types::{Afi, Safi, AfiSafi}; use crate::util::parser::{ParseError, parse_ipv4addr}; -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] pub struct Flags(u8); impl Flags { @@ -90,7 +91,7 @@ macro_rules! attribute { // TODO Serialize #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] - pub struct $name($data); + pub struct $name(pub(crate) $data); impl $name { pub fn new(data: $data) -> $name { $name(data) @@ -136,6 +137,307 @@ macro_rules! attribute { } } + +//------------ PathAttributesBuilder ----------------------------------------- + +pub type AttributesMap = BTreeMap; + +#[derive(Debug)] +pub struct PathAttributesBuilder { + attributes: AttributesMap, +} + +impl PathAttributesBuilder { + pub fn empty() -> Self { + Self { + attributes: BTreeMap::new() + } + } + + pub fn attributes(&self) -> &AttributesMap { + &self.attributes + } + + pub fn attributes_mut(&mut self) -> &mut AttributesMap { + &mut self.attributes + } + + pub fn set>( + &mut self, attr: A + ) { + self.attributes.insert(A::attribute_type(), attr.into()); + } + + pub fn get( + &self, + ) -> Option { + self.attributes + .get(&A::attribute_type()).and_then(|a| A::from_attribute(a.clone())) + } + + pub fn append(&mut self, other: &mut AttributesMap) { + self.attributes.append(other) + } + + pub fn merge(&mut self, other: &Self) -> Result<(), ComposeError> { + for val in other.attributes.values().cloned() { + self.add_attribute(val)?; + } + Ok(()) + } + + pub fn into_inner(self) -> AttributesMap { + self.attributes + } + + pub fn into_update_builder(self) -> UpdateBuilder> { + UpdateBuilder::>::from_attributes_builder(self) + } + + pub fn from_update_builder(builder: UpdateBuilder) -> Self { + Self { + attributes: builder.into_attributes() + } + } + + pub fn add_attribute(&mut self, pa: PathAttribute) + -> Result<(), ComposeError> + { + if let PathAttribute::Invalid(..) = pa { + warn!( + "adding Invalid attribute to UpdateBuilder: {}", + &pa.type_code() + ); + } + if let Some(existing_pa) = self.attributes.get_mut(&pa.type_code()) { + *existing_pa = pa; + } else { + self.attributes.insert(pa.type_code(), pa); + } + + Ok(()) + } + + pub fn remove_attribute(&mut self, pat: PathAttributeType) + -> Option + { + self.attributes.remove(&pat) + } + + pub fn from_update_pdu(pdu: &UpdateMessage) + -> Result + where + for<'a> Vec: OctetsFrom> + { + let mut res = Self::empty(); + for pa in pdu.path_attributes()? { + if let Ok(pa) = pa { + if pa.type_code() != PathAttributeType::MpReachNlri + && pa.type_code() != PathAttributeType::MpUnreachNlri + { + if let PathAttributeType::Invalid(n) = pa.type_code() { + warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); + } + res.add_attribute(pa.to_owned()?)?; + } + } else { + return Err(ComposeError::InvalidAttribute); + } + } + Ok(res) + } + + //-------- Specific path attribute methods ------------------------------- + // + pub fn set_origin(&mut self, origin: OriginType) + -> Result<(), ComposeError> + { + self.add_attribute(Origin::new(origin).into()) + } + + pub fn set_aspath(&mut self , aspath: HopPath) + -> Result<(), ComposeError> + { + // XXX there should be a HopPath::compose_len really, instead of + // relying on .to_as_path() first. + if let Ok(wireformat) = aspath.to_as_path::>() { + if wireformat.compose_len() > u16::MAX.into() { + return Err(ComposeError::AttributeTooLarge( + PathAttributeType::AsPath, + wireformat.compose_len() + )); + } + } else { + return Err(ComposeError::InvalidAttribute) + } + + self.add_attribute(AsPath::new(aspath).into()) + } + + pub fn prepend_to_aspath(&mut self, asn: Asn) -> Result<(), ComposeError> { + if let Some(PathAttribute::AsPath(as_path)) = self.attributes.get_mut(&PathAttributeType::AsPath) { + as_path.0.prepend( + asn, + ) + }; + + Ok(()) + } + + pub fn aspath(&self) -> Option { + self.attributes.get(&PathAttributeType::AsPath).and_then(|pa| + if let PathAttribute::AsPath(as_path) = pa { + Some(as_path.clone().inner()) + } else { + None + }) + } + + pub fn set_multi_exit_disc(&mut self, med: MultiExitDisc) + -> Result<(), ComposeError> + { + self.add_attribute(med.into()) + } + + pub fn set_local_pref(&mut self, local_pref: LocalPref) + -> Result<(), ComposeError> + { + self.add_attribute(local_pref.into()) + } +} + +impl From for PathAttributesBuilder { + fn from(value: AttributesMap) -> Self { + Self {attributes: value } + } +} + +// macro_rules! from_attributes_impl { +// ( +// $( $variant:ident($output_ty:ty), $attr:ty )+ +// ) => { + +// $( +// impl FromAttribute for $attr { +// type Output = $output_ty; + +// fn from_attribute(value: PathAttribute) -> Option<$output_ty> { +// if let PathAttribute::$variant(pa) = value { +// Some(pa.inner()) +// } else { +// None +// } +// } + +// fn attribute_type() -> PathAttributeType { +// PathAttributeType::$variant +// } +// } +// )+ +// } +// } + +// ident(ty) +// 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, +// 2 => AsPath(HopPath), Flags::WELLKNOWN, +// 3 => NextHop(Ipv4Addr), Flags::WELLKNOWN, +// 4 => MultiExitDisc(u32), Flags::OPT_NON_TRANS, +// 5 => LocalPref(u32), Flags::WELLKNOWN, +// 6 => AtomicAggregate(()), Flags::WELLKNOWN, +// 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, +// 8 => Communities(StandardCommunitiesBuilder), Flags::OPT_TRANS, +// 9 => OriginatorId(Ipv4Addr), Flags::OPT_NON_TRANS, +// 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, +// 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, +// 15 => MpUnreachNlri(MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, +// 16 => ExtendedCommunities(ExtendedCommunitiesList), Flags::OPT_TRANS, +// 17 => As4Path(HopPath), Flags::OPT_TRANS, +// 18 => As4Aggregator(AggregatorInfo), Flags::OPT_TRANS, +// 20 => Connector(Ipv4Addr), Flags::OPT_TRANS, +// 21 => AsPathLimit(AsPathLimitInfo), Flags::OPT_TRANS, +// //22 => PmsiTunnel(todo), Flags::OPT_TRANS, +// 25 => Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, +// 32 => LargeCommunities(LargeCommunitiesList), Flags::OPT_TRANS, +// // 33 => BgpsecAsPath, +// 35 => Otc(Asn), Flags::OPT_TRANS, +// //36 => BgpDomainPath(TODO), Flags:: , // https://datatracker.ietf.org/doc/draft-ietf-bess-evpn-ipvpn-interworking/06/ +// //40 => BgpPrefixSid(TODO), Flags::OPT_TRANS, // https://datatracker.ietf.org/doc/html/rfc8669#name-bgp-prefix-sid-attribute +// 128 => AttrSet(AttributeSet), Flags::OPT_TRANS, +// 255 => Reserved(ReservedRaw), Flags::OPT_TRANS, + + +// PathAttribute variant(output type), impl for Attribute +// from_attributes_impl!( +// AsPath(HopPath), crate::bgp::aspath::AsPath +// AsPath(HopPath), crate::bgp::aspath::HopPath +// NextHop(Ipv4Addr), crate::bgp::types::NextHop +// MultiExitDisc(u32), crate::bgp::message::update::MultiExitDisc +// Origin(OriginType), crate::bgp::types::OriginType +// LocalPref(u32), crate::bgp::message::update::LocalPref +// Communities(StandardCommunitiesBuilder), crate::bgp::message::update_builder::StandardCommunitiesBuilder +// As4Path(HopPath), crate::bgp::aspath::AsPath> +// AtomicAggregate(()), AtomicAggregate +// Aggregator(AggregatorInfo), AggregatorInfo +// OriginatorId(Ipv4Addr), OriginatorId +// ClusterList(ClusterIds), ClusterList +// MpReachNlri(MpReachNlriBuilder), MpReachNlriBuilder +// MpUnreachNlri(MpUnreachNlriBuilder), MpUnreachNlriBuilder +// ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList +// As4Aggregator(AggregatorInfo), As4Aggregator +// Connector(Ipv4Addr), Connector +// AsPathLimit(AsPathLimitInfo), AsPathLimitInfo +// Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunities +// LargeCommunities(LargeCommunitiesList), LargeCommunitiesList +// Otc(Asn), Otc +// ); + +// impl From> for PathAttribute { +// fn from(value: crate::bgp::aspath::AsPath) -> Self { +// PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value.to_hop_path())) +// } +// } + +// impl From>> for PathAttribute { +// fn from(value: crate::bgp::aspath::AsPath>) -> Self { +// PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value.to_hop_path())) +// } +// } + +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::NextHop) -> Self { +// if let crate::bgp::message::update::NextHop::Unicast(IpAddr::V4(ipv4)) = value { +// PathAttribute::NextHop(NextHop(ipv4)) +// } else { +// panic!("WHERE'S MY TRANSPARANT NEXTHOP IMPLEMENTATION!!@!@!?!?!?!?!?"); +// } +// } +// } + +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::MultiExitDisc) -> Self { +// PathAttribute::MultiExitDisc(crate::bgp::path_attributes::MultiExitDisc(value.0)) +// } +// } + +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::OriginType) -> Self { +// PathAttribute::Origin(crate::bgp::path_attributes::Origin(value)) +// } +// } + +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::LocalPref) -> Self { +// PathAttribute::LocalPref(crate::bgp::path_attributes::LocalPref(value.0)) +// } +// } + +// impl From for PathAttribute { +// fn from(value: crate::bgp::message::update_builder::StandardCommunitiesBuilder) -> Self { +// PathAttribute::Communities(crate::bgp::path_attributes::Communities(value)) +// } +// } + + macro_rules! path_attributes { ( $( @@ -145,7 +447,7 @@ macro_rules! path_attributes { //------------ PathAttribute ------------------------------------------------- - #[derive(Clone, Debug, Eq, PartialEq)] + #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum PathAttribute { $( $name($name) ),+, Unimplemented(UnimplementedPathAttribute), @@ -211,6 +513,20 @@ macro_rules! path_attributes { } } + $( + impl TryFrom for $name { + type Error = ComposeError; + + fn try_from(value: PathAttribute) -> Result<$name, ComposeError> { + if let PathAttribute::$name(pa) = value { + Ok(pa) + } else { + Err(ComposeError::Todo) + } + } + } + )+ + $( impl From<$name> for PathAttribute { fn from(pa: $name) -> PathAttribute { @@ -224,6 +540,8 @@ macro_rules! path_attributes { PathAttribute::Unimplemented(u) } } + + //------------ WireformatPathAttribute -------------------------------------- #[derive(Debug)] @@ -463,129 +781,6 @@ macro_rules! path_attributes { } */ -pub type AttributesMap = BTreeMap; - -pub struct PathAttributesBuilder { - attributes: AttributesMap, -} - -impl PathAttributesBuilder { - - pub fn empty() -> Self { - Self { - attributes: BTreeMap::new() - } - } - - pub fn from_update_pdu(pdu: &UpdateMessage) - -> Result - where - for<'a> Vec: OctetsFrom> - { - let mut res = Self::empty(); - for pa in pdu.path_attributes()? { - if let Ok(pa) = pa { - if pa.type_code() != PathAttributeType::MpReachNlri - && pa.type_code() != PathAttributeType::MpUnreachNlri - { - if let PathAttributeType::Invalid(n) = pa.type_code() { - warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); - } - res.add_attribute(pa.to_owned()?)?; - } - } else { - return Err(ComposeError::InvalidAttribute); - } - } - Ok(res) - } - - pub fn merge(&mut self, other: &Self) -> Result<(), ComposeError> { - for val in other.attributes.values().cloned() { - self.add_attribute(val)?; - } - Ok(()) - } - - pub fn into_inner(self) -> AttributesMap { - self.attributes - } - - pub fn into_update_builder(self) -> UpdateBuilder> { - UpdateBuilder::>::from_attributes_builder(self) - } - - pub fn from_update_builder(builder: UpdateBuilder) -> Self { - Self { - attributes: builder.into_attributes() - } - } - - pub fn add_attribute(&mut self, pa: PathAttribute) - -> Result<(), ComposeError> - { - if let PathAttribute::Invalid(..) = pa { - warn!( - "adding Invalid attribute to UpdateBuilder: {}", - &pa.type_code() - ); - } - if let Some(existing_pa) = self.attributes.get_mut(&pa.type_code()) { - *existing_pa = pa; - } else { - self.attributes.insert(pa.type_code(), pa); - } - - Ok(()) - } - - pub fn remove_attribute(&mut self, pat: PathAttributeType) - -> Option - { - self.attributes.remove(&pat) - } - - //-------- Specific path attribute methods ------------------------------- - // - pub fn set_origin(&mut self, origin: OriginType) - -> Result<(), ComposeError> - { - self.add_attribute(Origin::new(origin).into()) - } - - pub fn set_aspath(&mut self , aspath: HopPath) - -> Result<(), ComposeError> - { - // XXX there should be a HopPath::compose_len really, instead of - // relying on .to_as_path() first. - if let Ok(wireformat) = aspath.to_as_path::>() { - if wireformat.compose_len() > u16::MAX.into() { - return Err(ComposeError::AttributeTooLarge( - PathAttributeType::AsPath, - wireformat.compose_len() - )); - } - } else { - return Err(ComposeError::InvalidAttribute) - } - - self.add_attribute(AsPath::new(aspath).into()) - } - - pub fn set_multi_exit_disc(&mut self, med: MultiExitDisc) - -> Result<(), ComposeError> - { - self.add_attribute(med.into()) - } - - pub fn set_local_pref(&mut self, local_pref: LocalPref) - -> Result<(), ComposeError> - { - self.add_attribute(local_pref.into()) - } -} - - //------------ PathAttributeType --------------------------------------------- #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -640,14 +835,14 @@ impl PathAttributesBuilder { } path_attributes!( - 1 => Origin(OriginType), Flags::WELLKNOWN, + 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, 2 => AsPath(HopPath), Flags::WELLKNOWN, 3 => NextHop(Ipv4Addr), Flags::WELLKNOWN, 4 => MultiExitDisc(u32), Flags::OPT_NON_TRANS, - 5 => LocalPref(u32), Flags::WELLKNOWN, + 5 => LocalPref(crate::bgp::types::LocalPref), Flags::WELLKNOWN, 6 => AtomicAggregate(()), Flags::WELLKNOWN, 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, - 8 => Communities(StandardCommunitiesBuilder), Flags::OPT_TRANS, + 8 => StandardCommunities(StandardCommunitiesBuilder), Flags::OPT_TRANS, 9 => OriginatorId(Ipv4Addr), Flags::OPT_NON_TRANS, 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, @@ -669,7 +864,7 @@ path_attributes!( ); -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct UnimplementedPathAttribute { flags: Flags, type_code: u8, @@ -744,7 +939,6 @@ impl<'a, Octs: Octets> UnimplementedWireformat<'a, Octs> { pub trait Attribute: AttributeHeader + Clone { - fn compose_len(&self) -> usize { self.header_len() + self.value_len() } @@ -761,6 +955,8 @@ pub trait Attribute: AttributeHeader + Clone { } } + // fn inner(&self) -> &Self { todo!() } + fn value_len(&self) -> usize; fn compose(&self, target: &mut Target) @@ -903,7 +1099,6 @@ impl Attribute for Origin { } } - //--- AsPath (see bgp::aspath) impl Attribute for AsPath { @@ -1015,13 +1210,13 @@ impl Attribute for LocalPref { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.to_be_bytes()) + target.append_slice(&self.0.0.to_be_bytes()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) -> Result { - Ok(LocalPref(parser.parse_u32_be()?)) + Ok(LocalPref(crate::bgp::types::LocalPref(parser.parse_u32_be()?))) } fn validate( @@ -1141,9 +1336,9 @@ impl Display for AggregatorInfo { } } -//--- Communities +//--- StandardCommunities -impl Attribute for Communities { +impl Attribute for StandardCommunities { fn value_len(&self) -> usize { self.0.communities().len() * 4 } @@ -1158,7 +1353,7 @@ impl Attribute for Communities { } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let mut builder = StandardCommunitiesBuilder::with_capacity( parser.remaining() / 4 @@ -1167,7 +1362,7 @@ impl Attribute for Communities { builder.add_community(parser.parse_u32_be()?.into()); } - Ok(Communities(builder)) + Ok(StandardCommunities(builder)) } fn validate( @@ -1184,6 +1379,13 @@ impl Attribute for Communities { } } +impl StandardCommunitiesBuilder { + pub fn fmap T>(self, fmap: F) -> Vec { + self.communities().iter().map(fmap).collect::>() + } +} + + //--- OriginatorId impl Attribute for OriginatorId { @@ -1548,6 +1750,14 @@ impl ExtendedCommunitiesList { pub fn communities(&self) -> &Vec { &self.communities } + + pub fn fmap T>(self, fmap: F) -> Vec { + self.communities.into_iter().map(fmap).collect::>() + } + + pub fn add_community(&mut self, comm: ExtendedCommunity) { + self.communities.push(comm); + } } @@ -1761,8 +1971,15 @@ impl Ipv6ExtendedCommunitiesList { pub fn communities(&self) -> &Vec { &self.communities } -} + pub fn fmap T>(self, fmap: F) -> Vec { + self.communities.into_iter().map(fmap).collect::>() + } + + pub fn add_community(&mut self, comm: Ipv6ExtendedCommunity) { + self.communities.push(comm); + } +} impl Attribute for Ipv6ExtendedCommunities { fn value_len(&self) -> usize { @@ -1807,6 +2024,8 @@ impl Attribute for Ipv6ExtendedCommunities { //--- LargeCommunities use crate::bgp::communities::LargeCommunity; + +use super::workshop::route::FromAttribute; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct LargeCommunitiesList { @@ -1823,6 +2042,14 @@ impl LargeCommunitiesList { pub fn communities(&self) -> &Vec { &self.communities } + + pub fn fmap T>(self, fmap: F) -> Vec { + self.communities.into_iter().map(fmap).collect::>() + } + + pub fn add_community(&mut self, comm: LargeCommunity) { + self.communities.push(comm) + } } @@ -2075,7 +2302,7 @@ mod tests { check( vec![0x40, 0x05, 0x04, 0x00, 0x00, 0x00, 0x0a], - PA::LocalPref(LocalPref::new(10)) + PA::LocalPref(LocalPref::new(crate::bgp::types::LocalPref(10))) ); check( @@ -2106,7 +2333,7 @@ mod tests { builder.add_community(Wellknown::NoExport.into()); builder.add_community(Wellknown::NoAdvertise.into()); builder.add_community(Wellknown::NoExportSubconfed.into()); - PA::Communities(Communities(builder)) + PA::StandardCommunities(StandardCommunities(builder)) } ); diff --git a/src/bgp/workshop/afisafi_nlri.rs b/src/bgp/workshop/afisafi_nlri.rs new file mode 100644 index 00000000..fe3d339a --- /dev/null +++ b/src/bgp/workshop/afisafi_nlri.rs @@ -0,0 +1,123 @@ +use std::{fmt::Debug, hash::Hash}; + +use octseq::Octets; + +use crate::{addr::Prefix, bgp::message::{nlri::{BasicNlri, FlowSpecNlri}, update::AfiSafi}}; + +pub trait AfiSafiNlri: Clone + Hash + Debug { + type Nlri; + fn nlri(&self) -> Self::Nlri; + fn afi_safi() -> AfiSafi; + // fn make_route(&self, path_attributes: PathAttributesBuilder) -> impl RotoType; +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4UnicastNlri(pub BasicNlri); +impl AfiSafiNlri for Ipv4UnicastNlri { + type Nlri = BasicNlri; + + fn nlri(&self) -> Self::Nlri { self.0 } + + fn afi_safi() -> AfiSafi { + AfiSafi::Ipv4Unicast + } + + // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { + // Route:: { + // nlri: self.0, + // status, + // // afi_safi: AfiSafi::Ipv4Unicast, + // path_attributes: path_attributes.into_inner() + // } + // } +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6UnicastNlri(pub BasicNlri); + +impl AfiSafiNlri for Ipv6UnicastNlri { + type Nlri = BasicNlri; + + fn nlri(&self) -> Self::Nlri { self.0 } + + fn afi_safi() -> AfiSafi { + AfiSafi::Ipv6Unicast + } + + // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { + // Route:: { + // nlri: self.0, + // status, + // path_attributes: path_attributes.into_inner() + // } + // } +} + +impl From for Ipv6UnicastNlri { + fn from(value: Prefix) -> Self { + Self(BasicNlri { prefix: value, path_id: None }) + } +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4MulticastNlri(pub BasicNlri); + +impl AfiSafiNlri for Ipv4MulticastNlri { + type Nlri = BasicNlri; + + fn nlri(&self) -> Self::Nlri { self.0 } + + fn afi_safi() -> AfiSafi { + AfiSafi::Ipv4Multicast + } + + // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { + // Route:: { + // nlri: self.0, + // status, + // path_attributes: path_attributes.into_inner() + // } + // } +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6MulticastNlri(pub BasicNlri); + +impl AfiSafiNlri for Ipv6MulticastNlri { + type Nlri = BasicNlri; + + fn nlri(&self) -> Self::Nlri { self.0 } + + fn afi_safi() -> AfiSafi { + AfiSafi::Ipv6Multicast + } + + // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { + // Route:: { + // nlri: self.0, + // status, + // path_attributes: path_attributes.into_inner() + // } + // } +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); + +impl AfiSafiNlri for Ipv4FlowSpecNlri { + type Nlri = Ipv4FlowSpecNlri; + + fn nlri(&self) -> Self::Nlri { Ipv4FlowSpecNlri(self.0.clone()) } + + fn afi_safi() -> AfiSafi { + AfiSafi::Ipv4FlowSpec + } + + // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { + // Route::> { + // nlri: self.0.clone(), + // status, + // path_attributes: path_attributes.into_inner() + // } + // } +} \ No newline at end of file diff --git a/src/bgp/workshop/mod.rs b/src/bgp/workshop/mod.rs new file mode 100644 index 00000000..62eacc4e --- /dev/null +++ b/src/bgp/workshop/mod.rs @@ -0,0 +1,2 @@ +pub mod route; +pub mod afisafi_nlri; \ No newline at end of file diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs new file mode 100644 index 00000000..9ad09103 --- /dev/null +++ b/src/bgp/workshop/route.rs @@ -0,0 +1,694 @@ +use std::fmt::Debug; +use std::hash::Hash; +use std::net::{IpAddr, Ipv4Addr}; + +use octseq::{Octets, OctetsFrom}; + +use crate::bgp::communities::{Community, StandardCommunity}; +use crate::bgp::message::update::MultiExitDisc; +use crate::bgp::message::update_builder::ComposeError; +use crate::bgp::message::UpdateMessage; +use crate::bgp::path_attributes::PathAttributesBuilder; +use crate::{ + asn::Asn, + bgp::{ + aspath::HopPath, + message::{ + nlri::Nlri, update::OriginType, + update_builder::StandardCommunitiesBuilder, + }, + path_attributes::{ + AggregatorInfo, As4Aggregator, AsPathLimitInfo, AtomicAggregate, + AttributesMap, ClusterIds, ClusterList, Connector, + ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, + LargeCommunitiesList, NextHop, OriginatorId, Otc, PathAttribute, + PathAttributeType, + }, + }, +}; + +#[derive(Debug)] +pub enum TypedRoute { + Announce(Route), + Withdraw(Nlri), +} + +#[derive(Debug)] +pub struct Route(N, AttributesMap); + +impl Route { + pub fn nlri(&self) -> &N { + &self.0 + } + + pub fn get(&self) -> Option { + self.1 + .get(&A::attribute_type()) + .and_then(|a| A::from_attribute(a.clone())) + } +} + +pub trait FromAttribute { + type Output; + fn from_attribute(value: PathAttribute) -> Option; + fn attribute_type() -> PathAttributeType; +} + +macro_rules! from_attributes_impl { + ( + $( $variant:ident($output_ty:ty), $attr:ty )+ + ) => { + $( + impl FromAttribute for $attr { + type Output = $output_ty; + + fn from_attribute(value: PathAttribute) -> Option<$output_ty> { + if let PathAttribute::$variant(pa) = value { + Some(pa.inner()) + } else { + None + } + } + + fn attribute_type() -> PathAttributeType { + PathAttributeType::$variant + } + } + )+ + } +} + +// ident(ty) +// 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, +// 2 => AsPath(HopPath), Flags::WELLKNOWN, +// 3 => NextHop(Ipv4Addr), Flags::WELLKNOWN, +// 4 => MultiExitDisc(u32), Flags::OPT_NON_TRANS, +// 5 => LocalPref(u32), Flags::WELLKNOWN, +// 6 => AtomicAggregate(()), Flags::WELLKNOWN, +// 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, +// 8 => Communities(StandardCommunitiesBuilder), Flags::OPT_TRANS, +// 9 => OriginatorId(Ipv4Addr), Flags::OPT_NON_TRANS, +// 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, +// 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, +// 15 => MpUnreachNlri(MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, +// 16 => ExtendedCommunities(ExtendedCommunitiesList), Flags::OPT_TRANS, +// 17 => As4Path(HopPath), Flags::OPT_TRANS, +// 18 => As4Aggregator(AggregatorInfo), Flags::OPT_TRANS, +// 20 => Connector(Ipv4Addr), Flags::OPT_TRANS, +// 21 => AsPathLimit(AsPathLimitInfo), Flags::OPT_TRANS, +// //22 => PmsiTunnel(todo), Flags::OPT_TRANS, +// 25 => Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, +// 32 => LargeCommunities(LargeCommunitiesList), Flags::OPT_TRANS, +// // 33 => BgpsecAsPath, +// 35 => Otc(Asn), Flags::OPT_TRANS, +// //36 => BgpDomainPath(TODO), Flags:: , // https://datatracker.ietf.org/doc/draft-ietf-bess-evpn-ipvpn-interworking/06/ +// //40 => BgpPrefixSid(TODO), Flags::OPT_TRANS, // https://datatracker.ietf.org/doc/html/rfc8669#name-bgp-prefix-sid-attribute +// 128 => AttrSet(AttributeSet), Flags::OPT_TRANS, +// 255 => Reserved(ReservedRaw), Flags::OPT_TRANS, + +// PathAttribute variant(output type), impl for Attribute +from_attributes_impl!( + AsPath(HopPath), crate::bgp::aspath::AsPath + // NextHop(NextHop), crate::bgp::types::NextHop + MultiExitDisc(u32), crate::bgp::message::update::MultiExitDisc + Origin(OriginType), crate::bgp::types::OriginType + LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref + StandardCommunities(StandardCommunitiesBuilder), StandardCommunitiesBuilder + As4Path(HopPath), crate::bgp::aspath::AsPath> + AtomicAggregate(()), AtomicAggregate + Aggregator(AggregatorInfo), AggregatorInfo + OriginatorId(Ipv4Addr), OriginatorId + ClusterList(ClusterIds), ClusterList + ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList + As4Aggregator(AggregatorInfo), As4Aggregator + Connector(Ipv4Addr), Connector + AsPathLimit(AsPathLimitInfo), AsPathLimitInfo + Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunitiesList + LargeCommunities(LargeCommunitiesList), LargeCommunitiesList + Otc(Asn), Otc +); + +//------------ StandardCommunitiesList --------------------------------------- + +// pub struct StandardCommunitiesList(Vec); + +// impl FromAttribute for StandardCommunitiesList { +// type Output = StandardCommunitiesList; + +// fn attribute_type() -> PathAttributeType { +// PathAttributeType::StandardCommunities +// } + +// fn from_attribute(value: PathAttribute) -> Option { +// if let PathAttribute::StandardCommunities(comms) = value { +// Some(StandardCommunitiesList(comms.0.communities().clone())) +// } else { +// None +// } +// } +// } + +//------------ FromAttribute impls ------------------------------------------- + +impl FromAttribute for crate::bgp::aspath::HopPath { + type Output = HopPath; + + fn attribute_type() -> PathAttributeType { + PathAttributeType::AsPath + } + + fn from_attribute(value: PathAttribute) -> Option { + if let PathAttribute::AsPath(as_path) = value { + Some(as_path.0) + } else { + None + } + } +} + +impl FromAttribute for crate::bgp::types::NextHop { + type Output = NextHop; + + fn attribute_type() -> PathAttributeType { + PathAttributeType::NextHop + } + + fn from_attribute(value: PathAttribute) -> Option { + if let PathAttribute::NextHop(nh) = value { + Some(nh) + } else { + None + } + } +} + +impl FromAttribute for crate::bgp::path_attributes::MultiExitDisc { + type Output = crate::bgp::path_attributes::MultiExitDisc; + + fn attribute_type() -> PathAttributeType { + PathAttributeType::MultiExitDisc + } + + fn from_attribute(value: PathAttribute) -> Option { + if let PathAttribute::MultiExitDisc(cl) = value { + Some(cl) + } else { + None + } + } +} + +impl FromAttribute for crate::bgp::path_attributes::ClusterIds { + type Output = ClusterIds; + + fn attribute_type() -> PathAttributeType { + PathAttributeType::ClusterList + } + + fn from_attribute(value: PathAttribute) -> Option { + if let PathAttribute::ClusterList(cl) = value { + Some(cl.inner()) + } else { + None + } + } +} + +//------------ From impls for PathAttribute ---------------------------------- + +// These conversions are used on the `set()` method of the +// PathAttributesBuilder, so not in the Workshop! + +impl From> for PathAttribute { + fn from(value: crate::bgp::aspath::AsPath) -> Self { + PathAttribute::AsPath(crate::bgp::path_attributes::AsPath( + value.to_hop_path(), + )) + } +} + +impl From>> for PathAttribute { + fn from(value: crate::bgp::aspath::AsPath>) -> Self { + PathAttribute::AsPath(crate::bgp::path_attributes::AsPath( + value.to_hop_path(), + )) + } +} + +impl From for PathAttribute { + fn from(value: crate::bgp::aspath::HopPath) -> Self { + PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value)) + } +} + +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::NextHop) -> Self { +// if let crate::bgp::message::update::NextHop::Unicast(IpAddr::V4( +// ipv4, +// )) = value +// { +// PathAttribute::NextHop(NextHop(ipv4)) +// } else { +// panic!("WHERE'S MY TRANSPARANT NEXTHOP IMPLEMENTATION!!@?!?"); +// } +// } +// } + +impl From for PathAttribute { + fn from(value: LargeCommunitiesList) -> Self { + PathAttribute::LargeCommunities( + crate::bgp::path_attributes::LargeCommunities(value), + ) + } +} + +impl From for PathAttribute { + fn from(value: ExtendedCommunitiesList) -> Self { + PathAttribute::ExtendedCommunities( + crate::bgp::path_attributes::ExtendedCommunities(value), + ) + } +} + +impl From for PathAttribute { + fn from(value: Ipv6ExtendedCommunitiesList) -> Self { + PathAttribute::Ipv6ExtendedCommunities( + crate::bgp::path_attributes::Ipv6ExtendedCommunities(value), + ) + } +} + +impl From for PathAttribute { + fn from(value: Asn) -> Self { + PathAttribute::Otc(crate::bgp::path_attributes::Otc(value)) + } +} + +impl From for PathAttribute { + fn from(value: StandardCommunitiesBuilder) -> Self { + PathAttribute::StandardCommunities(crate::bgp::path_attributes::StandardCommunities(value)) + } +} + +// impl From for PathAttribute { +// fn from(value: CommunitiesWorkshop) -> Self { +// let mut std_comms = StandardCommunitiesBuilder::new(); +// for comm in value.0 { +// if let Community::Standard(stdc) = comm { +// std_comms.add_community(stdc) +// } +// } + +// PathAttribute::Communities(crate::bgp::path_attributes::Communities( +// std_comms, +// )) +// } +// } + +// impl From for PathAttribute { +// fn from(value: crate::bgp::path_attributes::MultiExitDisc) -> Self { +// PathAttribute::MultiExitDisc( +// crate::bgp::path_attributes::MultiExitDisc(value.0), +// ) +// } +// } + +impl From for PathAttribute { + fn from(value: crate::bgp::types::OriginType) -> Self { + PathAttribute::Origin(crate::bgp::path_attributes::Origin(value)) + } +} + +impl From for PathAttribute { + fn from(value: crate::bgp::types::LocalPref) -> Self { + PathAttribute::LocalPref(crate::bgp::path_attributes::LocalPref( + value, + )) + } +} + +impl From for PathAttribute { + fn from(value: crate::bgp::path_attributes::AggregatorInfo) -> Self { + PathAttribute::Aggregator(crate::bgp::path_attributes::Aggregator( + value, + )) + } +} + +impl From for PathAttribute { + fn from(value: crate::bgp::path_attributes::AsPathLimitInfo) -> Self { + PathAttribute::AsPathLimit(crate::bgp::path_attributes::AsPathLimit( + value, + )) + } +} + +impl From for PathAttribute { + fn from(value: crate::bgp::path_attributes::ClusterIds) -> Self { + PathAttribute::ClusterList(crate::bgp::path_attributes::ClusterList( + value, + )) + } +} + +// impl From for PathAttribute { +// fn from(value: crate::bgp::path_attributes::StandardCommunities) -> Self { +// let mut b = StandardCommunitiesBuilder::with_capacity(value.0.len()); +// value.0.into_iter().for_each(|c| b.add_community(c)); +// PathAttribute::StandardCommunities(crate::bgp::path_attributes::StandardCommunities( +// b, +// )) +// } +// } + +//------------ The Workshop -------------------------------------------------- + +#[derive(Debug)] +pub struct RouteWorkshop( + Option, + Option, +); + +impl RouteWorkshop { + pub fn from_update_pdu( + pdu: &UpdateMessage, + ) -> Result + where + for<'a> Vec: OctetsFrom>, + { + PathAttributesBuilder::from_update_pdu(pdu) + .map(|r| Self(None, Some(r))) + } + + pub fn set>( + &mut self, + attr: A, + ) -> Result<(), ComposeError> { + let mut res = Err(ComposeError::InvalidAttribute); + if let Some(b) = &mut self.1 { + res = A::to_value(attr, b); + } + res + } + + // pub fn set + Workshop>( + // &mut self, + // mut attr: A, + // ) { + // attr = attr.to_value(&mut self.1); + // self.1.insert(A::attribute_type(), attr.into()); + // } + + // pub fn get(&self) -> Option + // where + // A::Output: Workshop, + // { + // self.1.get(&A::attribute_type()).and_then(|a| { + // A::from_attribute(a.clone()).map(|a| a.from_value(&self.1)) + // }) + // } + + // pub fn get(&self) -> Option<<::Output as Workshop>::Output> + pub fn get( + &self, + ) -> Option<::Output> + where + A::Output: Workshop, + { + self.1 + .as_ref() + .and_then(|b| b.get::().map(|a| a.into_retrieved(b))) + } +} + +macro_rules! impl_workshop { + ( + $( $attr:ty )+ + ) => { + $( + impl Workshop for $attr { + fn to_value(local_attrs: Self, attrs: &mut PathAttributesBuilder) -> + Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } + fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> + Self { self } + } + )+ + } +} + +impl_workshop!( + crate::bgp::aspath::AsPath + crate::bgp::aspath::HopPath + // crate::bgp::types::NextHop + crate::bgp::types::LocalPref + crate::bgp::path_attributes::MultiExitDisc + crate::bgp::types::OriginType + crate::bgp::path_attributes::OriginatorId + crate::bgp::path_attributes::AggregatorInfo + crate::bgp::path_attributes::ExtendedCommunitiesList + crate::bgp::path_attributes::AsPathLimitInfo + crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList + crate::bgp::path_attributes::LargeCommunitiesList + crate::bgp::path_attributes::ClusterIds + crate::bgp::message::update_builder::StandardCommunitiesBuilder +); + +impl Workshop for () { + // type Output = Self; + + fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> Self { + self + } + + fn to_value( + local_attrs: Self, + attrs: &mut PathAttributesBuilder, + ) -> Result<(), ComposeError> { + attrs.set(crate::bgp::path_attributes::AtomicAggregate(local_attrs)); + Ok(()) + } +} + +//------------ Workshop ------------------------------------------------------ + +pub(crate) trait Workshop { + fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self; + fn to_value( + local_attrs: Self, + attrs: &mut PathAttributesBuilder, + ) -> Result<(), ComposeError>; +} + +//------------ CommunitiesWorkshop ------------------------------------------- + +impl Workshop for Vec { + fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { + let mut c = attrs + .get::() + .unwrap() + .fmap(|c| Community::Standard(*c)); + c.append( + &mut attrs + .get::() + .unwrap() + .fmap(Community::Extended), + ); + c.append( + &mut attrs + .get::() + .unwrap() + .fmap(Community::Ipv6Extended), + ); + c.append( + &mut attrs + .get::() + .unwrap() + .fmap(Community::Large), + ); + + c + } + + fn to_value( + local_attr: Self, + attrs: &mut PathAttributesBuilder, + ) -> Result<(), ComposeError> { + for comm in local_attr { + match comm { + Community::Standard(c) => { + if let Some(mut b) = + attrs.get::() + { + b.add_community(c) + } + } + Community::Extended(c) => { + if let Some(mut b) = + attrs.get::() + { + b.add_community(c) + } + } + Community::Ipv6Extended(c) => { + if let Some(mut b) = + attrs.get::() + { + b.add_community(c) + } + } + Community::Large(_) => todo!(), + }; + } + + Ok(()) + } +} + +// pub struct CommunitiesWorkshop; + +// impl CommunitiesWorkshop { +// pub fn add_community( +// attrs: &mut PathAttributesBuilder, +// comm: Community, +// ) -> Result<(), ComposeError> { +// match comm { +// Community::Standard(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// Community::Extended(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// Community::Ipv6Extended(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// Community::Large(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// }; +// Ok(()) +// } +// } + +// impl Workshop for CommunitiesWorkshop { +// type Output = Vec; + +// fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Vec { +// let mut c = attrs +// .get::() +// .unwrap() +// .fmap(|c| Community::Standard(*c)); +// c.append( +// &mut attrs +// .get::() +// .unwrap() +// .fmap(Community::Extended), +// ); +// c.append( +// &mut attrs +// .get::() +// .unwrap() +// .fmap(Community::Ipv6Extended), +// ); +// c.append( +// &mut attrs +// .get::() +// .unwrap() +// .fmap(Community::Large), +// ); + +// c +// } + +// fn to_value( +// local_attr: Vec, +// attrs: &mut PathAttributesBuilder, +// ) -> Result<(), ComposeError> { +// for comm in local_attr { +// match comm { +// Community::Standard(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// Community::Extended(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// Community::Ipv6Extended(c) => { +// if let Some(mut b) = attrs.get::() { +// b.add_community(c) +// } +// }, +// Community::Large(_) => todo!(), +// }; +// } + +// Ok(()) +// } +// } + +// impl FromAttribute for CommunitiesWorkshop { +// type Output = CommunitiesWorkshop; + +// fn attribute_type() -> PathAttributeType { +// PathAttributeType::Communities +// } + +// fn from_attribute(value: PathAttribute) -> Option { +// if let PathAttribute::Communities(_) = value { +// Some(CommunitiesWorkshop) +// } else { +// None +// } +// } +// } + +impl FromAttribute for Vec { + type Output = Self; + + fn from_attribute(value: PathAttribute) -> Option { + todo!() + } + + fn attribute_type() -> PathAttributeType { + todo!() + } +} + +//------------ NextHopWorkshop ----------------------------------------------- + +impl Workshop for crate::bgp::types::NextHop { + fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { + if let Some(next_hop) = attrs.get::() { + crate::bgp::types::NextHop::Unicast(next_hop.inner().into()) + } else if let Some(PathAttribute::MpReachNlri(nlri)) = + attrs.attributes().get(&PathAttributeType::MpReachNlri) + { + *(nlri.clone().inner().get_nexthop()) + } else { + crate::bgp::types::NextHop::Empty + } + } + + fn to_value( + local_attr: Self, + attrs: &mut PathAttributesBuilder, + ) -> Result<(), ComposeError> { + if let Some(PathAttribute::MpReachNlri(nlri)) = + attrs.attributes().get(&PathAttributeType::MpReachNlri) + { + nlri.clone().inner().set_nexthop(local_attr) + } else { + Err(ComposeError::InvalidAttribute) + } + } +} From f082cbedfd363d0b23d22b4e4870cd11f88f78e5 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 15:31:06 +0100 Subject: [PATCH 05/96] Serialize route --- src/bgp/path_attributes.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 65a43005..6b9a6d6a 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -4,6 +4,7 @@ use std::net::{IpAddr, Ipv4Addr}; use log::{debug, warn}; use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; +use serde::{Deserialize, Serialize}; use crate::asn::Asn; use crate::bgp::aspath::HopPath; @@ -24,6 +25,7 @@ use crate::util::parser::{ParseError, parse_ipv4addr}; #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Flags(u8); impl Flags { @@ -138,11 +140,18 @@ macro_rules! attribute { } +#[derive(Debug, Eq, PartialEq, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct MaterializedRoute2 { + pub path_attributes: AttributesMap, +} + //------------ PathAttributesBuilder ----------------------------------------- pub type AttributesMap = BTreeMap; -#[derive(Debug)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct PathAttributesBuilder { attributes: AttributesMap, } @@ -162,6 +171,10 @@ impl PathAttributesBuilder { &mut self.attributes } + pub fn attributes_owned(self) -> AttributesMap { + self.attributes + } + pub fn set>( &mut self, attr: A ) { @@ -224,6 +237,8 @@ impl PathAttributesBuilder { self.attributes.remove(&pat) } + // Assemble a AttributesMap, but skipping MP_*REACH_NLRI, so that the + // returned result is valid for all NLRI in this message. pub fn from_update_pdu(pdu: &UpdateMessage) -> Result where @@ -448,6 +463,7 @@ macro_rules! path_attributes { //------------ PathAttribute ------------------------------------------------- #[derive(Clone, Debug, Eq, Hash, PartialEq)] + #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum PathAttribute { $( $name($name) ),+, Unimplemented(UnimplementedPathAttribute), @@ -784,6 +800,7 @@ macro_rules! path_attributes { //------------ PathAttributeType --------------------------------------------- #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum PathAttributeType { $( $name ),+, Unimplemented(u8), @@ -865,6 +882,7 @@ path_attributes!( ); #[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct UnimplementedPathAttribute { flags: Flags, type_code: u8, From d71103ef1c0cf98735d7dc08b6aac3db7b6e6ee2 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 15:31:34 +0100 Subject: [PATCH 06/96] FlowSpecNlri generic over Octets --- src/bgp/workshop/afisafi_nlri.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bgp/workshop/afisafi_nlri.rs b/src/bgp/workshop/afisafi_nlri.rs index fe3d339a..33810cad 100644 --- a/src/bgp/workshop/afisafi_nlri.rs +++ b/src/bgp/workshop/afisafi_nlri.rs @@ -102,10 +102,10 @@ impl AfiSafiNlri for Ipv6MulticastNlri { } #[derive(Clone, Debug, Hash)] -pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); +pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); -impl AfiSafiNlri for Ipv4FlowSpecNlri { - type Nlri = Ipv4FlowSpecNlri; +impl AfiSafiNlri for Ipv4FlowSpecNlri { + type Nlri = Ipv4FlowSpecNlri; fn nlri(&self) -> Self::Nlri { Ipv4FlowSpecNlri(self.0.clone()) } @@ -120,4 +120,4 @@ impl AfiSafiNlri for Ipv4FlowSpecNlri { // path_attributes: path_attributes.into_inner() // } // } -} \ No newline at end of file +} From 43ebbaad53ef89f152a3a76ae62b98290076dfb7 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 15:31:47 +0100 Subject: [PATCH 07/96] make_route --- src/bgp/workshop/route.rs | 95 ++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 9ad09103..258274aa 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -1,14 +1,16 @@ use std::fmt::Debug; use std::hash::Hash; -use std::net::{IpAddr, Ipv4Addr}; +use std::marker::PhantomData; +use std::net::Ipv4Addr; use octseq::{Octets, OctetsFrom}; +use serde::Serialize; -use crate::bgp::communities::{Community, StandardCommunity}; +use crate::bgp::communities::Community; use crate::bgp::message::update::MultiExitDisc; use crate::bgp::message::update_builder::ComposeError; use crate::bgp::message::UpdateMessage; -use crate::bgp::path_attributes::PathAttributesBuilder; +use crate::bgp::path_attributes::{Attribute, PathAttributesBuilder}; use crate::{ asn::Asn, bgp::{ @@ -27,25 +29,39 @@ use crate::{ }, }; +use super::afisafi_nlri::AfiSafiNlri; + #[derive(Debug)] -pub enum TypedRoute { +pub enum TypedRoute> { Announce(Route), Withdraw(Nlri), } -#[derive(Debug)] -pub struct Route(N, AttributesMap); +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize)] +pub struct Route(N, AttributesMap); + +impl Route { + pub fn new(nlri: N, attrs: AttributesMap) -> Self { + Self ( nlri, attrs ) + } -impl Route { pub fn nlri(&self) -> &N { &self.0 } - pub fn get(&self) -> Option { + pub fn get_attr(&self) -> Option { self.1 .get(&A::attribute_type()) .and_then(|a| A::from_attribute(a.clone())) } + + pub fn attributes(&self) -> &AttributesMap { + &self.1 + } + + pub fn attributes_mut(&mut self) -> &mut AttributesMap { + &mut self.1 + } } pub trait FromAttribute { @@ -364,12 +380,13 @@ impl From for PathAttribute { //------------ The Workshop -------------------------------------------------- #[derive(Debug)] -pub struct RouteWorkshop( +pub struct RouteWorkshop( Option, Option, + // PhantomData ); -impl RouteWorkshop { +impl RouteWorkshop { pub fn from_update_pdu( pdu: &UpdateMessage, ) -> Result @@ -380,7 +397,11 @@ impl RouteWorkshop { .map(|r| Self(None, Some(r))) } - pub fn set>( + pub fn nlri(&self) -> &Option { + &self.0 + } + + pub fn set_attr + FromAttribute>( &mut self, attr: A, ) -> Result<(), ComposeError> { @@ -391,25 +412,7 @@ impl RouteWorkshop { res } - // pub fn set + Workshop>( - // &mut self, - // mut attr: A, - // ) { - // attr = attr.to_value(&mut self.1); - // self.1.insert(A::attribute_type(), attr.into()); - // } - - // pub fn get(&self) -> Option - // where - // A::Output: Workshop, - // { - // self.1.get(&A::attribute_type()).and_then(|a| { - // A::from_attribute(a.clone()).map(|a| a.from_value(&self.1)) - // }) - // } - - // pub fn get(&self) -> Option<<::Output as Workshop>::Output> - pub fn get( + pub fn get_attr( &self, ) -> Option<::Output> where @@ -419,6 +422,26 @@ impl RouteWorkshop { .as_ref() .and_then(|b| b.get::().map(|a| a.into_retrieved(b))) } + + pub fn make_route(&self) -> Option> { + match self { + RouteWorkshop(Some(nlri), Some(pab),) => Some(Route::( + nlri.clone(), + pab.attributes().clone(), + )), + _ => None + } + } + + pub fn make_route_with_nlri(&self, nlri: N) -> Option> { + match self { + RouteWorkshop(_, Some(pab),) => Some(Route::( + nlri.clone(), + pab.attributes().clone(), + )), + _ => None + } + } } macro_rules! impl_workshop { @@ -426,7 +449,7 @@ macro_rules! impl_workshop { $( $attr:ty )+ ) => { $( - impl Workshop for $attr { + impl> Workshop for $attr { fn to_value(local_attrs: Self, attrs: &mut PathAttributesBuilder) -> Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> @@ -453,7 +476,7 @@ impl_workshop!( crate::bgp::message::update_builder::StandardCommunitiesBuilder ); -impl Workshop for () { +impl> Workshop for () { // type Output = Self; fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> Self { @@ -471,7 +494,7 @@ impl Workshop for () { //------------ Workshop ------------------------------------------------------ -pub(crate) trait Workshop { +pub trait Workshop { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self; fn to_value( local_attrs: Self, @@ -481,7 +504,7 @@ pub(crate) trait Workshop { //------------ CommunitiesWorkshop ------------------------------------------- -impl Workshop for Vec { +impl> Workshop for Vec { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { let mut c = attrs .get::() @@ -655,7 +678,7 @@ impl Workshop for Vec { impl FromAttribute for Vec { type Output = Self; - fn from_attribute(value: PathAttribute) -> Option { + fn from_attribute(_value: PathAttribute) -> Option { todo!() } @@ -666,7 +689,7 @@ impl FromAttribute for Vec { //------------ NextHopWorkshop ----------------------------------------------- -impl Workshop for crate::bgp::types::NextHop { +impl Workshop for crate::bgp::types::NextHop { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { if let Some(next_hop) = attrs.get::() { crate::bgp::types::NextHop::Unicast(next_hop.inner().into()) From 03b009f6cc9ab50eee457552ada14c8b2c2e9bde Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 15:32:28 +0100 Subject: [PATCH 08/96] bytes::Bytes --- src/bgp/workshop/route.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 258274aa..fb0b0ff9 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -32,7 +32,7 @@ use crate::{ use super::afisafi_nlri::AfiSafiNlri; #[derive(Debug)] -pub enum TypedRoute> { +pub enum TypedRoute> { Announce(Route), Withdraw(Nlri), } From f025e455839123eca718c3484c3f691319438876 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 15:59:26 +0100 Subject: [PATCH 09/96] clippy & rustfmt --- src/bgp/path_attributes.rs | 3 +- src/bgp/workshop/afisafi_nlri.rs | 93 +++++++++++++------------------- src/bgp/workshop/route.rs | 40 ++++++-------- 3 files changed, 54 insertions(+), 82 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 6b9a6d6a..2a9abfa4 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -1,10 +1,9 @@ use std::collections::BTreeMap; use std::fmt::{self, Display}; -use std::net::{IpAddr, Ipv4Addr}; +use std::net::Ipv4Addr; use log::{debug, warn}; use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; -use serde::{Deserialize, Serialize}; use crate::asn::Asn; use crate::bgp::aspath::HopPath; diff --git a/src/bgp/workshop/afisafi_nlri.rs b/src/bgp/workshop/afisafi_nlri.rs index 33810cad..b4ea6a6f 100644 --- a/src/bgp/workshop/afisafi_nlri.rs +++ b/src/bgp/workshop/afisafi_nlri.rs @@ -2,122 +2,101 @@ use std::{fmt::Debug, hash::Hash}; use octseq::Octets; -use crate::{addr::Prefix, bgp::message::{nlri::{BasicNlri, FlowSpecNlri}, update::AfiSafi}}; - -pub trait AfiSafiNlri: Clone + Hash + Debug { +use crate::{ + addr::Prefix, + bgp::message::{ + nlri::{BasicNlri, FlowSpecNlri}, + update::AfiSafi, + }, +}; + +pub trait AfiSafiNlri: Clone + Hash + Debug { type Nlri; fn nlri(&self) -> Self::Nlri; fn afi_safi() -> AfiSafi; - // fn make_route(&self, path_attributes: PathAttributesBuilder) -> impl RotoType; } #[derive(Clone, Debug, Hash)] pub struct Ipv4UnicastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv4UnicastNlri { +impl AfiSafiNlri for Ipv4UnicastNlri { type Nlri = BasicNlri; - fn nlri(&self) -> Self::Nlri { self.0 } + fn nlri(&self) -> Self::Nlri { + self.0 + } fn afi_safi() -> AfiSafi { AfiSafi::Ipv4Unicast } - - // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { - // Route:: { - // nlri: self.0, - // status, - // // afi_safi: AfiSafi::Ipv4Unicast, - // path_attributes: path_attributes.into_inner() - // } - // } } #[derive(Clone, Debug, Hash)] pub struct Ipv6UnicastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv6UnicastNlri { +impl AfiSafiNlri for Ipv6UnicastNlri { type Nlri = BasicNlri; - fn nlri(&self) -> Self::Nlri { self.0 } + fn nlri(&self) -> Self::Nlri { + self.0 + } fn afi_safi() -> AfiSafi { AfiSafi::Ipv6Unicast } - - // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { - // Route:: { - // nlri: self.0, - // status, - // path_attributes: path_attributes.into_inner() - // } - // } } impl From for Ipv6UnicastNlri { fn from(value: Prefix) -> Self { - Self(BasicNlri { prefix: value, path_id: None }) + Self(BasicNlri { + prefix: value, + path_id: None, + }) } } #[derive(Clone, Debug, Hash)] pub struct Ipv4MulticastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv4MulticastNlri { +impl AfiSafiNlri for Ipv4MulticastNlri { type Nlri = BasicNlri; - fn nlri(&self) -> Self::Nlri { self.0 } + fn nlri(&self) -> Self::Nlri { + self.0 + } fn afi_safi() -> AfiSafi { AfiSafi::Ipv4Multicast } - - // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { - // Route:: { - // nlri: self.0, - // status, - // path_attributes: path_attributes.into_inner() - // } - // } } #[derive(Clone, Debug, Hash)] pub struct Ipv6MulticastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv6MulticastNlri { +impl AfiSafiNlri for Ipv6MulticastNlri { type Nlri = BasicNlri; - fn nlri(&self) -> Self::Nlri { self.0 } + fn nlri(&self) -> Self::Nlri { + self.0 + } fn afi_safi() -> AfiSafi { AfiSafi::Ipv6Multicast } - - // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { - // Route:: { - // nlri: self.0, - // status, - // path_attributes: path_attributes.into_inner() - // } - // } } #[derive(Clone, Debug, Hash)] pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); -impl AfiSafiNlri for Ipv4FlowSpecNlri { - type Nlri = Ipv4FlowSpecNlri; +impl AfiSafiNlri + for Ipv4FlowSpecNlri +{ + type Nlri = Ipv4FlowSpecNlri; - fn nlri(&self) -> Self::Nlri { Ipv4FlowSpecNlri(self.0.clone()) } + fn nlri(&self) -> Self::Nlri { + Ipv4FlowSpecNlri(self.0.clone()) + } fn afi_safi() -> AfiSafi { AfiSafi::Ipv4FlowSpec } - - // fn make_route(&self, status: NlriStatus, path_attributes: PathAttributesBuilder) -> impl RotoType { - // Route::> { - // nlri: self.0.clone(), - // status, - // path_attributes: path_attributes.into_inner() - // } - // } } diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index fb0b0ff9..7a8f902d 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -1,16 +1,14 @@ use std::fmt::Debug; use std::hash::Hash; -use std::marker::PhantomData; use std::net::Ipv4Addr; use octseq::{Octets, OctetsFrom}; use serde::Serialize; use crate::bgp::communities::Community; -use crate::bgp::message::update::MultiExitDisc; use crate::bgp::message::update_builder::ComposeError; use crate::bgp::message::UpdateMessage; -use crate::bgp::path_attributes::{Attribute, PathAttributesBuilder}; +use crate::bgp::path_attributes::PathAttributesBuilder; use crate::{ asn::Asn, bgp::{ @@ -29,10 +27,8 @@ use crate::{ }, }; -use super::afisafi_nlri::AfiSafiNlri; - #[derive(Debug)] -pub enum TypedRoute> { +pub enum TypedRoute { Announce(Route), Withdraw(Nlri), } @@ -42,7 +38,7 @@ pub struct Route(N, AttributesMap); impl Route { pub fn new(nlri: N, attrs: AttributesMap) -> Self { - Self ( nlri, attrs ) + Self(nlri, attrs) } pub fn nlri(&self) -> &N { @@ -302,7 +298,9 @@ impl From for PathAttribute { impl From for PathAttribute { fn from(value: StandardCommunitiesBuilder) -> Self { - PathAttribute::StandardCommunities(crate::bgp::path_attributes::StandardCommunities(value)) + PathAttribute::StandardCommunities( + crate::bgp::path_attributes::StandardCommunities(value), + ) } } @@ -425,21 +423,19 @@ impl RouteWorkshop { pub fn make_route(&self) -> Option> { match self { - RouteWorkshop(Some(nlri), Some(pab),) => Some(Route::( - nlri.clone(), - pab.attributes().clone(), - )), - _ => None + RouteWorkshop(Some(nlri), Some(pab)) => { + Some(Route::(nlri.clone(), pab.attributes().clone())) + } + _ => None, } } pub fn make_route_with_nlri(&self, nlri: N) -> Option> { match self { - RouteWorkshop(_, Some(pab),) => Some(Route::( - nlri.clone(), - pab.attributes().clone(), - )), - _ => None + RouteWorkshop(_, Some(pab)) => { + Some(Route::(nlri.clone(), pab.attributes().clone())) + } + _ => None, } } } @@ -449,7 +445,7 @@ macro_rules! impl_workshop { $( $attr:ty )+ ) => { $( - impl> Workshop for $attr { + impl Workshop for $attr { fn to_value(local_attrs: Self, attrs: &mut PathAttributesBuilder) -> Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> @@ -476,9 +472,7 @@ impl_workshop!( crate::bgp::message::update_builder::StandardCommunitiesBuilder ); -impl> Workshop for () { - // type Output = Self; - +impl Workshop for () { fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> Self { self } @@ -504,7 +498,7 @@ pub trait Workshop { //------------ CommunitiesWorkshop ------------------------------------------- -impl> Workshop for Vec { +impl Workshop for Vec { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { let mut c = attrs .get::() From c637d3704131bb4c4299756b89ea8ce12531d263 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 16:39:11 +0100 Subject: [PATCH 10/96] bump minimal bytes version to something that works --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4df2add3..1a7b622e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ license = "BSD-3-Clause" [dependencies] arbitrary = { version = "1", optional = true, features = ["derive"] } -bytes = { version = "1", optional = true } +bytes = { version = "1.2", optional = true } chrono = { version = "0.4.20", optional = true, default-features = false } const-str = { version = "0.5", optional = true, features = ["case"] } log = { version = "0.4.4", optional = true } From 088fa8d611bba847bf629cfbf42238343ad1b6e9 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 13 Feb 2024 16:39:28 +0100 Subject: [PATCH 11/96] WorkshopAttribute --- src/bgp/workshop/route.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 7a8f902d..304e0d8d 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -381,7 +381,6 @@ impl From for PathAttribute { pub struct RouteWorkshop( Option, Option, - // PhantomData ); impl RouteWorkshop { @@ -399,7 +398,7 @@ impl RouteWorkshop { &self.0 } - pub fn set_attr + FromAttribute>( + pub fn set_attr + FromAttribute>( &mut self, attr: A, ) -> Result<(), ComposeError> { @@ -414,7 +413,7 @@ impl RouteWorkshop { &self, ) -> Option<::Output> where - A::Output: Workshop, + A::Output: WorkshopAttribute, { self.1 .as_ref() @@ -445,7 +444,7 @@ macro_rules! impl_workshop { $( $attr:ty )+ ) => { $( - impl Workshop for $attr { + impl WorkshopAttribute for $attr { fn to_value(local_attrs: Self, attrs: &mut PathAttributesBuilder) -> Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> @@ -472,7 +471,7 @@ impl_workshop!( crate::bgp::message::update_builder::StandardCommunitiesBuilder ); -impl Workshop for () { +impl WorkshopAttribute for () { fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> Self { self } @@ -488,7 +487,7 @@ impl Workshop for () { //------------ Workshop ------------------------------------------------------ -pub trait Workshop { +pub trait WorkshopAttribute { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self; fn to_value( local_attrs: Self, @@ -498,7 +497,7 @@ pub trait Workshop { //------------ CommunitiesWorkshop ------------------------------------------- -impl Workshop for Vec { +impl WorkshopAttribute for Vec { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { let mut c = attrs .get::() @@ -683,7 +682,7 @@ impl FromAttribute for Vec { //------------ NextHopWorkshop ----------------------------------------------- -impl Workshop for crate::bgp::types::NextHop { +impl WorkshopAttribute for crate::bgp::types::NextHop { fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { if let Some(next_hop) = attrs.get::() { crate::bgp::types::NextHop::Unicast(next_hop.inner().into()) From 49d6907b7cdcdf23927c83c15a81290955016079 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 11:20:06 +0100 Subject: [PATCH 12/96] wrapped types for PathAttribute data fields & ConventionalNextHop variant name --- src/bgp/message/update.rs | 4 +- src/bgp/message/update_builder.rs | 22 +- src/bgp/path_attributes.rs | 167 ++++++++++----- src/bgp/types.rs | 40 +++- src/bgp/workshop/route.rs | 334 ++++++++++++++++-------------- src/bmp/message.rs | 2 +- 6 files changed, 353 insertions(+), 216 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 9985007d..106acfed 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -582,7 +582,7 @@ impl UpdateMessage { pub fn conventional_next_hop(&self) -> Result, ParseError> { - if let Some(WireformatPathAttribute::NextHop(epa)) = self.path_attributes()?.get(PathAttributeType::NextHop) { + if let Some(WireformatPathAttribute::ConventionalNextHop(epa)) = self.path_attributes()?.get(PathAttributeType::ConventionalNextHop) { Ok(Some(NextHop::Unicast(Ipv4Addr::from(epa.value_into_parser().parse_u32_be()?).into()))) } else { Ok(None) @@ -1597,7 +1597,7 @@ mod tests { assert_eq!(update.aspath().unwrap().unwrap(), asp); let pa3 = pa_iter.next().unwrap().unwrap(); - assert_eq!(pa3.type_code(), PathAttributeType::NextHop); + assert_eq!(pa3.type_code(), PathAttributeType::ConventionalNextHop); assert_eq!(pa3.flags(), 0x40.into()); assert_eq!(pa3.length(), 4); //assert_eq!(pa3.as_ref(), &[10, 255, 0, 101]); diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 1cc310f1..67926eb2 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -22,7 +22,7 @@ use crate::bgp::path_attributes::{ MpReachNlri, MpUnreachNlri, MultiExitDisc, - NextHop as NextHopAttribute, + ConventionalNextHop, Origin, AttributesMap, PathAttribute, PathAttributesBuilder, PathAttributeType }; @@ -255,7 +255,7 @@ where Target: octseq::Truncate } pub fn set_conventional_nexthop(&mut self, addr: Ipv4Addr) -> Result<(), ComposeError> { - self.add_attribute(NextHopAttribute::new(addr).into()) + self.add_attribute(ConventionalNextHop::new(crate::bgp::types::ConventionalNextHop(addr)).into()) } pub fn set_mp_nexthop(&mut self, nexthop: NextHop) -> Result<(), ComposeError> { @@ -398,7 +398,7 @@ where Target: octseq::Truncate { if !self.attributes.contains_key(&PathAttributeType::StandardCommunities) { self.add_attribute(StandardCommunities::new( - StandardCommunitiesBuilder::new() + StandardCommunitiesList::new() ).into())?; } let pa = self.attributes.get_mut(&PathAttributeType::StandardCommunities) @@ -1324,23 +1324,23 @@ impl MpUnreachNlriBuilder { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct StandardCommunitiesBuilder { +pub struct StandardCommunitiesList { communities: Vec, len: usize, // size of value, excluding path attribute flags+type_code+len extended: bool, } -impl StandardCommunitiesBuilder { - pub(crate) fn new() -> StandardCommunitiesBuilder { - StandardCommunitiesBuilder { +impl StandardCommunitiesList { + pub(crate) fn new() -> StandardCommunitiesList { + StandardCommunitiesList { communities: Vec::new(), len: 0, extended: false } } - pub(crate) fn with_capacity(c: usize) -> StandardCommunitiesBuilder { - StandardCommunitiesBuilder { + pub(crate) fn with_capacity(c: usize) -> StandardCommunitiesList { + StandardCommunitiesList { communities: Vec::with_capacity(c), len: 0, extended: false @@ -1754,7 +1754,7 @@ mod tests { ).unwrap(); } builder.set_local_pref(LocalPref::new(crate::bgp::types::LocalPref(123))).unwrap(); - builder.set_multi_exit_disc(MultiExitDisc::new(123)).unwrap(); + builder.set_multi_exit_disc(MultiExitDisc::new(crate::bgp::types::MultiExitDisc(123))).unwrap(); (1..=300).for_each(|n| { builder.add_community(StandardCommunity::new(n.into(), Tag::new(123))).unwrap(); }); @@ -2118,7 +2118,7 @@ mod tests { //builder.set_aspath::>(path.to_as_path().unwrap()).unwrap(); builder.set_aspath(path).unwrap(); - builder.set_multi_exit_disc(MultiExitDisc::new(1234)).unwrap(); + builder.set_multi_exit_disc(MultiExitDisc::new(crate::bgp::types::MultiExitDisc(1234))).unwrap(); builder.set_local_pref(LocalPref::new(crate::bgp::types::LocalPref(9876))).unwrap(); let msg = builder.into_message().unwrap(); diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 2a9abfa4..65b07dd2 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -16,7 +16,7 @@ use crate::bgp::message::{ use crate::bgp::message::update_builder::{ MpReachNlriBuilder, MpUnreachNlriBuilder, - StandardCommunitiesBuilder + StandardCommunitiesList }; use crate::bgp::communities::StandardCommunity; use crate::bgp::types::{Afi, Safi, AfiSafi}; @@ -177,14 +177,20 @@ impl PathAttributesBuilder { pub fn set>( &mut self, attr: A ) { - self.attributes.insert(A::attribute_type(), attr.into()); + if let Some(attr_type) = A::attribute_type() { + self.attributes.insert(attr_type, attr.into()); + } } pub fn get( &self, ) -> Option { - self.attributes - .get(&A::attribute_type()).and_then(|a| A::from_attribute(a.clone())) + if let Some(attr_type) = A::attribute_type() { + self.attributes + .get(&attr_type).and_then(|a| A::from_attribute(a.clone())) + } else { + None + } } pub fn append(&mut self, other: &mut AttributesMap) { @@ -528,20 +534,37 @@ macro_rules! path_attributes { } } - $( - impl TryFrom for $name { - type Error = ComposeError; + // $( + // impl TryFrom for $name { + // type Error = ComposeError; - fn try_from(value: PathAttribute) -> Result<$name, ComposeError> { - if let PathAttribute::$name(pa) = value { - Ok(pa) - } else { - Err(ComposeError::Todo) + // fn try_from(value: PathAttribute) -> Result<$name, ComposeError> { + // if let PathAttribute::$name(pa) = value { + // Ok(pa) + // } else { + // Err(ComposeError::Todo) + // } + // } + // } + // )+ + $( + // impl From for PathAttribute { + // fn from(value: StandardCommunitiesList) -> Self { + // PathAttribute::StandardCommunities( + // crate::bgp::path_attributes::StandardCommunities(value), + // ) + // } + // } + impl From<$data> for PathAttribute { + fn from(value: $data) -> Self { + PathAttribute::$name( + $name(value), + ) } } - } )+ + $( impl From<$name> for PathAttribute { fn from(pa: $name) -> PathAttribute { @@ -847,26 +870,44 @@ macro_rules! path_attributes { $( attribute!($name($data), $flags, $type_code); )+ + + // $( + // impl FromAttribute for $data { + // type Output = $name; + + // fn from_attribute(value: PathAttribute) -> Option<$name> { + // if let PathAttribute::$name(pa) = value { + // Some(pa.inner()) + // } else { + // None + // } + // } + + // fn attribute_type() -> Option { + // Some(PathAttributeType::$name) + // } + // } + // )+ } } path_attributes!( 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, - 2 => AsPath(HopPath), Flags::WELLKNOWN, - 3 => NextHop(Ipv4Addr), Flags::WELLKNOWN, - 4 => MultiExitDisc(u32), Flags::OPT_NON_TRANS, + 2 => AsPath(crate::bgp::aspath::HopPath), Flags::WELLKNOWN, + 3 => ConventionalNextHop(crate::bgp::types::ConventionalNextHop), Flags::WELLKNOWN, + 4 => MultiExitDisc(crate::bgp::types::MultiExitDisc), Flags::OPT_NON_TRANS, 5 => LocalPref(crate::bgp::types::LocalPref), Flags::WELLKNOWN, 6 => AtomicAggregate(()), Flags::WELLKNOWN, 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, - 8 => StandardCommunities(StandardCommunitiesBuilder), Flags::OPT_TRANS, - 9 => OriginatorId(Ipv4Addr), Flags::OPT_NON_TRANS, + 8 => StandardCommunities(StandardCommunitiesList), Flags::OPT_TRANS, + 9 => OriginatorId(crate::bgp::types::OriginatorId), Flags::OPT_NON_TRANS, 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, 15 => MpUnreachNlri(MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, 16 => ExtendedCommunities(ExtendedCommunitiesList), Flags::OPT_TRANS, - 17 => As4Path(HopPath), Flags::OPT_TRANS, - 18 => As4Aggregator(AggregatorInfo), Flags::OPT_TRANS, - 20 => Connector(Ipv4Addr), Flags::OPT_TRANS, + 17 => As4Path(crate::bgp::types::As4Path), Flags::OPT_TRANS, + 18 => As4Aggregator(crate::bgp::types::As4Aggregator), Flags::OPT_TRANS, + 20 => Connector(crate::bgp::types::Connector), Flags::OPT_TRANS, 21 => AsPathLimit(AsPathLimitInfo), Flags::OPT_TRANS, //22 => PmsiTunnel(todo), Flags::OPT_TRANS, 25 => Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, @@ -880,6 +921,29 @@ path_attributes!( ); +//$( $variant:ident($output_ty:ty), $attr:ty )+ + +// from_attributes_impl!( +// Origin(OriginType), crate::bgp::types::OriginType +// AsPath(HopPath), crate::bgp::aspath::AsPath +// ConventionalNextHop(ConventionalNextHop), crate::bgp::types::ConventionalNextHop +// MultiExitDisc(MultiExitDisc), crate::bgp::message::update::MultiExitDisc +// LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref +// AtomicAggregate(()), AtomicAggregate +// Aggregator(AggregatorInfo), AggregatorInfo +// StandardCommunities(StandardCommunitiesList), StandardCommunitiesList +// OriginatorId(OriginatorId), crate::bgp::types::OriginatorId +// ClusterList(ClusterIds), ClusterList +// ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList +// As4Path(As4Path), crate::bgp::aspath::AsPath> +// As4Aggregator(As4Aggregator), crate::bgp::types::As4Aggregator +// Connector(Connector), crate::bgp::types::Connector +// AsPathLimit(AsPathLimitInfo), AsPathLimitInfo +// Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunitiesList +// LargeCommunities(LargeCommunitiesList), LargeCommunitiesList +// Otc(Asn), Otc +// ); + #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct UnimplementedPathAttribute { @@ -1169,19 +1233,19 @@ impl Attribute for AsPath { //--- NextHop -impl Attribute for NextHop { +impl Attribute for ConventionalNextHop { fn value_len(&self) -> usize { 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.octets()) + target.append_slice(&self.0.0.octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(NextHop(parse_ipv4addr(parser)?)) + Ok(Self(crate::bgp::types::ConventionalNextHop(parse_ipv4addr(parser)?))) } fn validate( @@ -1201,13 +1265,13 @@ impl Attribute for MultiExitDisc { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.to_be_bytes()) + target.append_slice(&self.0.0.to_be_bytes()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) -> Result { - Ok(MultiExitDisc(parser.parse_u32_be()?)) + Ok(MultiExitDisc(crate::bgp::types::MultiExitDisc(parser.parse_u32_be()?))) } fn validate( @@ -1282,7 +1346,7 @@ impl Display for AtomicAggregate { //--- Aggregator #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AggregatorInfo { asn: Asn, address: Ipv4Addr, @@ -1372,7 +1436,7 @@ impl Attribute for StandardCommunities { fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) -> Result { - let mut builder = StandardCommunitiesBuilder::with_capacity( + let mut builder = StandardCommunitiesList::with_capacity( parser.remaining() / 4 ); while parser.remaining() > 0 { @@ -1396,7 +1460,7 @@ impl Attribute for StandardCommunities { } } -impl StandardCommunitiesBuilder { +impl StandardCommunitiesList { pub fn fmap T>(self, fmap: F) -> Vec { self.communities().iter().map(fmap).collect::>() } @@ -1411,13 +1475,13 @@ impl Attribute for OriginatorId { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.octets()) + target.append_slice(&self.0.0.octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) -> Result { - Ok(OriginatorId(parse_ipv4addr(parser)?)) + Ok(OriginatorId(crate::bgp::types::OriginatorId(parse_ipv4addr(parser)?))) } fn validate( @@ -1822,14 +1886,14 @@ impl Attribute for ExtendedCommunities { impl Attribute for As4Path { fn value_len(&self) -> usize { - self.0.to_as_path::>().unwrap().into_inner().len() + self.0.0.to_as_path::>().unwrap().into_inner().len() } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { target.append_slice( - self.0.to_as_path::>().unwrap().into_inner().as_ref() + self.0.0.to_as_path::>().unwrap().into_inner().as_ref() ) } @@ -1841,7 +1905,7 @@ impl Attribute for As4Path { parser.peek_all().to_vec(), sc.has_four_octet_asn() ).map_err(|_| ParseError::form_error("invalid AS4_PATH"))?; - Ok(As4Path(asp.to_hop_path())) + Ok(As4Path(crate::bgp::types::As4Path(asp.to_hop_path()))) } fn validate( @@ -1872,8 +1936,8 @@ impl Attribute for As4Aggregator { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.asn().to_raw())?; - target.append_slice(&self.0.address().octets()) + target.append_slice(&self.0.0.asn().to_raw())?; + target.append_slice(&self.0.0.address().octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) @@ -1881,7 +1945,7 @@ impl Attribute for As4Aggregator { { let asn = Asn::from_u32(parser.parse_u32_be()?); let address = parse_ipv4addr(parser)?; - Ok(As4Aggregator(AggregatorInfo::new(asn, address))) + Ok(As4Aggregator(crate::bgp::types::As4Aggregator(AggregatorInfo::new(asn, address)))) } fn validate( @@ -1902,13 +1966,13 @@ impl Attribute for Connector { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.octets()) + target.append_slice(&self.0.0.octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) -> Result { - Ok(Connector(parse_ipv4addr(parser)?)) + Ok(Connector(crate::bgp::types::Connector(parse_ipv4addr(parser)?))) } fn validate( @@ -2042,7 +2106,8 @@ impl Attribute for Ipv6ExtendedCommunities { use crate::bgp::communities::LargeCommunity; -use super::workshop::route::FromAttribute; +// use super::types::ConventionalNextHop; +use super::workshop::route::{FromAttribute, WorkshopAttribute}; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct LargeCommunitiesList { @@ -2309,12 +2374,12 @@ mod tests { check( vec![0x40, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04], - PA::NextHop(NextHop("1.2.3.4".parse().unwrap())) + PA::ConventionalNextHop(ConventionalNextHop(crate::bgp::types::ConventionalNextHop("1.2.3.4".parse().unwrap()))) ); check( vec![0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0xff], - PA::MultiExitDisc(MultiExitDisc::new(255)) + PA::MultiExitDisc(MultiExitDisc::new(crate::bgp::types::MultiExitDisc(255))) ); check( @@ -2345,7 +2410,7 @@ mod tests { 0xff, 0xff, 0x03 ], { - let mut builder = StandardCommunitiesBuilder::new(); + let mut builder = StandardCommunitiesList::new(); builder.add_community("AS42:518".parse().unwrap()); builder.add_community(Wellknown::NoExport.into()); builder.add_community(Wellknown::NoAdvertise.into()); @@ -2356,7 +2421,7 @@ mod tests { check( vec![0x80, 0x09, 0x04, 0x0a, 0x00, 0x00, 0x04], - OriginatorId("10.0.0.4".parse().unwrap()).into() + OriginatorId(crate::bgp::types::OriginatorId("10.0.0.4".parse().unwrap())).into() ); check( @@ -2442,10 +2507,10 @@ mod tests { 0x00, 0x00, 0x00, 100, 0x00, 0x00, 0x00, 200, ], - PA::As4Path(As4Path::new(HopPath::from(vec![ + PA::As4Path(As4Path::new(crate::bgp::types::As4Path(HopPath::from(vec![ Asn::from_u32(100), Asn::from_u32(200)] - ))) + )))) ); check( @@ -2453,15 +2518,15 @@ mod tests { 0xc0, 0x12, 0x08, 0x00, 0x00, 0x04, 0xd2, 10, 0, 0, 99 ], - As4Aggregator(AggregatorInfo::new( + As4Aggregator(crate::bgp::types::As4Aggregator(AggregatorInfo::new( Asn::from_u32(1234), "10.0.0.99".parse().unwrap() - )).into() + ))).into() ); check( vec![0xc0, 0x14, 0x04, 1, 2, 3, 4], - Connector("1.2.3.4".parse().unwrap()).into() + Connector(crate::bgp::types::Connector("1.2.3.4".parse().unwrap())).into() ); check( @@ -2536,7 +2601,7 @@ mod tests { assert!(pas.get(PathAttributeType::Origin).is_some()); assert!(pas.get(PathAttributeType::AsPath).is_none()); assert!(pas.get(PathAttributeType::MultiExitDisc).is_some()); - assert!(pas.get(PathAttributeType::NextHop).is_some()); + assert!(pas.get(PathAttributeType::ConventionalNextHop).is_some()); } #[test] diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 94f4a9ea..eaedf083 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -7,6 +7,9 @@ use crate::bgp::message::nlri::RouteDistinguisher; #[cfg(feature = "serde")] use serde::{Serialize, Deserialize}; +use super::aspath::HopPath; +use super::path_attributes::AggregatorInfo; + typeenum!( /// AFI as used in BGP OPEN and UPDATE messages. #[cfg_attr(feature = "serde", serde(from = "u16"))] @@ -243,7 +246,7 @@ typeenum!( 0 => Reserved, 1 => Origin, 2 => AsPath, - 3 => NextHop, + 3 => ConventionalNextHop, 4 => MultiExitDisc, 5 => LocalPref, 6 => AtomicAggregate, @@ -349,3 +352,38 @@ impl std::fmt::Display for NextHop { } } } + + +/// Conventional NextHop only, this gets stored in the +/// `PathAttribute::ConventionalNextHop` variant. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ConventionalNextHop(pub Ipv4Addr); + +impl ConventionalNextHop { + fn new() -> Self { + Self(Ipv4Addr::from(0)) + } +} + +impl std::fmt::Display for ConventionalNextHop { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct OriginatorId(pub Ipv4Addr); + +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Connector(pub Ipv4Addr); + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct As4Path(pub HopPath); + +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct As4Aggregator(pub AggregatorInfo); \ No newline at end of file diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 304e0d8d..58a45f52 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -6,25 +6,30 @@ use octseq::{Octets, OctetsFrom}; use serde::Serialize; use crate::bgp::communities::Community; +use crate::bgp::message::update::{MultiExitDisc, NextHop}; use crate::bgp::message::update_builder::ComposeError; use crate::bgp::message::UpdateMessage; use crate::bgp::path_attributes::PathAttributesBuilder; +use crate::bgp::types::As4Path; use crate::{ asn::Asn, bgp::{ aspath::HopPath, message::{ nlri::Nlri, update::OriginType, - update_builder::StandardCommunitiesBuilder, + update_builder::StandardCommunitiesList, }, path_attributes::{ - AggregatorInfo, As4Aggregator, AsPathLimitInfo, AtomicAggregate, - AttributesMap, ClusterIds, ClusterList, Connector, + AggregatorInfo, AsPathLimitInfo, AtomicAggregate, + AttributesMap, ClusterIds, ClusterList, ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, - LargeCommunitiesList, NextHop, OriginatorId, Otc, PathAttribute, + LargeCommunitiesList, Otc, PathAttribute, PathAttributeType, }, }, + bgp::types::{ + OriginatorId, ConventionalNextHop, As4Aggregator, Connector + } }; #[derive(Debug)] @@ -46,9 +51,13 @@ impl Route { } pub fn get_attr(&self) -> Option { - self.1 - .get(&A::attribute_type()) - .and_then(|a| A::from_attribute(a.clone())) + if let Some(attr_type) = A::attribute_type() { + self.1 + .get(&attr_type) + .and_then(|a| A::from_attribute(a.clone())) + } else { + None + } } pub fn attributes(&self) -> &AttributesMap { @@ -63,7 +72,7 @@ impl Route { pub trait FromAttribute { type Output; fn from_attribute(value: PathAttribute) -> Option; - fn attribute_type() -> PathAttributeType; + fn attribute_type() -> Option; } macro_rules! from_attributes_impl { @@ -82,8 +91,8 @@ macro_rules! from_attributes_impl { } } - fn attribute_type() -> PathAttributeType { - PathAttributeType::$variant + fn attribute_type() -> Option { + Some(PathAttributeType::$variant) } } )+ @@ -121,19 +130,19 @@ macro_rules! from_attributes_impl { // PathAttribute variant(output type), impl for Attribute from_attributes_impl!( AsPath(HopPath), crate::bgp::aspath::AsPath - // NextHop(NextHop), crate::bgp::types::NextHop - MultiExitDisc(u32), crate::bgp::message::update::MultiExitDisc + ConventionalNextHop(ConventionalNextHop), crate::bgp::types::ConventionalNextHop + MultiExitDisc(MultiExitDisc), crate::bgp::message::update::MultiExitDisc Origin(OriginType), crate::bgp::types::OriginType LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref - StandardCommunities(StandardCommunitiesBuilder), StandardCommunitiesBuilder - As4Path(HopPath), crate::bgp::aspath::AsPath> + StandardCommunities(StandardCommunitiesList), StandardCommunitiesList + As4Path(As4Path), crate::bgp::aspath::AsPath> AtomicAggregate(()), AtomicAggregate Aggregator(AggregatorInfo), AggregatorInfo - OriginatorId(Ipv4Addr), OriginatorId + OriginatorId(OriginatorId), crate::bgp::types::OriginatorId ClusterList(ClusterIds), ClusterList ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList - As4Aggregator(AggregatorInfo), As4Aggregator - Connector(Ipv4Addr), Connector + As4Aggregator(As4Aggregator), crate::bgp::types::As4Aggregator + Connector(Connector), crate::bgp::types::Connector AsPathLimit(AsPathLimitInfo), AsPathLimitInfo Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunitiesList LargeCommunities(LargeCommunitiesList), LargeCommunitiesList @@ -165,8 +174,8 @@ from_attributes_impl!( impl FromAttribute for crate::bgp::aspath::HopPath { type Output = HopPath; - fn attribute_type() -> PathAttributeType { - PathAttributeType::AsPath + fn attribute_type() -> Option { + Some(PathAttributeType::AsPath) } fn from_attribute(value: PathAttribute) -> Option { @@ -181,40 +190,36 @@ impl FromAttribute for crate::bgp::aspath::HopPath { impl FromAttribute for crate::bgp::types::NextHop { type Output = NextHop; - fn attribute_type() -> PathAttributeType { - PathAttributeType::NextHop + fn attribute_type() -> Option { + None } - fn from_attribute(value: PathAttribute) -> Option { - if let PathAttribute::NextHop(nh) = value { - Some(nh) - } else { - None - } + fn from_attribute(_value: PathAttribute) -> Option { + None } } -impl FromAttribute for crate::bgp::path_attributes::MultiExitDisc { - type Output = crate::bgp::path_attributes::MultiExitDisc; +// impl FromAttribute for crate::bgp::path_attributes::MultiExitDisc { +// type Output = crate::bgp::path_attributes::MultiExitDisc; - fn attribute_type() -> PathAttributeType { - PathAttributeType::MultiExitDisc - } +// fn attribute_type() -> Option { +// Some(PathAttributeType::MultiExitDisc) +// } - fn from_attribute(value: PathAttribute) -> Option { - if let PathAttribute::MultiExitDisc(cl) = value { - Some(cl) - } else { - None - } - } -} +// fn from_attribute(value: PathAttribute) -> Option { +// if let PathAttribute::MultiExitDisc(cl) = value { +// Some(cl) +// } else { +// None +// } +// } +// } impl FromAttribute for crate::bgp::path_attributes::ClusterIds { type Output = ClusterIds; - fn attribute_type() -> PathAttributeType { - PathAttributeType::ClusterList + fn attribute_type() -> Option { + Some(PathAttributeType::ClusterList) } fn from_attribute(value: PathAttribute) -> Option { @@ -226,6 +231,23 @@ impl FromAttribute for crate::bgp::path_attributes::ClusterIds { } } +// impl FromAttribute for crate::bgp::path_attributes::OriginatorId { +// type Output = OriginatorId; + +// fn attribute_type() -> Option { +// Some(PathAttributeType::OriginatorId) +// } + +// fn from_attribute(value: PathAttribute) -> Option { +// if let PathAttribute::OriginatorId(cl) = value { +// Some(cl.inner()) +// } else { +// None +// } +// } +// } + + //------------ From impls for PathAttribute ---------------------------------- // These conversions are used on the `set()` method of the @@ -247,11 +269,11 @@ impl From>> for PathAttribute { } } -impl From for PathAttribute { - fn from(value: crate::bgp::aspath::HopPath) -> Self { - PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value)) - } -} +// impl From for PathAttribute { +// fn from(value: crate::bgp::aspath::HopPath) -> Self { +// PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value)) +// } +// } // impl From for PathAttribute { // fn from(value: crate::bgp::types::NextHop) -> Self { @@ -266,43 +288,43 @@ impl From for PathAttribute { // } // } -impl From for PathAttribute { - fn from(value: LargeCommunitiesList) -> Self { - PathAttribute::LargeCommunities( - crate::bgp::path_attributes::LargeCommunities(value), - ) - } -} +// impl From for PathAttribute { +// fn from(value: LargeCommunitiesList) -> Self { +// PathAttribute::LargeCommunities( +// crate::bgp::path_attributes::LargeCommunities(value), +// ) +// } +// } -impl From for PathAttribute { - fn from(value: ExtendedCommunitiesList) -> Self { - PathAttribute::ExtendedCommunities( - crate::bgp::path_attributes::ExtendedCommunities(value), - ) - } -} +// impl From for PathAttribute { +// fn from(value: ExtendedCommunitiesList) -> Self { +// PathAttribute::ExtendedCommunities( +// crate::bgp::path_attributes::ExtendedCommunities(value), +// ) +// } +// } -impl From for PathAttribute { - fn from(value: Ipv6ExtendedCommunitiesList) -> Self { - PathAttribute::Ipv6ExtendedCommunities( - crate::bgp::path_attributes::Ipv6ExtendedCommunities(value), - ) - } -} +// impl From for PathAttribute { +// fn from(value: Ipv6ExtendedCommunitiesList) -> Self { +// PathAttribute::Ipv6ExtendedCommunities( +// crate::bgp::path_attributes::Ipv6ExtendedCommunities(value), +// ) +// } +// } -impl From for PathAttribute { - fn from(value: Asn) -> Self { - PathAttribute::Otc(crate::bgp::path_attributes::Otc(value)) - } -} +// impl From for PathAttribute { +// fn from(value: Asn) -> Self { +// PathAttribute::Otc(crate::bgp::path_attributes::Otc(value)) +// } +// } -impl From for PathAttribute { - fn from(value: StandardCommunitiesBuilder) -> Self { - PathAttribute::StandardCommunities( - crate::bgp::path_attributes::StandardCommunities(value), - ) - } -} +// impl From for PathAttribute { +// fn from(value: StandardCommunitiesList) -> Self { +// PathAttribute::StandardCommunities( +// crate::bgp::path_attributes::StandardCommunities(value), +// ) +// } +// } // impl From for PathAttribute { // fn from(value: CommunitiesWorkshop) -> Self { @@ -327,43 +349,43 @@ impl From for PathAttribute { // } // } -impl From for PathAttribute { - fn from(value: crate::bgp::types::OriginType) -> Self { - PathAttribute::Origin(crate::bgp::path_attributes::Origin(value)) - } -} +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::OriginType) -> Self { +// PathAttribute::Origin(crate::bgp::path_attributes::Origin(value)) +// } +// } -impl From for PathAttribute { - fn from(value: crate::bgp::types::LocalPref) -> Self { - PathAttribute::LocalPref(crate::bgp::path_attributes::LocalPref( - value, - )) - } -} +// impl From for PathAttribute { +// fn from(value: crate::bgp::types::LocalPref) -> Self { +// PathAttribute::LocalPref(crate::bgp::path_attributes::LocalPref( +// value, +// )) +// } +// } -impl From for PathAttribute { - fn from(value: crate::bgp::path_attributes::AggregatorInfo) -> Self { - PathAttribute::Aggregator(crate::bgp::path_attributes::Aggregator( - value, - )) - } -} +// impl From for PathAttribute { +// fn from(value: crate::bgp::path_attributes::AggregatorInfo) -> Self { +// PathAttribute::Aggregator(crate::bgp::path_attributes::Aggregator( +// value, +// )) +// } +// } -impl From for PathAttribute { - fn from(value: crate::bgp::path_attributes::AsPathLimitInfo) -> Self { - PathAttribute::AsPathLimit(crate::bgp::path_attributes::AsPathLimit( - value, - )) - } -} +// impl From for PathAttribute { +// fn from(value: crate::bgp::path_attributes::AsPathLimitInfo) -> Self { +// PathAttribute::AsPathLimit(crate::bgp::path_attributes::AsPathLimit( +// value, +// )) +// } +// } -impl From for PathAttribute { - fn from(value: crate::bgp::path_attributes::ClusterIds) -> Self { - PathAttribute::ClusterList(crate::bgp::path_attributes::ClusterList( - value, - )) - } -} +// impl From for PathAttribute { +// fn from(value: crate::bgp::path_attributes::ClusterIds) -> Self { +// PathAttribute::ClusterList(crate::bgp::path_attributes::ClusterList( +// value, +// )) +// } +// } // impl From for PathAttribute { // fn from(value: crate::bgp::path_attributes::StandardCommunities) -> Self { @@ -398,26 +420,26 @@ impl RouteWorkshop { &self.0 } - pub fn set_attr + FromAttribute>( + pub fn set_attr>( &mut self, - attr: A, + attr: WA, ) -> Result<(), ComposeError> { let mut res = Err(ComposeError::InvalidAttribute); if let Some(b) = &mut self.1 { - res = A::to_value(attr, b); + res = WA::to_value(attr, b); } res } - pub fn get_attr( + pub fn get_attr>( &self, - ) -> Option<::Output> + ) -> Option where A::Output: WorkshopAttribute, { self.1 .as_ref() - .and_then(|b| b.get::().map(|a| a.into_retrieved(b))) + .and_then(|b| b.get::().or_else(|| ::into_retrieved(b))) } pub fn make_route(&self) -> Option> { @@ -447,8 +469,8 @@ macro_rules! impl_workshop { impl WorkshopAttribute for $attr { fn to_value(local_attrs: Self, attrs: &mut PathAttributesBuilder) -> Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } - fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> - Self { self } + fn into_retrieved(_attrs: &PathAttributesBuilder) -> + Option { None } } )+ } @@ -459,36 +481,36 @@ impl_workshop!( crate::bgp::aspath::HopPath // crate::bgp::types::NextHop crate::bgp::types::LocalPref - crate::bgp::path_attributes::MultiExitDisc + crate::bgp::types::MultiExitDisc crate::bgp::types::OriginType - crate::bgp::path_attributes::OriginatorId + crate::bgp::types::OriginatorId crate::bgp::path_attributes::AggregatorInfo crate::bgp::path_attributes::ExtendedCommunitiesList crate::bgp::path_attributes::AsPathLimitInfo crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList crate::bgp::path_attributes::LargeCommunitiesList crate::bgp::path_attributes::ClusterIds - crate::bgp::message::update_builder::StandardCommunitiesBuilder + crate::bgp::message::update_builder::StandardCommunitiesList ); -impl WorkshopAttribute for () { - fn into_retrieved(self, _attrs: &PathAttributesBuilder) -> Self { - self - } +// impl WorkshopAttribute for () { +// fn into_retrieved(_attrs: &PathAttributesBuilder) -> Option { +// None +// } - fn to_value( - local_attrs: Self, - attrs: &mut PathAttributesBuilder, - ) -> Result<(), ComposeError> { - attrs.set(crate::bgp::path_attributes::AtomicAggregate(local_attrs)); - Ok(()) - } -} +// fn to_value( +// local_attrs: Self, +// attrs: &mut PathAttributesBuilder, +// ) -> Result<(), ComposeError> { +// attrs.set(crate::bgp::path_attributes::AtomicAggregate(local_attrs)); +// Ok(()) +// } +// } //------------ Workshop ------------------------------------------------------ -pub trait WorkshopAttribute { - fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self; +pub trait WorkshopAttribute: FromAttribute { + fn into_retrieved(attrs: &PathAttributesBuilder) -> Option where Self: Sized; fn to_value( local_attrs: Self, attrs: &mut PathAttributesBuilder, @@ -498,9 +520,9 @@ pub trait WorkshopAttribute { //------------ CommunitiesWorkshop ------------------------------------------- impl WorkshopAttribute for Vec { - fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { + fn into_retrieved(attrs: &PathAttributesBuilder) -> Option { let mut c = attrs - .get::() + .get::() .unwrap() .fmap(|c| Community::Standard(*c)); c.append( @@ -522,7 +544,7 @@ impl WorkshopAttribute for Vec { .fmap(Community::Large), ); - c + Some(c) } fn to_value( @@ -533,7 +555,7 @@ impl WorkshopAttribute for Vec { match comm { Community::Standard(c) => { if let Some(mut b) = - attrs.get::() + attrs.get::() { b.add_community(c) } @@ -672,26 +694,38 @@ impl FromAttribute for Vec { type Output = Self; fn from_attribute(_value: PathAttribute) -> Option { - todo!() + None } - fn attribute_type() -> PathAttributeType { - todo!() + fn attribute_type() -> Option { + None } } //------------ NextHopWorkshop ----------------------------------------------- +// impl FromAttribute for NextHop { +// type Output = Self; + +// fn from_attribute(_value: PathAttribute) -> Option { +// todo!() +// } + +// fn attribute_type() -> PathAttributeType { +// todo!() +// } +// } + impl WorkshopAttribute for crate::bgp::types::NextHop { - fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Self { - if let Some(next_hop) = attrs.get::() { - crate::bgp::types::NextHop::Unicast(next_hop.inner().into()) + fn into_retrieved(attrs: &PathAttributesBuilder) -> Option { + if let Some(next_hop) = attrs.get::() { + Some(crate::bgp::types::NextHop::Unicast(next_hop.0.into())) } else if let Some(PathAttribute::MpReachNlri(nlri)) = attrs.attributes().get(&PathAttributeType::MpReachNlri) { - *(nlri.clone().inner().get_nexthop()) + Some(*(nlri.clone().inner().get_nexthop())) } else { - crate::bgp::types::NextHop::Empty + Some(crate::bgp::types::NextHop::Empty) } } diff --git a/src/bmp/message.rs b/src/bmp/message.rs index c40545d2..61356215 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -1807,7 +1807,7 @@ mod tests { // TODO check actual AS_PATH contents let pa3 = pas.next().unwrap().unwrap(); - assert_eq!(pa3.type_code(), PathAttributeType::NextHop); + assert_eq!(pa3.type_code(), PathAttributeType::ConventionalNextHop); assert_eq!(pa3.flags(), 0x40.into()); //assert_eq!(pa3.as_ref(), [10, 255, 0, 101]); From c4f2ef43e980f64d1513e97cc845eacaa829b90e Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 12:24:16 +0100 Subject: [PATCH 13/96] merge impl macros --- src/bgp/path_attributes.rs | 83 ++++++++------------ src/bgp/types.rs | 19 +++++ src/bgp/workshop/route.rs | 151 ++++++++++++++++++------------------- 3 files changed, 122 insertions(+), 131 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 65b07dd2..abcd29d8 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -871,23 +871,23 @@ macro_rules! path_attributes { attribute!($name($data), $flags, $type_code); )+ - // $( - // impl FromAttribute for $data { - // type Output = $name; + $( + impl FromAttribute for $data { + type Output = $data; - // fn from_attribute(value: PathAttribute) -> Option<$name> { - // if let PathAttribute::$name(pa) = value { - // Some(pa.inner()) - // } else { - // None - // } - // } + fn from_attribute(value: PathAttribute) -> Option<$data> { + if let PathAttribute::$name(pa) = value { + Some(pa.inner()) + } else { + None + } + } - // fn attribute_type() -> Option { - // Some(PathAttributeType::$name) - // } - // } - // )+ + fn attribute_type() -> Option { + Some(PathAttributeType::$name) + } + } + )+ } } @@ -898,52 +898,29 @@ path_attributes!( 4 => MultiExitDisc(crate::bgp::types::MultiExitDisc), Flags::OPT_NON_TRANS, 5 => LocalPref(crate::bgp::types::LocalPref), Flags::WELLKNOWN, 6 => AtomicAggregate(()), Flags::WELLKNOWN, - 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, - 8 => StandardCommunities(StandardCommunitiesList), Flags::OPT_TRANS, + 7 => Aggregator(crate::bgp::path_attributes::AggregatorInfo), Flags::OPT_TRANS, + 8 => StandardCommunities(crate::bgp::message::update_builder::StandardCommunitiesList), Flags::OPT_TRANS, 9 => OriginatorId(crate::bgp::types::OriginatorId), Flags::OPT_NON_TRANS, - 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, - 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, - 15 => MpUnreachNlri(MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, - 16 => ExtendedCommunities(ExtendedCommunitiesList), Flags::OPT_TRANS, + 10 => ClusterList(crate::bgp::path_attributes::ClusterIds), Flags::OPT_NON_TRANS, + 14 => MpReachNlri(crate::bgp::message::update_builder::MpReachNlriBuilder), Flags::OPT_NON_TRANS, + 15 => MpUnreachNlri(crate::bgp::message::update_builder::MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, + 16 => ExtendedCommunities(crate::bgp::path_attributes::ExtendedCommunitiesList), Flags::OPT_TRANS, 17 => As4Path(crate::bgp::types::As4Path), Flags::OPT_TRANS, 18 => As4Aggregator(crate::bgp::types::As4Aggregator), Flags::OPT_TRANS, 20 => Connector(crate::bgp::types::Connector), Flags::OPT_TRANS, - 21 => AsPathLimit(AsPathLimitInfo), Flags::OPT_TRANS, + 21 => AsPathLimit(crate::bgp::path_attributes::AsPathLimitInfo), Flags::OPT_TRANS, //22 => PmsiTunnel(todo), Flags::OPT_TRANS, - 25 => Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, - 32 => LargeCommunities(LargeCommunitiesList), Flags::OPT_TRANS, + 25 => Ipv6ExtendedCommunities(crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, + 32 => LargeCommunities(crate::bgp::path_attributes::LargeCommunitiesList), Flags::OPT_TRANS, // 33 => BgpsecAsPath, - 35 => Otc(Asn), Flags::OPT_TRANS, + 35 => Otc(crate::bgp::types::Otc), Flags::OPT_TRANS, //36 => BgpDomainPath(TODO), Flags:: , // https://datatracker.ietf.org/doc/draft-ietf-bess-evpn-ipvpn-interworking/06/ //40 => BgpPrefixSid(TODO), Flags::OPT_TRANS, // https://datatracker.ietf.org/doc/html/rfc8669#name-bgp-prefix-sid-attribute - 128 => AttrSet(AttributeSet), Flags::OPT_TRANS, - 255 => Reserved(ReservedRaw), Flags::OPT_TRANS, + 128 => AttrSet(crate::bgp::path_attributes::AttributeSet), Flags::OPT_TRANS, + 255 => Reserved(crate::bgp::path_attributes::ReservedRaw), Flags::OPT_TRANS, ); -//$( $variant:ident($output_ty:ty), $attr:ty )+ - -// from_attributes_impl!( -// Origin(OriginType), crate::bgp::types::OriginType -// AsPath(HopPath), crate::bgp::aspath::AsPath -// ConventionalNextHop(ConventionalNextHop), crate::bgp::types::ConventionalNextHop -// MultiExitDisc(MultiExitDisc), crate::bgp::message::update::MultiExitDisc -// LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref -// AtomicAggregate(()), AtomicAggregate -// Aggregator(AggregatorInfo), AggregatorInfo -// StandardCommunities(StandardCommunitiesList), StandardCommunitiesList -// OriginatorId(OriginatorId), crate::bgp::types::OriginatorId -// ClusterList(ClusterIds), ClusterList -// ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList -// As4Path(As4Path), crate::bgp::aspath::AsPath> -// As4Aggregator(As4Aggregator), crate::bgp::types::As4Aggregator -// Connector(Connector), crate::bgp::types::Connector -// AsPathLimit(AsPathLimitInfo), AsPathLimitInfo -// Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunitiesList -// LargeCommunities(LargeCommunitiesList), LargeCommunitiesList -// Otc(Asn), Otc -// ); - #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct UnimplementedPathAttribute { @@ -2183,13 +2160,13 @@ impl Attribute for Otc { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.to_raw()) + target.append_slice(&self.0.0.to_raw()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) -> Result { - Ok(Otc(Asn::from_u32(parser.parse_u32_be()?))) + Ok(Otc(crate::bgp::types::Otc(Asn::from_u32(parser.parse_u32_be()?)))) } fn validate( @@ -2563,7 +2540,7 @@ mod tests { check( vec![0xc0, 0x23, 0x04, 0x00, 0x00, 0x04, 0xd2], - Otc::new(Asn::from_u32(1234)).into() + Otc::new(crate::bgp::types::Otc(Asn::from_u32(1234))).into() ); // TODO AttrSet diff --git a/src/bgp/types.rs b/src/bgp/types.rs index eaedf083..e8023c53 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -1,3 +1,4 @@ +use crate::asn::Asn; use crate::typeenum; // from util::macros use std::fmt; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; @@ -265,6 +266,7 @@ typeenum!( 25 => Ipv6ExtendedCommunities, 32 => LargeCommunities, 33 => BgpsecAsPath, + 35 => Otc, 128 => AttrSet, 255 => RsrvdDevelopment } @@ -298,6 +300,23 @@ impl std::fmt::Display for LocalPref { } } +/// Wrapper for the 4 byte OnlyToCustomer (Otc) value in path attributes. +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Otc(pub Asn); + +impl From for u32 { + fn from(value: Otc) -> Self { + value.0.into() + } +} + +impl std::fmt::Display for Otc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + /// Conventional and BGP-MP Next Hop variants. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 58a45f52..17a31f43 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -6,30 +6,25 @@ use octseq::{Octets, OctetsFrom}; use serde::Serialize; use crate::bgp::communities::Community; -use crate::bgp::message::update::{MultiExitDisc, NextHop}; +use crate::bgp::message::update::NextHop; use crate::bgp::message::update_builder::ComposeError; use crate::bgp::message::UpdateMessage; use crate::bgp::path_attributes::PathAttributesBuilder; -use crate::bgp::types::As4Path; use crate::{ asn::Asn, bgp::{ aspath::HopPath, message::{ - nlri::Nlri, update::OriginType, + nlri::Nlri, update_builder::StandardCommunitiesList, }, path_attributes::{ - AggregatorInfo, AsPathLimitInfo, AtomicAggregate, - AttributesMap, ClusterIds, ClusterList, + AttributesMap, ClusterIds, ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, - LargeCommunitiesList, Otc, PathAttribute, + LargeCommunitiesList, PathAttribute, PathAttributeType, }, }, - bgp::types::{ - OriginatorId, ConventionalNextHop, As4Aggregator, Connector - } }; #[derive(Debug)] @@ -75,29 +70,29 @@ pub trait FromAttribute { fn attribute_type() -> Option; } -macro_rules! from_attributes_impl { - ( - $( $variant:ident($output_ty:ty), $attr:ty )+ - ) => { - $( - impl FromAttribute for $attr { - type Output = $output_ty; - - fn from_attribute(value: PathAttribute) -> Option<$output_ty> { - if let PathAttribute::$variant(pa) = value { - Some(pa.inner()) - } else { - None - } - } +// macro_rules! from_attributes_impl { +// ( +// $( $variant:ident($output_ty:ty), $attr:ty )+ +// ) => { +// $( +// impl FromAttribute for $attr { +// type Output = $output_ty; + +// fn from_attribute(value: PathAttribute) -> Option<$output_ty> { +// if let PathAttribute::$variant(pa) = value { +// Some(pa.inner()) +// } else { +// None +// } +// } - fn attribute_type() -> Option { - Some(PathAttributeType::$variant) - } - } - )+ - } -} +// fn attribute_type() -> Option { +// Some(PathAttributeType::$variant) +// } +// } +// )+ +// } +// } // ident(ty) // 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, @@ -128,26 +123,26 @@ macro_rules! from_attributes_impl { // 255 => Reserved(ReservedRaw), Flags::OPT_TRANS, // PathAttribute variant(output type), impl for Attribute -from_attributes_impl!( - AsPath(HopPath), crate::bgp::aspath::AsPath - ConventionalNextHop(ConventionalNextHop), crate::bgp::types::ConventionalNextHop - MultiExitDisc(MultiExitDisc), crate::bgp::message::update::MultiExitDisc - Origin(OriginType), crate::bgp::types::OriginType - LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref - StandardCommunities(StandardCommunitiesList), StandardCommunitiesList - As4Path(As4Path), crate::bgp::aspath::AsPath> - AtomicAggregate(()), AtomicAggregate - Aggregator(AggregatorInfo), AggregatorInfo - OriginatorId(OriginatorId), crate::bgp::types::OriginatorId - ClusterList(ClusterIds), ClusterList - ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList - As4Aggregator(As4Aggregator), crate::bgp::types::As4Aggregator - Connector(Connector), crate::bgp::types::Connector - AsPathLimit(AsPathLimitInfo), AsPathLimitInfo - Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunitiesList - LargeCommunities(LargeCommunitiesList), LargeCommunitiesList - Otc(Asn), Otc -); +// from_attributes_impl!( +// AsPath(crate::bgp::aspath::HopPath), crate::bgp::aspath::AsPath +// ConventionalNextHop(crate::bgp::types::ConventionalNextHop), crate::bgp::types::ConventionalNextHop +// MultiExitDisc(crate::bgp::types::MultiExitDisc), crate::bgp::message::update::MultiExitDisc +// Origin(crate::bgp::types::OriginType), crate::bgp::types::OriginType +// LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref +// StandardCommunities(crate::bgp::message::update_builder::StandardCommunitiesList), crate::bgp::message::update_builder::StandardCommunitiesList +// As4Path(crate::bgp::types::As4Path), crate::bgp::aspath::AsPath> +// AtomicAggregate(()), crate::bgp::path_attributes::AtomicAggregate +// Aggregator(crate::bgp::path_attributes::AggregatorInfo), crate::bgp::path_attributes::AggregatorInfo +// OriginatorId(crate::bgp::types::OriginatorId), crate::bgp::types::OriginatorId +// ClusterList(crate::bgp::path_attributes::ClusterIds), crate::bgp::path_attributes::ClusterList +// ExtendedCommunities(crate::bgp::path_attributes::ExtendedCommunitiesList), crate::bgp::path_attributes::ExtendedCommunitiesList +// As4Aggregator(crate::bgp::types::As4Aggregator), crate::bgp::types::As4Aggregator +// Connector(crate::bgp::types::Connector), crate::bgp::types::Connector +// AsPathLimit(crate::bgp::path_attributes::AsPathLimitInfo), crate::bgp::path_attributes::AsPathLimitInfo +// Ipv6ExtendedCommunities(crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList), crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList +// LargeCommunities(crate::bgp::path_attributes::LargeCommunitiesList), crate::bgp::path_attributes::LargeCommunitiesList +// Otc(crate::asn::Asn), crate::bgp::path_attributes::Otc +// ); //------------ StandardCommunitiesList --------------------------------------- @@ -171,21 +166,21 @@ from_attributes_impl!( //------------ FromAttribute impls ------------------------------------------- -impl FromAttribute for crate::bgp::aspath::HopPath { - type Output = HopPath; +// impl FromAttribute for crate::bgp::aspath::HopPath { +// type Output = HopPath; - fn attribute_type() -> Option { - Some(PathAttributeType::AsPath) - } +// fn attribute_type() -> Option { +// Some(PathAttributeType::AsPath) +// } - fn from_attribute(value: PathAttribute) -> Option { - if let PathAttribute::AsPath(as_path) = value { - Some(as_path.0) - } else { - None - } - } -} +// fn from_attribute(value: PathAttribute) -> Option { +// if let PathAttribute::AsPath(as_path) = value { +// Some(as_path.0) +// } else { +// None +// } +// } +// } impl FromAttribute for crate::bgp::types::NextHop { type Output = NextHop; @@ -215,21 +210,21 @@ impl FromAttribute for crate::bgp::types::NextHop { // } // } -impl FromAttribute for crate::bgp::path_attributes::ClusterIds { - type Output = ClusterIds; +// impl FromAttribute for crate::bgp::path_attributes::ClusterIds { +// type Output = ClusterIds; - fn attribute_type() -> Option { - Some(PathAttributeType::ClusterList) - } +// fn attribute_type() -> Option { +// Some(PathAttributeType::ClusterList) +// } - fn from_attribute(value: PathAttribute) -> Option { - if let PathAttribute::ClusterList(cl) = value { - Some(cl.inner()) - } else { - None - } - } -} +// fn from_attribute(value: PathAttribute) -> Option { +// if let PathAttribute::ClusterList(cl) = value { +// Some(cl.inner()) +// } else { +// None +// } +// } +// } // impl FromAttribute for crate::bgp::path_attributes::OriginatorId { // type Output = OriginatorId; @@ -477,7 +472,7 @@ macro_rules! impl_workshop { } impl_workshop!( - crate::bgp::aspath::AsPath + // crate::bgp::aspath::AsPath crate::bgp::aspath::HopPath // crate::bgp::types::NextHop crate::bgp::types::LocalPref From 68d8235b3694c4415816575ca6ee3c972541f846 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 21:55:23 +0100 Subject: [PATCH 14/96] cleanup --- src/bgp/path_attributes.rs | 431 ++++++++++++++----------------------- 1 file changed, 159 insertions(+), 272 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index abcd29d8..cfde5a1b 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -6,12 +6,11 @@ use log::{debug, warn}; use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; use crate::asn::Asn; -use crate::bgp::aspath::HopPath; use crate::bgp::message::{ SessionConfig, UpdateMessage, nlri::FixedNlriIter, - update_builder::{ComposeError, UpdateBuilder}, + update_builder::ComposeError, }; use crate::bgp::message::update_builder::{ MpReachNlriBuilder, @@ -139,39 +138,46 @@ macro_rules! attribute { } -#[derive(Debug, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct MaterializedRoute2 { - pub path_attributes: AttributesMap, -} - //------------ PathAttributesBuilder ----------------------------------------- pub type AttributesMap = BTreeMap; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct PathAttributesBuilder { +pub struct PaMap { attributes: AttributesMap, } -impl PathAttributesBuilder { +impl PaMap { pub fn empty() -> Self { Self { attributes: BTreeMap::new() } } - pub fn attributes(&self) -> &AttributesMap { - &self.attributes - } - - pub fn attributes_mut(&mut self) -> &mut AttributesMap { - &mut self.attributes - } - - pub fn attributes_owned(self) -> AttributesMap { - self.attributes + // Assemble a AttributesMap, but skipping MP_*REACH_NLRI, so that the + // returned result is valid for all NLRI in this message. + pub fn from_update_pdu(pdu: &UpdateMessage) + -> Result + where + for<'a> Vec: OctetsFrom> + { + let mut pa_map = Self::empty(); + for pa in pdu.path_attributes()? { + if let Ok(pa) = pa { + if pa.type_code() != PathAttributeType::MpReachNlri + && pa.type_code() != PathAttributeType::MpUnreachNlri + { + if let PathAttributeType::Invalid(n) = pa.type_code() { + warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); + } + pa_map.attributes_mut().insert(pa.type_code(), pa.to_owned()?); + } + } else { + return Err(ComposeError::InvalidAttribute); + } + } + Ok(pa_map) } pub fn set>( @@ -184,7 +190,7 @@ impl PathAttributesBuilder { pub fn get( &self, - ) -> Option { + ) -> Option { if let Some(attr_type) = A::attribute_type() { self.attributes .get(&attr_type).and_then(|a| A::from_attribute(a.clone())) @@ -193,270 +199,168 @@ impl PathAttributesBuilder { } } - pub fn append(&mut self, other: &mut AttributesMap) { - self.attributes.append(other) + pub fn attributes(&self) -> &AttributesMap { + &self.attributes } - pub fn merge(&mut self, other: &Self) -> Result<(), ComposeError> { - for val in other.attributes.values().cloned() { - self.add_attribute(val)?; - } - Ok(()) + pub fn attributes_mut(&mut self) -> &mut AttributesMap { + &mut self.attributes } - pub fn into_inner(self) -> AttributesMap { + pub fn attributes_owned(self) -> AttributesMap { self.attributes } - pub fn into_update_builder(self) -> UpdateBuilder> { - UpdateBuilder::>::from_attributes_builder(self) - } - - pub fn from_update_builder(builder: UpdateBuilder) -> Self { - Self { - attributes: builder.into_attributes() - } - } - - pub fn add_attribute(&mut self, pa: PathAttribute) - -> Result<(), ComposeError> + pub fn remove(&mut self) -> Option { - if let PathAttribute::Invalid(..) = pa { - warn!( - "adding Invalid attribute to UpdateBuilder: {}", - &pa.type_code() - ); - } - if let Some(existing_pa) = self.attributes.get_mut(&pa.type_code()) { - *existing_pa = pa; + if let Some(attr_type) = A::attribute_type() { + self.attributes.remove(&attr_type).and_then(|a| A::from_attribute(a)) } else { - self.attributes.insert(pa.type_code(), pa); + None } - - Ok(()) } - pub fn remove_attribute(&mut self, pat: PathAttributeType) - -> Option - { - self.attributes.remove(&pat) + pub fn merge_upsert(&mut self, other: &mut Self) { + self.attributes_mut().append(other.attributes_mut()) } - // Assemble a AttributesMap, but skipping MP_*REACH_NLRI, so that the - // returned result is valid for all NLRI in this message. - pub fn from_update_pdu(pdu: &UpdateMessage) - -> Result - where - for<'a> Vec: OctetsFrom> - { - let mut res = Self::empty(); - for pa in pdu.path_attributes()? { - if let Ok(pa) = pa { - if pa.type_code() != PathAttributeType::MpReachNlri - && pa.type_code() != PathAttributeType::MpUnreachNlri - { - if let PathAttributeType::Invalid(n) = pa.type_code() { - warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); - } - res.add_attribute(pa.to_owned()?)?; - } - } else { - return Err(ComposeError::InvalidAttribute); - } + pub(in crate::bgp) fn get_owned(&mut self) -> Option { + if let Some(attr_type) = A::attribute_type() { + self.attributes_mut() + .get_mut(&attr_type).and_then(|a| A::from_attribute(std::mem::take(a))) + } else { + None } - Ok(res) } - //-------- Specific path attribute methods ------------------------------- - // - pub fn set_origin(&mut self, origin: OriginType) - -> Result<(), ComposeError> - { - self.add_attribute(Origin::new(origin).into()) - } - - pub fn set_aspath(&mut self , aspath: HopPath) - -> Result<(), ComposeError> - { - // XXX there should be a HopPath::compose_len really, instead of - // relying on .to_as_path() first. - if let Ok(wireformat) = aspath.to_as_path::>() { - if wireformat.compose_len() > u16::MAX.into() { - return Err(ComposeError::AttributeTooLarge( - PathAttributeType::AsPath, - wireformat.compose_len() - )); - } + pub(in crate::bgp) fn contains_attr(&self) -> bool { + if let Some(attr_type) = A::attribute_type() { + self.attributes().contains_key(&attr_type) } else { - return Err(ComposeError::InvalidAttribute) + false } - - self.add_attribute(AsPath::new(aspath).into()) } - pub fn prepend_to_aspath(&mut self, asn: Asn) -> Result<(), ComposeError> { - if let Some(PathAttribute::AsPath(as_path)) = self.attributes.get_mut(&PathAttributeType::AsPath) { - as_path.0.prepend( - asn, - ) - }; + pub fn len(&self) -> usize { self.attributes().len() } - Ok(()) - } + pub fn is_empty(&self) -> bool { self.attributes().is_empty() } - pub fn aspath(&self) -> Option { - self.attributes.get(&PathAttributeType::AsPath).and_then(|pa| - if let PathAttribute::AsPath(as_path) = pa { - Some(as_path.clone().inner()) - } else { - None - }) + // Length of the Path attributes in bytes + pub fn bytes_len(&self) -> usize { + self.attributes.values() + .fold(0, |sum, a| sum + a.compose_len()) } - pub fn set_multi_exit_disc(&mut self, med: MultiExitDisc) - -> Result<(), ComposeError> - { - self.add_attribute(med.into()) - } - pub fn set_local_pref(&mut self, local_pref: LocalPref) - -> Result<(), ComposeError> - { - self.add_attribute(local_pref.into()) - } -} + // pub fn append(&mut self, other: &mut AttributesMap) { + // self.attributes.append(other) + // } -impl From for PathAttributesBuilder { - fn from(value: AttributesMap) -> Self { - Self {attributes: value } - } -} + // pub fn merge(&mut self, other: &Self) -> Result<(), ComposeError> { + // for val in other.attributes.values().cloned() { + // self.add_attribute(val)?; + // } + // Ok(()) + // } -// macro_rules! from_attributes_impl { -// ( -// $( $variant:ident($output_ty:ty), $attr:ty )+ -// ) => { + // pub fn into_inner(self) -> AttributesMap { + // self.attributes + // } -// $( -// impl FromAttribute for $attr { -// type Output = $output_ty; - -// fn from_attribute(value: PathAttribute) -> Option<$output_ty> { -// if let PathAttribute::$variant(pa) = value { -// Some(pa.inner()) -// } else { -// None -// } -// } - -// fn attribute_type() -> PathAttributeType { -// PathAttributeType::$variant -// } -// } -// )+ -// } -// } + // pub fn into_update_builder(self) -> UpdateBuilder> { + // UpdateBuilder::>::from_attributes_builder(self) + // } -// ident(ty) -// 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, -// 2 => AsPath(HopPath), Flags::WELLKNOWN, -// 3 => NextHop(Ipv4Addr), Flags::WELLKNOWN, -// 4 => MultiExitDisc(u32), Flags::OPT_NON_TRANS, -// 5 => LocalPref(u32), Flags::WELLKNOWN, -// 6 => AtomicAggregate(()), Flags::WELLKNOWN, -// 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, -// 8 => Communities(StandardCommunitiesBuilder), Flags::OPT_TRANS, -// 9 => OriginatorId(Ipv4Addr), Flags::OPT_NON_TRANS, -// 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, -// 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, -// 15 => MpUnreachNlri(MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, -// 16 => ExtendedCommunities(ExtendedCommunitiesList), Flags::OPT_TRANS, -// 17 => As4Path(HopPath), Flags::OPT_TRANS, -// 18 => As4Aggregator(AggregatorInfo), Flags::OPT_TRANS, -// 20 => Connector(Ipv4Addr), Flags::OPT_TRANS, -// 21 => AsPathLimit(AsPathLimitInfo), Flags::OPT_TRANS, -// //22 => PmsiTunnel(todo), Flags::OPT_TRANS, -// 25 => Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, -// 32 => LargeCommunities(LargeCommunitiesList), Flags::OPT_TRANS, -// // 33 => BgpsecAsPath, -// 35 => Otc(Asn), Flags::OPT_TRANS, -// //36 => BgpDomainPath(TODO), Flags:: , // https://datatracker.ietf.org/doc/draft-ietf-bess-evpn-ipvpn-interworking/06/ -// //40 => BgpPrefixSid(TODO), Flags::OPT_TRANS, // https://datatracker.ietf.org/doc/html/rfc8669#name-bgp-prefix-sid-attribute -// 128 => AttrSet(AttributeSet), Flags::OPT_TRANS, -// 255 => Reserved(ReservedRaw), Flags::OPT_TRANS, - - -// PathAttribute variant(output type), impl for Attribute -// from_attributes_impl!( -// AsPath(HopPath), crate::bgp::aspath::AsPath -// AsPath(HopPath), crate::bgp::aspath::HopPath -// NextHop(Ipv4Addr), crate::bgp::types::NextHop -// MultiExitDisc(u32), crate::bgp::message::update::MultiExitDisc -// Origin(OriginType), crate::bgp::types::OriginType -// LocalPref(u32), crate::bgp::message::update::LocalPref -// Communities(StandardCommunitiesBuilder), crate::bgp::message::update_builder::StandardCommunitiesBuilder -// As4Path(HopPath), crate::bgp::aspath::AsPath> -// AtomicAggregate(()), AtomicAggregate -// Aggregator(AggregatorInfo), AggregatorInfo -// OriginatorId(Ipv4Addr), OriginatorId -// ClusterList(ClusterIds), ClusterList -// MpReachNlri(MpReachNlriBuilder), MpReachNlriBuilder -// MpUnreachNlri(MpUnreachNlriBuilder), MpUnreachNlriBuilder -// ExtendedCommunities(ExtendedCommunitiesList), ExtendedCommunitiesList -// As4Aggregator(AggregatorInfo), As4Aggregator -// Connector(Ipv4Addr), Connector -// AsPathLimit(AsPathLimitInfo), AsPathLimitInfo -// Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Ipv6ExtendedCommunities -// LargeCommunities(LargeCommunitiesList), LargeCommunitiesList -// Otc(Asn), Otc -// ); - -// impl From> for PathAttribute { -// fn from(value: crate::bgp::aspath::AsPath) -> Self { -// PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value.to_hop_path())) -// } -// } + // pub fn from_update_builder(builder: UpdateBuilder) -> Self { + // Self { + // attributes: builder.into_attributes() + // } + // } + + // fn add_attribute(&mut self, pa: PathAttribute) + // -> Result<(), ComposeError> + // { + // if let PathAttribute::Invalid(..) = pa { + // warn!( + // "adding Invalid attribute to UpdateBuilder: {}", + // &pa.type_code() + // ); + // } + // if let Some(existing_pa) = self.attributes.get_mut(&pa.type_code()) { + // *existing_pa = pa; + // } else { + // self.attributes.insert(pa.type_code(), pa); + // } + + // Ok(()) + // } +} -// impl From>> for PathAttribute { -// fn from(value: crate::bgp::aspath::AsPath>) -> Self { -// PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value.to_hop_path())) + //-------- Specific path attribute methods ------------------------------- + // +// pub fn set_origin(&mut self, origin: OriginType) +// -> Result<(), ComposeError> +// { +// self.add_attribute(Origin::new(origin).into()) // } -// } -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::NextHop) -> Self { -// if let crate::bgp::message::update::NextHop::Unicast(IpAddr::V4(ipv4)) = value { -// PathAttribute::NextHop(NextHop(ipv4)) +// pub fn set_aspath(&mut self , aspath: HopPath) +// -> Result<(), ComposeError> +// { +// // XXX there should be a HopPath::compose_len really, instead of +// // relying on .to_as_path() first. +// if let Ok(wireformat) = aspath.to_as_path::>() { +// if wireformat.compose_len() > u16::MAX.into() { +// return Err(ComposeError::AttributeTooLarge( +// PathAttributeType::AsPath, +// wireformat.compose_len() +// )); +// } // } else { -// panic!("WHERE'S MY TRANSPARANT NEXTHOP IMPLEMENTATION!!@!@!?!?!?!?!?"); +// return Err(ComposeError::InvalidAttribute) // } + +// self.add_attribute(AsPath::new(aspath).into()) // } -// } -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::MultiExitDisc) -> Self { -// PathAttribute::MultiExitDisc(crate::bgp::path_attributes::MultiExitDisc(value.0)) +// pub fn prepend_to_aspath(&mut self, asn: Asn) -> Result<(), ComposeError> { +// if let Some(PathAttribute::AsPath(as_path)) = self.attributes.get_mut(&PathAttributeType::AsPath) { +// as_path.0.prepend( +// asn, +// ) +// }; + +// Ok(()) // } -// } -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::OriginType) -> Self { -// PathAttribute::Origin(crate::bgp::path_attributes::Origin(value)) +// pub fn aspath(&self) -> Option { +// self.attributes.get(&PathAttributeType::AsPath).and_then(|pa| +// if let PathAttribute::AsPath(as_path) = pa { +// Some(as_path.clone().inner()) +// } else { +// None +// }) // } -// } -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::LocalPref) -> Self { -// PathAttribute::LocalPref(crate::bgp::path_attributes::LocalPref(value.0)) +// pub fn set_multi_exit_disc(&mut self, med: MultiExitDisc) +// -> Result<(), ComposeError> +// { +// self.add_attribute(med.into()) // } -// } -// impl From for PathAttribute { -// fn from(value: crate::bgp::message::update_builder::StandardCommunitiesBuilder) -> Self { -// PathAttribute::Communities(crate::bgp::path_attributes::Communities(value)) +// pub fn set_local_pref(&mut self, local_pref: LocalPref) +// -> Result<(), ComposeError> +// { +// self.add_attribute(local_pref.into()) // } // } +impl From for PaMap { + fn from(value: AttributesMap) -> Self { + Self {attributes: value } + } +} + macro_rules! path_attributes { ( @@ -534,37 +438,16 @@ macro_rules! path_attributes { } } - // $( - // impl TryFrom for $name { - // type Error = ComposeError; - - // fn try_from(value: PathAttribute) -> Result<$name, ComposeError> { - // if let PathAttribute::$name(pa) = value { - // Ok(pa) - // } else { - // Err(ComposeError::Todo) - // } - // } - // } - // )+ $( - // impl From for PathAttribute { - // fn from(value: StandardCommunitiesList) -> Self { - // PathAttribute::StandardCommunities( - // crate::bgp::path_attributes::StandardCommunities(value), - // ) - // } - // } - impl From<$data> for PathAttribute { - fn from(value: $data) -> Self { - PathAttribute::$name( - $name(value), - ) - } + impl From<$data> for PathAttribute { + fn from(value: $data) -> Self { + PathAttribute::$name( + $name(value), + ) } + } )+ - $( impl From<$name> for PathAttribute { fn from(pa: $name) -> PathAttribute { @@ -819,6 +702,12 @@ macro_rules! path_attributes { } */ +impl Default for PathAttribute { + fn default() -> Self { + Self::Unimplemented(UnimplementedPathAttribute{flags: Flags(0), type_code: 0, value: vec![]}) + } +} + //------------ PathAttributeType --------------------------------------------- #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -873,7 +762,7 @@ macro_rules! path_attributes { $( impl FromAttribute for $data { - type Output = $data; + // type Output = $data; fn from_attribute(value: PathAttribute) -> Option<$data> { if let PathAttribute::$name(pa) = value { @@ -1131,8 +1020,6 @@ macro_rules! check_len_exact { //--- Origin -use crate::bgp::message::update::OriginType; - impl Attribute for Origin { fn value_len(&self) -> usize { 1 } @@ -2083,8 +1970,7 @@ impl Attribute for Ipv6ExtendedCommunities { use crate::bgp::communities::LargeCommunity; -// use super::types::ConventionalNextHop; -use super::workshop::route::{FromAttribute, WorkshopAttribute}; +use super::workshop::route::FromAttribute; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct LargeCommunitiesList { @@ -2320,6 +2206,7 @@ mod tests { use crate::bgp::communities::Wellknown; use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::NextHop; + use crate::bgp::aspath::HopPath; #[test] fn wireformat_to_owned_and_back() { From 10bb67c89b51632843e8f937985b5268304c4c28 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 21:55:34 +0100 Subject: [PATCH 15/96] cleanup --- src/bgp/path_attributes.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index cfde5a1b..bc6a32f2 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -762,8 +762,6 @@ impl Default for PathAttribute { $( impl FromAttribute for $data { - // type Output = $data; - fn from_attribute(value: PathAttribute) -> Option<$data> { if let PathAttribute::$name(pa) = value { Some(pa.inner()) From 0b61d10b4caa39003bd504a9c1dfcbcdbc0926de Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 21:55:44 +0100 Subject: [PATCH 16/96] remove unused fn --- src/bgp/types.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bgp/types.rs b/src/bgp/types.rs index e8023c53..29699f29 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -379,12 +379,6 @@ impl std::fmt::Display for NextHop { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ConventionalNextHop(pub Ipv4Addr); -impl ConventionalNextHop { - fn new() -> Self { - Self(Ipv4Addr::from(0)) - } -} - impl std::fmt::Display for ConventionalNextHop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) From 2df9a19ecc54d5ba4b51804b3a6f75325309fbf8 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 21:56:12 +0100 Subject: [PATCH 17/96] cleanup --- src/bgp/workshop/route.rs | 518 +++----------------------------------- 1 file changed, 39 insertions(+), 479 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 17a31f43..8123cccc 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -1,29 +1,18 @@ use std::fmt::Debug; use std::hash::Hash; -use std::net::Ipv4Addr; use octseq::{Octets, OctetsFrom}; use serde::Serialize; use crate::bgp::communities::Community; -use crate::bgp::message::update::NextHop; use crate::bgp::message::update_builder::ComposeError; use crate::bgp::message::UpdateMessage; -use crate::bgp::path_attributes::PathAttributesBuilder; -use crate::{ - asn::Asn, - bgp::{ - aspath::HopPath, - message::{ - nlri::Nlri, - update_builder::StandardCommunitiesList, - }, - path_attributes::{ - AttributesMap, ClusterIds, - ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, - LargeCommunitiesList, PathAttribute, - PathAttributeType, - }, +use crate::bgp::path_attributes::PaMap; +use crate::bgp::{ + message::{nlri::Nlri, update_builder::StandardCommunitiesList}, + path_attributes::{ + AttributesMap, ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, + LargeCommunitiesList, PathAttribute, PathAttributeType, }, }; @@ -45,7 +34,7 @@ impl Route { &self.0 } - pub fn get_attr(&self) -> Option { + pub fn get_attr(&self) -> Option { if let Some(attr_type) = A::attribute_type() { self.1 .get(&attr_type) @@ -65,188 +54,16 @@ impl Route { } pub trait FromAttribute { - type Output; - fn from_attribute(value: PathAttribute) -> Option; + fn from_attribute(value: PathAttribute) -> Option + where + Self: Sized; fn attribute_type() -> Option; } -// macro_rules! from_attributes_impl { -// ( -// $( $variant:ident($output_ty:ty), $attr:ty )+ -// ) => { -// $( -// impl FromAttribute for $attr { -// type Output = $output_ty; - -// fn from_attribute(value: PathAttribute) -> Option<$output_ty> { -// if let PathAttribute::$variant(pa) = value { -// Some(pa.inner()) -// } else { -// None -// } -// } - -// fn attribute_type() -> Option { -// Some(PathAttributeType::$variant) -// } -// } -// )+ -// } -// } - -// ident(ty) -// 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, -// 2 => AsPath(HopPath), Flags::WELLKNOWN, -// 3 => NextHop(Ipv4Addr), Flags::WELLKNOWN, -// 4 => MultiExitDisc(u32), Flags::OPT_NON_TRANS, -// 5 => LocalPref(u32), Flags::WELLKNOWN, -// 6 => AtomicAggregate(()), Flags::WELLKNOWN, -// 7 => Aggregator(AggregatorInfo), Flags::OPT_TRANS, -// 8 => Communities(StandardCommunitiesBuilder), Flags::OPT_TRANS, -// 9 => OriginatorId(Ipv4Addr), Flags::OPT_NON_TRANS, -// 10 => ClusterList(ClusterIds), Flags::OPT_NON_TRANS, -// 14 => MpReachNlri(MpReachNlriBuilder), Flags::OPT_NON_TRANS, -// 15 => MpUnreachNlri(MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, -// 16 => ExtendedCommunities(ExtendedCommunitiesList), Flags::OPT_TRANS, -// 17 => As4Path(HopPath), Flags::OPT_TRANS, -// 18 => As4Aggregator(AggregatorInfo), Flags::OPT_TRANS, -// 20 => Connector(Ipv4Addr), Flags::OPT_TRANS, -// 21 => AsPathLimit(AsPathLimitInfo), Flags::OPT_TRANS, -// //22 => PmsiTunnel(todo), Flags::OPT_TRANS, -// 25 => Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList), Flags::OPT_TRANS, -// 32 => LargeCommunities(LargeCommunitiesList), Flags::OPT_TRANS, -// // 33 => BgpsecAsPath, -// 35 => Otc(Asn), Flags::OPT_TRANS, -// //36 => BgpDomainPath(TODO), Flags:: , // https://datatracker.ietf.org/doc/draft-ietf-bess-evpn-ipvpn-interworking/06/ -// //40 => BgpPrefixSid(TODO), Flags::OPT_TRANS, // https://datatracker.ietf.org/doc/html/rfc8669#name-bgp-prefix-sid-attribute -// 128 => AttrSet(AttributeSet), Flags::OPT_TRANS, -// 255 => Reserved(ReservedRaw), Flags::OPT_TRANS, - -// PathAttribute variant(output type), impl for Attribute -// from_attributes_impl!( -// AsPath(crate::bgp::aspath::HopPath), crate::bgp::aspath::AsPath -// ConventionalNextHop(crate::bgp::types::ConventionalNextHop), crate::bgp::types::ConventionalNextHop -// MultiExitDisc(crate::bgp::types::MultiExitDisc), crate::bgp::message::update::MultiExitDisc -// Origin(crate::bgp::types::OriginType), crate::bgp::types::OriginType -// LocalPref(crate::bgp::types::LocalPref), crate::bgp::message::update::LocalPref -// StandardCommunities(crate::bgp::message::update_builder::StandardCommunitiesList), crate::bgp::message::update_builder::StandardCommunitiesList -// As4Path(crate::bgp::types::As4Path), crate::bgp::aspath::AsPath> -// AtomicAggregate(()), crate::bgp::path_attributes::AtomicAggregate -// Aggregator(crate::bgp::path_attributes::AggregatorInfo), crate::bgp::path_attributes::AggregatorInfo -// OriginatorId(crate::bgp::types::OriginatorId), crate::bgp::types::OriginatorId -// ClusterList(crate::bgp::path_attributes::ClusterIds), crate::bgp::path_attributes::ClusterList -// ExtendedCommunities(crate::bgp::path_attributes::ExtendedCommunitiesList), crate::bgp::path_attributes::ExtendedCommunitiesList -// As4Aggregator(crate::bgp::types::As4Aggregator), crate::bgp::types::As4Aggregator -// Connector(crate::bgp::types::Connector), crate::bgp::types::Connector -// AsPathLimit(crate::bgp::path_attributes::AsPathLimitInfo), crate::bgp::path_attributes::AsPathLimitInfo -// Ipv6ExtendedCommunities(crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList), crate::bgp::path_attributes::Ipv6ExtendedCommunitiesList -// LargeCommunities(crate::bgp::path_attributes::LargeCommunitiesList), crate::bgp::path_attributes::LargeCommunitiesList -// Otc(crate::asn::Asn), crate::bgp::path_attributes::Otc -// ); - -//------------ StandardCommunitiesList --------------------------------------- - -// pub struct StandardCommunitiesList(Vec); - -// impl FromAttribute for StandardCommunitiesList { -// type Output = StandardCommunitiesList; - -// fn attribute_type() -> PathAttributeType { -// PathAttributeType::StandardCommunities -// } - -// fn from_attribute(value: PathAttribute) -> Option { -// if let PathAttribute::StandardCommunities(comms) = value { -// Some(StandardCommunitiesList(comms.0.communities().clone())) -// } else { -// None -// } -// } -// } - -//------------ FromAttribute impls ------------------------------------------- - -// impl FromAttribute for crate::bgp::aspath::HopPath { -// type Output = HopPath; - -// fn attribute_type() -> Option { -// Some(PathAttributeType::AsPath) -// } - -// fn from_attribute(value: PathAttribute) -> Option { -// if let PathAttribute::AsPath(as_path) = value { -// Some(as_path.0) -// } else { -// None -// } -// } -// } - -impl FromAttribute for crate::bgp::types::NextHop { - type Output = NextHop; - - fn attribute_type() -> Option { - None - } - - fn from_attribute(_value: PathAttribute) -> Option { - None - } -} - -// impl FromAttribute for crate::bgp::path_attributes::MultiExitDisc { -// type Output = crate::bgp::path_attributes::MultiExitDisc; - -// fn attribute_type() -> Option { -// Some(PathAttributeType::MultiExitDisc) -// } - -// fn from_attribute(value: PathAttribute) -> Option { -// if let PathAttribute::MultiExitDisc(cl) = value { -// Some(cl) -// } else { -// None -// } -// } -// } - -// impl FromAttribute for crate::bgp::path_attributes::ClusterIds { -// type Output = ClusterIds; - -// fn attribute_type() -> Option { -// Some(PathAttributeType::ClusterList) -// } - -// fn from_attribute(value: PathAttribute) -> Option { -// if let PathAttribute::ClusterList(cl) = value { -// Some(cl.inner()) -// } else { -// None -// } -// } -// } - -// impl FromAttribute for crate::bgp::path_attributes::OriginatorId { -// type Output = OriginatorId; - -// fn attribute_type() -> Option { -// Some(PathAttributeType::OriginatorId) -// } - -// fn from_attribute(value: PathAttribute) -> Option { -// if let PathAttribute::OriginatorId(cl) = value { -// Some(cl.inner()) -// } else { -// None -// } -// } -// } - - //------------ From impls for PathAttribute ---------------------------------- // These conversions are used on the `set()` method of the -// PathAttributesBuilder, so not in the Workshop! +// PaMap, so not in the Workshop! impl From> for PathAttribute { fn from(value: crate::bgp::aspath::AsPath) -> Self { @@ -264,141 +81,10 @@ impl From>> for PathAttribute { } } -// impl From for PathAttribute { -// fn from(value: crate::bgp::aspath::HopPath) -> Self { -// PathAttribute::AsPath(crate::bgp::path_attributes::AsPath(value)) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::NextHop) -> Self { -// if let crate::bgp::message::update::NextHop::Unicast(IpAddr::V4( -// ipv4, -// )) = value -// { -// PathAttribute::NextHop(NextHop(ipv4)) -// } else { -// panic!("WHERE'S MY TRANSPARANT NEXTHOP IMPLEMENTATION!!@?!?"); -// } -// } -// } - -// impl From for PathAttribute { -// fn from(value: LargeCommunitiesList) -> Self { -// PathAttribute::LargeCommunities( -// crate::bgp::path_attributes::LargeCommunities(value), -// ) -// } -// } - -// impl From for PathAttribute { -// fn from(value: ExtendedCommunitiesList) -> Self { -// PathAttribute::ExtendedCommunities( -// crate::bgp::path_attributes::ExtendedCommunities(value), -// ) -// } -// } - -// impl From for PathAttribute { -// fn from(value: Ipv6ExtendedCommunitiesList) -> Self { -// PathAttribute::Ipv6ExtendedCommunities( -// crate::bgp::path_attributes::Ipv6ExtendedCommunities(value), -// ) -// } -// } - -// impl From for PathAttribute { -// fn from(value: Asn) -> Self { -// PathAttribute::Otc(crate::bgp::path_attributes::Otc(value)) -// } -// } - -// impl From for PathAttribute { -// fn from(value: StandardCommunitiesList) -> Self { -// PathAttribute::StandardCommunities( -// crate::bgp::path_attributes::StandardCommunities(value), -// ) -// } -// } - -// impl From for PathAttribute { -// fn from(value: CommunitiesWorkshop) -> Self { -// let mut std_comms = StandardCommunitiesBuilder::new(); -// for comm in value.0 { -// if let Community::Standard(stdc) = comm { -// std_comms.add_community(stdc) -// } -// } - -// PathAttribute::Communities(crate::bgp::path_attributes::Communities( -// std_comms, -// )) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::path_attributes::MultiExitDisc) -> Self { -// PathAttribute::MultiExitDisc( -// crate::bgp::path_attributes::MultiExitDisc(value.0), -// ) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::OriginType) -> Self { -// PathAttribute::Origin(crate::bgp::path_attributes::Origin(value)) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::types::LocalPref) -> Self { -// PathAttribute::LocalPref(crate::bgp::path_attributes::LocalPref( -// value, -// )) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::path_attributes::AggregatorInfo) -> Self { -// PathAttribute::Aggregator(crate::bgp::path_attributes::Aggregator( -// value, -// )) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::path_attributes::AsPathLimitInfo) -> Self { -// PathAttribute::AsPathLimit(crate::bgp::path_attributes::AsPathLimit( -// value, -// )) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::path_attributes::ClusterIds) -> Self { -// PathAttribute::ClusterList(crate::bgp::path_attributes::ClusterList( -// value, -// )) -// } -// } - -// impl From for PathAttribute { -// fn from(value: crate::bgp::path_attributes::StandardCommunities) -> Self { -// let mut b = StandardCommunitiesBuilder::with_capacity(value.0.len()); -// value.0.into_iter().for_each(|c| b.add_community(c)); -// PathAttribute::StandardCommunities(crate::bgp::path_attributes::StandardCommunities( -// b, -// )) -// } -// } - //------------ The Workshop -------------------------------------------------- #[derive(Debug)] -pub struct RouteWorkshop( - Option, - Option, -); +pub struct RouteWorkshop(Option, Option); impl RouteWorkshop { pub fn from_update_pdu( @@ -407,7 +93,7 @@ impl RouteWorkshop { where for<'a> Vec: OctetsFrom>, { - PathAttributesBuilder::from_update_pdu(pdu) + PaMap::from_update_pdu(pdu) .map(|r| Self(None, Some(r))) } @@ -428,10 +114,7 @@ impl RouteWorkshop { pub fn get_attr>( &self, - ) -> Option - where - A::Output: WorkshopAttribute, - { + ) -> Option { self.1 .as_ref() .and_then(|b| b.get::().or_else(|| ::into_retrieved(b))) @@ -462,19 +145,17 @@ macro_rules! impl_workshop { ) => { $( impl WorkshopAttribute for $attr { - fn to_value(local_attrs: Self, attrs: &mut PathAttributesBuilder) -> + fn to_value(local_attrs: Self, attrs: &mut PaMap) -> Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } - fn into_retrieved(_attrs: &PathAttributesBuilder) -> - Option { None } + fn into_retrieved(_attrs: &PaMap) -> + Option { None } } )+ } } impl_workshop!( - // crate::bgp::aspath::AsPath crate::bgp::aspath::HopPath - // crate::bgp::types::NextHop crate::bgp::types::LocalPref crate::bgp::types::MultiExitDisc crate::bgp::types::OriginType @@ -486,36 +167,25 @@ impl_workshop!( crate::bgp::path_attributes::LargeCommunitiesList crate::bgp::path_attributes::ClusterIds crate::bgp::message::update_builder::StandardCommunitiesList + crate::bgp::types::Otc ); -// impl WorkshopAttribute for () { -// fn into_retrieved(_attrs: &PathAttributesBuilder) -> Option { -// None -// } - -// fn to_value( -// local_attrs: Self, -// attrs: &mut PathAttributesBuilder, -// ) -> Result<(), ComposeError> { -// attrs.set(crate::bgp::path_attributes::AtomicAggregate(local_attrs)); -// Ok(()) -// } -// } - -//------------ Workshop ------------------------------------------------------ +//------------ WorkshopAttribute --------------------------------------------- pub trait WorkshopAttribute: FromAttribute { - fn into_retrieved(attrs: &PathAttributesBuilder) -> Option where Self: Sized; + fn into_retrieved(attrs: &PaMap) -> Option + where + Self: Sized; fn to_value( local_attrs: Self, - attrs: &mut PathAttributesBuilder, + attrs: &mut PaMap, ) -> Result<(), ComposeError>; } //------------ CommunitiesWorkshop ------------------------------------------- impl WorkshopAttribute for Vec { - fn into_retrieved(attrs: &PathAttributesBuilder) -> Option { + fn into_retrieved(attrs: &PaMap) -> Option { let mut c = attrs .get::() .unwrap() @@ -544,7 +214,7 @@ impl WorkshopAttribute for Vec { fn to_value( local_attr: Self, - attrs: &mut PathAttributesBuilder, + attrs: &mut PaMap, ) -> Result<(), ComposeError> { for comm in local_attr { match comm { @@ -577,118 +247,8 @@ impl WorkshopAttribute for Vec { } } -// pub struct CommunitiesWorkshop; - -// impl CommunitiesWorkshop { -// pub fn add_community( -// attrs: &mut PathAttributesBuilder, -// comm: Community, -// ) -> Result<(), ComposeError> { -// match comm { -// Community::Standard(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// Community::Extended(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// Community::Ipv6Extended(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// Community::Large(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// }; -// Ok(()) -// } -// } - -// impl Workshop for CommunitiesWorkshop { -// type Output = Vec; - -// fn into_retrieved(self, attrs: &PathAttributesBuilder) -> Vec { -// let mut c = attrs -// .get::() -// .unwrap() -// .fmap(|c| Community::Standard(*c)); -// c.append( -// &mut attrs -// .get::() -// .unwrap() -// .fmap(Community::Extended), -// ); -// c.append( -// &mut attrs -// .get::() -// .unwrap() -// .fmap(Community::Ipv6Extended), -// ); -// c.append( -// &mut attrs -// .get::() -// .unwrap() -// .fmap(Community::Large), -// ); - -// c -// } - -// fn to_value( -// local_attr: Vec, -// attrs: &mut PathAttributesBuilder, -// ) -> Result<(), ComposeError> { -// for comm in local_attr { -// match comm { -// Community::Standard(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// Community::Extended(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// Community::Ipv6Extended(c) => { -// if let Some(mut b) = attrs.get::() { -// b.add_community(c) -// } -// }, -// Community::Large(_) => todo!(), -// }; -// } - -// Ok(()) -// } -// } - -// impl FromAttribute for CommunitiesWorkshop { -// type Output = CommunitiesWorkshop; - -// fn attribute_type() -> PathAttributeType { -// PathAttributeType::Communities -// } - -// fn from_attribute(value: PathAttribute) -> Option { -// if let PathAttribute::Communities(_) = value { -// Some(CommunitiesWorkshop) -// } else { -// None -// } -// } -// } - impl FromAttribute for Vec { - type Output = Self; - - fn from_attribute(_value: PathAttribute) -> Option { + fn from_attribute(_value: PathAttribute) -> Option { None } @@ -699,21 +259,21 @@ impl FromAttribute for Vec { //------------ NextHopWorkshop ----------------------------------------------- -// impl FromAttribute for NextHop { -// type Output = Self; - -// fn from_attribute(_value: PathAttribute) -> Option { -// todo!() -// } +impl FromAttribute for crate::bgp::types::NextHop { + fn attribute_type() -> Option { + None + } -// fn attribute_type() -> PathAttributeType { -// todo!() -// } -// } + fn from_attribute(_value: PathAttribute) -> Option { + None + } +} impl WorkshopAttribute for crate::bgp::types::NextHop { - fn into_retrieved(attrs: &PathAttributesBuilder) -> Option { - if let Some(next_hop) = attrs.get::() { + fn into_retrieved(attrs: &PaMap) -> Option { + if let Some(next_hop) = + attrs.get::() + { Some(crate::bgp::types::NextHop::Unicast(next_hop.0.into())) } else if let Some(PathAttribute::MpReachNlri(nlri)) = attrs.attributes().get(&PathAttributeType::MpReachNlri) @@ -726,7 +286,7 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { fn to_value( local_attr: Self, - attrs: &mut PathAttributesBuilder, + attrs: &mut PaMap, ) -> Result<(), ComposeError> { if let Some(PathAttribute::MpReachNlri(nlri)) = attrs.attributes().get(&PathAttributeType::MpReachNlri) From 732a802c0f5eea11ad92604147ee299b5dad178e Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 22:29:30 +0100 Subject: [PATCH 18/96] Use PaMap in update_builder --- src/bgp/message/update_builder.rs | 345 ++++++++++++++---------------- 1 file changed, 166 insertions(+), 179 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 67926eb2..371ddf29 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -24,7 +24,9 @@ use crate::bgp::path_attributes::{ MultiExitDisc, ConventionalNextHop, Origin, - AttributesMap, PathAttribute, PathAttributesBuilder, PathAttributeType + PaMap, + PathAttribute, + PathAttributeType }; //------------ UpdateBuilder ------------------------------------------------- @@ -37,7 +39,7 @@ pub struct UpdateBuilder { withdrawals: Vec>>, addpath_enabled: Option, // for conventional nlri (unicast v4) // attributes: - attributes: AttributesMap + attributes: PaMap, } impl UpdateBuilder { @@ -53,22 +55,22 @@ where Target: octseq::Truncate let mut h = Header::<&[u8]>::new(); h.set_length(19 + 2 + 2); h.set_type(MsgType::Update); - let _ =target.append_slice(h.as_ref()); + let _ = target.append_slice(h.as_ref()); Ok(UpdateBuilder { target, announcements: Vec::new(), withdrawals: Vec::new(), addpath_enabled: None, - attributes: AttributesMap::new() + attributes: PaMap::empty(), }) } - pub fn from_attributes_builder(attributes: PathAttributesBuilder) - -> UpdateBuilder> - { + pub fn from_attributes_builder( + attributes: PaMap, + ) -> UpdateBuilder> { let mut res = UpdateBuilder::>::new_vec(); - res.attributes = attributes.into_inner(); + res.attributes = attributes; res } @@ -139,22 +141,25 @@ where Target: octseq::Truncate // Again, like with Communities, we cant rely on // entry().or_insert(), because that does not do a max pdu // length check and it does not allow us to error out. - - if !self.attributes.contains_key( - &PathAttributeType::MpUnreachNlri - ) { - self.add_attribute(MpUnreachNlri::new( - MpUnreachNlriBuilder::new( - Afi::Ipv6, Safi::Unicast, - b.is_addpath() - ) - ).into())?; + + if !self + .attributes + .contains_attr::() + { + self.add_attribute( + MpUnreachNlri::new(MpUnreachNlriBuilder::new( + Afi::Ipv6, + Safi::Unicast, + b.is_addpath(), + )) + .into(), + )?; } - let pa = self.attributes.get_mut(&PathAttributeType::MpUnreachNlri) + + let mut builder = self + .attributes + .get_owned::() .unwrap(); // Just added it, so we know it is there. - - if let PathAttribute::MpUnreachNlri(ref mut pa) = pa { - let builder = pa.as_mut(); if !builder.valid_combination( Afi::Ipv6, Safi::Unicast, b.is_addpath() @@ -166,15 +171,11 @@ where Target: octseq::Truncate return Err(ComposeError::IllegalCombination); } - builder.add_withdrawal(withdrawal); - - } else { - unreachable!() - } + builder.add_withdrawal(withdrawal); + self.attributes.set::(builder); } - } - _ => todo!() // TODO + _ => todo!(), // TODO }; Ok(()) } @@ -211,21 +212,24 @@ where Target: octseq::Truncate /// the new Path Attribute would cause the total PDU length to exceed the /// maximum, a `ComposeError::PduTooLarge` is returned. - pub fn add_attribute(&mut self, pa: PathAttribute) - -> Result<(), ComposeError> - { + pub fn add_attribute( + &mut self, + pa: PathAttribute, + ) -> Result<(), ComposeError> { if let PathAttribute::Invalid(..) = pa { warn!( "adding Invalid attribute to UpdateBuilder: {}", - &pa.type_code() + &pa.type_code() ); } - if let Some(existing_pa) = self.attributes.get_mut(&pa.type_code()) { + if let Some(existing_pa) = + self.attributes.attributes_mut().get_mut(&pa.type_code()) + { *existing_pa = pa; } else { - self.attributes.insert(pa.type_code(), pa); + self.attributes.attributes_mut().insert(pa.type_code(), pa); } - + Ok(()) } @@ -254,21 +258,29 @@ where Target: octseq::Truncate self.add_attribute(AsPath::new(aspath).into()) } - pub fn set_conventional_nexthop(&mut self, addr: Ipv4Addr) -> Result<(), ComposeError> { - self.add_attribute(ConventionalNextHop::new(crate::bgp::types::ConventionalNextHop(addr)).into()) + pub fn set_conventional_nexthop( + &mut self, + addr: Ipv4Addr, + ) -> Result<(), ComposeError> { + self.add_attribute( + ConventionalNextHop::new(crate::bgp::types::ConventionalNextHop( + addr, + )) + .into(), + ) } - pub fn set_mp_nexthop(&mut self, nexthop: NextHop) -> Result<(), ComposeError> { - if let Some(PathAttribute::MpReachNlri(ref mut pa)) = self.attributes.get_mut( - &PathAttributeType::MpReachNlri - ) { - let builder = pa.as_mut(); - builder.set_nexthop(nexthop) - } else { - // We need to know the AfiSafi of MP_REACH_NLRI, so it should be - // added first via either an NLRI, or explicitly via fn new. - Err(ComposeError::IllegalCombination) - } + pub fn set_mp_nexthop( + &mut self, + nexthop: NextHop, + ) -> Result<(), ComposeError> { + let mut builder = self + .attributes + .get_owned::() + .ok_or(ComposeError::IllegalCombination)?; + builder.set_nexthop(nexthop)?; + self.attributes.set(builder); + Ok(()) } pub fn set_nexthop_unicast(&mut self, addr: IpAddr) -> Result<(), ComposeError> { @@ -283,29 +295,27 @@ where Target: octseq::Truncate { // We could/should check for addr.is_unicast_link_local() once that // lands in stable. - - if let Some(ref mut pa) = self.attributes.get_mut( - &PathAttributeType::MpReachNlri - ) { - if let PathAttribute::MpReachNlri(ref mut pa) = pa { - let builder = pa.as_mut(); - match builder.get_nexthop() { - NextHop::Unicast(a) if a.is_ipv6() => { } , - NextHop::Ipv6LL(_,_) => { }, - _ => return Err(ComposeError::IllegalCombination), - } - - builder.set_nexthop_ll_addr(addr); - } else { - unreachable!() + if let Some(mut builder) = + self.attributes.get_owned::() + { + match builder.get_nexthop() { + NextHop::Unicast(a) if a.is_ipv6() => {} + NextHop::Ipv6LL(_, _) => {} + _ => return Err(ComposeError::IllegalCombination), } + + builder.set_nexthop_ll_addr(addr); + self.attributes.set(builder); } else { - self.add_attribute(MpReachNlri::new( - MpReachNlriBuilder::new( - Afi::Ipv6, Safi::Unicast, NextHop::Ipv6LL(0.into(), addr), - false - ) - ).into())?; + self.add_attribute( + MpReachNlri::new(MpReachNlriBuilder::new( + Afi::Ipv6, + Safi::Unicast, + NextHop::Ipv6LL(0.into(), addr), + false, + )) + .into(), + )?; } Ok(()) @@ -349,30 +359,29 @@ where Target: octseq::Truncate ); } n => { - if !self.attributes.contains_key(&PathAttributeType::MpReachNlri) { - self.add_attribute(MpReachNlri::new( - MpReachNlriBuilder::new_for_nlri(n) - ).into())?; + if !self.attributes.contains_attr::() { + self.add_attribute( + MpReachNlri::new(MpReachNlriBuilder::new_for_nlri(n)) + .into(), + )?; } - let pa = self.attributes.get_mut(&PathAttributeType::MpReachNlri) + let mut builder = self + .attributes + .get_owned::() .unwrap(); // Just added it, so we know it is there. - - if let PathAttribute::MpReachNlri(ref mut pa) = pa { - let builder = pa.as_mut(); - - if !builder.valid_combination(n) { - // We are already constructing a - // MP_UNREACH_NLRI but for a different - // AFI,SAFI than the prefix in `announcement`, - // or we are mixing addpath with non-addpath. - return Err(ComposeError::IllegalCombination); - } - builder.add_announcement(announcement); - } else { - unreachable!() + if !builder.valid_combination(n) { + // We are already constructing a + // MP_UNREACH_NLRI but for a different + // AFI,SAFI than the prefix in `announcement`, + // or we are mixing addpath with non-addpath. + self.attributes.set(builder); + return Err(ComposeError::IllegalCombination); } + + builder.add_announcement(announcement); + self.attributes.set(builder); } } @@ -392,25 +401,24 @@ where Target: octseq::Truncate } //--- Standard communities - - pub fn add_community(&mut self, community: StandardCommunity) - -> Result<(), ComposeError> - { - if !self.attributes.contains_key(&PathAttributeType::StandardCommunities) { - self.add_attribute(StandardCommunities::new( - StandardCommunitiesList::new() - ).into())?; - } - let pa = self.attributes.get_mut(&PathAttributeType::StandardCommunities) + + pub fn add_community( + &mut self, + community: StandardCommunity, + ) -> Result<(), ComposeError> { + if !self.attributes.contains_attr::() { + self.add_attribute( + StandardCommunities::new(StandardCommunitiesList::new()) + .into(), + )?; + } + let mut builder = self + .attributes + .get_owned::() .unwrap(); // Just added it, so we know it is there. - - if let PathAttribute::StandardCommunities(ref mut pa) = pa { - let builder = pa.as_mut(); - builder.add_community(community); - Ok(()) - } else { - unreachable!() - } + builder.add_community(community); + self.attributes.set(builder); + Ok(()) } } @@ -602,50 +610,32 @@ impl UpdateBuilder )?) } - pub fn into_attributes(self) -> AttributesMap { - self.attributes - } - - pub fn attributes(&self) -> &AttributesMap { + pub fn attributes(&self) -> &PaMap { &self.attributes } - - pub fn from_map(&self, map: &mut AttributesMap) -> PathAttributesBuilder { - let mut pab = PathAttributesBuilder::empty(); - pab.append(map); + pub fn from_map(&self, map: &mut PaMap) -> PaMap { + let mut pab = PaMap::empty(); + pab.merge_upsert(map); pab } - // Check whether the combination of NLRI and attributes would produce a // valid UPDATE pdu. fn is_valid(&self) -> Result<(), ComposeError> { // If we have builders for MP_(UN)REACH_NLRI, they should carry // prefixes. - if let Some(pa) = self.attributes.get( - &PathAttributeType::MpReachNlri - ) { - if let PathAttribute::MpReachNlri(pa) = pa { - if pa.as_ref().is_empty() { - return Err(ComposeError::EmptyMpReachNlri); - } - } else { - unreachable!() + if let Some(pa) = self.attributes.get::() { + if pa.is_empty() { + return Err(ComposeError::EmptyMpReachNlri); } } - if let Some(pa) = self.attributes.get( - &PathAttributeType::MpUnreachNlri - ) { - if let PathAttribute::MpUnreachNlri(pa) = pa { - // FIXME an empty MP_UNREACH_NLRI can be valid when signaling - // EoR, but then it has to be the only path attribute. - if pa.as_ref().is_empty() { - return Err(ComposeError::EmptyMpUnreachNlri); - } - } else { - unreachable!() + if let Some(pa) = self.attributes.get::() { + // FIXME an empty MP_UNREACH_NLRI can be valid when signaling + // EoR, but then it has to be the only path attribute. + if pa.is_empty() { + return Err(ComposeError::EmptyMpUnreachNlri); } } @@ -661,8 +651,7 @@ impl UpdateBuilder .fold(0, |sum, w| sum + w.compose_len()); // Path attributes, 2 bytes for length + N bytes for attributes: - res += 2 + self.attributes.values() - .fold(0, |sum, pa| sum + pa.compose_len()); + res += 2 + self.attributes.bytes_len(); // Announcements, no length bytes: res += self.announcements.iter() @@ -675,9 +664,9 @@ impl UpdateBuilder // TODO add more 'quick returns' here, e.g. for MpUnreachNlri or // conventional withdrawals/announcements. - if let Some(PathAttribute::MpReachNlri(b)) = self.attributes.get(&PathAttributeType::MpReachNlri) { - if b.as_ref().announcements.len() * 2 > max { - return true + if let Some(b) = self.attributes.get::() { + if b.announcements.len() * 2 > max { + return true; } } self.calculate_pdu_length() > max @@ -736,33 +725,35 @@ impl UpdateBuilder // Scenario 2: many withdrawals in MP_UNREACH_NLRI // At this point, we have no conventional withdrawals anymore. - - let maybe_pdu = - if let Some(PathAttribute::MpUnreachNlri(b)) = self.attributes.get_mut(&PathAttributeType::MpUnreachNlri) { - let unreach_builder = b.as_mut(); - let mut split_at = 0; - if !unreach_builder.withdrawals.is_empty() { - let mut compose_len = 0; - for (idx, w) in unreach_builder.withdrawals.iter().enumerate() { - compose_len += w.compose_len(); - if compose_len > 4000 { - split_at = idx; - break; - } + + let maybe_pdu = if let Some(mut unreach_builder) = + self.attributes.get_owned::() + { + let mut split_at = 0; + if !unreach_builder.withdrawals.is_empty() { + let mut compose_len = 0; + for (idx, w) in unreach_builder.withdrawals.iter().enumerate() + { + compose_len += w.compose_len(); + if compose_len > 4000 { + split_at = idx; + break; } + } - let this_batch = unreach_builder.split(split_at); - let mut builder = Self::from_target(self.target.clone()).unwrap(); - builder.add_attribute(MpUnreachNlri::new(this_batch).into()).unwrap(); + let this_batch = unreach_builder.split(split_at); + self.attributes.set(unreach_builder); + let mut builder = + Self::from_target(self.target.clone()).unwrap(); + builder.attributes.set(this_batch); - Some(builder.into_message()) - } else { - None - } + Some(builder.into_message()) } else { None } - ; + } else { + None + }; // Bit of a clumsy workaround as we can not return Some(self) from // within the if let ... self.attributes.get_mut above if let Some(pdu) = maybe_pdu { @@ -775,10 +766,7 @@ impl UpdateBuilder // have no conventional withdrawals left, and no MP_UNREACH_NLRI. if !self.announcements.is_empty() { - //let announcement_len = self.announcements.iter() - // .fold(0, |sum, a| sum + a.compose_len()); - - let split_at = std::cmp::min(self.announcements.len() / 2, 450); + let split_at = std::cmp::min(self.announcements.len() / 2, 450); let this_batch = self.announcements.drain(..split_at); let mut builder = Self::from_target(self.target.clone()).unwrap(); @@ -786,7 +774,6 @@ impl UpdateBuilder builder.attributes = self.attributes.clone(); return (builder.into_message(), Some(self)); - } // Scenario 4: many MP_REACH_NLRI announcements @@ -799,13 +786,13 @@ impl UpdateBuilder // sets. The first PDUs take longer to construct than the later ones. // Flamegraph currently hints at the fn split on MpReachNlriBuilder. - let maybe_pdu = - if let Some(PathAttribute::MpReachNlri(b)) = self.attributes.remove(&PathAttributeType::MpReachNlri) { - let mut reach_builder = b.inner(); - let mut split_at = 0; + let maybe_pdu = if let Some(mut reach_builder) = + self.attributes.remove::() + { + let mut split_at = 0; - let other_attrs_len = self.attributes.values().fold(0, |sum, pa| sum + pa.compose_len()); - let limit = Self::MAX_PDU + let other_attrs_len = self.attributes.bytes_len(); + let limit = Self::MAX_PDU // marker/len/type, wdraw len, total pa len - (16 + 2 + 1 + 2 + 2) // MP_REACH_NLRI flags/type/len/afi/safi/rsrved, next_hop @@ -917,15 +904,15 @@ impl UpdateBuilder // attributes_len` is checked to be <= 4096 or <= 65535 // so it will always fit in a u16. - let attributes_len = self.attributes.values() - .fold(0, |sum, a| sum + a.compose_len()); + let attributes_len = self.attributes.bytes_len(); let _ = self.target.append_slice( &u16::try_from(attributes_len).unwrap().to_be_bytes() ); - self.attributes.iter().try_for_each( - |(_tc, pa)| pa.compose(&mut self.target) - )?; + self.attributes + .attributes() + .iter() + .try_for_each(|(_tc, pa)| pa.compose(&mut self.target))?; // XXX Here, in the conventional NLRI field at the end of the PDU, we // write IPv4 Unicast announcements. But what if we have agreed to do From 14e9f21fb0b32f7e346b34fa52f4f9004aac7c10 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Wed, 14 Feb 2024 23:07:53 +0100 Subject: [PATCH 19/96] use more attributes.set --- src/bgp/message/update_builder.rs | 140 +++++++++++------------------- 1 file changed, 49 insertions(+), 91 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 371ddf29..c3e38b1d 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -1,5 +1,5 @@ use std::fmt; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{IpAddr, Ipv6Addr}; use bytes::BytesMut; use octseq::{FreezeBuilder, Octets, OctetsBuilder, OctetsFrom, OctetsInto, ShortBuf}; @@ -10,23 +10,12 @@ use crate::bgp::communities::StandardCommunity; use crate::bgp::message::{Header, MsgType, SessionConfig, UpdateMessage}; use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::{Afi, Safi, AfiSafi, NextHop}; -use crate::bgp::types::OriginType; use crate::util::parser::ParseError; //use rotonda_fsm::bgp::session::AgreedConfig; use crate::bgp::path_attributes::{ - AsPath, - StandardCommunities, - LocalPref, - MpReachNlri, - MpUnreachNlri, - MultiExitDisc, - ConventionalNextHop, - Origin, - PaMap, - PathAttribute, - PathAttributeType + PaMap, PathAttribute, PathAttributeType }; //------------ UpdateBuilder ------------------------------------------------- @@ -146,14 +135,13 @@ where Target: octseq::Truncate .attributes .contains_attr::() { - self.add_attribute( - MpUnreachNlri::new(MpUnreachNlriBuilder::new( + self.attributes.set( + MpUnreachNlriBuilder::new( Afi::Ipv6, Safi::Unicast, b.is_addpath(), - )) - .into(), - )?; + ) + ); } let mut builder = self @@ -222,23 +210,18 @@ where Target: octseq::Truncate &pa.type_code() ); } - if let Some(existing_pa) = - self.attributes.attributes_mut().get_mut(&pa.type_code()) - { - *existing_pa = pa; - } else { - self.attributes.attributes_mut().insert(pa.type_code(), pa); - } + // if let Some(existing_pa) = + // self.attributes.attributes_mut().get_mut(&pa.type_code()) + // { + // *existing_pa = pa; + // } else { + // self.attributes.attributes_mut().insert(pa.type_code(), pa); + // } + self.attributes.attributes_mut().insert(pa.type_code(), pa); Ok(()) } - pub fn set_origin(&mut self, origin: OriginType) - -> Result<(), ComposeError> - { - self.add_attribute(Origin::new(origin).into()) - } - pub fn set_aspath(&mut self , aspath: HopPath) -> Result<(), ComposeError> { @@ -255,19 +238,8 @@ where Target: octseq::Truncate return Err(ComposeError::InvalidAttribute) } - self.add_attribute(AsPath::new(aspath).into()) - } - - pub fn set_conventional_nexthop( - &mut self, - addr: Ipv4Addr, - ) -> Result<(), ComposeError> { - self.add_attribute( - ConventionalNextHop::new(crate::bgp::types::ConventionalNextHop( - addr, - )) - .into(), - ) + self.attributes.set(aspath); + Ok(()) } pub fn set_mp_nexthop( @@ -285,7 +257,10 @@ where Target: octseq::Truncate pub fn set_nexthop_unicast(&mut self, addr: IpAddr) -> Result<(), ComposeError> { match addr { - IpAddr::V4(a) => self.set_conventional_nexthop(a), + IpAddr::V4(a) => { + self.attributes.set(crate::bgp::types::ConventionalNextHop(a)); + Ok(()) + }, IpAddr::V6(_) => self.set_mp_nexthop(NextHop::Unicast(addr)) } } @@ -307,33 +282,20 @@ where Target: octseq::Truncate builder.set_nexthop_ll_addr(addr); self.attributes.set(builder); } else { - self.add_attribute( - MpReachNlri::new(MpReachNlriBuilder::new( + self.attributes.set( + MpReachNlriBuilder::new( Afi::Ipv6, Safi::Unicast, NextHop::Ipv6LL(0.into(), addr), false, - )) - .into(), - )?; + ) + ); } Ok(()) } - pub fn set_multi_exit_disc(&mut self, med: MultiExitDisc) - -> Result<(), ComposeError> - { - self.add_attribute(med.into()) - } - - pub fn set_local_pref(&mut self, local_pref: LocalPref) - -> Result<(), ComposeError> - { - self.add_attribute(local_pref.into()) - } - - + //--- Announcements pub fn add_announcement(&mut self, announcement: &Nlri) @@ -360,10 +322,9 @@ where Target: octseq::Truncate } n => { if !self.attributes.contains_attr::() { - self.add_attribute( - MpReachNlri::new(MpReachNlriBuilder::new_for_nlri(n)) - .into(), - )?; + self.attributes.set( + MpReachNlriBuilder::new_for_nlri(n) + ); } let mut builder = self @@ -407,10 +368,7 @@ where Target: octseq::Truncate community: StandardCommunity, ) -> Result<(), ComposeError> { if !self.attributes.contains_attr::() { - self.add_attribute( - StandardCommunities::new(StandardCommunitiesList::new()) - .into(), - )?; + self.attributes.set(StandardCommunitiesList::new()); } let mut builder = self .attributes @@ -812,8 +770,8 @@ impl UpdateBuilder let this_batch = reach_builder.split(split_at); let mut builder = Self::from_target(self.target.clone()).unwrap(); builder.attributes = self.attributes.clone(); - self.add_attribute(MpReachNlri::new(reach_builder).into()).unwrap(); - builder.add_attribute(MpReachNlri::new(this_batch).into()).unwrap(); + self.attributes.set(reach_builder); + builder.attributes.set(this_batch); Some(builder.into_message()) } else { @@ -1455,7 +1413,7 @@ mod tests { use octseq::Parser; - use crate::addr::Prefix; + use crate::{addr::Prefix, bgp::message::update::OriginType}; use crate::asn::Asn; //use crate::bgp::communities::Wellknown; use crate::bgp::message::nlri::BasicNlri; @@ -1740,8 +1698,8 @@ mod tests { // .unwrap() ).unwrap(); } - builder.set_local_pref(LocalPref::new(crate::bgp::types::LocalPref(123))).unwrap(); - builder.set_multi_exit_disc(MultiExitDisc::new(crate::bgp::types::MultiExitDisc(123))).unwrap(); + builder.attributes.set(crate::bgp::types::LocalPref(123)); + builder.attributes.set(crate::bgp::types::MultiExitDisc(123)); (1..=300).for_each(|n| { builder.add_community(StandardCommunity::new(n.into(), Tag::new(123))).unwrap(); }); @@ -1937,7 +1895,7 @@ mod tests { "1.0.4.0/24", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.set_nexthop_unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(123); 70 @@ -1964,7 +1922,7 @@ mod tests { "2001:db8:3::/48", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.set_nexthop_unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -1984,7 +1942,7 @@ mod tests { use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.set_nexthop_unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -2012,7 +1970,7 @@ mod tests { ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap(); @@ -2035,7 +1993,7 @@ mod tests { use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); //builder.set_nexthop("2001:db8::1".parse().unwrap()).unwrap(); builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap(); let path = HopPath::from([ @@ -2062,7 +2020,7 @@ mod tests { "1.0.4.0/24", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.set_nexthop_unicast("1.2.3.4".parse::().unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -2095,7 +2053,7 @@ mod tests { "1.0.4.0/24", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.set_nexthop_unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -2105,8 +2063,8 @@ mod tests { //builder.set_aspath::>(path.to_as_path().unwrap()).unwrap(); builder.set_aspath(path).unwrap(); - builder.set_multi_exit_disc(MultiExitDisc::new(crate::bgp::types::MultiExitDisc(1234))).unwrap(); - builder.set_local_pref(LocalPref::new(crate::bgp::types::LocalPref(9876))).unwrap(); + builder.attributes.set(crate::bgp::types::MultiExitDisc(1234)); + builder.attributes.set(crate::bgp::types::LocalPref(9876)); let msg = builder.into_message().unwrap(); msg.print_pcap(); @@ -2164,9 +2122,9 @@ mod tests { assert_eq!(builder.attributes.len(), 4); - builder.set_origin(OriginType::Igp).unwrap(); - builder.set_origin(OriginType::Egp).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); + builder.attributes.set(OriginType::Egp); + builder.attributes.set(OriginType::Igp); assert_eq!(builder.attributes.len(), 4); @@ -2184,7 +2142,7 @@ mod tests { builder.add_community( StandardCommunity::from_str("AS1234:999").unwrap() ).unwrap(); - builder.set_origin(OriginType::Igp).unwrap(); + builder.attributes.set(OriginType::Igp); builder.add_community( StandardCommunity::from_str("AS1234:1000").unwrap() ).unwrap(); @@ -2232,7 +2190,7 @@ mod tests { if let Some(nh) = original.conventional_next_hop().unwrap() { if let NextHop::Unicast(IpAddr::V4(a))= nh { - builder.set_conventional_nexthop(a).unwrap(); + builder.attributes.set(crate::bgp::types::ConventionalNextHop(a)); } else { unreachable!() } @@ -2271,7 +2229,7 @@ mod tests { // compare as much as possible: - #[allow(clippy::blocks_in_if_conditions)] + #[allow(clippy::blocks_in_conditions)] if std::panic::catch_unwind(|| { assert_eq!(original.origin(), composed.origin()); //assert_eq!(original.aspath(), composed.aspath()); From d77fb9ba5ad3f2a15f89d977a7cca989b0cb3b93 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 15 Feb 2024 09:55:04 +0100 Subject: [PATCH 20/96] remove AttrChangeSet --- src/bgp/message/attr_change_set.rs | 131 ----------------------------- 1 file changed, 131 deletions(-) delete mode 100644 src/bgp/message/attr_change_set.rs diff --git a/src/bgp/message/attr_change_set.rs b/src/bgp/message/attr_change_set.rs deleted file mode 100644 index d38c215a..00000000 --- a/src/bgp/message/attr_change_set.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::addr::Prefix; -use crate::bgp::aspath::AsPath; -use crate::bgp::communities::{ - ExtendedCommunity, Ipv6ExtendedCommunity, LargeCommunity, - StandardCommunity, -}; - -use crate::bgp::message::update::{ - /*Aggregator,*/ LocalPref, MultiExitDisc, NextHop, OriginType, -}; - -use crate::bgp::message::nlri::Nlri; - -type Todo = (); - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ChangedOption { - value: Option, - changed: bool -} - -impl ChangedOption { - pub fn new() -> ChangedOption { - ChangedOption { - value: None, - changed: false, - } - } - - pub fn into_opt(self) -> Option - { - self.value - } - - pub fn as_ref(&self) -> Option<&T> { - self.value.as_ref() - } - - pub fn is_changed(&self) -> bool { - self.changed - } - - pub fn set_cleared() -> ChangedOption { - ChangedOption { - value: None, - changed: true, - } - } - - pub fn set>(&mut self, value: S) { - let val = &mut Some(value.into()); - std::mem::swap(&mut self.value, val); - self.changed = true; - } -} - -// A attributes Change Set allows a user to create a set of changes to an -// existing (raw) BGP Update message. -#[derive(Debug)] -pub struct AttrChangeSet { - // NLRI - // Routecore gets to decide where the different parts of the NLRI go, in - // the regular nlri or in MP_REACH_NLRI. - pub nlri: ChangedOption>>, - pub withdrawals: ChangedOption>, - - // Path Attributes - - // PA: AS_PATH - pub as_path: ChangedOption>>, - pub as4_path: ChangedOption>>, - - // PA: communities - pub standard_communities: ChangedOption>, - pub extended_communities: ChangedOption>, - pub ipv6_extended_communities: ChangedOption>, - pub large_communities: ChangedOption>, - - // PA: others - pub origin_type: ChangedOption, - pub next_hop: ChangedOption, - pub multi_exit_discriminator: ChangedOption, - pub local_pref: ChangedOption, - pub atomic_aggregate: ChangedOption, - pub aggregator: ChangedOption, - - // PA: unimplemented - pub originator_id: Todo, - pub cluster_list: Todo, - pub as4_aggregator: Todo, - pub connector: Todo, - pub as_path_limit: Todo, - pub pmsi_tunnel: Todo, - pub bgpsec_as_path: Todo, - pub attr_set: Todo, - pub rsrvd_development: Todo, -} - -impl AttrChangeSet { - pub fn empty() -> Self { - AttrChangeSet { - nlri: ChangedOption::new(), - withdrawals: ChangedOption::new(), - - as_path: ChangedOption::new(), - as4_path: ChangedOption::new(), - - standard_communities: ChangedOption::new(), - extended_communities: ChangedOption::new(), - ipv6_extended_communities: ChangedOption::new(), - large_communities: ChangedOption::new(), - - origin_type: ChangedOption::new(), - next_hop: ChangedOption::new(), - multi_exit_discriminator: ChangedOption::new(), - local_pref: ChangedOption::new(), - atomic_aggregate: ChangedOption::new(), - aggregator: ChangedOption::new(), - - originator_id: (), - cluster_list: (), - as4_aggregator: (), - connector: (), - as_path_limit: (), - pmsi_tunnel: (), - bgpsec_as_path: (), - attr_set: (), - rsrvd_development: (), - } - } -} From 8884944b2b87d67996995a79554a0c214130d167 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 15 Feb 2024 11:45:55 +0100 Subject: [PATCH 21/96] varia --- src/bgp/message/update_builder.rs | 30 +++++++++--------- src/bgp/path_attributes.rs | 52 +++++++++++++++++++------------ src/bgp/workshop/route.rs | 33 +++++++++++--------- 3 files changed, 67 insertions(+), 48 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index c3e38b1d..703f78bb 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -10,6 +10,7 @@ use crate::bgp::communities::StandardCommunity; use crate::bgp::message::{Header, MsgType, SessionConfig, UpdateMessage}; use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::{Afi, Safi, AfiSafi, NextHop}; +use crate::bgp::workshop::route::FromAttribute; use crate::util::parser::ParseError; //use rotonda_fsm::bgp::session::AgreedConfig; @@ -27,7 +28,8 @@ pub struct UpdateBuilder { announcements: Vec>>, withdrawals: Vec>>, addpath_enabled: Option, // for conventional nlri (unicast v4) - // attributes: + // data structure that stores all path attributes, by default not the NLRI + // (but it could). attributes: PaMap, } @@ -133,7 +135,7 @@ where Target: octseq::Truncate if !self .attributes - .contains_attr::() + .contains::() { self.attributes.set( MpUnreachNlriBuilder::new( @@ -146,7 +148,7 @@ where Target: octseq::Truncate let mut builder = self .attributes - .get_owned::() + .take::() .unwrap(); // Just added it, so we know it is there. if !builder.valid_combination( @@ -202,12 +204,12 @@ where Target: octseq::Truncate pub fn add_attribute( &mut self, - pa: PathAttribute, + attr: PathAttribute, ) -> Result<(), ComposeError> { - if let PathAttribute::Invalid(..) = pa { + if let PathAttribute::Invalid(..) = attr { warn!( "adding Invalid attribute to UpdateBuilder: {}", - &pa.type_code() + &attr.type_code() ); } // if let Some(existing_pa) = @@ -217,7 +219,7 @@ where Target: octseq::Truncate // } else { // self.attributes.attributes_mut().insert(pa.type_code(), pa); // } - self.attributes.attributes_mut().insert(pa.type_code(), pa); + self.attributes.attributes_mut().insert(attr.type_code(),attr); Ok(()) } @@ -248,7 +250,7 @@ where Target: octseq::Truncate ) -> Result<(), ComposeError> { let mut builder = self .attributes - .get_owned::() + .take::() .ok_or(ComposeError::IllegalCombination)?; builder.set_nexthop(nexthop)?; self.attributes.set(builder); @@ -271,7 +273,7 @@ where Target: octseq::Truncate // We could/should check for addr.is_unicast_link_local() once that // lands in stable. if let Some(mut builder) = - self.attributes.get_owned::() + self.attributes.take::() { match builder.get_nexthop() { NextHop::Unicast(a) if a.is_ipv6() => {} @@ -321,7 +323,7 @@ where Target: octseq::Truncate ); } n => { - if !self.attributes.contains_attr::() { + if !self.attributes.contains::() { self.attributes.set( MpReachNlriBuilder::new_for_nlri(n) ); @@ -329,7 +331,7 @@ where Target: octseq::Truncate let mut builder = self .attributes - .get_owned::() + .take::() .unwrap(); // Just added it, so we know it is there. if !builder.valid_combination(n) { @@ -367,12 +369,12 @@ where Target: octseq::Truncate &mut self, community: StandardCommunity, ) -> Result<(), ComposeError> { - if !self.attributes.contains_attr::() { + if !self.attributes.contains::() { self.attributes.set(StandardCommunitiesList::new()); } let mut builder = self .attributes - .get_owned::() + .take::() .unwrap(); // Just added it, so we know it is there. builder.add_community(community); self.attributes.set(builder); @@ -685,7 +687,7 @@ impl UpdateBuilder // At this point, we have no conventional withdrawals anymore. let maybe_pdu = if let Some(mut unreach_builder) = - self.attributes.get_owned::() + self.attributes.take::() { let mut split_at = 0; if !unreach_builder.withdrawals.is_empty() { diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index bc6a32f2..2d0e26ae 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -224,7 +224,7 @@ impl PaMap { self.attributes_mut().append(other.attributes_mut()) } - pub(in crate::bgp) fn get_owned(&mut self) -> Option { + pub(in crate::bgp) fn take(&mut self) -> Option { if let Some(attr_type) = A::attribute_type() { self.attributes_mut() .get_mut(&attr_type).and_then(|a| A::from_attribute(std::mem::take(a))) @@ -233,7 +233,7 @@ impl PaMap { } } - pub(in crate::bgp) fn contains_attr(&self) -> bool { + pub(in crate::bgp) fn contains(&self) -> bool { if let Some(attr_type) = A::attribute_type() { self.attributes().contains_key(&attr_type) } else { @@ -357,7 +357,7 @@ impl PaMap { impl From for PaMap { fn from(value: AttributesMap) -> Self { - Self {attributes: value } + Self { attributes: value } } } @@ -456,13 +456,6 @@ macro_rules! path_attributes { } )+ - impl From for PathAttribute { - fn from(u: UnimplementedPathAttribute) -> PathAttribute { - PathAttribute::Unimplemented(u) - } - } - - //------------ WireformatPathAttribute -------------------------------------- #[derive(Debug)] @@ -702,12 +695,6 @@ macro_rules! path_attributes { } */ -impl Default for PathAttribute { - fn default() -> Self { - Self::Unimplemented(UnimplementedPathAttribute{flags: Flags(0), type_code: 0, value: vec![]}) - } -} - //------------ PathAttributeType --------------------------------------------- #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -736,7 +723,6 @@ impl Default for PathAttribute { } } - impl From for PathAttributeType { fn from(code: u8) -> PathAttributeType { match code { @@ -760,6 +746,14 @@ impl Default for PathAttribute { attribute!($name($data), $flags, $type_code); )+ + // You might think that the Attribute trait (implemented for all path + // attributes with the attribute! macro above this comment), can be + // merged with the FromAttribute trait down here. There are however + // types that do not and should not implement `Attribute`, but they + // should implement `FromAttribute`, namely types that are usable by + // end-users, but are internally reworked to be stored in multiple + // path attributes, e.g. Vec, or a value that can be stored + // in one of several path attributes, e.g. NextHop. $( impl FromAttribute for $data { fn from_attribute(value: PathAttribute) -> Option<$data> { @@ -805,9 +799,19 @@ path_attributes!( //40 => BgpPrefixSid(TODO), Flags::OPT_TRANS, // https://datatracker.ietf.org/doc/html/rfc8669#name-bgp-prefix-sid-attribute 128 => AttrSet(crate::bgp::path_attributes::AttributeSet), Flags::OPT_TRANS, 255 => Reserved(crate::bgp::path_attributes::ReservedRaw), Flags::OPT_TRANS, - ); +// Default implementation is needed to be able to take() an attribute. When +// done so it gets replaced with Unimplemented. +impl Default for PathAttribute { + fn default() -> Self { + Self::Unimplemented(UnimplementedPathAttribute{flags: Flags(0), type_code: 0, value: vec![]}) + } +} + + +//------------ UnimplementedPathAttribute ------------------------------------ + #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct UnimplementedPathAttribute { @@ -847,6 +851,12 @@ impl UnimplementedPathAttribute { } } +impl From for PathAttribute { + fn from(u: UnimplementedPathAttribute) -> PathAttribute { + PathAttribute::Unimplemented(u) + } +} + pub struct UnimplementedWireformat<'a, Octs: Octets> { flags: Flags, type_code: u8, @@ -883,6 +893,8 @@ impl<'a, Octs: Octets> UnimplementedWireformat<'a, Octs> { } +//------------ Attribute trait ----------------------------------------------- + pub trait Attribute: AttributeHeader + Clone { fn compose_len(&self) -> usize { self.header_len() + self.value_len() @@ -900,8 +912,6 @@ pub trait Attribute: AttributeHeader + Clone { } } - // fn inner(&self) -> &Self { todo!() } - fn value_len(&self) -> usize; fn compose(&self, target: &mut Target) @@ -951,6 +961,8 @@ pub trait Attribute: AttributeHeader + Clone { } +//------------ PathAttributes ------------------------------------------------ + #[derive(Debug)] pub struct PathAttributes<'a, Octs> { pub parser: Parser<'a, Octs>, diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 8123cccc..d34b7ce6 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -16,12 +16,18 @@ use crate::bgp::{ }, }; + +//------------ TypedRoute ---------------------------------------------------- + #[derive(Debug)] pub enum TypedRoute { Announce(Route), Withdraw(Nlri), } + +//------------ Route --------------------------------------------------------- + #[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize)] pub struct Route(N, AttributesMap); @@ -60,10 +66,8 @@ pub trait FromAttribute { fn attribute_type() -> Option; } -//------------ From impls for PathAttribute ---------------------------------- -// These conversions are used on the `set()` method of the -// PaMap, so not in the Workshop! +//------------ From impls for PathAttribute ---------------------------------- impl From> for PathAttribute { fn from(value: crate::bgp::aspath::AsPath) -> Self { @@ -81,6 +85,7 @@ impl From>> for PathAttribute { } } + //------------ The Workshop -------------------------------------------------- #[derive(Debug)] @@ -103,11 +108,11 @@ impl RouteWorkshop { pub fn set_attr>( &mut self, - attr: WA, + value: WA, ) -> Result<(), ComposeError> { let mut res = Err(ComposeError::InvalidAttribute); if let Some(b) = &mut self.1 { - res = WA::to_value(attr, b); + res = WA::store(value, b); } res } @@ -117,7 +122,7 @@ impl RouteWorkshop { ) -> Option { self.1 .as_ref() - .and_then(|b| b.get::().or_else(|| ::into_retrieved(b))) + .and_then(|b| b.get::().or_else(|| ::retrieve(b))) } pub fn make_route(&self) -> Option> { @@ -145,9 +150,9 @@ macro_rules! impl_workshop { ) => { $( impl WorkshopAttribute for $attr { - fn to_value(local_attrs: Self, attrs: &mut PaMap) -> + fn store(local_attrs: Self, attrs: &mut PaMap) -> Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } - fn into_retrieved(_attrs: &PaMap) -> + fn retrieve(_attrs: &PaMap) -> Option { None } } )+ @@ -173,10 +178,10 @@ impl_workshop!( //------------ WorkshopAttribute --------------------------------------------- pub trait WorkshopAttribute: FromAttribute { - fn into_retrieved(attrs: &PaMap) -> Option + fn retrieve(attrs: &PaMap) -> Option where Self: Sized; - fn to_value( + fn store( local_attrs: Self, attrs: &mut PaMap, ) -> Result<(), ComposeError>; @@ -185,7 +190,7 @@ pub trait WorkshopAttribute: FromAttribute { //------------ CommunitiesWorkshop ------------------------------------------- impl WorkshopAttribute for Vec { - fn into_retrieved(attrs: &PaMap) -> Option { + fn retrieve(attrs: &PaMap) -> Option { let mut c = attrs .get::() .unwrap() @@ -212,7 +217,7 @@ impl WorkshopAttribute for Vec { Some(c) } - fn to_value( + fn store( local_attr: Self, attrs: &mut PaMap, ) -> Result<(), ComposeError> { @@ -270,7 +275,7 @@ impl FromAttribute for crate::bgp::types::NextHop { } impl WorkshopAttribute for crate::bgp::types::NextHop { - fn into_retrieved(attrs: &PaMap) -> Option { + fn retrieve(attrs: &PaMap) -> Option { if let Some(next_hop) = attrs.get::() { @@ -284,7 +289,7 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { } } - fn to_value( + fn store( local_attr: Self, attrs: &mut PaMap, ) -> Result<(), ComposeError> { From 2ef9caae5e75d3e6372137a08162de291f670865 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Fri, 16 Feb 2024 14:05:10 +0100 Subject: [PATCH 22/96] move add_attribute and FromAttribute trait --- src/bgp/path_attributes.rs | 82 ++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 2d0e26ae..930f84f3 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -140,9 +140,9 @@ macro_rules! attribute { //------------ PathAttributesBuilder ----------------------------------------- -pub type AttributesMap = BTreeMap; +pub type AttributesMap = BTreeMap; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct PaMap { attributes: AttributesMap, @@ -157,18 +157,18 @@ impl PaMap { // Assemble a AttributesMap, but skipping MP_*REACH_NLRI, so that the // returned result is valid for all NLRI in this message. - pub fn from_update_pdu(pdu: &UpdateMessage) + pub fn from_update_pdu<'a, Octs: Octets>(pdu: &'a UpdateMessage) -> Result where - for<'a> Vec: OctetsFrom> + Vec: OctetsFrom> { let mut pa_map = Self::empty(); for pa in pdu.path_attributes()? { if let Ok(pa) = pa { - if pa.type_code() != PathAttributeType::MpReachNlri - && pa.type_code() != PathAttributeType::MpUnreachNlri + if pa.type_code() != MpReachNlri::TYPE_CODE + && pa.type_code() != MpUnreachNlri::TYPE_CODE { - if let PathAttributeType::Invalid(n) = pa.type_code() { + if let PathAttributeType::Invalid(n) = pa.type_code().into() { warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); } pa_map.attributes_mut().insert(pa.type_code(), pa.to_owned()?); @@ -184,7 +184,7 @@ impl PaMap { &mut self, attr: A ) { if let Some(attr_type) = A::attribute_type() { - self.attributes.insert(attr_type, attr.into()); + self.attributes.insert(attr_type.into(), attr.into()); } } @@ -193,12 +193,35 @@ impl PaMap { ) -> Option { if let Some(attr_type) = A::attribute_type() { self.attributes - .get(&attr_type).and_then(|a| A::from_attribute(a.clone())) + .get(&attr_type.into()).and_then(|a| A::from_attribute(a.clone())) } else { None } } + pub fn get_by_type_code(&self, type_code: u8) -> Option<&PathAttribute> { + self.attributes.get(&type_code) + } + + pub fn get_mut_by_type_code(&mut self, type_code: u8) -> Option<&mut PathAttribute> { + self.attributes.get_mut(&type_code) + } + + pub fn add_attribute( + &mut self, + attr: PathAttribute, + ) -> Result<(), ComposeError> { + if let PathAttribute::Invalid(..) = attr { + warn!( + "adding Invalid attribute to UpdateBuilder: {}", + &attr.type_code() + ); + } + self.attributes_mut().insert(attr.type_code(),attr); + + Ok(()) + } + pub fn attributes(&self) -> &AttributesMap { &self.attributes } @@ -214,7 +237,7 @@ impl PaMap { pub fn remove(&mut self) -> Option { if let Some(attr_type) = A::attribute_type() { - self.attributes.remove(&attr_type).and_then(|a| A::from_attribute(a)) + self.attributes.remove(&attr_type.into()).and_then(|a| A::from_attribute(a)) } else { None } @@ -227,7 +250,7 @@ impl PaMap { pub(in crate::bgp) fn take(&mut self) -> Option { if let Some(attr_type) = A::attribute_type() { self.attributes_mut() - .get_mut(&attr_type).and_then(|a| A::from_attribute(std::mem::take(a))) + .get_mut(&attr_type.into()).and_then(|a| A::from_attribute(std::mem::take(a))) } else { None } @@ -235,7 +258,7 @@ impl PaMap { pub(in crate::bgp) fn contains(&self) -> bool { if let Some(attr_type) = A::attribute_type() { - self.attributes().contains_key(&attr_type) + self.attributes().contains_key(&attr_type.into()) } else { false } @@ -251,7 +274,7 @@ impl PaMap { .fold(0, |sum, a| sum + a.compose_len()) } - + // pub fn append(&mut self, other: &mut AttributesMap) { // self.attributes.append(other) // } @@ -355,6 +378,14 @@ impl PaMap { // } // } + +pub trait FromAttribute { + fn from_attribute(value: PathAttribute) -> Option + where + Self: Sized; + fn attribute_type() -> Option; +} + impl From for PaMap { fn from(value: AttributesMap) -> Self { Self { attributes: value } @@ -422,17 +453,17 @@ macro_rules! path_attributes { } } - pub fn type_code(&self) -> PathAttributeType { + pub fn type_code(&self) -> u8 { match self { $( - PathAttribute::$name(_) => - PathAttributeType::$name + PathAttribute::$name(_pa) => + $name::TYPE_CODE ),+, PathAttribute::Unimplemented(u) => { - PathAttributeType::Unimplemented(u.type_code()) + u.type_code() } PathAttribute::Invalid(_, tc, _) => { - PathAttributeType::Invalid(*tc) + *tc } } } @@ -614,21 +645,22 @@ macro_rules! path_attributes { } } - pub fn type_code(&self) -> PathAttributeType { + pub fn type_code(&self) -> u8 { match self { $( - WireformatPathAttribute::$name(..) => - PathAttributeType::$name + WireformatPathAttribute::$name(_) => + $type_code ),+, WireformatPathAttribute::Unimplemented(u) => { - PathAttributeType::Unimplemented(u.type_code()) + u.type_code() } WireformatPathAttribute::Invalid(_, tc, _) => { - PathAttributeType::Invalid(*tc) + *tc } } } + pub fn flags(&self) -> Flags { match self { $( Self::$name(epa) => { epa.flags() }),+, @@ -992,13 +1024,12 @@ impl<'a, Octs: Octets> PathAttributes<'a, Octs> { //XXX We need Rust 1.70 for is_ok_and() //pa.as_ref().is_ok_and(|pa| pa.type_code() == pat) if let Ok(pa) = pa.as_ref() { - pa.type_code() == pat + pat == pa.type_code().into() } else { false } ).map(|res| res.unwrap()) // res is Ok(pa), so we can unwrap. } - } impl<'a, Octs: Octets> Iterator for PathAttributes<'a, Octs> { @@ -1980,7 +2011,6 @@ impl Attribute for Ipv6ExtendedCommunities { use crate::bgp::communities::LargeCommunity; -use super::workshop::route::FromAttribute; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct LargeCommunitiesList { From 4b05c9c8d7dbe91eb37a9b74ab37b7ecf70d0ce4 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Fri, 16 Feb 2024 14:06:06 +0100 Subject: [PATCH 23/96] eliminate PathAttributeType in PaMap --- src/bgp/message/update.rs | 12 +++---- src/bgp/message/update_builder.rs | 57 +++++------------------------- src/bgp/workshop/route.rs | 58 +++++++++++++++---------------- src/bmp/message.rs | 11 +++--- 4 files changed, 49 insertions(+), 89 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 106acfed..a60f92fd 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -1574,7 +1574,7 @@ mod tests { let mut pa_iter = pas.into_iter(); let pa1 = pa_iter.next().unwrap().unwrap(); - assert_eq!(pa1.type_code(), PathAttributeType::Origin); + assert_eq!(pa1.type_code(), PathAttributeType::Origin.into()); assert_eq!(pa1.flags(), 0x40.into()); assert!(!pa1.flags().is_optional()); assert!(pa1.flags().is_transitive()); @@ -1584,7 +1584,7 @@ mod tests { assert_eq!(pa1.length(), 1); let pa2 = pa_iter.next().unwrap().unwrap(); - assert_eq!(pa2.type_code(), PathAttributeType::AsPath); + assert_eq!(pa2.type_code(), PathAttributeType::AsPath.into()); assert_eq!(pa2.flags(), 0x40.into()); assert_eq!(pa2.length(), 6); @@ -1597,7 +1597,7 @@ mod tests { assert_eq!(update.aspath().unwrap().unwrap(), asp); let pa3 = pa_iter.next().unwrap().unwrap(); - assert_eq!(pa3.type_code(), PathAttributeType::ConventionalNextHop); + assert_eq!(pa3.type_code(), PathAttributeType::ConventionalNextHop.into()); assert_eq!(pa3.flags(), 0x40.into()); assert_eq!(pa3.length(), 4); //assert_eq!(pa3.as_ref(), &[10, 255, 0, 101]); @@ -1607,7 +1607,7 @@ mod tests { ); let pa4 = pa_iter.next().unwrap().unwrap(); - assert_eq!(pa4.type_code(), PathAttributeType::MultiExitDisc); + assert_eq!(pa4.type_code(), PathAttributeType::MultiExitDisc.into()); assert_eq!(pa4.flags(), 0x80.into()); assert!( pa4.flags().is_optional()); assert!(!pa4.flags().is_transitive()); @@ -1951,7 +1951,7 @@ mod tests { update.path_attributes().iter();//.count(); if let Some(Ok(aspath)) = update.path_attributes().unwrap() - .find(|pa| pa.as_ref().unwrap().type_code() == PathAttributeType::AsPath) + .find(|pa| pa.as_ref().unwrap().type_code() == PathAttributeType::AsPath.into()) { assert_eq!(aspath.flags(), 0x50.into()); assert!(aspath.flags().is_transitive()); @@ -1963,7 +1963,7 @@ mod tests { } if let Some(Ok(as4path)) = update.path_attributes().unwrap() - .find(|pa| pa.as_ref().unwrap().type_code() == PathAttributeType::As4Path) + .find(|pa| pa.as_ref().unwrap().type_code() == PathAttributeType::As4Path.into()) { assert_eq!(as4path.flags(), 0xd0.into()); assert_eq!(as4path.length(), 42); diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 703f78bb..06ebca25 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -10,13 +10,12 @@ use crate::bgp::communities::StandardCommunity; use crate::bgp::message::{Header, MsgType, SessionConfig, UpdateMessage}; use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::{Afi, Safi, AfiSafi, NextHop}; -use crate::bgp::workshop::route::FromAttribute; use crate::util::parser::ParseError; //use rotonda_fsm::bgp::session::AgreedConfig; use crate::bgp::path_attributes::{ - PaMap, PathAttribute, PathAttributeType + PaMap, PathAttributeType }; //------------ UpdateBuilder ------------------------------------------------- @@ -80,22 +79,8 @@ where Target: octseq::Truncate let mut builder = UpdateBuilder::from_target(target) .map_err(|_| ComposeError::ShortBuf)?; - // Add all path attributes, except for MP_(UN)REACH_NLRI, ordered by - // their type_code. - for pa in pdu.path_attributes()? { - if let Ok(pa) = pa { - if pa.type_code() != PathAttributeType::MpReachNlri - && pa.type_code() != PathAttributeType::MpUnreachNlri - { - if let PathAttributeType::Invalid(n) = pa.type_code() { - warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); - } - builder.add_attribute(pa.to_owned()?)?; - } - } else { - return Err(ComposeError::InvalidAttribute); - } - } + let pa_map = PaMap::from_update_pdu(pdu)?; + builder.attributes = pa_map; Ok(builder) } @@ -196,33 +181,6 @@ where Target: octseq::Truncate //--- Path Attributes - /// Upsert a Path Attribute. - /// - /// Insert a new, or update an existing Path Attribute in this builder. If - /// the new Path Attribute would cause the total PDU length to exceed the - /// maximum, a `ComposeError::PduTooLarge` is returned. - - pub fn add_attribute( - &mut self, - attr: PathAttribute, - ) -> Result<(), ComposeError> { - if let PathAttribute::Invalid(..) = attr { - warn!( - "adding Invalid attribute to UpdateBuilder: {}", - &attr.type_code() - ); - } - // if let Some(existing_pa) = - // self.attributes.attributes_mut().get_mut(&pa.type_code()) - // { - // *existing_pa = pa; - // } else { - // self.attributes.attributes_mut().insert(pa.type_code(), pa); - // } - self.attributes.attributes_mut().insert(attr.type_code(),attr); - - Ok(()) - } pub fn set_aspath(&mut self , aspath: HopPath) -> Result<(), ComposeError> @@ -1415,6 +1373,7 @@ mod tests { use octseq::Parser; + use crate::bgp::path_attributes::AttributeHeader; use crate::{addr::Prefix, bgp::message::update::OriginType}; use crate::asn::Asn; //use crate::bgp::communities::Wellknown; @@ -2154,7 +2113,7 @@ mod tests { let pdu = builder.into_message().unwrap(); let mut prev_type_code = 0_u8; for pa in pdu.path_attributes().unwrap() { - let type_code = u8::from(pa.unwrap().type_code()); + let type_code = pa.unwrap().type_code(); assert!(prev_type_code < type_code); prev_type_code = type_code; } @@ -2258,10 +2217,10 @@ mod tests { ).collect(); if !diff_pas.is_empty() { for d in &diff_pas { - match d { + match *d { // FIXME: check if MPU is the _only_ attribute, // perhaps we are dealing with an EoR here? - PathAttributeType::MpUnreachNlri => { + &crate::bgp::path_attributes::MpUnreachNlri::TYPE_CODE => { // XXX RIS data contains empty-but-non-EoR // MP_UNREACH_NLRI for some reason. //assert!(original.is_eor().is_some()); @@ -2269,7 +2228,7 @@ mod tests { } _ => { - dbg!(&diff_pas); + dbg!(diff_pas); panic!("unclear why PAs differ") } } diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index d34b7ce6..edcd4f3a 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -5,13 +5,13 @@ use octseq::{Octets, OctetsFrom}; use serde::Serialize; use crate::bgp::communities::Community; -use crate::bgp::message::update_builder::ComposeError; +use crate::bgp::message::update_builder::{ComposeError, MpReachNlriBuilder}; use crate::bgp::message::UpdateMessage; -use crate::bgp::path_attributes::PaMap; +use crate::bgp::path_attributes::{PaMap, FromAttribute}; use crate::bgp::{ message::{nlri::Nlri, update_builder::StandardCommunitiesList}, path_attributes::{ - AttributesMap, ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, + ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, LargeCommunitiesList, PathAttribute, PathAttributeType, }, }; @@ -29,10 +29,10 @@ pub enum TypedRoute { //------------ Route --------------------------------------------------------- #[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize)] -pub struct Route(N, AttributesMap); +pub struct Route(N, PaMap); impl Route { - pub fn new(nlri: N, attrs: AttributesMap) -> Self { + pub fn new(nlri: N, attrs: PaMap) -> Self { Self(nlri, attrs) } @@ -40,32 +40,23 @@ impl Route { &self.0 } - pub fn get_attr(&self) -> Option { - if let Some(attr_type) = A::attribute_type() { - self.1 - .get(&attr_type) - .and_then(|a| A::from_attribute(a.clone())) + pub fn get_attr(&self) -> Option { + if A::attribute_type().is_some() { + self.1.get::() } else { None } } - pub fn attributes(&self) -> &AttributesMap { + pub fn attributes(&self) -> &PaMap { &self.1 } - pub fn attributes_mut(&mut self) -> &mut AttributesMap { + pub fn attributes_mut(&mut self) -> &mut PaMap { &mut self.1 } } -pub trait FromAttribute { - fn from_attribute(value: PathAttribute) -> Option - where - Self: Sized; - fn attribute_type() -> Option; -} - //------------ From impls for PathAttribute ---------------------------------- @@ -88,7 +79,7 @@ impl From>> for PathAttribute { //------------ The Workshop -------------------------------------------------- -#[derive(Debug)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct RouteWorkshop(Option, Option); impl RouteWorkshop { @@ -125,10 +116,19 @@ impl RouteWorkshop { .and_then(|b| b.get::().or_else(|| ::retrieve(b))) } - pub fn make_route(&self) -> Option> { + pub fn clone_into_route(&self) -> Option> { + match self { + RouteWorkshop(Some(nlri), Some(pab)) => { + Some(Route::(nlri.clone(), pab.clone())) + } + _ => None, + } + } + + pub fn into_route(self) -> Option> { match self { RouteWorkshop(Some(nlri), Some(pab)) => { - Some(Route::(nlri.clone(), pab.attributes().clone())) + Some(Route::(nlri, pab)) } _ => None, } @@ -137,7 +137,7 @@ impl RouteWorkshop { pub fn make_route_with_nlri(&self, nlri: N) -> Option> { match self { RouteWorkshop(_, Some(pab)) => { - Some(Route::(nlri.clone(), pab.attributes().clone())) + Some(Route::(nlri.clone(), pab.clone())) } _ => None, } @@ -280,10 +280,10 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { attrs.get::() { Some(crate::bgp::types::NextHop::Unicast(next_hop.0.into())) - } else if let Some(PathAttribute::MpReachNlri(nlri)) = - attrs.attributes().get(&PathAttributeType::MpReachNlri) + } else if let Some(nlri) = + attrs.get::() { - Some(*(nlri.clone().inner().get_nexthop())) + Some(*nlri.get_nexthop()) } else { Some(crate::bgp::types::NextHop::Empty) } @@ -293,10 +293,10 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { local_attr: Self, attrs: &mut PaMap, ) -> Result<(), ComposeError> { - if let Some(PathAttribute::MpReachNlri(nlri)) = - attrs.attributes().get(&PathAttributeType::MpReachNlri) + if let Some(mut nlri) = + attrs.get::() { - nlri.clone().inner().set_nexthop(local_attr) + nlri.set_nexthop(local_attr) } else { Err(ComposeError::InvalidAttribute) } diff --git a/src/bmp/message.rs b/src/bmp/message.rs index 61356215..37cf0e6c 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -1677,7 +1677,8 @@ mod tests { use std::str::FromStr; use crate::addr::Prefix; use crate::bgp::types::{Afi, Safi}; - use crate::bgp::path_attributes::PathAttributeType; + use crate::bgp::path_attributes::{AsPath, AttributeHeader, + ConventionalNextHop, MultiExitDisc}; use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::{FourOctetAsn, SessionConfig}; @@ -1794,7 +1795,7 @@ mod tests { let mut pas = bgp_update.path_attributes().unwrap().into_iter(); let pa1 = pas.next().unwrap().unwrap(); - assert_eq!(pa1.type_code(), PathAttributeType::Origin); + assert_eq!(pa1.type_code(), crate::bgp::path_attributes::Origin::TYPE_CODE); assert_eq!(pa1.flags(), 0x40.into()); assert!( pa1.flags().is_transitive()); assert!(!pa1.flags().is_optional()); @@ -1802,17 +1803,17 @@ mod tests { //assert_eq!(pa1.as_ref(), [0x00]); let pa2 = pas.next().unwrap().unwrap(); - assert_eq!(pa2.type_code(), PathAttributeType::AsPath); + assert_eq!(pa2.type_code(), AsPath::TYPE_CODE); assert_eq!(pa2.flags(), 0x40.into()); // TODO check actual AS_PATH contents let pa3 = pas.next().unwrap().unwrap(); - assert_eq!(pa3.type_code(), PathAttributeType::ConventionalNextHop); + assert_eq!(pa3.type_code(), ConventionalNextHop::TYPE_CODE); assert_eq!(pa3.flags(), 0x40.into()); //assert_eq!(pa3.as_ref(), [10, 255, 0, 101]); let pa4 = pas.next().unwrap().unwrap(); - assert_eq!(pa4.type_code(), PathAttributeType::MultiExitDisc); + assert_eq!(pa4.type_code(), MultiExitDisc::TYPE_CODE); assert_eq!(pa4.flags(), 0x80.into()); assert!(pa4.flags().is_optional()); //assert_eq!(pa4.as_ref(), [0, 0, 0, 1]); From 28bd7f7e3fd801689037934e9aa7a56e24e2a0bd Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 22 Feb 2024 10:46:19 +0100 Subject: [PATCH 24/96] single NLRI facilities --- src/bgp/message/update.rs | 35 +++++++++++++++++++++++++++++++ src/bgp/message/update_builder.rs | 15 ++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index a60f92fd..11868ab0 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -1,4 +1,8 @@ +use std::fmt::Debug; +use std::hash::Hash; + use crate::bgp::message::Header; +use crate::bgp::workshop::afisafi_nlri::AfiSafiNlri; use octseq::{Octets, Parser}; //use log::debug; @@ -31,6 +35,8 @@ use crate::bgp::communities::{ LargeCommunity, }; +use super::update_builder::ComposeError; + /// BGP UPDATE message, variant of the [`Message`] enum. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -301,6 +307,35 @@ impl UpdateMessage { Ok(None) } + pub fn single_afi_safi_nlri<'a, N: + Clone + + Debug + + Hash + + AfiSafiNlri + + TryFrom::Range<'a>>, + Error = ComposeError>>(&'a self) -> Result where + Octs: + 'a, + ::Range<'a>>>>::Error: Debug { + let pp = Parser::with_range( + self.octets(), + self.announcements.clone() + ); + + let iter = Nlris { + parser: pp, + session_config: self.session_config, + afi_safi: AfiSafi::Ipv4Unicast, + }; + + if let Some(n) = iter.iter().next() { + n?.try_into() + } else { + Err(ComposeError::EmptyMpReachNlri) + } + } + /// Returns a combined iterator of conventional and MP_REACH_NLRI. /// /// Consuming the returned iterator requires care. The `Item` is a diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 06ebca25..41b2c2e6 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -967,7 +967,7 @@ impl MpReachNlriBuilder { } } - fn new_for_nlri(nlri: &Nlri) -> Self + pub(crate) fn new_for_nlri(nlri: &Nlri) -> Self where T: Octets, Vec: OctetsFrom { @@ -987,10 +987,23 @@ impl MpReachNlriBuilder { self.announcements.is_empty() } + pub(crate) fn first_nlri(self) -> Option>> { + self.announcements.first().cloned() + } + pub(crate) fn get_nexthop(&self) -> &NextHop { &self.nexthop } + pub(crate) fn set_nlri(&mut self, nlri: Nlri>) -> Result<(), ComposeError> { + let (afi, safi) = nlri.afi_safi().split(); + self.afi = afi; + self.safi = safi; + self.addpath_enabled = nlri.is_addpath(); + self.announcements = vec![nlri]; + Ok(()) + } + pub(crate) fn set_nexthop(&mut self, nexthop: NextHop) -> Result<(), ComposeError> { if !self.announcements.is_empty() && From adce9e24a33d94d000b15ffd7b7dc39cf6814acd Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 22 Feb 2024 10:47:14 +0100 Subject: [PATCH 25/96] Workshop without Options --- src/bgp/workshop/route.rs | 106 +++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index edcd4f3a..1057e421 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -1,5 +1,6 @@ use std::fmt::Debug; use std::hash::Hash; +use std::marker::PhantomData; use octseq::{Octets, OctetsFrom}; use serde::Serialize; @@ -7,7 +8,7 @@ use serde::Serialize; use crate::bgp::communities::Community; use crate::bgp::message::update_builder::{ComposeError, MpReachNlriBuilder}; use crate::bgp::message::UpdateMessage; -use crate::bgp::path_attributes::{PaMap, FromAttribute}; +use crate::bgp::path_attributes::{FromAttribute, PaMap}; use crate::bgp::{ message::{nlri::Nlri, update_builder::StandardCommunitiesList}, path_attributes::{ @@ -79,21 +80,30 @@ impl From>> for PathAttribute { //------------ The Workshop -------------------------------------------------- -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct RouteWorkshop(Option, Option); +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)] +pub struct RouteWorkshop(N, PaMap, PhantomData); -impl RouteWorkshop { - pub fn from_update_pdu( +impl RouteWorkshop { + pub fn new(nlri: N) -> Self { + Self(nlri, PaMap::empty(), PhantomData) + } + + pub fn from_pa_map(nlri: N, pa_map: PaMap) -> Self { + Self(nlri, pa_map, PhantomData) + } + + pub fn from_update_pdu( + nlri: N, pdu: &UpdateMessage, ) -> Result where for<'a> Vec: OctetsFrom>, { PaMap::from_update_pdu(pdu) - .map(|r| Self(None, Some(r))) + .map(|r| Self(nlri, r, PhantomData)) } - pub fn nlri(&self) -> &Option { + pub fn nlri(&self) -> &N { &self.0 } @@ -101,46 +111,29 @@ impl RouteWorkshop { &mut self, value: WA, ) -> Result<(), ComposeError> { - let mut res = Err(ComposeError::InvalidAttribute); - if let Some(b) = &mut self.1 { - res = WA::store(value, b); - } - res + WA::store(value, &mut self.1) } pub fn get_attr>( &self, ) -> Option { - self.1 - .as_ref() - .and_then(|b| b.get::().or_else(|| ::retrieve(b))) + self.1.get::() } - pub fn clone_into_route(&self) -> Option> { - match self { - RouteWorkshop(Some(nlri), Some(pab)) => { - Some(Route::(nlri.clone(), pab.clone())) - } - _ => None, - } + pub fn clone_into_route(&self) -> Route { + Route::(self.0.clone(), self.1.clone()) } - pub fn into_route(self) -> Option> { - match self { - RouteWorkshop(Some(nlri), Some(pab)) => { - Some(Route::(nlri, pab)) - } - _ => None, - } + pub fn into_route(self) -> Route { + Route::(self.0, self.1) } - pub fn make_route_with_nlri(&self, nlri: N) -> Option> { - match self { - RouteWorkshop(_, Some(pab)) => { - Some(Route::(nlri.clone(), pab.clone())) - } - _ => None, - } + pub fn attributes(&self) -> &PaMap { + &self.1 + } + + pub fn attributes_mut(&mut self) -> &mut PaMap { + &mut self.1 } } @@ -262,6 +255,39 @@ impl FromAttribute for Vec { } } +//------------ NlriWorkshop -------------------------------------------------- + +impl FromAttribute for crate::bgp::message::nlri::Nlri> { + fn from_attribute(_value: PathAttribute) -> Option + where + Self: Sized { + None + } + + fn attribute_type() -> Option { + None + } +} + +impl WorkshopAttribute for crate::bgp::message::nlri::Nlri> { + fn retrieve(attrs: &PaMap) -> Option + where + Self: Sized { + attrs.get::().and_then(|mr| mr.first_nlri()) + } + + fn store( + local_attr: Self, + attrs: &mut PaMap, + ) -> Result<(), ComposeError> { + if let Some(mut nlri) = attrs.get::() { + nlri.set_nlri(local_attr) + } else { + Err(ComposeError::InvalidAttribute) + } + } +} + //------------ NextHopWorkshop ----------------------------------------------- impl FromAttribute for crate::bgp::types::NextHop { @@ -280,9 +306,7 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { attrs.get::() { Some(crate::bgp::types::NextHop::Unicast(next_hop.0.into())) - } else if let Some(nlri) = - attrs.get::() - { + } else if let Some(nlri) = attrs.get::() { Some(*nlri.get_nexthop()) } else { Some(crate::bgp::types::NextHop::Empty) @@ -293,9 +317,7 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { local_attr: Self, attrs: &mut PaMap, ) -> Result<(), ComposeError> { - if let Some(mut nlri) = - attrs.get::() - { + if let Some(mut nlri) = attrs.get::() { nlri.set_nexthop(local_attr) } else { Err(ComposeError::InvalidAttribute) From 1de1ac6c614c6ca41ab3718481c0bddd6c45c6d8 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 22 Feb 2024 10:47:30 +0100 Subject: [PATCH 26/96] impl traits on AfiSafiNlris --- src/bgp/workshop/afisafi_nlri.rs | 154 ++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 4 deletions(-) diff --git a/src/bgp/workshop/afisafi_nlri.rs b/src/bgp/workshop/afisafi_nlri.rs index b4ea6a6f..89721a0c 100644 --- a/src/bgp/workshop/afisafi_nlri.rs +++ b/src/bgp/workshop/afisafi_nlri.rs @@ -4,20 +4,37 @@ use octseq::Octets; use crate::{ addr::Prefix, - bgp::message::{ - nlri::{BasicNlri, FlowSpecNlri}, - update::AfiSafi, - }, + bgp::{message::{ + nlri::{BasicNlri, FlowSpecNlri, Nlri}, + update::AfiSafi, update_builder::ComposeError, + }, path_attributes::PaMap, ParseError}, }; +use super::route::RouteWorkshop; + +//------------ AfiSafiNlri --------------------------------------------------- + pub trait AfiSafiNlri: Clone + Hash + Debug { type Nlri; fn nlri(&self) -> Self::Nlri; fn afi_safi() -> AfiSafi; } + +//------------ HasBasicNlri -------------------------------------------------- + +pub trait HasBasicNlri { + fn basic_nlri(&self) -> BasicNlri; + fn make_route_with_nlri(nlri: M, pa: &PaMap) + -> RouteWorkshop where M: AfiSafiNlri; +} + + +//------------ Ipv4UnicastNlri ----------------------------------------------- + #[derive(Clone, Debug, Hash)] pub struct Ipv4UnicastNlri(pub BasicNlri); + impl AfiSafiNlri for Ipv4UnicastNlri { type Nlri = BasicNlri; @@ -30,6 +47,37 @@ impl AfiSafiNlri for Ipv4UnicastNlri { } } +impl HasBasicNlri for Ipv4UnicastNlri { + fn basic_nlri(&self) -> BasicNlri { + self.0 + } + + fn make_route_with_nlri(nlri: M, pa: &PaMap) + -> RouteWorkshop where M: AfiSafiNlri { + RouteWorkshop::::from_pa_map(nlri, pa.clone()) + } +} + +impl TryFrom> + for Ipv4UnicastNlri { + type Error = ComposeError; + + fn try_from(value: crate::bgp::message::nlri::Nlri) + -> Result { + if let Nlri::Unicast(n) = value { + if n.prefix.is_v4() { + Ok(Ipv4UnicastNlri(n)) + } else { + Err(ComposeError::InvalidAttribute) + } + } else { + Err(ComposeError::InvalidAttribute) + } + } +} + +//------------ Ipv6UnicastNlri ----------------------------------------------- + #[derive(Clone, Debug, Hash)] pub struct Ipv6UnicastNlri(pub BasicNlri); @@ -54,6 +102,38 @@ impl From for Ipv6UnicastNlri { } } +impl HasBasicNlri for Ipv6UnicastNlri { + fn basic_nlri(&self) -> BasicNlri { + self.0 + } + + fn make_route_with_nlri(nlri: M, pa: &PaMap) + -> RouteWorkshop where M: AfiSafiNlri { + RouteWorkshop::::from_pa_map(nlri, pa.clone()) + } +} + +impl TryFrom> + for Ipv6UnicastNlri { + type Error = ComposeError; + + fn try_from(value: crate::bgp::message::nlri::Nlri) + -> Result { + if let Nlri::Unicast(n) = value { + if !n.prefix.is_v4() { + Ok(Ipv6UnicastNlri(n)) + } else { + Err(ComposeError::InvalidAttribute) + } + } else { + Err(ComposeError::InvalidAttribute) + } + } +} + + +//------------ Ipv4MulticastNlri --------------------------------------------- + #[derive(Clone, Debug, Hash)] pub struct Ipv4MulticastNlri(pub BasicNlri); @@ -69,6 +149,38 @@ impl AfiSafiNlri for Ipv4MulticastNlri { } } +impl HasBasicNlri for Ipv4MulticastNlri { + fn basic_nlri(&self) -> BasicNlri { + self.0 + } + + fn make_route_with_nlri(nlri: M, pa: &PaMap) + -> RouteWorkshop where M: AfiSafiNlri { + RouteWorkshop::::from_pa_map(nlri, pa.clone()) + } +} + +impl TryFrom> + for Ipv4MulticastNlri { + type Error = ComposeError; + + fn try_from(value: crate::bgp::message::nlri::Nlri) + -> Result { + if let Nlri::Multicast(n) = value { + if n.prefix.is_v4() { + Ok(Ipv4MulticastNlri(n)) + } else { + Err(ComposeError::InvalidAttribute) + } + } else { + Err(ComposeError::InvalidAttribute) + } + } +} + + +//------------ Ipv6MulticastNlri --------------------------------------------- + #[derive(Clone, Debug, Hash)] pub struct Ipv6MulticastNlri(pub BasicNlri); @@ -84,6 +196,27 @@ impl AfiSafiNlri for Ipv6MulticastNlri { } } +impl TryFrom> + for Ipv6MulticastNlri { + type Error = ComposeError; + + fn try_from(value: crate::bgp::message::nlri::Nlri) + -> Result { + if let Nlri::Multicast(n) = value { + if !n.prefix.is_v4() { + Ok(Ipv6MulticastNlri(n)) + } else { + Err(ComposeError::InvalidAttribute) + } + } else { + Err(ComposeError::InvalidAttribute) + } + } +} + + +//------------ Ipv4FlowSpecNlri ---------------------------------------------- + #[derive(Clone, Debug, Hash)] pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); @@ -100,3 +233,16 @@ impl AfiSafiNlri AfiSafi::Ipv4FlowSpec } } + +impl TryFrom>> for Ipv4UnicastNlri { + type Error = ParseError; + + fn try_from(val: Nlri>) -> Result { + if let Nlri::Unicast(b) = val { + if b.prefix.is_v4() { + return Ok(Ipv4UnicastNlri(b)); + } + } + Err(ParseError::Unsupported) + } +} \ No newline at end of file From bf0b4cf383f7b204f0fd4efef31052f4225f6bc7 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Mon, 26 Feb 2024 10:15:14 +0100 Subject: [PATCH 27/96] remove weird single_first_nlri method --- src/bgp/message/update.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 11868ab0..77ddb1b1 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -307,35 +307,6 @@ impl UpdateMessage { Ok(None) } - pub fn single_afi_safi_nlri<'a, N: - Clone + - Debug + - Hash + - AfiSafiNlri + - TryFrom::Range<'a>>, - Error = ComposeError>>(&'a self) -> Result where - Octs: - 'a, - ::Range<'a>>>>::Error: Debug { - let pp = Parser::with_range( - self.octets(), - self.announcements.clone() - ); - - let iter = Nlris { - parser: pp, - session_config: self.session_config, - afi_safi: AfiSafi::Ipv4Unicast, - }; - - if let Some(n) = iter.iter().next() { - n?.try_into() - } else { - Err(ComposeError::EmptyMpReachNlri) - } - } - /// Returns a combined iterator of conventional and MP_REACH_NLRI. /// /// Consuming the returned iterator requires care. The `Item` is a From 9bc915eef03241ec606fca0bc6502054b4cbf6f0 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Mon, 26 Feb 2024 10:18:26 +0100 Subject: [PATCH 28/96] clippy --- src/bgp/message/update.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 77ddb1b1..35be4b10 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -2,9 +2,7 @@ use std::fmt::Debug; use std::hash::Hash; use crate::bgp::message::Header; -use crate::bgp::workshop::afisafi_nlri::AfiSafiNlri; use octseq::{Octets, Parser}; -//use log::debug; use crate::asn::Asn; use crate::bgp::aspath::AsPath; @@ -35,8 +33,6 @@ use crate::bgp::communities::{ LargeCommunity, }; -use super::update_builder::ComposeError; - /// BGP UPDATE message, variant of the [`Message`] enum. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] From 912a53092698478d9c7b91133b45d3c9960cd89a Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 13:18:25 +0100 Subject: [PATCH 29/96] Set Partial flag when composing an UnimplementedPathAttribute --- src/bgp/path_attributes.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 930f84f3..169c180a 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -26,6 +26,22 @@ use crate::util::parser::{ParseError, parse_ipv4addr}; #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Flags(u8); +impl core::ops::BitOr for Flags { + type Output = Self; + + fn bitor(self, rhs: u8) -> Self::Output { + Self(self.0 | rhs) + } +} + +impl core::ops::BitOr for Flags { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + impl Flags { // 0 1 2 3 4 5 6 7 // @@ -2219,10 +2235,12 @@ impl UnimplementedPathAttribute { -> Result<(), Target::AppendError> { let len = self.value().len(); + // We did not recognize this attribute, so we set the Partial flag. + let flags = self.flags() | Flags::PARTIAL; target.append_slice( - &[self.flags().into(), self.type_code()] + &[flags.into(), self.type_code()] )?; - if self.flags().is_extended_length() { + if flags.is_extended_length() { target.append_slice( &u16::try_from(len).unwrap_or(u16::MAX) .to_be_bytes() From 17deeb6c3c1ade3730b58e2cb0a283a9d2ae7da0 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 15:42:08 +0100 Subject: [PATCH 30/96] Remove unnecessary layer in PathAttributes enum, impl Attribute on types directly --- src/bgp/path_attributes.rs | 344 +++++++++++++++++++++++++------------ src/bgp/types.rs | 12 +- src/bgp/workshop/route.rs | 10 +- 3 files changed, 249 insertions(+), 117 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 169c180a..42f522e9 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -98,13 +98,13 @@ pub trait AttributeHeader { const TYPE_CODE: u8; } +/* macro_rules! attribute { ($name:ident($data:ty), $flags:expr, $type_code:expr ) => { - // TODO Serialize #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct $name(pub(crate) $data); @@ -152,6 +152,7 @@ macro_rules! attribute { } } } +*/ //------------ PathAttributesBuilder ----------------------------------------- @@ -181,8 +182,8 @@ impl PaMap { let mut pa_map = Self::empty(); for pa in pdu.path_attributes()? { if let Ok(pa) = pa { - if pa.type_code() != MpReachNlri::TYPE_CODE - && pa.type_code() != MpUnreachNlri::TYPE_CODE + if pa.type_code() != MpReachNlriBuilder::TYPE_CODE + && pa.type_code() != MpUnreachNlriBuilder::TYPE_CODE { if let PathAttributeType::Invalid(n) = pa.type_code().into() { warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); @@ -421,7 +422,8 @@ macro_rules! path_attributes { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum PathAttribute { - $( $name($name) ),+, + //$( $name($name) ),+, + $( $name($data) ),+, Unimplemented(UnimplementedPathAttribute), Invalid(Flags, u8, Vec), } @@ -473,7 +475,7 @@ macro_rules! path_attributes { match self { $( PathAttribute::$name(_pa) => - $name::TYPE_CODE + <$data>::TYPE_CODE ),+, PathAttribute::Unimplemented(u) => { u.type_code() @@ -488,13 +490,12 @@ macro_rules! path_attributes { $( impl From<$data> for PathAttribute { fn from(value: $data) -> Self { - PathAttribute::$name( - $name(value), - ) + PathAttribute::$name(value) } } )+ + /* $( impl From<$name> for PathAttribute { fn from(pa: $name) -> PathAttribute { @@ -502,6 +503,7 @@ macro_rules! path_attributes { } } )+ + */ //------------ WireformatPathAttribute -------------------------------------- @@ -560,7 +562,6 @@ macro_rules! path_attributes { #[derive(Debug)] pub enum WireformatPathAttribute<'a, Octs: Octets> { - //$( $name(Parser<'a, Octs>, SessionConfig) ),+, $( $name(EncodedPathAttribute<'a, Octs>) ),+, Unimplemented(UnimplementedWireformat<'a, Octs>), Invalid(Flags, u8, Parser<'a, Octs>), @@ -584,7 +585,7 @@ macro_rules! path_attributes { let res = match type_code { $( $type_code => { - if let Err(e) = $name::validate( + if let Err(e) = <$data>::validate( flags.into(), &mut pp, sc ) { debug!("failed to parse path attribute: {e}"); @@ -637,7 +638,7 @@ macro_rules! path_attributes { WireformatPathAttribute::$name(epa) => { Ok(PathAttribute::$name( //$name::parse(&mut p.clone(), *sc)? - $name::parse( + <$data>::parse( &mut epa.value_into_parser(), epa.session_config() )? @@ -790,9 +791,17 @@ macro_rules! path_attributes { } } + /* $( attribute!($name($data), $flags, $type_code); )+ + */ + $( + impl AttributeHeader for $data { + const FLAGS: u8 = $flags; + const TYPE_CODE: u8 = $type_code; + } + )+ // You might think that the Attribute trait (implemented for all path // attributes with the attribute! macro above this comment), can be @@ -806,7 +815,7 @@ macro_rules! path_attributes { impl FromAttribute for $data { fn from_attribute(value: PathAttribute) -> Option<$data> { if let PathAttribute::$name(pa) = value { - Some(pa.inner()) + Some(pa) } else { None } @@ -821,12 +830,12 @@ macro_rules! path_attributes { } path_attributes!( - 1 => Origin(crate::bgp::types::OriginType), Flags::WELLKNOWN, + 1 => Origin(crate::bgp::types::Origin), Flags::WELLKNOWN, 2 => AsPath(crate::bgp::aspath::HopPath), Flags::WELLKNOWN, 3 => ConventionalNextHop(crate::bgp::types::ConventionalNextHop), Flags::WELLKNOWN, 4 => MultiExitDisc(crate::bgp::types::MultiExitDisc), Flags::OPT_NON_TRANS, 5 => LocalPref(crate::bgp::types::LocalPref), Flags::WELLKNOWN, - 6 => AtomicAggregate(()), Flags::WELLKNOWN, + 6 => AtomicAggregate(crate::bgp::types::AtomicAggregate), Flags::WELLKNOWN, 7 => Aggregator(crate::bgp::path_attributes::AggregatorInfo), Flags::OPT_TRANS, 8 => StandardCommunities(crate::bgp::message::update_builder::StandardCommunitiesList), Flags::OPT_TRANS, 9 => OriginatorId(crate::bgp::types::OriginatorId), Flags::OPT_NON_TRANS, @@ -1077,7 +1086,7 @@ macro_rules! check_len_exact { //--- Origin -impl Attribute for Origin { +impl Attribute for crate::bgp::types::Origin { fn value_len(&self) -> usize { 1 } fn compose_value(&self, target: &mut Target) @@ -1087,9 +1096,9 @@ impl Attribute for Origin { } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'_, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(Origin(parser.parse_u8()?.into())) + Ok(Self(parser.parse_u8()?.into())) } fn validate( @@ -1103,6 +1112,69 @@ impl Attribute for Origin { //--- AsPath (see bgp::aspath) + +// attempt to impl on actual type instead of inner wrapper + +/* +impl AttributeHeader for crate::bgp::aspath::HopPath { + const FLAGS: u8 = Flags::WELLKNOWN; + const TYPE_CODE: u8 = 2; +} +*/ + +impl Attribute for crate::bgp::aspath::HopPath { + fn value_len(&self) -> usize { + self.to_as_path::>().unwrap().into_inner().len() + } + + fn compose_value(&self, target: &mut Target) + -> Result<(), Target::AppendError> + { + target.append_slice( + self.to_as_path::>().unwrap().into_inner().as_ref() + ) + } + + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + -> Result + { + // XXX reusing the old/existing AsPath here for the time being + let asp = crate::bgp::aspath::AsPath::new( + parser.peek_all().to_vec(), + sc.has_four_octet_asn() + ).map_err(|_| ParseError::form_error("invalid AS_PATH"))?; + + Ok(asp.to_hop_path()) + } + + fn validate( + _flags: Flags, + parser: &mut Parser<'_, Octs>, + session_config: SessionConfig + ) -> Result<(), ParseError> { + let asn_size = if session_config.has_four_octet_asn() { + 4 + } else { + 2 + }; + while parser.remaining() > 0 { + let segment_type = parser.parse_u8()?; + if !(1..=4).contains(&segment_type) { + return Err(ParseError::form_error( + "illegal segment type in AS_PATH" + )); + } + let len = usize::from(parser.parse_u8()?); // ASNs in segment + parser.advance(len * asn_size)?; // ASNs. + } + Ok(()) + } +} + +// end of attempt + + +/* impl Attribute for AsPath { fn value_len(&self) -> usize { self.0.to_as_path::>().unwrap().into_inner().len() @@ -1151,22 +1223,23 @@ impl Attribute for AsPath { Ok(()) } } +*/ //--- NextHop -impl Attribute for ConventionalNextHop { +impl Attribute for crate::bgp::types::ConventionalNextHop { fn value_len(&self) -> usize { 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.0.octets()) + target.append_slice(&self.0.octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(Self(crate::bgp::types::ConventionalNextHop(parse_ipv4addr(parser)?))) + Ok(Self(parse_ipv4addr(parser)?)) } fn validate( @@ -1180,6 +1253,60 @@ impl Attribute for ConventionalNextHop { //--- MultiExitDisc +// attempt to impl traits on the actual type instead of the inner wrapper +// generated by attribute! / path_attributes! +/* +impl AttributeHeader for crate::bgp::types::MultiExitDisc { + const FLAGS: u8 = Flags::OPT_NON_TRANS; + const TYPE_CODE: u8 = 4; +} +*/ +impl Attribute for crate::bgp::types::MultiExitDisc { + fn value_len(&self) -> usize { 4 } + + fn compose_value(&self, target: &mut Target) + -> Result<(), Target::AppendError> + { + target.append_slice(&self.0.to_be_bytes()) + } + + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + -> Result + { + Ok(Self(parser.parse_u32_be()?)) + } + + fn validate( + _flags: Flags, + parser: &mut Parser<'_, Octs>, + _session_config: SessionConfig + ) -> Result<(), ParseError> { + check_len_exact!(parser, 4, "MULTI_EXIT_DISC") + } +} + +/* works, but commented out because this impl conflicts with the one in the + * macro +impl FromAttribute for crate::bgp::types::MultiExitDisc { + fn from_attribute(pa: PathAttribute) -> Option { + if let PathAttribute::MultiExitDisc(value) = pa { + Some(value) + } else { + None + } + } + + fn attribute_type() -> Option { + Some(PathAttributeType::MultiExitDisc) + } +} +*/ + + +// end of attempt + + +/* impl Attribute for MultiExitDisc { fn value_len(&self) -> usize { 4 } @@ -1203,22 +1330,23 @@ impl Attribute for MultiExitDisc { check_len_exact!(parser, 4, "MULTI_EXIT_DISC") } } +*/ //--- LocalPref -impl Attribute for LocalPref { +impl Attribute for crate::bgp::types::LocalPref { fn value_len(&self) -> usize { 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.0.to_be_bytes()) + target.append_slice(&self.0.to_be_bytes()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(LocalPref(crate::bgp::types::LocalPref(parser.parse_u32_be()?))) + Ok(Self(parser.parse_u32_be()?)) } fn validate( @@ -1232,7 +1360,7 @@ impl Attribute for LocalPref { //--- AtomicAggregate -impl Attribute for AtomicAggregate { +impl Attribute for crate::bgp::types::AtomicAggregate { fn value_len(&self) -> usize { 0 } fn compose_value(&self, _target: &mut Target) @@ -1242,9 +1370,9 @@ impl Attribute for AtomicAggregate { } fn parse<'a, Octs: 'a + Octets>(_parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(AtomicAggregate(())) + Ok(Self) } fn validate( @@ -1256,9 +1384,7 @@ impl Attribute for AtomicAggregate { } } -impl Copy for AtomicAggregate { } - -impl Display for AtomicAggregate { +impl Display for crate::bgp::types::AtomicAggregate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ATOMIC_AGGREGATE") } @@ -1286,7 +1412,7 @@ impl AggregatorInfo { } -impl Attribute for Aggregator { +impl Attribute for AggregatorInfo { // FIXME for legacy 2-byte ASNs, this should be 6. // Should we pass a (&)SessionConfig to this method as well? // Note that `fn compose_len` would then also need a SessionConfig, @@ -1298,12 +1424,12 @@ impl Attribute for Aggregator { fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.asn().to_raw())?; - target.append_slice(&self.0.address().octets()) + target.append_slice(&self.asn().to_raw())?; + target.append_slice(&self.address().octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) - -> Result + -> Result { let asn = if sc.has_four_octet_asn() { Asn::from_u32(parser.parse_u32_be()?) @@ -1312,7 +1438,7 @@ impl Attribute for Aggregator { }; let address = parse_ipv4addr(parser)?; - Ok(Aggregator(AggregatorInfo::new(asn, address))) + Ok(Self::new(asn, address)) } fn validate( @@ -1340,22 +1466,22 @@ impl Display for AggregatorInfo { //--- StandardCommunities -impl Attribute for StandardCommunities { +impl Attribute for crate::bgp::message::update_builder::StandardCommunitiesList { fn value_len(&self) -> usize { - self.0.communities().len() * 4 + self.communities().len() * 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - for c in self.0.communities() { + for c in self.communities() { target.append_slice(&c.to_raw())?; } Ok(()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let mut builder = StandardCommunitiesList::with_capacity( parser.remaining() / 4 @@ -1364,7 +1490,7 @@ impl Attribute for StandardCommunities { builder.add_community(parser.parse_u32_be()?.into()); } - Ok(StandardCommunities(builder)) + Ok(builder) } fn validate( @@ -1390,19 +1516,19 @@ impl StandardCommunitiesList { //--- OriginatorId -impl Attribute for OriginatorId { +impl Attribute for crate::bgp::types::OriginatorId { fn value_len(&self) -> usize { 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.0.octets()) + target.append_slice(&self.0.octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(OriginatorId(crate::bgp::types::OriginatorId(parse_ipv4addr(parser)?))) + Ok(Self(parse_ipv4addr(parser)?)) } fn validate( @@ -1450,28 +1576,28 @@ impl ClusterIds { } -impl Attribute for ClusterList { +impl Attribute for ClusterIds { fn value_len(&self) -> usize { - self.0.cluster_ids.len() * 4 + self.cluster_ids.len() * 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - for c in &self.0.cluster_ids { + for c in &self.cluster_ids { target.append_slice(&c.0)?; } Ok(()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let mut cluster_ids = Vec::with_capacity(parser.remaining() / 4); while parser.remaining() > 0 { cluster_ids.push(parser.parse_u32_be()?.to_be_bytes().into()); } - Ok(ClusterList(ClusterIds::new(cluster_ids))) + Ok(ClusterIds::new(cluster_ids)) } fn validate( @@ -1489,19 +1615,19 @@ impl Attribute for ClusterList { } //--- MpReachNlri -impl Attribute for MpReachNlri { +impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { fn value_len(&self) -> usize { - self.0.value_len() + self.value_len() } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - self.0.compose_value(target) + self.compose_value(target) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) - -> Result + -> Result where Vec: OctetsFrom> { @@ -1536,7 +1662,7 @@ impl Attribute for MpReachNlri { builder.add_announcement(&nlri?); } - Ok(MpReachNlri(builder)) + Ok(builder) } fn validate( @@ -1620,19 +1746,19 @@ impl Attribute for MpReachNlri { } //--- MpUnreachNlri -impl Attribute for MpUnreachNlri { +impl Attribute for crate::bgp::message::update_builder::MpUnreachNlriBuilder { fn value_len(&self) -> usize { - self.0.value_len() + self.value_len() } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - self.0.compose_value(target) + self.compose_value(target) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) - -> Result + -> Result where Vec: OctetsFrom> { @@ -1656,7 +1782,7 @@ impl Attribute for MpUnreachNlri { for nlri in nlri_iter { builder.add_withdrawal(&nlri?); } - Ok(MpUnreachNlri(builder)) + Ok(builder) } fn validate( @@ -1763,22 +1889,22 @@ impl ExtendedCommunitiesList { } -impl Attribute for ExtendedCommunities { +impl Attribute for crate::bgp::path_attributes::ExtendedCommunitiesList { fn value_len(&self) -> usize { - self.0.communities.len() * 8 + self.communities.len() * 8 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - for c in &self.0.communities { + for c in &self.communities { target.append_slice(&c.to_raw())?; } Ok(()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let mut communities = Vec::with_capacity(parser.remaining() / 8); let mut buf = [0u8; 8]; @@ -1786,7 +1912,7 @@ impl Attribute for ExtendedCommunities { parser.parse_buf(&mut buf)?; communities.push(buf.into()); } - Ok(ExtendedCommunities(ExtendedCommunitiesList::new(communities))) + Ok(Self::new(communities)) } fn validate( @@ -1805,28 +1931,28 @@ impl Attribute for ExtendedCommunities { //--- As4Path (see bgp::aspath) -impl Attribute for As4Path { +impl Attribute for crate::bgp::types::As4Path { fn value_len(&self) -> usize { - self.0.0.to_as_path::>().unwrap().into_inner().len() + self.0.to_as_path::>().unwrap().into_inner().len() } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { target.append_slice( - self.0.0.to_as_path::>().unwrap().into_inner().as_ref() + self.0.to_as_path::>().unwrap().into_inner().as_ref() ) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) - -> Result + -> Result { // XXX Same as with AsPath, reusing the old/existing As4Path here let asp = crate::bgp::aspath::AsPath::new( parser.peek_all().to_vec(), sc.has_four_octet_asn() ).map_err(|_| ParseError::form_error("invalid AS4_PATH"))?; - Ok(As4Path(crate::bgp::types::As4Path(asp.to_hop_path()))) + Ok(Self(asp.to_hop_path())) } fn validate( @@ -1851,22 +1977,22 @@ impl Attribute for As4Path { //--- As4Aggregator -impl Attribute for As4Aggregator { +impl Attribute for crate::bgp::types::As4Aggregator { fn value_len(&self) -> usize { 8 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.0.asn().to_raw())?; - target.append_slice(&self.0.0.address().octets()) + target.append_slice(&self.0.asn().to_raw())?; + target.append_slice(&self.0.address().octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let asn = Asn::from_u32(parser.parse_u32_be()?); let address = parse_ipv4addr(parser)?; - Ok(As4Aggregator(crate::bgp::types::As4Aggregator(AggregatorInfo::new(asn, address)))) + Ok(Self(AggregatorInfo::new(asn, address))) } fn validate( @@ -1881,19 +2007,19 @@ impl Attribute for As4Aggregator { //--- Connector (deprecated) -impl Attribute for Connector { +impl Attribute for crate::bgp::types::Connector { fn value_len(&self) -> usize { 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.0.octets()) + target.append_slice(&self.0.octets()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(Connector(crate::bgp::types::Connector(parse_ipv4addr(parser)?))) + Ok(Self(parse_ipv4addr(parser)?)) } fn validate( @@ -1922,25 +2048,25 @@ impl AsPathLimitInfo { } } -impl Attribute for AsPathLimit { +impl Attribute for AsPathLimitInfo { fn value_len(&self) -> usize { 5 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&[self.0.upper_bound])?; - target.append_slice(&self.0.attacher.to_raw()) + target.append_slice(&[self.upper_bound])?; + target.append_slice(&self.attacher.to_raw()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let info = AsPathLimitInfo { upper_bound: parser.parse_u8()?, attacher: Asn::from_u32(parser.parse_u32_be()?) }; - Ok(AsPathLimit(info)) + Ok(info) } fn validate( @@ -1983,22 +2109,22 @@ impl Ipv6ExtendedCommunitiesList { } } -impl Attribute for Ipv6ExtendedCommunities { +impl Attribute for Ipv6ExtendedCommunitiesList { fn value_len(&self) -> usize { - self.0.communities.len() * 20 + self.communities.len() * 20 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - for c in &self.0.communities { + for c in &self.communities { target.append_slice(&c.to_raw())?; } Ok(()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let mut communities = Vec::with_capacity(parser.remaining() / 20); let mut buf = [0u8; 20]; @@ -2006,7 +2132,7 @@ impl Attribute for Ipv6ExtendedCommunities { parser.parse_buf(&mut buf)?; communities.push(buf.into()); } - Ok(Ipv6ExtendedCommunities(Ipv6ExtendedCommunitiesList::new(communities))) + Ok(Ipv6ExtendedCommunitiesList::new(communities)) } fn validate( @@ -2054,22 +2180,22 @@ impl LargeCommunitiesList { } -impl Attribute for LargeCommunities { +impl Attribute for LargeCommunitiesList { fn value_len(&self) -> usize { - self.0.communities.len() * 12 + self.communities.len() * 12 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - for c in &self.0.communities { + for c in &self.communities { target.append_slice(&c.to_raw())?; } Ok(()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let mut communities = Vec::with_capacity(parser.remaining() / 12); let mut buf = [0u8; 12]; @@ -2077,7 +2203,7 @@ impl Attribute for LargeCommunities { parser.parse_buf(&mut buf)?; communities.push(buf.into()); } - Ok(LargeCommunities(LargeCommunitiesList::new(communities))) + Ok(Self::new(communities)) } fn validate( @@ -2096,19 +2222,19 @@ impl Attribute for LargeCommunities { //--- Otc -impl Attribute for Otc { +impl Attribute for crate::bgp::types::Otc { fn value_len(&self) -> usize { 4 } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.0.to_raw()) + target.append_slice(&self.0.to_raw()) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { - Ok(Otc(crate::bgp::types::Otc(Asn::from_u32(parser.parse_u32_be()?)))) + Ok(Self(Asn::from_u32(parser.parse_u32_be()?))) } fn validate( @@ -2137,24 +2263,24 @@ impl AttributeSet { } } -impl Attribute for AttrSet { +impl Attribute for AttributeSet { fn value_len(&self) -> usize { - 4 + self.0.attributes.len() + 4 + self.attributes.len() } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.origin.to_raw())?; - target.append_slice(&self.0.attributes) + target.append_slice(&self.origin.to_raw())?; + target.append_slice(&self.attributes) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let origin = Asn::from_u32(parser.parse_u32_be()?); let attributes = parser.peek_all().to_vec(); - Ok(AttrSet(AttributeSet::new(origin, attributes))) + Ok(Self::new(origin, attributes)) } fn validate( @@ -2190,22 +2316,22 @@ impl ReservedRaw { } } -impl Attribute for Reserved { +impl Attribute for ReservedRaw { fn value_len(&self) -> usize { - self.0.raw.len() + self.raw.len() } fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&self.0.raw) + target.append_slice(&self.raw) } fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result + -> Result { let raw = parser.peek_all().to_vec(); - Ok(Reserved(ReservedRaw::new(raw))) + Ok(Self::new(raw)) } fn validate( diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 29699f29..9288b5ea 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -272,6 +272,11 @@ typeenum!( } ); +/// Wrapper for the 1 byte Origin +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Origin(pub OriginType); + /// 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))] @@ -300,6 +305,11 @@ impl std::fmt::Display for LocalPref { } } + +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AtomicAggregate; + /// Wrapper for the 4 byte OnlyToCustomer (Otc) value in path attributes. #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -399,4 +409,4 @@ pub struct As4Path(pub HopPath); #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct As4Aggregator(pub AggregatorInfo); \ No newline at end of file +pub struct As4Aggregator(pub AggregatorInfo); diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 1057e421..038b55c7 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -63,17 +63,13 @@ impl Route { impl From> for PathAttribute { fn from(value: crate::bgp::aspath::AsPath) -> Self { - PathAttribute::AsPath(crate::bgp::path_attributes::AsPath( - value.to_hop_path(), - )) + PathAttribute::AsPath(value.to_hop_path()) } } impl From>> for PathAttribute { fn from(value: crate::bgp::aspath::AsPath>) -> Self { - PathAttribute::AsPath(crate::bgp::path_attributes::AsPath( - value.to_hop_path(), - )) + PathAttribute::AsPath(value.to_hop_path()) } } @@ -156,7 +152,7 @@ impl_workshop!( crate::bgp::aspath::HopPath crate::bgp::types::LocalPref crate::bgp::types::MultiExitDisc - crate::bgp::types::OriginType + crate::bgp::types::Origin crate::bgp::types::OriginatorId crate::bgp::path_attributes::AggregatorInfo crate::bgp::path_attributes::ExtendedCommunitiesList From 74a94fef7f7050171a30c47fad958b4c58186f58 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 16:19:58 +0100 Subject: [PATCH 31/96] Fix tests --- src/bgp/message/update_builder.rs | 24 +++++----- src/bgp/path_attributes.rs | 73 +++++++++++++++---------------- src/bgp/types.rs | 6 +++ src/bmp/message.rs | 8 ++-- 4 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 41b2c2e6..a7ffc23f 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -1869,7 +1869,7 @@ mod tests { "1.0.4.0/24", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.set_nexthop_unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(123); 70 @@ -1896,7 +1896,7 @@ mod tests { "2001:db8:3::/48", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.set_nexthop_unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -1916,7 +1916,7 @@ mod tests { use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.set_nexthop_unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -1944,7 +1944,7 @@ mod tests { ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap(); @@ -1967,7 +1967,7 @@ mod tests { use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); //builder.set_nexthop("2001:db8::1".parse().unwrap()).unwrap(); builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap(); let path = HopPath::from([ @@ -1994,7 +1994,7 @@ mod tests { "1.0.4.0/24", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.set_nexthop_unicast("1.2.3.4".parse::().unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -2027,7 +2027,7 @@ mod tests { "1.0.4.0/24", ].map(|p| Nlri::unicast_from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.set_nexthop_unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into()).unwrap(); let path = HopPath::from([ Asn::from_u32(100), @@ -2096,9 +2096,9 @@ mod tests { assert_eq!(builder.attributes.len(), 4); - builder.attributes.set(OriginType::Igp); - builder.attributes.set(OriginType::Egp); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Egp)); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); assert_eq!(builder.attributes.len(), 4); @@ -2116,7 +2116,7 @@ mod tests { builder.add_community( StandardCommunity::from_str("AS1234:999").unwrap() ).unwrap(); - builder.attributes.set(OriginType::Igp); + builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); builder.add_community( StandardCommunity::from_str("AS1234:1000").unwrap() ).unwrap(); @@ -2233,7 +2233,7 @@ mod tests { match *d { // FIXME: check if MPU is the _only_ attribute, // perhaps we are dealing with an EoR here? - &crate::bgp::path_attributes::MpUnreachNlri::TYPE_CODE => { + &crate::bgp::message::update_builder::MpUnreachNlriBuilder::TYPE_CODE => { // XXX RIS data contains empty-but-non-EoR // MP_UNREACH_NLRI for some reason. //assert!(original.is_eor().is_some()); diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 42f522e9..3d940654 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -2391,6 +2391,7 @@ mod tests { use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::NextHop; use crate::bgp::aspath::HopPath; + use crate::bgp::types::OriginType; #[test] fn wireformat_to_owned_and_back() { @@ -2406,7 +2407,7 @@ mod tests { assert_eq!(target, raw); } - check(vec![0x40, 0x01, 0x01, 0x00], PA::Origin(Origin::new(0.into()))); + check(vec![0x40, 0x01, 0x01, 0x00], PA::Origin(OriginType::Igp.into())); check( vec![0x40, 0x02, 10, @@ -2414,30 +2415,30 @@ mod tests { 0x00, 0x00, 0x00, 100, 0x00, 0x00, 0x00, 200, ], - PA::AsPath(AsPath(HopPath::from(vec![ + PA::AsPath(HopPath::from(vec![ Asn::from_u32(100), Asn::from_u32(200)] - ))) + )) ); check( vec![0x40, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04], - PA::ConventionalNextHop(ConventionalNextHop(crate::bgp::types::ConventionalNextHop("1.2.3.4".parse().unwrap()))) + PA::ConventionalNextHop(crate::bgp::types::ConventionalNextHop("1.2.3.4".parse().unwrap())) ); check( vec![0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0xff], - PA::MultiExitDisc(MultiExitDisc::new(crate::bgp::types::MultiExitDisc(255))) + PA::MultiExitDisc(crate::bgp::types::MultiExitDisc(255)) ); check( vec![0x40, 0x05, 0x04, 0x00, 0x00, 0x00, 0x0a], - PA::LocalPref(LocalPref::new(crate::bgp::types::LocalPref(10))) + PA::LocalPref(crate::bgp::types::LocalPref(10)) ); check( vec![0x40, 0x06, 0x00], - PA::AtomicAggregate(AtomicAggregate::new(())) + PA::AtomicAggregate(crate::bgp::types::AtomicAggregate) ); check( @@ -2445,10 +2446,10 @@ mod tests { 0xc0, 0x07, 0x08, 0x00, 0x00, 0x00, 0x65, 0xc6, 0x33, 0x64, 0x01 ], - PA::Aggregator(Aggregator(AggregatorInfo::new( + PA::Aggregator(AggregatorInfo::new( Asn::from_u32(101), "198.51.100.1".parse().unwrap() - ))) + )) ); check( @@ -2463,20 +2464,18 @@ mod tests { builder.add_community(Wellknown::NoExport.into()); builder.add_community(Wellknown::NoAdvertise.into()); builder.add_community(Wellknown::NoExportSubconfed.into()); - PA::StandardCommunities(StandardCommunities(builder)) + PA::StandardCommunities(builder) } ); check( vec![0x80, 0x09, 0x04, 0x0a, 0x00, 0x00, 0x04], - OriginatorId(crate::bgp::types::OriginatorId("10.0.0.4".parse().unwrap())).into() + crate::bgp::types::OriginatorId("10.0.0.4".parse().unwrap()).into() ); check( vec![0x80, 0x0a, 0x04, 0x0a, 0x00, 0x00, 0x03], - ClusterList(ClusterIds::new( - vec![[10, 0, 0, 3].into()] - )).into() + ClusterIds::new(vec![[10, 0, 0, 3].into()]).into() ); check( @@ -2500,7 +2499,7 @@ mod tests { &Nlri::unicast_from_str("2001:db8:aabb::/48").unwrap() ); - MpReachNlri(builder).into() + builder.into() } ); @@ -2534,7 +2533,7 @@ mod tests { ); }); - MpUnreachNlri(builder).into() + builder.into() } ); @@ -2543,9 +2542,9 @@ mod tests { 0xc0, 0x10, 0x08, 0x00, 0x02, 0xfc, 0x85, 0x00, 0x00, 0xcf, 0x08 ], - ExtendedCommunities(ExtendedCommunitiesList::new(vec![ + ExtendedCommunitiesList::new(vec![ "rt:64645:53000".parse().unwrap() - ])).into() + ]).into() ); check( @@ -2555,10 +2554,10 @@ mod tests { 0x00, 0x00, 0x00, 100, 0x00, 0x00, 0x00, 200, ], - PA::As4Path(As4Path::new(crate::bgp::types::As4Path(HopPath::from(vec![ + PA::As4Path(crate::bgp::types::As4Path(HopPath::from(vec![ Asn::from_u32(100), Asn::from_u32(200)] - )))) + ))) ); check( @@ -2566,22 +2565,20 @@ mod tests { 0xc0, 0x12, 0x08, 0x00, 0x00, 0x04, 0xd2, 10, 0, 0, 99 ], - As4Aggregator(crate::bgp::types::As4Aggregator(AggregatorInfo::new( + crate::bgp::types::As4Aggregator(AggregatorInfo::new( Asn::from_u32(1234), "10.0.0.99".parse().unwrap() - ))).into() + )).into() ); check( vec![0xc0, 0x14, 0x04, 1, 2, 3, 4], - Connector(crate::bgp::types::Connector("1.2.3.4".parse().unwrap())).into() + crate::bgp::types::Connector("1.2.3.4".parse().unwrap()).into() ); check( vec![0xc0, 0x15, 0x05, 0x14, 0x00, 0x00, 0x04, 0xd2], - AsPathLimit( - AsPathLimitInfo::new(20, Asn::from_u32(1234)) - ).into() + AsPathLimitInfo::new(20, Asn::from_u32(1234)).into() ); //TODO 22 PmsiTunnel @@ -2598,7 +2595,7 @@ mod tests { 0x00, 0x00, 0x01, 0x00, 0x00, 0xe2, 0x0a, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x1f ], - LargeCommunities(LargeCommunitiesList::new( + LargeCommunitiesList::new( vec![ "AS8283:6:15".parse().unwrap(), "AS57866:100:2914".parse().unwrap(), @@ -2606,29 +2603,29 @@ mod tests { "AS57866:103:1".parse().unwrap(), "AS57866:104:31".parse().unwrap(), ] - )).into() + ).into() ); check( vec![0xc0, 0x23, 0x04, 0x00, 0x00, 0x04, 0xd2], - Otc::new(crate::bgp::types::Otc(Asn::from_u32(1234))).into() + crate::bgp::types::Otc(Asn::from_u32(1234)).into() ); // TODO AttrSet // TODO Reserved? // UnimplementedPathAttribute + // Note that we need to set the Partial flag on the input already, + // otherwise the first assert_eq in fn check fails. + let flags = Flags::OPT_TRANS | Flags::PARTIAL; check( - vec![0xc0, 254, 0x04, 0x01, 0x02, 0x03, 0x04], + vec![flags, 254, 0x04, 0x01, 0x02, 0x03, 0x04], UnimplementedPathAttribute::new( - Flags::OPT_TRANS.into(), + flags.into(), 254, vec![0x01, 0x02, 0x03, 0x04] ).into() ); - - - } #[test] @@ -2726,10 +2723,10 @@ mod tests { ).unwrap(); let mut owned = pa.to_owned().unwrap(); if let PathAttribute::AsPath(ref mut asp) = owned { - assert_eq!(format!("{}", asp.as_ref()), "AS100 AS200"); - asp.as_mut().prepend(Asn::from_u32(50)); - assert_eq!(format!("{}", asp.as_ref()), "AS50 AS100 AS200"); - assert_eq!(3, asp.as_ref().hop_count()); + assert_eq!(format!("{}", asp), "AS100 AS200"); + asp.prepend(Asn::from_u32(50)); + assert_eq!(format!("{}", asp), "AS50 AS100 AS200"); + assert_eq!(3, asp.hop_count()); } let mut composed = Vec::new(); diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 9288b5ea..922f8a5e 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -277,6 +277,12 @@ typeenum!( #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Origin(pub OriginType); +impl From for Origin { + fn from(t: OriginType) -> Origin { + Origin(t) + } +} + /// 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))] diff --git a/src/bmp/message.rs b/src/bmp/message.rs index 37cf0e6c..4a13c97f 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -1677,8 +1677,8 @@ mod tests { use std::str::FromStr; use crate::addr::Prefix; use crate::bgp::types::{Afi, Safi}; - use crate::bgp::path_attributes::{AsPath, AttributeHeader, - ConventionalNextHop, MultiExitDisc}; + use crate::bgp::path_attributes::AttributeHeader; + use crate::bgp::types::{ConventionalNextHop, MultiExitDisc}; use crate::bgp::message::nlri::Nlri; use crate::bgp::message::update::{FourOctetAsn, SessionConfig}; @@ -1795,7 +1795,7 @@ mod tests { let mut pas = bgp_update.path_attributes().unwrap().into_iter(); let pa1 = pas.next().unwrap().unwrap(); - assert_eq!(pa1.type_code(), crate::bgp::path_attributes::Origin::TYPE_CODE); + assert_eq!(pa1.type_code(), crate::bgp::types::Origin::TYPE_CODE); assert_eq!(pa1.flags(), 0x40.into()); assert!( pa1.flags().is_transitive()); assert!(!pa1.flags().is_optional()); @@ -1803,7 +1803,7 @@ mod tests { //assert_eq!(pa1.as_ref(), [0x00]); let pa2 = pas.next().unwrap().unwrap(); - assert_eq!(pa2.type_code(), AsPath::TYPE_CODE); + assert_eq!(pa2.type_code(), crate::bgp::aspath::HopPath::TYPE_CODE); assert_eq!(pa2.flags(), 0x40.into()); // TODO check actual AS_PATH contents From 422c2dc813dd4951ccb5a367a082c85490a652df Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 16:34:40 +0100 Subject: [PATCH 32/96] Cleanup --- src/bgp/path_attributes.rs | 226 +------------------------------------ src/bgp/types.rs | 2 +- src/bmp/message.rs | 2 - 3 files changed, 2 insertions(+), 228 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 3d940654..40fd3a1d 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -98,62 +98,6 @@ pub trait AttributeHeader { const TYPE_CODE: u8; } -/* -macro_rules! attribute { - ($name:ident($data:ty), - $flags:expr, - $type_code:expr - ) => { - - #[derive(Clone, Debug, Eq, Hash, PartialEq)] - #[cfg_attr(feature = "serde", derive(serde::Serialize))] - pub struct $name(pub(crate) $data); - impl $name { - pub fn new(data: $data) -> $name { - $name(data) - } - - pub fn inner(self) -> $data { - self.0 - } - } - - impl std::convert::AsRef<$data> for $name { - fn as_ref(&self) -> &$data { - &self.0 - } - } - - impl std::convert::AsMut<$data> for $name { - fn as_mut(&mut self) -> &mut $data { - &mut self.0 - } - } - - /* - impl std::ops::Deref for $name { - type Target = $data; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl std::ops::DerefMut for $name { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - */ - - impl AttributeHeader for $name { - const FLAGS: u8 = $flags; - const TYPE_CODE: u8 = $type_code; - } - } -} -*/ - //------------ PathAttributesBuilder ----------------------------------------- @@ -422,7 +366,6 @@ macro_rules! path_attributes { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum PathAttribute { - //$( $name($name) ),+, $( $name($data) ),+, Unimplemented(UnimplementedPathAttribute), Invalid(Flags, u8, Vec), @@ -495,15 +438,6 @@ macro_rules! path_attributes { } )+ - /* - $( - impl From<$name> for PathAttribute { - fn from(pa: $name) -> PathAttribute { - PathAttribute::$name(pa) - } - } - )+ - */ //------------ WireformatPathAttribute -------------------------------------- @@ -717,32 +651,6 @@ macro_rules! path_attributes { } } -/* -//------------ MaterializedPathAttributes ------------------------------------ - - paste! { - #[derive(Clone, Debug, Eq, Hash, PartialEq)] - pub struct MaterializedPathAttributes { - $( pub [<$name:snake>] : Option<$name> ),+, - pub unknown_transitives: Vec, - } - } - - impl MaterializedPathAttributes { - - pub fn from_message(msg: super::message::UpdateMessage<()>) -> Self { - todo!() - } - } - - impl From for MaterializedPathAttributes - where I: Iterator - { - fn from(_iter: I) -> Self { - todo!() - } - } -*/ //------------ PathAttributeType --------------------------------------------- @@ -791,11 +699,6 @@ macro_rules! path_attributes { } } - /* - $( - attribute!($name($data), $flags, $type_code); - )+ - */ $( impl AttributeHeader for $data { const FLAGS: u8 = $flags; @@ -1110,17 +1013,7 @@ impl Attribute for crate::bgp::types::Origin { } } -//--- AsPath (see bgp::aspath) - - -// attempt to impl on actual type instead of inner wrapper - -/* -impl AttributeHeader for crate::bgp::aspath::HopPath { - const FLAGS: u8 = Flags::WELLKNOWN; - const TYPE_CODE: u8 = 2; -} -*/ +//--- AsPath (HopPath) impl Attribute for crate::bgp::aspath::HopPath { fn value_len(&self) -> usize { @@ -1171,60 +1064,6 @@ impl Attribute for crate::bgp::aspath::HopPath { } } -// end of attempt - - -/* -impl Attribute for AsPath { - fn value_len(&self) -> usize { - self.0.to_as_path::>().unwrap().into_inner().len() - } - - fn compose_value(&self, target: &mut Target) - -> Result<(), Target::AppendError> - { - target.append_slice( - self.0.to_as_path::>().unwrap().into_inner().as_ref() - ) - } - - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) - -> Result - { - // XXX reusing the old/existing AsPath here for the time being - let asp = crate::bgp::aspath::AsPath::new( - parser.peek_all().to_vec(), - sc.has_four_octet_asn() - ).map_err(|_| ParseError::form_error("invalid AS_PATH"))?; - - Ok(AsPath(asp.to_hop_path())) - } - - fn validate( - _flags: Flags, - parser: &mut Parser<'_, Octs>, - session_config: SessionConfig - ) -> Result<(), ParseError> { - let asn_size = if session_config.has_four_octet_asn() { - 4 - } else { - 2 - }; - while parser.remaining() > 0 { - let segment_type = parser.parse_u8()?; - if !(1..=4).contains(&segment_type) { - return Err(ParseError::form_error( - "illegal segment type in AS_PATH" - )); - } - let len = usize::from(parser.parse_u8()?); // ASNs in segment - parser.advance(len * asn_size)?; // ASNs. - } - Ok(()) - } -} -*/ - //--- NextHop impl Attribute for crate::bgp::types::ConventionalNextHop { @@ -1253,14 +1092,6 @@ impl Attribute for crate::bgp::types::ConventionalNextHop { //--- MultiExitDisc -// attempt to impl traits on the actual type instead of the inner wrapper -// generated by attribute! / path_attributes! -/* -impl AttributeHeader for crate::bgp::types::MultiExitDisc { - const FLAGS: u8 = Flags::OPT_NON_TRANS; - const TYPE_CODE: u8 = 4; -} -*/ impl Attribute for crate::bgp::types::MultiExitDisc { fn value_len(&self) -> usize { 4 } @@ -1285,53 +1116,6 @@ impl Attribute for crate::bgp::types::MultiExitDisc { } } -/* works, but commented out because this impl conflicts with the one in the - * macro -impl FromAttribute for crate::bgp::types::MultiExitDisc { - fn from_attribute(pa: PathAttribute) -> Option { - if let PathAttribute::MultiExitDisc(value) = pa { - Some(value) - } else { - None - } - } - - fn attribute_type() -> Option { - Some(PathAttributeType::MultiExitDisc) - } -} -*/ - - -// end of attempt - - -/* -impl Attribute for MultiExitDisc { - fn value_len(&self) -> usize { 4 } - - fn compose_value(&self, target: &mut Target) - -> Result<(), Target::AppendError> - { - target.append_slice(&self.0.0.to_be_bytes()) - } - - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) - -> Result - { - Ok(MultiExitDisc(crate::bgp::types::MultiExitDisc(parser.parse_u32_be()?))) - } - - fn validate( - _flags: Flags, - parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig - ) -> Result<(), ParseError> { - check_len_exact!(parser, 4, "MULTI_EXIT_DISC") - } -} -*/ - //--- LocalPref impl Attribute for crate::bgp::types::LocalPref { @@ -1552,14 +1336,6 @@ impl From<[u8; 4]> for BgpIdentifier { } } -/* -impl From for BgpIdentifier { - fn from(raw: u32) -> BgpIdentifier { - BgpIdentifier(raw.to_be_bytes()) - } -} -*/ - #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct ClusterIds { diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 922f8a5e..427a6820 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -272,7 +272,7 @@ typeenum!( } ); -/// Wrapper for the 1 byte Origin +/// Wrapper for the 1 byte Origin. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Origin(pub OriginType); diff --git a/src/bmp/message.rs b/src/bmp/message.rs index 4a13c97f..25cd7254 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -1799,8 +1799,6 @@ mod tests { assert_eq!(pa1.flags(), 0x40.into()); assert!( pa1.flags().is_transitive()); assert!(!pa1.flags().is_optional()); - //TODO implement enum for Origins - //assert_eq!(pa1.as_ref(), [0x00]); let pa2 = pas.next().unwrap().unwrap(); assert_eq!(pa2.type_code(), crate::bgp::aspath::HopPath::TYPE_CODE); From c565171593c2c3683bb267e191eb7e1a45accd9f Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 16:47:46 +0100 Subject: [PATCH 33/96] Provide default fn impls in FromAttribute trait --- src/bgp/path_attributes.rs | 16 +++++++++++----- src/bgp/workshop/route.rs | 34 ++++------------------------------ 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 40fd3a1d..386fe830 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -341,15 +341,21 @@ impl PaMap { pub trait FromAttribute { - fn from_attribute(value: PathAttribute) -> Option + fn from_attribute(_value: PathAttribute) -> Option where - Self: Sized; - fn attribute_type() -> Option; + Self: Sized { + None + } + + fn attribute_type() -> Option { + None + } + } impl From for PaMap { - fn from(value: AttributesMap) -> Self { - Self { attributes: value } + fn from(attributes: AttributesMap) -> Self { + Self { attributes } } } diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 038b55c7..5dcb0ac3 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -13,7 +13,7 @@ use crate::bgp::{ message::{nlri::Nlri, update_builder::StandardCommunitiesList}, path_attributes::{ ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, - LargeCommunitiesList, PathAttribute, PathAttributeType, + LargeCommunitiesList, PathAttribute, }, }; @@ -241,29 +241,11 @@ impl WorkshopAttribute for Vec { } } -impl FromAttribute for Vec { - fn from_attribute(_value: PathAttribute) -> Option { - None - } - - fn attribute_type() -> Option { - None - } -} +impl FromAttribute for Vec { } //------------ NlriWorkshop -------------------------------------------------- -impl FromAttribute for crate::bgp::message::nlri::Nlri> { - fn from_attribute(_value: PathAttribute) -> Option - where - Self: Sized { - None - } - - fn attribute_type() -> Option { - None - } -} +impl FromAttribute for crate::bgp::message::nlri::Nlri> { } impl WorkshopAttribute for crate::bgp::message::nlri::Nlri> { fn retrieve(attrs: &PaMap) -> Option @@ -286,15 +268,7 @@ impl WorkshopAttribute for crate::bgp::message::nlri::Nlri Option { - None - } - - fn from_attribute(_value: PathAttribute) -> Option { - None - } -} +impl FromAttribute for crate::bgp::types::NextHop { } impl WorkshopAttribute for crate::bgp::types::NextHop { fn retrieve(attrs: &PaMap) -> Option { From 416814d37b1f4b933ddf9f6454a809aafc428066 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 16:50:01 +0100 Subject: [PATCH 34/96] Get rid of Default impl for PathAttribute --- src/bgp/path_attributes.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 386fe830..6b022e76 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -209,12 +209,15 @@ impl PaMap { } pub(in crate::bgp) fn take(&mut self) -> Option { + /* if let Some(attr_type) = A::attribute_type() { self.attributes_mut() .get_mut(&attr_type.into()).and_then(|a| A::from_attribute(std::mem::take(a))) } else { None } + */ + self.remove::() } pub(in crate::bgp) fn contains(&self) -> bool { @@ -769,11 +772,13 @@ path_attributes!( // Default implementation is needed to be able to take() an attribute. When // done so it gets replaced with Unimplemented. +/* impl Default for PathAttribute { fn default() -> Self { Self::Unimplemented(UnimplementedPathAttribute{flags: Flags(0), type_code: 0, value: vec![]}) } } +*/ //------------ UnimplementedPathAttribute ------------------------------------ From f2abf4beb5d801708122988cc0236c3d8aac2fe5 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 16:59:30 +0100 Subject: [PATCH 35/96] Rename attributes_owned to into_attributes --- src/bgp/path_attributes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 6b022e76..b460c857 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -191,7 +191,7 @@ impl PaMap { &mut self.attributes } - pub fn attributes_owned(self) -> AttributesMap { + pub fn into_attributes(self) -> AttributesMap { self.attributes } From 5e567a76b3ef59f00e722dbc7fa1959dbd2fd046 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 26 Feb 2024 17:07:59 +0100 Subject: [PATCH 36/96] Return replaced value from PaMap.set/add_attribute --- src/bgp/path_attributes.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index b460c857..165d6fc7 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -143,10 +143,9 @@ impl PaMap { pub fn set>( &mut self, attr: A - ) { - if let Some(attr_type) = A::attribute_type() { - self.attributes.insert(attr_type.into(), attr.into()); - } + ) -> Option { + let attr = attr.into(); + self.attributes.insert(attr.type_code(), attr).map(A::from_attribute)? } pub fn get( @@ -171,16 +170,16 @@ impl PaMap { pub fn add_attribute( &mut self, attr: PathAttribute, - ) -> Result<(), ComposeError> { + ) -> Result, ComposeError> { if let PathAttribute::Invalid(..) = attr { warn!( "adding Invalid attribute to UpdateBuilder: {}", &attr.type_code() ); } - self.attributes_mut().insert(attr.type_code(),attr); - Ok(()) + let replaced = self.attributes_mut().insert(attr.type_code(),attr); + Ok(replaced) } pub fn attributes(&self) -> &AttributesMap { From f96a767c90c675a9fb25eb25f7be7387b688dd65 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 27 Feb 2024 10:59:09 +0100 Subject: [PATCH 37/96] Cleanup in UpdateBuilder --- src/bgp/message/update_builder.rs | 173 ------------------------------ 1 file changed, 173 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index a7ffc23f..a9707c1b 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -340,179 +340,6 @@ where Target: octseq::Truncate } } -/* -impl> UpdateBuilder -where Infallible: From<::AppendError> -{ - #[deprecated] - pub fn build_acs(mut self, acs: AttrChangeSet) - -> Result - { - // Withdrawals - let withdraw_len = 0_usize; - // placeholder - let _ = self.target.append_slice(&(withdraw_len as u16).to_be_bytes()); - //self.target.as_mut()[19..=20].copy_from_slice( - // &(withdraw_len as u16).to_be_bytes() - //); - // TODO actual withdrawals - - - // Path Attributes - // flags (from msb to lsb): - // optional - // transitive - // partial - // extended_length (2 octet length) - - let mut total_pa_len = 0_usize; - // Total Path Attribute len place holder: - let _ = self.target.append_slice(&[0x00, 0x00]); - - if let Some(origin) = acs.origin_type.into_opt() { - let attr_flags = 0b0100_0000; - let attr_type_code = PathAttributeType::Origin.into(); - let attr_len = 1_u8; - let _ = self.target.append_slice( - &[attr_flags, attr_type_code, attr_len, origin.into()]); - total_pa_len += 2 + 1 + usize::from(attr_len); - } - - if let Some(as_path) = acs.as_path.into_opt() { - let attr_flags = 0b0101_0000; - let attr_type_code = PathAttributeType::AsPath.into(); - let asp = as_path.into_inner(); - let attr_len = asp.len(); - if u16::try_from(attr_len).is_err() { - return Err(ComposeError::AttributeTooLarge( - PathAttributeType::AsPath, - attr_len - )); - } - let _ = self.target.append_slice(&[attr_flags, attr_type_code]); - let _ = self.target.append_slice(&(attr_len as u16).to_be_bytes()); - let _ = self.target.append_slice(&asp); - - total_pa_len += 2 + 2 + attr_len; - } - - - // XXX the next_hop is either a (conventional, for v4/unicast) path - // attribute, or, it is part of MP_REACH_NLRI. - // Should/must v4/unicast always go in MP_REACH_NLRI when both peers - // sent such capability though? - if let Some(next_hop) = acs.next_hop.into_opt() { - match next_hop { - NextHop::Ipv4(v4addr) => { - let attr_flags = 0b0100_0000; - let attr_type_code = PathAttributeType::NextHop.into(); - let attr_len = 4_u8; - - let _ = self.target.append_slice( - &[attr_flags, attr_type_code, attr_len] - ); - let _ = self.target.append_slice(&v4addr.octets()); - - total_pa_len += 2 + 1 + usize::from(attr_len); - } - _ => todo!() // this is MP_REACH_NLRI territory - } - } - - - if let Some(comms) = acs.standard_communities.into_opt() { - let attr_flags = 0b0100_0000; - let attr_type_code = PathAttributeType::Communities.into(); - let attr_len = match u8::try_from(4 * comms.len()) { - Ok(n) => n, - Err(..) => { - return Err(ComposeError::AttributeTooLarge( - PathAttributeType::Communities, - 4 * comms.len() - )); - } - }; - - let _ = self.target.append_slice( - &[attr_flags, attr_type_code, attr_len] - ); - - for c in comms { - let _ = self.target.append_slice(&c.to_raw()); - } - total_pa_len += 2 + 1 + usize::from(attr_len); - } - - - if u16::try_from(total_pa_len).is_err() { - return Err(ComposeError::AttributesTooLarge(total_pa_len)); - } - - // update total path attribute len: - self.target.as_mut()[21+withdraw_len..21+withdraw_len+2] - .copy_from_slice(&(total_pa_len as u16).to_be_bytes()); - - - // NLRI - // TODO this all needs to be a lot more sophisticated: - // - prefixes can not occur in both withdrawals and nlris, so check - // for that; - // - non v4/unicast NLRI should go in MP_REACH_NLRI, not here (at the - // end of the PDU); - // - we should be able to put multiple NLRI in one UPDATE, though - // currently the AttrChangeSet only holds one; - // - probably more - - let mut nlri_len = 0; - - if let Some(nlri) = acs.nlri.into_opt() { - match nlri { - Nlri::Unicast(b) => { - if let Some(id) = b.path_id() { - let _ = self.target.append_slice(&id.to_raw()); - nlri_len += 4; - } - match b.prefix().addr_and_len() { - (std::net::IpAddr::V4(addr), len) => { - let _ = self.target.append_slice(&[len]); - let len_bytes = (usize::from(len)-1) / 8 + 1; - let _ = self.target.append_slice( - &addr.octets()[0..len_bytes] - ); - nlri_len += 1 + len_bytes; - } - _ => todo!() - } - } - _ => todo!() - } - } - - // update pdu len - let msg_len = 19 - + 2 + withdraw_len - + 2 + total_pa_len - + nlri_len - ; - - if msg_len > 4096 { - // TODO handle Extended Messages (max pdu size 65535) - return Err(ComposeError::PduTooLarge(msg_len)); - } - - //if u16::try_from(msg_len).is_err() { - // return Err(ComposeError::PduTooLarge(msg_len)); - //} - - self.target.as_mut()[16..=17].copy_from_slice( - &(msg_len as u16).to_be_bytes() - ); - - Ok(self.target) - } -} -*/ - impl UpdateBuilder { pub fn into_message(self) -> From 3d0eb2b14ef1debdf8ef99936c101d3590e5f34b Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 27 Feb 2024 20:07:24 +0100 Subject: [PATCH 38/96] WIP new afisafi mod in bgp --- Cargo.toml | 1 + src/bgp/afisafi.rs | 273 +++++++++++++++++++++++++++++++++++++++++++++ src/bgp/mod.rs | 1 + 3 files changed, 275 insertions(+) create mode 100644 src/bgp/afisafi.rs diff --git a/Cargo.toml b/Cargo.toml index 1a7b622e..bb12873f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ chrono = { version = "0.4.20", optional = true, default-features = false } const-str = { version = "0.5", optional = true, features = ["case"] } log = { version = "0.4.4", optional = true } octseq = { version = "0.4.0", optional = true, features = ["bytes"] } +paste = { version = "1" } serde = { version = "1.0.165", optional = true, features = ["derive"] } tokio = { version = ">=1.24.2", optional = true, features = ["sync", "rt"] } diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs new file mode 100644 index 00000000..7104ec86 --- /dev/null +++ b/src/bgp/afisafi.rs @@ -0,0 +1,273 @@ +//use crate::typeenum; // from util::macros + +use crate::bgp::message::nlri::{BasicNlri, PathId}; +use paste::paste; + + +use core::hash::Hash; +use core::fmt::Debug; + +macro_rules! afisafi { + ( + $( + $afi_code:expr => $afi_name:ident [ $( $safi_code:expr => $safi_name:ident ),+ $(,)* ] + ),+ $(,)* + ) => { + #[derive(Debug)] + pub enum Afi { + $( $afi_name ),+ + } + + + paste! { + #[derive(Debug)] + pub enum AfiSafiType { + $( + $( [<$afi_name $safi_name>] ,)+ + )+ + } + + // this enforces these derives on all *Nlri structs. + #[derive(Clone, Debug, Hash)] + pub enum Nlri { + $( + $( + [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]) + ,)+ + )+ + } + + impl Nlri { + pub fn afi_safi(&self) -> AfiSafiType { + match self { + $( + $( + Self::[<$afi_name $safi_name>](..) => AfiSafiType::[<$afi_name $safi_name >] + ,)+ + )+ + } + } + } + + + $( + $( + // Instead of doing: + //pub struct [<$afi_name $safi_name Nlri>]; + // + // .. we rely on the impl below, forcing us to actually + // create the $Afi$SafiNlri struct, along with all its + // basic or exotic data fields and whatnot. We can not do + // that in a generic way in this macro. + impl AfiSafi for [<$afi_name $safi_name Nlri>] { + fn afi(&self) -> Afi { Afi::$afi_name } + fn afi_safi(&self) -> AfiSafiType { + AfiSafiType::[<$afi_name $safi_name>] + } + } + + impl From<[<$afi_name $safi_name Nlri>]> for Nlri { + fn from(n: [<$afi_name $safi_name Nlri>]) -> Self { + Nlri::[<$afi_name $safi_name>](n) + } + + } + + )+ + )+ + } + } +} + +/// A type characterized by an AFI and SAFI. +pub trait AfiSafi { + fn afi(&self) -> Afi; + fn afi_safi(&self) -> AfiSafiType; +} + +/// A type representing an NLRI for a certain AFI+SAFI. +pub trait AfiSafiNlri: AfiSafi + Clone + Hash + Debug { + type Nlri; //: AfiSafi; + fn nlri(&self) -> Self::Nlri; + + // TODO adapt these from nlri.rs: + //fn parse_nlri<'a, Octs: Octets>( + // parser: &mut Parser<'a, Octs> + //) -> Result>, ParseError>; + + //fn skip_nlri(parser: &mut Parser<'_, Octs>) + // -> Result<(), ParseError>; + + //perhaps something like + //fn parse(&mut parser) -> Result; + //so do not return Self::Item, but Self + //though perhaps we need Self::item in the actual parsing logic + +} + + +/// A type containing a BasicNlri. +pub trait HasBasicNlri { + fn basic_nlri(&self) -> BasicNlri; + + // TODO + // fn into_routeworkshop() -> RouteWorkshop<_>; +} + +// blanket impl +impl HasBasicNlri for T where T: AfiSafiNlri { + fn basic_nlri(&self) -> BasicNlri { + self.nlri() + } +} + +//afisafi! { +// 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], +// 2 => Ipv6 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], +// 25 => L2Vpn [ 65 => Vpls, 70 => Evpn ], +//} + +// adding AFI/SAFIs here requires some manual labor: +// - at the least, add a struct for $Afi$SafiNlri , deriving Clone,Debug,Hash +// - impl AfiSafiNlri for it to make it useful +afisafi! { + 1 => Ipv4 [ 1 => Unicast, 2 => Multicast ], + 2 => Ipv6 [ 1 => Unicast ], +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4MulticastNlri(BasicNlri); +/* +impl AfiSafi for BasicNlri { + fn afi(&self) -> Afi { + match self.is_v4() { + true => Afi::Ipv4, + false => Afi::Ipv6 + } + } + //fn safi(&self) -> u8 { + // panic!() // we don't know! + //} + //fn afi_safi(&self) -> AfiSafiType { + // panic!() // we still don't know! + //} +} +*/ + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4UnicastNlri(BasicNlri); +impl AfiSafiNlri for Ipv4UnicastNlri { + type Nlri = BasicNlri; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6UnicastNlri(BasicNlri); +impl AfiSafiNlri for Ipv6UnicastNlri { + type Nlri = BasicNlri; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +// what about a 'custom' Nlri, like an ADD-PATH one? +// +// This needs some more conversion magic and manual typy typy, but seems +// doable so far. +// The main benefit would be in the (yet to be added) fn parse in the +// AfiSafiNlri trait. +// What might be confusing in this particular case though, is that an +// Ipv4UnicastNlri can hold a BasicNlri with a PathId, and likewise, a +// *AddpathNlri can hold a BasicNlri _without_ a PathId. +// So this is really only useful in the FixedNlriIter scenario. +// Or we should introduce a BasicAddpathNlri type or somesuch. +// Maybe it is not that bad of an idea to make the PathId more explicit +// instead of hiding it behind an Option<>: it is crucial to distinguish +// between two ADD-PATH'd announcements. +// +#[derive(Clone, Debug, Hash)] +pub struct Ipv4UnicastAddpathNlri(BasicNlri, PathId); +impl AfiSafiNlri for Ipv4UnicastAddpathNlri { + type Nlri = BasicNlri; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +impl AfiSafi for Ipv4UnicastAddpathNlri { + fn afi(&self) -> Afi { Afi::Ipv4} + fn afi_safi(&self) -> AfiSafiType { AfiSafiType::Ipv4Unicast } +} + +impl AddPath for Ipv4UnicastAddpathNlri { + fn path_id(&self) -> PathId { + self.1 + } +} + +impl From for Ipv4UnicastNlri { + fn from(n: Ipv4UnicastAddpathNlri) -> Self { + Self(n.0) + } +} +impl From for Nlri { + fn from(n: Ipv4UnicastAddpathNlri) -> Nlri { + Nlri::Ipv4Unicast(n.into()) + } +} + +/// An Nlri containing a Path Id. +pub trait AddPath: AfiSafiNlri { + fn path_id(&self) -> PathId; +} + + +#[cfg(test)] +mod tests { + + use super::*; + use crate::bgp::message::nlri::BasicNlri; + use crate::addr::Prefix; + use std::str::FromStr; + + #[test] + fn test1() { + let b = BasicNlri::new(Prefix::from_str("1.2.3.0/24").unwrap()); + let n = Ipv4UnicastNlri(b); + dbg!(&n); + + let n2 = n.clone().nlri(); + dbg!(n2); + + let b2 = n.basic_nlri(); + + let nlri_type: Nlri = n.into(); + dbg!(&nlri_type); + + let mc = Ipv4MulticastNlri(b); + let nlri_type2: Nlri = mc.clone().into(); + dbg!(&mc); + + dbg!(nlri_type2); + } + + #[test] + fn addpath() { + let b = BasicNlri::with_path_id( + Prefix::from_str("1.2.3.0/24").unwrap(), + PathId::from_u32(12) + ); + let n = Ipv4UnicastAddpathNlri(b, PathId::from_u32(13)); + dbg!(&n); + let nlri: Nlri = n.clone().into(); + dbg!(&nlri.afi_safi()); + dbg!(&n.afi()); + dbg!(&n.path_id()); + dbg!(&b.path_id()); + + // and this is why we need a distinc BasicNlriWithPathId type: + //assert_eq!(n.path_id(), b.path_id().unwrap()); + } +} diff --git a/src/bgp/mod.rs b/src/bgp/mod.rs index 66bc1267..0bd1b98d 100644 --- a/src/bgp/mod.rs +++ b/src/bgp/mod.rs @@ -1,5 +1,6 @@ //! Types and parsing for BGP messages. +pub mod afisafi; pub mod aspath; pub mod communities; pub mod path_attributes; From ce8920f9d41aec55b6c18a4064d6d8b8c0712368 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 28 Feb 2024 14:52:32 +0100 Subject: [PATCH 39/96] Make optional in afisafi macro --- src/bgp/afisafi.rs | 103 +++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 26 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 7104ec86..aad2abe6 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -1,8 +1,12 @@ //use crate::typeenum; // from util::macros +use crate::addr::Prefix; use crate::bgp::message::nlri::{BasicNlri, PathId}; use paste::paste; +use std::fmt; + +use octseq::Octets; use core::hash::Hash; use core::fmt::Debug; @@ -10,7 +14,7 @@ use core::fmt::Debug; macro_rules! afisafi { ( $( - $afi_code:expr => $afi_name:ident [ $( $safi_code:expr => $safi_name:ident ),+ $(,)* ] + $afi_code:expr => $afi_name:ident [ $( $safi_code:expr => $safi_name:ident$(<$gen:ident>)? ),+ $(,)* ] ),+ $(,)* ) => { #[derive(Debug)] @@ -29,15 +33,15 @@ macro_rules! afisafi { // this enforces these derives on all *Nlri structs. #[derive(Clone, Debug, Hash)] - pub enum Nlri { + pub enum Nlri { $( $( - [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]) + [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]$(<$gen>)?) ,)+ )+ } - impl Nlri { + impl Nlri { pub fn afi_safi(&self) -> AfiSafiType { match self { $( @@ -59,15 +63,15 @@ macro_rules! afisafi { // create the $Afi$SafiNlri struct, along with all its // basic or exotic data fields and whatnot. We can not do // that in a generic way in this macro. - impl AfiSafi for [<$afi_name $safi_name Nlri>] { + impl$(<$gen>)? AfiSafi for [<$afi_name $safi_name Nlri>]$(<$gen>)? { fn afi(&self) -> Afi { Afi::$afi_name } fn afi_safi(&self) -> AfiSafiType { AfiSafiType::[<$afi_name $safi_name>] } } - impl From<[<$afi_name $safi_name Nlri>]> for Nlri { - fn from(n: [<$afi_name $safi_name Nlri>]) -> Self { + impl From<[<$afi_name $safi_name Nlri>]$(<$gen>)?> for Nlri { + fn from(n: [<$afi_name $safi_name Nlri>]$(<$gen>)?) -> Self { Nlri::[<$afi_name $safi_name>](n) } @@ -115,12 +119,18 @@ pub trait HasBasicNlri { } // blanket impl -impl HasBasicNlri for T where T: AfiSafiNlri { +//impl HasBasicNlri for T where T: AfiSafiNlri { +// fn basic_nlri(&self) -> BasicNlri { +// self.nlri() +// } +//} +impl HasBasicNlri for T where T: AfiSafiNlri, B: Into { fn basic_nlri(&self) -> BasicNlri { - self.nlri() + self.nlri().into() } } + //afisafi! { // 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], // 2 => Ipv6 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], @@ -131,7 +141,7 @@ impl HasBasicNlri for T where T: AfiSafiNlri { // - at the least, add a struct for $Afi$SafiNlri , deriving Clone,Debug,Hash // - impl AfiSafiNlri for it to make it useful afisafi! { - 1 => Ipv4 [ 1 => Unicast, 2 => Multicast ], + 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast], 2 => Ipv6 [ 1 => Unicast ], } @@ -172,6 +182,22 @@ impl AfiSafiNlri for Ipv6UnicastNlri { } } +use crate::bgp::message::nlri::MplsNlri; +#[derive(Clone, Hash)] +pub struct Ipv4MplsUnicastNlri(MplsNlri); + +impl AfiSafiNlri for Ipv4MplsUnicastNlri { + type Nlri = MplsNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} +impl Debug for Ipv4MplsUnicastNlri { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "") + } +} + // what about a 'custom' Nlri, like an ADD-PATH one? // // This needs some more conversion magic and manual typy typy, but seems @@ -186,11 +212,33 @@ impl AfiSafiNlri for Ipv6UnicastNlri { // Maybe it is not that bad of an idea to make the PathId more explicit // instead of hiding it behind an Option<>: it is crucial to distinguish // between two ADD-PATH'd announcements. -// + +// some more thoughts: +// if we split up BasicNlri into an AddPath and a non-AddPath version, the +// latter is basically just a addr::Prefix. + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct BasicAddpathNlri { + pub prefix: Prefix, + pub path_id: PathId, +} +impl BasicAddpathNlri { + pub fn new(prefix: Prefix, path_id: PathId) -> Self { + Self{ prefix, path_id } + } +} +impl From for BasicNlri { + fn from(b: BasicAddpathNlri) -> Self { + Self::with_path_id(b.prefix, b.path_id) + } +} + #[derive(Clone, Debug, Hash)] -pub struct Ipv4UnicastAddpathNlri(BasicNlri, PathId); +pub struct Ipv4UnicastAddpathNlri(BasicAddpathNlri); impl AfiSafiNlri for Ipv4UnicastAddpathNlri { - type Nlri = BasicNlri; + //type Nlri = BasicNlri; + type Nlri = BasicAddpathNlri; fn nlri(&self) -> Self::Nlri { self.0 } @@ -203,17 +251,17 @@ impl AfiSafi for Ipv4UnicastAddpathNlri { impl AddPath for Ipv4UnicastAddpathNlri { fn path_id(&self) -> PathId { - self.1 + self.0.path_id } } impl From for Ipv4UnicastNlri { fn from(n: Ipv4UnicastAddpathNlri) -> Self { - Self(n.0) + Self(n.0.prefix.into()) } } -impl From for Nlri { - fn from(n: Ipv4UnicastAddpathNlri) -> Nlri { +impl From for Nlri { + fn from(n: Ipv4UnicastAddpathNlri) -> Nlri { Nlri::Ipv4Unicast(n.into()) } } @@ -243,11 +291,11 @@ mod tests { let b2 = n.basic_nlri(); - let nlri_type: Nlri = n.into(); + let nlri_type: Nlri<()> = n.into(); dbg!(&nlri_type); let mc = Ipv4MulticastNlri(b); - let nlri_type2: Nlri = mc.clone().into(); + let nlri_type2: Nlri<()> = mc.clone().into(); dbg!(&mc); dbg!(nlri_type2); @@ -255,17 +303,20 @@ mod tests { #[test] fn addpath() { - let b = BasicNlri::with_path_id( - Prefix::from_str("1.2.3.0/24").unwrap(), - PathId::from_u32(12) - ); - let n = Ipv4UnicastAddpathNlri(b, PathId::from_u32(13)); + //let b = BasicNlri::with_path_id( + // Prefix::from_str("1.2.3.0/24").unwrap(), + // PathId::from_u32(12) + // ); + let n = Ipv4UnicastAddpathNlri(BasicAddpathNlri::new( + Prefix::from_str("1.2.3.0/24").unwrap(), + PathId::from_u32(13) + )); dbg!(&n); - let nlri: Nlri = n.clone().into(); + let nlri: Nlri<()> = n.clone().into(); dbg!(&nlri.afi_safi()); dbg!(&n.afi()); dbg!(&n.path_id()); - dbg!(&b.path_id()); + dbg!(&n.basic_nlri()); // and this is why we need a distinc BasicNlriWithPathId type: //assert_eq!(n.path_id(), b.path_id().unwrap()); From 84c712a19487986cdf08905b5553634ebca8067c Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 09:07:27 +0100 Subject: [PATCH 40/96] Make MpReachNlriBuilder::new_for_nlri pub --- src/bgp/message/update_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index a9707c1b..6d610e57 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -794,7 +794,7 @@ impl MpReachNlriBuilder { } } - pub(crate) fn new_for_nlri(nlri: &Nlri) -> Self + pub fn new_for_nlri(nlri: &Nlri) -> Self where T: Octets, Vec: OctetsFrom { From acdbb1ecd1ccfdb44ddc6134bd37b44a42226ec0 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 29 Feb 2024 09:28:14 +0100 Subject: [PATCH 41/96] Display impl for Origin --- src/bgp/types.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 427a6820..33830b8f 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -283,6 +283,12 @@ impl From for Origin { } } +impl std::fmt::Display for Origin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + /// 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))] From 5524cb372f904568c6a8f87b3e047de30ad8d3fe Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 14:02:43 +0100 Subject: [PATCH 42/96] WIP AfiSafiParse trait --- src/bgp/afisafi.rs | 170 +++++++++++++++++++++++++++++++++++----- src/bgp/message/nlri.rs | 17 +++- 2 files changed, 167 insertions(+), 20 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index aad2abe6..b6d9e1f6 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -1,12 +1,14 @@ //use crate::typeenum; // from util::macros use crate::addr::Prefix; -use crate::bgp::message::nlri::{BasicNlri, PathId}; +use crate::bgp::message::nlri::{BasicNlri, PathId, parse_prefix}; +use crate::util::parser::ParseError; +use crate::bgp::types::Afi; use paste::paste; use std::fmt; -use octseq::Octets; +use octseq::{Octets, Parser}; use core::hash::Hash; use core::fmt::Debug; @@ -17,10 +19,12 @@ macro_rules! afisafi { $afi_code:expr => $afi_name:ident [ $( $safi_code:expr => $safi_name:ident$(<$gen:ident>)? ),+ $(,)* ] ),+ $(,)* ) => { + /* #[derive(Debug)] pub enum Afi { $( $afi_name ),+ } + */ paste! { @@ -53,6 +57,20 @@ macro_rules! afisafi { } } + impl fmt::Display for Nlri<()> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + $( + $( + Self::[<$afi_name $safi_name>](i) => fmt::Display::fmt(i, f) + ,)+ + )+ + + } + } + } + + $( $( @@ -83,6 +101,8 @@ macro_rules! afisafi { } } + + /// A type characterized by an AFI and SAFI. pub trait AfiSafi { fn afi(&self) -> Afi; @@ -94,19 +114,19 @@ pub trait AfiSafiNlri: AfiSafi + Clone + Hash + Debug { type Nlri; //: AfiSafi; fn nlri(&self) -> Self::Nlri; - // TODO adapt these from nlri.rs: - //fn parse_nlri<'a, Octs: Octets>( - // parser: &mut Parser<'a, Octs> - //) -> Result>, ParseError>; - - //fn skip_nlri(parser: &mut Parser<'_, Octs>) - // -> Result<(), ParseError>; + // TODO + // can/should we merge in AfiSafiParse here? - //perhaps something like - //fn parse(&mut parser) -> Result; - //so do not return Self::Item, but Self - //though perhaps we need Self::item in the actual parsing logic +} +pub trait AfiSafiParse<'a, O, P>: Sized + where P: 'a + Octets = O> +{ + type Output; + fn parse( + parser: &mut Parser<'a, P> + ) + -> Result; } @@ -163,6 +183,11 @@ impl AfiSafi for BasicNlri { //} } */ +impl fmt::Display for Ipv4MulticastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0.prefix()) + } +} #[derive(Clone, Debug, Hash)] pub struct Ipv4UnicastNlri(BasicNlri); @@ -173,6 +198,34 @@ impl AfiSafiNlri for Ipv4UnicastNlri { } } +//impl Ipv4UnicastNlri { +// pub fn parse(parser: &mut Parser

) -> Result { +// Ok( +// Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?)) +// ) +// } +//} +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + Ok( + Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?)) + ) + } +} + +impl fmt::Display for Ipv4UnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0.prefix()) + } +} + + + #[derive(Clone, Debug, Hash)] pub struct Ipv6UnicastNlri(BasicNlri); impl AfiSafiNlri for Ipv6UnicastNlri { @@ -182,19 +235,71 @@ impl AfiSafiNlri for Ipv6UnicastNlri { } } +impl fmt::Display for Ipv6UnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0.prefix()) + } +} + use crate::bgp::message::nlri::MplsNlri; -#[derive(Clone, Hash)] +#[derive(Clone, Debug, Hash)] pub struct Ipv4MplsUnicastNlri(MplsNlri); -impl AfiSafiNlri for Ipv4MplsUnicastNlri { +impl PartialEq> for Ipv4MplsUnicastNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4MplsUnicastNlri) -> bool { + self.0 == other.0 + } +} + +//impl Ipv4MplsUnicastNlri { +// // TODO clean up, this is just to test trait bounds et al +// pub fn parse<'a, P: Octets = O>>(parser: &mut Parser<'a, P>) +// -> Result +// { +// let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, crate::bgp::types::AfiSafi::Ipv4Unicast)?; +// let basic = BasicNlri::new(prefix); +// +// Ok( +// Self(MplsNlri::new(basic,labels,)) +// ) +// } +//} + +//impl<'a, O: Octets, P: 'a + Octets = O>> AfiSafiParse<'a, O, P> for Ipv4MplsUnicastNlri +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MplsUnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + // where P: Octets = Octs> + { + // XXX not sure how correct this all is, just testing trait bounds etc + let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, crate::bgp::types::AfiSafi::Ipv4Unicast)?; + let basic = BasicNlri::new(prefix); + + Ok( + Self(MplsNlri::new(basic,labels,)) + ) + } +} + +impl AfiSafiNlri for Ipv4MplsUnicastNlri { type Nlri = MplsNlri; fn nlri(&self) -> Self::Nlri { self.0.clone() } } -impl Debug for Ipv4MplsUnicastNlri { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "") + +impl fmt::Display for Ipv4MplsUnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) } } @@ -321,4 +426,33 @@ mod tests { // and this is why we need a distinc BasicNlriWithPathId type: //assert_eq!(n.path_id(), b.path_id().unwrap()); } + + #[test] + fn parse_ipv4unicast() { + let raw = vec![24,1,2,3]; + let mut parser = Parser::from_ref(&raw); + let n = Ipv4UnicastNlri::parse(&mut parser).unwrap(); + //dbg!(&n); + eprintln!("{}", &n); + } + #[test] + fn parse_ipv4mplsunicast() { + // Label 8000 10.0.0.9/32 + let raw = vec![0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x09]; + let mut parser = Parser::from_ref(&raw); + let n = Ipv4MplsUnicastNlri::parse(&mut parser).unwrap(); + eprintln!("{}", &n); + + let raw = bytes::Bytes::from_static(&[0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x09]); + let mut parser = Parser::from_ref(&raw); + let n2 = Ipv4MplsUnicastNlri::parse(&mut parser).unwrap(); + eprintln!("{}", &n); + assert_eq!(n, n2); + } + + #[test] + fn display() { + let n: Nlri<_> = Ipv4UnicastNlri(Prefix::from_str("1.2.3.0/24").unwrap().into()).into(); + eprintln!("{}", n); + } } diff --git a/src/bgp/message/nlri.rs b/src/bgp/message/nlri.rs index 3a0e4a21..b0d45a96 100644 --- a/src/bgp/message/nlri.rs +++ b/src/bgp/message/nlri.rs @@ -1611,7 +1611,7 @@ fn skip_prefix_addpath(parser: &mut Parser<'_, R>) Ok(parser.advance(prefix_bytes)?) } -fn parse_prefix(parser: &mut Parser<'_, R>, afi: Afi) +pub(crate) fn parse_prefix(parser: &mut Parser<'_, R>, afi: Afi) -> Result { let prefix_bits = parser.parse_u8()?; @@ -1938,12 +1938,25 @@ impl MplsNlri { check_prefix(parser, prefix_bits, afisafi.afi())?; Ok(()) } +} +impl MplsNlri { + pub fn new(basic: BasicNlri, labels: Labels) -> Self { + Self { basic, labels } + } pub fn basic(&self) -> BasicNlri { self.basic } } +impl fmt::Display for MplsNlri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MPLS:{}", self.basic().prefix()) + + } +} + + impl MplsNlri { pub fn parse<'a, R>( parser: &mut Parser<'a, R>, @@ -2007,7 +2020,7 @@ impl MplsNlri { ) } - fn parse_labels_and_prefix<'a, R>( + pub fn parse_labels_and_prefix<'a, R>( parser: &mut Parser<'a, R>, afisafi: AfiSafi, ) -> Result<(Prefix, Labels), ParseError> From 9d1dfac36b9922035e6e4362af0112db58fddea2 Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 29 Feb 2024 14:08:53 +0100 Subject: [PATCH 43/96] pub the new communities --- src/bgp/path_attributes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 165d6fc7..df838e4b 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -1655,7 +1655,7 @@ pub struct ExtendedCommunitiesList { } impl ExtendedCommunitiesList { - fn new(communities: Vec) + pub fn new(communities: Vec) -> ExtendedCommunitiesList { ExtendedCommunitiesList {communities } @@ -1876,7 +1876,7 @@ pub struct Ipv6ExtendedCommunitiesList { } impl Ipv6ExtendedCommunitiesList { - fn new(communities: Vec) + pub fn new(communities: Vec) -> Ipv6ExtendedCommunitiesList { Ipv6ExtendedCommunitiesList {communities } From 8617b494ab5b4296ce42835aba23a9c21dce430e Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Thu, 29 Feb 2024 14:09:17 +0100 Subject: [PATCH 44/96] fix builders --- src/bgp/workshop/route.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 5dcb0ac3..266feb45 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -113,7 +113,7 @@ impl RouteWorkshop { pub fn get_attr>( &self, ) -> Option { - self.1.get::() + self.1.get::().or_else(|| A::retrieve(&self.1)) } pub fn clone_into_route(&self) -> Route { @@ -162,6 +162,7 @@ impl_workshop!( crate::bgp::path_attributes::ClusterIds crate::bgp::message::update_builder::StandardCommunitiesList crate::bgp::types::Otc + crate::bgp::message::update_builder::MpReachNlriBuilder ); //------------ WorkshopAttribute --------------------------------------------- @@ -182,25 +183,25 @@ impl WorkshopAttribute for Vec { fn retrieve(attrs: &PaMap) -> Option { let mut c = attrs .get::() - .unwrap() - .fmap(|c| Community::Standard(*c)); + .map(|c| c.fmap(|c| Community::Standard(*c))) + .unwrap_or_default(); c.append( &mut attrs .get::() - .unwrap() - .fmap(Community::Extended), + .map(|c| c.fmap(Community::Extended)) + .unwrap_or_default() ); c.append( &mut attrs .get::() - .unwrap() - .fmap(Community::Ipv6Extended), + .map(|c| c.fmap(Community::Ipv6Extended)) + .unwrap_or_default() ); c.append( &mut attrs .get::() - .unwrap() - .fmap(Community::Large), + .map(|c| c.fmap(Community::Large)) + .unwrap_or_default() ); Some(c) @@ -288,7 +289,9 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { attrs: &mut PaMap, ) -> Result<(), ComposeError> { if let Some(mut nlri) = attrs.get::() { - nlri.set_nexthop(local_attr) + nlri.set_nexthop(local_attr)?; + attrs.set(nlri).ok_or(ComposeError::InvalidAttribute)?; + Ok(()) } else { Err(ComposeError::InvalidAttribute) } From 08b459fb88e8a95b8064b97d2f95ae1c5e633aec Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 15:16:47 +0100 Subject: [PATCH 45/96] NlriIter generic over AfiSafiParse --- src/bgp/afisafi.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index b6d9e1f6..7344b649 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -376,6 +376,72 @@ pub trait AddPath: AfiSafiNlri { fn path_id(&self) -> PathId; } +//------------ Iteration ------------------------------------------------------ + +// Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. +// one or more (possibly >1000) NLRI of one single AfiSafiType. We therefore +// want type specific iterators, yielding exact types (e.g. Ipv6UnicastNlri) +// instead of the Nlri enum, as that would require the user to match/unpack +// every single item returned by next() (which would/should always be of the +// same type, anyway). +// +// For convenience or whatever other use cases, we might still want to provide +// an iterator yielding variants of the Nlri enum, probably based on the +// type-specific ones. + + +pub struct NlriIter<'a, O, P, ASP> { + parser: Parser<'a, P>, + asp: std::marker::PhantomData, + output: std::marker::PhantomData, +} + +impl<'a, O, P, ASP> NlriIter<'a, O, P, ASP> +where + O: Octets, + P: Octets = O> +{ + pub fn new(parser: Parser<'a, P>) -> Self { + NlriIter { + parser, + asp: std::marker::PhantomData, + output: std::marker::PhantomData + } + } + + + // + // Validate the entire parser so we can safely return items from this + // iterator, instead of returning Option, ParseError> + // + //pub fn validate(&self) { } + +} +impl<'a, O, P> NlriIter<'a, O, P, Ipv4MplsUnicastNlri> +where + O: Octets, + P: Octets = O> +{ + pub fn ipv4_mplsunicast(parser: Parser<'a, P>) -> Self { + NlriIter::<'a, O, P, Ipv4MplsUnicastNlri>::new(parser) + } +} + +impl<'a, O, P, ASP: AfiSafiParse<'a, O, P>> Iterator for NlriIter<'a, O, P, ASP> +where + P: Octets = O> +{ + type Item = ASP::Output; + + fn next(&mut self) -> Option { + if self.parser.remaining() == 0 { + return None; + } + Some(ASP::parse(&mut self.parser).unwrap()) + } +} + + #[cfg(test)] mod tests { @@ -446,8 +512,12 @@ mod tests { let raw = bytes::Bytes::from_static(&[0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x09]); let mut parser = Parser::from_ref(&raw); let n2 = Ipv4MplsUnicastNlri::parse(&mut parser).unwrap(); - eprintln!("{}", &n); + eprintln!("{}", &n2); + assert_eq!(n, n2); + + let _n: Nlri<_> = n.into(); + let _n2: Nlri<_> = n2.into(); } #[test] @@ -455,4 +525,15 @@ mod tests { let n: Nlri<_> = Ipv4UnicastNlri(Prefix::from_str("1.2.3.0/24").unwrap().into()).into(); eprintln!("{}", n); } + + #[test] + fn iter() { + let raw = vec![ + 0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x09, + 0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x0a, + ]; + let parser = Parser::from_ref(&raw); + let iter = NlriIter::ipv4_mplsunicast(parser); + assert_eq!(iter.count(), 2); + } } From c7e6322f732ef5e866ac92080febed521c5cfb05 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 16:10:03 +0100 Subject: [PATCH 46/96] Test for generic iteration --- src/bgp/afisafi.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 7344b649..60ab2611 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -388,6 +388,14 @@ pub trait AddPath: AfiSafiNlri { // For convenience or whatever other use cases, we might still want to provide // an iterator yielding variants of the Nlri enum, probably based on the // type-specific ones. +// Because of the From impls generated in the macro call, we can already do: +// +// NlriIter::ipv4_mplsunicast(parser).map(Nlri::<_>::from) +// +// .. to turn a specific iterator into a generic one, returning the Nlri enum +// type. +// +// pub struct NlriIter<'a, O, P, ASP> { @@ -415,8 +423,18 @@ where // iterator, instead of returning Option, ParseError> // //pub fn validate(&self) { } +} +impl<'a, O, P> NlriIter<'a, O, P, Ipv4UnicastNlri> +where + O: Octets, + P: Octets = O> +{ + pub fn ipv4_unicast(parser: Parser<'a, P>) -> Self { + NlriIter::<'a, O, P, Ipv4UnicastNlri>::new(parser) + } } + impl<'a, O, P> NlriIter<'a, O, P, Ipv4MplsUnicastNlri> where O: Octets, @@ -441,8 +459,6 @@ where } } - - #[cfg(test)] mod tests { @@ -536,4 +552,23 @@ mod tests { let iter = NlriIter::ipv4_mplsunicast(parser); assert_eq!(iter.count(), 2); } + + #[test] + fn iter_generic() { + let mpls_raw = vec![ + 0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x09, + 0x38, 0x01, 0xf4, 0x01, 0x0a, 0x00, 0x00, 0x0a, + ]; + let parser = Parser::from_ref(&mpls_raw); + let mpls_iter = NlriIter::ipv4_mplsunicast(parser); + + let v4_raw = vec![24, 1, 2, 3]; + let parser = Parser::from_ref(&v4_raw); + let v4_iter = NlriIter::ipv4_unicast(parser); + + + for n in v4_iter.map(Nlri::<_>::from).chain(mpls_iter.map(Nlri::<_>::from)) { + dbg!(&n); + } + } } From f0bf4c51104a7f61ed45abeac622e0fc65362442 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 16:54:11 +0100 Subject: [PATCH 47/96] Test iterating over an Addpath Nlri --- src/bgp/afisafi.rs | 72 +++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 60ab2611..1def7017 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -254,21 +254,6 @@ where Octs: AsRef<[u8]>, } } -//impl Ipv4MplsUnicastNlri { -// // TODO clean up, this is just to test trait bounds et al -// pub fn parse<'a, P: Octets = O>>(parser: &mut Parser<'a, P>) -// -> Result -// { -// let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, crate::bgp::types::AfiSafi::Ipv4Unicast)?; -// let basic = BasicNlri::new(prefix); -// -// Ok( -// Self(MplsNlri::new(basic,labels,)) -// ) -// } -//} - -//impl<'a, O: Octets, P: 'a + Octets = O>> AfiSafiParse<'a, O, P> for Ipv4MplsUnicastNlri impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MplsUnicastNlri where O: Octets, @@ -278,7 +263,6 @@ where fn parse(parser: &mut Parser<'a, P>) -> Result - // where P: Octets = Octs> { // XXX not sure how correct this all is, just testing trait bounds etc let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, crate::bgp::types::AfiSafi::Ipv4Unicast)?; @@ -354,6 +338,21 @@ impl AfiSafi for Ipv4UnicastAddpathNlri { fn afi_safi(&self) -> AfiSafiType { AfiSafiType::Ipv4Unicast } } +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastAddpathNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + let path_id = PathId::from_u32(parser.parse_u32_be()?); + let prefix = parse_prefix(parser, Afi::Ipv4)?; + Ok( + Self(BasicAddpathNlri::new(prefix, path_id)) + ) + } +} + impl AddPath for Ipv4UnicastAddpathNlri { fn path_id(&self) -> PathId { self.0.path_id @@ -395,7 +394,6 @@ pub trait AddPath: AfiSafiNlri { // .. to turn a specific iterator into a generic one, returning the Nlri enum // type. // -// pub struct NlriIter<'a, O, P, ASP> { @@ -407,7 +405,8 @@ pub struct NlriIter<'a, O, P, ASP> { impl<'a, O, P, ASP> NlriIter<'a, O, P, ASP> where O: Octets, - P: Octets = O> + P: Octets = O>, + ASP: AfiSafiParse<'a, O, P> { pub fn new(parser: Parser<'a, P>) -> Self { NlriIter { @@ -417,7 +416,6 @@ where } } - // // Validate the entire parser so we can safely return items from this // iterator, instead of returning Option, ParseError> @@ -435,6 +433,16 @@ where } } +impl<'a, O, P> NlriIter<'a, O, P, Ipv4UnicastAddpathNlri> +where + O: Octets, + P: Octets = O> +{ + pub fn ipv4_unicast_addpath(parser: Parser<'a, P>) -> Self { + NlriIter::<'a, O, P, Ipv4UnicastAddpathNlri>::new(parser) + } +} + impl<'a, O, P> NlriIter<'a, O, P, Ipv4MplsUnicastNlri> where O: Octets, @@ -445,6 +453,9 @@ where } } + + + impl<'a, O, P, ASP: AfiSafiParse<'a, O, P>> Iterator for NlriIter<'a, O, P, ASP> where P: Octets = O> @@ -490,10 +501,6 @@ mod tests { #[test] fn addpath() { - //let b = BasicNlri::with_path_id( - // Prefix::from_str("1.2.3.0/24").unwrap(), - // PathId::from_u32(12) - // ); let n = Ipv4UnicastAddpathNlri(BasicAddpathNlri::new( Prefix::from_str("1.2.3.0/24").unwrap(), PathId::from_u32(13) @@ -571,4 +578,23 @@ mod tests { dbg!(&n); } } + + #[test] + fn iter_addpath() { + let raw = vec![ + 0, 0, 0, 1, 24, 1, 2, 1, + 0, 0, 0, 2, 24, 1, 2, 2, + 0, 0, 0, 3, 24, 1, 2, 3, + 0, 0, 0, 4, 24, 1, 2, 4, + ]; + + let parser = Parser::from_ref(&raw); + let iter = NlriIter::ipv4_unicast_addpath(parser); + assert_eq!(iter.count(), 4); + + let iter = NlriIter::ipv4_unicast_addpath(parser); + for n in iter.map(|e| e.basic_nlri()) { + dbg!(&n); + } + } } From d6010044924531df15a1ad415e8343f04535188a Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 17:08:43 +0100 Subject: [PATCH 48/96] Cleanup --- src/bgp/afisafi.rs | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 1def7017..1da6f24c 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -20,6 +20,8 @@ macro_rules! afisafi { ),+ $(,)* ) => { /* + // not generating this so we can use the existing one in types.rs + // while this is all WIP #[derive(Debug)] pub enum Afi { $( $afi_name ),+ @@ -138,12 +140,6 @@ pub trait HasBasicNlri { // fn into_routeworkshop() -> RouteWorkshop<_>; } -// blanket impl -//impl HasBasicNlri for T where T: AfiSafiNlri { -// fn basic_nlri(&self) -> BasicNlri { -// self.nlri() -// } -//} impl HasBasicNlri for T where T: AfiSafiNlri, B: Into { fn basic_nlri(&self) -> BasicNlri { self.nlri().into() @@ -167,22 +163,7 @@ afisafi! { #[derive(Clone, Debug, Hash)] pub struct Ipv4MulticastNlri(BasicNlri); -/* -impl AfiSafi for BasicNlri { - fn afi(&self) -> Afi { - match self.is_v4() { - true => Afi::Ipv4, - false => Afi::Ipv6 - } - } - //fn safi(&self) -> u8 { - // panic!() // we don't know! - //} - //fn afi_safi(&self) -> AfiSafiType { - // panic!() // we still don't know! - //} -} -*/ + impl fmt::Display for Ipv4MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0.prefix()) @@ -198,13 +179,6 @@ impl AfiSafiNlri for Ipv4UnicastNlri { } } -//impl Ipv4UnicastNlri { -// pub fn parse(parser: &mut Parser

) -> Result { -// Ok( -// Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?)) -// ) -// } -//} impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastNlri where O: Octets, @@ -326,7 +300,6 @@ impl From for BasicNlri { #[derive(Clone, Debug, Hash)] pub struct Ipv4UnicastAddpathNlri(BasicAddpathNlri); impl AfiSafiNlri for Ipv4UnicastAddpathNlri { - //type Nlri = BasicNlri; type Nlri = BasicAddpathNlri; fn nlri(&self) -> Self::Nlri { self.0 From c7cb21275db269aebff3753c72cc79fbcfeaab28 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 17:30:33 +0100 Subject: [PATCH 49/96] Cleanup / reordering --- src/bgp/afisafi.rs | 159 +++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 86 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 1da6f24c..eb9dbc8b 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -18,92 +18,77 @@ macro_rules! afisafi { $( $afi_code:expr => $afi_name:ident [ $( $safi_code:expr => $safi_name:ident$(<$gen:ident>)? ),+ $(,)* ] ),+ $(,)* - ) => { - /* - // not generating this so we can use the existing one in types.rs - // while this is all WIP - #[derive(Debug)] - pub enum Afi { - $( $afi_name ),+ + ) => +{ + /* + // not generating this so we can use the existing one in types.rs + // while this is all WIP + #[derive(Debug)] + pub enum Afi { + $( $afi_name ),+ + } + */ + +paste! { + #[derive(Debug)] + pub enum AfiSafiType { + $( $( [<$afi_name $safi_name>] ,)+)+ + } + + // this enforces these derives on all *Nlri structs. + #[derive(Clone, Debug, Hash)] + pub enum Nlri { + $($( + [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]$(<$gen>)?) + ,)+)+ + } + + impl Nlri { + pub fn afi_safi(&self) -> AfiSafiType { + match self { + $($( + Self::[<$afi_name $safi_name>](..) => AfiSafiType::[<$afi_name $safi_name >], + )+)+ } - */ - - - paste! { - #[derive(Debug)] - pub enum AfiSafiType { - $( - $( [<$afi_name $safi_name>] ,)+ - )+ - } - - // this enforces these derives on all *Nlri structs. - #[derive(Clone, Debug, Hash)] - pub enum Nlri { - $( - $( - [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]$(<$gen>)?) - ,)+ - )+ - } - - impl Nlri { - pub fn afi_safi(&self) -> AfiSafiType { - match self { - $( - $( - Self::[<$afi_name $safi_name>](..) => AfiSafiType::[<$afi_name $safi_name >] - ,)+ - )+ - } - } - } - - impl fmt::Display for Nlri<()> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - $( - $( - Self::[<$afi_name $safi_name>](i) => fmt::Display::fmt(i, f) - ,)+ - )+ - - } - } - } - - - - $( - $( - // Instead of doing: - //pub struct [<$afi_name $safi_name Nlri>]; - // - // .. we rely on the impl below, forcing us to actually - // create the $Afi$SafiNlri struct, along with all its - // basic or exotic data fields and whatnot. We can not do - // that in a generic way in this macro. - impl$(<$gen>)? AfiSafi for [<$afi_name $safi_name Nlri>]$(<$gen>)? { - fn afi(&self) -> Afi { Afi::$afi_name } - fn afi_safi(&self) -> AfiSafiType { - AfiSafiType::[<$afi_name $safi_name>] - } - } - - impl From<[<$afi_name $safi_name Nlri>]$(<$gen>)?> for Nlri { - fn from(n: [<$afi_name $safi_name Nlri>]$(<$gen>)?) -> Self { - Nlri::[<$afi_name $safi_name>](n) - } - - } - - )+ - )+ + } + } + + impl fmt::Display for Nlri<()> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + $($( + Self::[<$afi_name $safi_name>](i) => fmt::Display::fmt(i, f), + )+)+ } } -} + } + +$($( + // Instead of doing: + //pub struct [<$afi_name $safi_name Nlri>]; + // + // .. we rely on the impl below, forcing us to actually create the + // $Afi$SafiNlri struct, along with all its basic or exotic data fields + // and whatnot. We can not do that in a generic way in this macro. + impl$(<$gen>)? AfiSafi for [<$afi_name $safi_name Nlri>]$(<$gen>)? { + fn afi(&self) -> Afi { Afi::$afi_name } + fn afi_safi(&self) -> AfiSafiType { + AfiSafiType::[<$afi_name $safi_name>] + } + } + impl From<[<$afi_name $safi_name Nlri>]$(<$gen>)?> for Nlri { + fn from(n: [<$afi_name $safi_name Nlri>]$(<$gen>)?) -> Self { + Nlri::[<$afi_name $safi_name>](n) + } + + } +)+)+ + +}} +} +//------------ Traits --------------------------------------------------------- /// A type characterized by an AFI and SAFI. pub trait AfiSafi { @@ -146,6 +131,13 @@ impl HasBasicNlri for T where T: AfiSafiNlri, B: Into } } +/// An Nlri containing a Path Id. +pub trait AddPath: AfiSafiNlri { + fn path_id(&self) -> PathId; +} + + +//------------ Implementations ----------------------------------------------- //afisafi! { // 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], @@ -343,11 +335,6 @@ impl From for Nlri { } } -/// An Nlri containing a Path Id. -pub trait AddPath: AfiSafiNlri { - fn path_id(&self) -> PathId; -} - //------------ Iteration ------------------------------------------------------ // Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. From 98f5f857403e30c53198ca7a3b7a57d84d5186e7 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 20:07:51 +0100 Subject: [PATCH 50/96] Trying alternative Addpath approaches --- src/bgp/afisafi.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index eb9dbc8b..0b85e700 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -109,7 +109,7 @@ pub trait AfiSafiNlri: AfiSafi + Clone + Hash + Debug { pub trait AfiSafiParse<'a, O, P>: Sized where P: 'a + Octets = O> { - type Output; + type Output; // XXX do we actually still need this? fn parse( parser: &mut Parser<'a, P> ) @@ -335,6 +335,57 @@ impl From for Nlri { } } + +// As basically every NLRI can carry a PathId, perhaps we should not put it in +// the BasicNlri / MplsNlri etc, but in a more generic way. The danger of that +// is, that one can more easily lose it as it's not tied to the Nlri as much. +// This is an attempt, using v6 unicast, to try this alternative. +// +// notes: +// - Display impl is not forced as it's not included in the Nlri enum +// generation in the enum (yet) +// - if we remove PathId from BasicNlri/MplsNlri etc, we can't impl From for +// Nlri without losing the PathId. Or, we have to make the Addpath types +// also variants of the Nlri enum, but then that must be possible in the +// macro. +// - + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6UnicastAddpathNlri(PathId, Prefix); +impl AfiSafiNlri for Ipv6UnicastAddpathNlri { + type Nlri = Prefix; + fn nlri(&self) -> Self::Nlri { + self.1 + } +} + +impl AfiSafi for Ipv6UnicastAddpathNlri { + fn afi(&self) -> Afi { Afi::Ipv6} + fn afi_safi(&self) -> AfiSafiType { AfiSafiType::Ipv6Unicast } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastAddpathNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + let path_id = PathId::from_u32(parser.parse_u32_be()?); + let prefix = parse_prefix(parser, Afi::Ipv6)?; + Ok( + Self(path_id, prefix) + ) + } +} + +impl AddPath for Ipv6UnicastAddpathNlri { + fn path_id(&self) -> PathId { + self.0 + } +} + + //------------ Iteration ------------------------------------------------------ // Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. @@ -368,7 +419,7 @@ where P: Octets = O>, ASP: AfiSafiParse<'a, O, P> { - pub fn new(parser: Parser<'a, P>) -> Self { + fn new(parser: Parser<'a, P>) -> Self { NlriIter { parser, asp: std::marker::PhantomData, @@ -376,6 +427,8 @@ where } } + //pub fn for_afisafi(parser, afisafitype) -> Self { } + // // Validate the entire parser so we can safely return items from this // iterator, instead of returning Option, ParseError> @@ -403,6 +456,16 @@ where } } +impl<'a, O, P> NlriIter<'a, O, P, Ipv6UnicastAddpathNlri> +where + O: Octets, + P: Octets = O> +{ + pub fn ipv6_unicast_addpath(parser: Parser<'a, P>) -> Self { + NlriIter::<'a, O, P, Ipv6UnicastAddpathNlri>::new(parser) + } +} + impl<'a, O, P> NlriIter<'a, O, P, Ipv4MplsUnicastNlri> where O: Octets, @@ -557,4 +620,22 @@ mod tests { dbg!(&n); } } + + #[test] + fn iter_addpath_alternative() { + let raw = vec![ + 0, 0, 0, 1, 24, 1, 2, 1, + 0, 0, 0, 2, 24, 1, 2, 2, + 0, 0, 0, 3, 24, 1, 2, 3, + 0, 0, 0, 4, 24, 1, 2, 4, + ]; + + let parser = Parser::from_ref(&raw); + let iter = NlriIter::ipv6_unicast_addpath(parser); + //assert_eq!(iter.count(), 4); + for n in iter { + dbg!(&n); + } + } + } From 599fd1ec0733a2829bcef2b9be99deb854c92c15 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 29 Feb 2024 22:00:25 +0100 Subject: [PATCH 51/96] Introduce addpath! macro --- src/bgp/afisafi.rs | 120 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 11 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 0b85e700..3b426a52 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -13,6 +13,46 @@ use octseq::{Octets, Parser}; use core::hash::Hash; use core::fmt::Debug; +macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => +{ + +paste! { + #[derive(Clone, Debug, Hash)] + pub struct [<$nlri AddpathNlri>]$(<$gen>)?(PathId, [<$nlri Nlri>]$(<$gen>)?); + impl$(<$gen: Clone + Debug + Hash>)? AfiSafiNlri for [<$nlri AddpathNlri>]$(<$gen>)? { + type Nlri = <[<$nlri Nlri>]$(<$gen>)? as AfiSafiNlri>::Nlri; + fn nlri(&self) -> Self::Nlri { + self.1.nlri() + } + } + + impl$(<$gen>)? AfiSafi for [<$nlri AddpathNlri>]$(<$gen>)? { + fn afi(&self) -> Afi { self.1.afi() } + fn afi_safi(&self) -> AfiSafiType { self.1.afi_safi() } + } + + impl<'a, Octs, P> AfiSafiParse<'a, Octs, P> for [<$nlri AddpathNlri>]$(<$gen>)? + where + Octs: Octets, + P: 'a + Octets = Octs> + { + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + let path_id = PathId::from_u32(parser.parse_u32_be()?); + let inner = [<$nlri Nlri>]::parse(parser)?; + Ok( + Self(path_id, inner) + ) + } + } + + impl$(<$gen: Clone + Debug + Hash>)? AddPath for [<$nlri AddpathNlri>]$(<$gen>)? { + fn path_id(&self) -> PathId { + self.0 + } + } +}} +} macro_rules! afisafi { ( $( @@ -83,6 +123,9 @@ $($( } } + + // Create the Addpath version + addpath!([<$afi_name $safi_name>]$(<$gen>)?); )+)+ }} @@ -147,7 +190,7 @@ pub trait AddPath: AfiSafiNlri { // adding AFI/SAFIs here requires some manual labor: // - at the least, add a struct for $Afi$SafiNlri , deriving Clone,Debug,Hash -// - impl AfiSafiNlri for it to make it useful +// - impl AfiSafiNlri and AfiSafiParse afisafi! { 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast], 2 => Ipv6 [ 1 => Unicast ], @@ -155,6 +198,25 @@ afisafi! { #[derive(Clone, Debug, Hash)] pub struct Ipv4MulticastNlri(BasicNlri); +impl AfiSafiNlri for Ipv4MulticastNlri { + type Nlri = BasicNlri; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MulticastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + Ok( + Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?)) + ) + } +} impl fmt::Display for Ipv4MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -195,9 +257,22 @@ impl fmt::Display for Ipv4UnicastNlri { #[derive(Clone, Debug, Hash)] pub struct Ipv6UnicastNlri(BasicNlri); impl AfiSafiNlri for Ipv6UnicastNlri { - type Nlri = BasicNlri; + type Nlri = Prefix; fn nlri(&self) -> Self::Nlri { - self.0 + self.0.prefix() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + Ok( + Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv6)?)) + ) } } @@ -289,6 +364,7 @@ impl From for BasicNlri { } } +/* #[derive(Clone, Debug, Hash)] pub struct Ipv4UnicastAddpathNlri(BasicAddpathNlri); impl AfiSafiNlri for Ipv4UnicastAddpathNlri { @@ -334,6 +410,7 @@ impl From for Nlri { Nlri::Ipv4Unicast(n.into()) } } +*/ // As basically every NLRI can carry a PathId, perhaps we should not put it in @@ -348,8 +425,11 @@ impl From for Nlri { // Nlri without losing the PathId. Or, we have to make the Addpath types // also variants of the Nlri enum, but then that must be possible in the // macro. -// - +// - the AfiSafiParse impl is not forced in any way by the macro currently. +// update: if we call addpath! from the macro, it is, as is AfiSafiNlri +// - we still miss the enforcement of From for Nlri though +/* works #[derive(Clone, Debug, Hash)] pub struct Ipv6UnicastAddpathNlri(PathId, Prefix); impl AfiSafiNlri for Ipv6UnicastAddpathNlri { @@ -358,10 +438,20 @@ impl AfiSafiNlri for Ipv6UnicastAddpathNlri { self.1 } } +*/ +/* +#[derive(Clone, Debug, Hash)] +pub struct Ipv6UnicastAddpathNlri(PathId, Ipv6UnicastNlri); +impl AfiSafiNlri for Ipv6UnicastAddpathNlri { + type Nlri = ::Nlri; + fn nlri(&self) -> Self::Nlri { + self.1.nlri() + } +} impl AfiSafi for Ipv6UnicastAddpathNlri { - fn afi(&self) -> Afi { Afi::Ipv6} - fn afi_safi(&self) -> AfiSafiType { AfiSafiType::Ipv6Unicast } + fn afi(&self) -> Afi { self.1.afi() } + fn afi_safi(&self) -> AfiSafiType { self.1.afi_safi() } } impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastAddpathNlri @@ -372,9 +462,9 @@ where type Output = Self; fn parse(parser: &mut Parser<'a, P>) -> Result { let path_id = PathId::from_u32(parser.parse_u32_be()?); - let prefix = parse_prefix(parser, Afi::Ipv6)?; + let inner = Ipv6UnicastNlri::parse(parser)?; Ok( - Self(path_id, prefix) + Self(path_id, inner) ) } } @@ -384,7 +474,11 @@ impl AddPath for Ipv6UnicastAddpathNlri { self.0 } } +*/ + +//addpath!(Ipv4MplsUnicast); +//addpath!(Ipv6Unicast); //------------ Iteration ------------------------------------------------------ @@ -524,16 +618,20 @@ mod tests { #[test] fn addpath() { - let n = Ipv4UnicastAddpathNlri(BasicAddpathNlri::new( - Prefix::from_str("1.2.3.0/24").unwrap(), - PathId::from_u32(13) + let n = Ipv4UnicastAddpathNlri( + PathId::from_u32(13), + Ipv4UnicastNlri( + Prefix::from_str("1.2.3.0/24").unwrap().into() )); dbg!(&n); + // XXX From for Nlri is missing + /* let nlri: Nlri<()> = n.clone().into(); dbg!(&nlri.afi_safi()); dbg!(&n.afi()); dbg!(&n.path_id()); dbg!(&n.basic_nlri()); + */ // and this is why we need a distinc BasicNlriWithPathId type: //assert_eq!(n.path_id(), b.path_id().unwrap()); From cb2ea70c3501f00421acbaeb87cd9c64c8ef4eea Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 4 Mar 2024 15:01:24 +0100 Subject: [PATCH 52/96] Add (likely useless) next_with on NlriIter --- src/bgp/afisafi.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 3b426a52..daa67c8c 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -587,6 +587,16 @@ where } } +impl<'a, O, P, ASP: AfiSafiParse<'a, O, P>> NlriIter<'a, O, P, ASP> +where + O: Octets, + P: Octets = O> +{ + pub fn next_with::Item) -> T>(&mut self, fmap: F) -> Option { + self.next().map(fmap) + } +} + #[cfg(test)] mod tests { @@ -736,4 +746,25 @@ mod tests { } } + #[test] + fn iter_with() { + let raw = vec![ + 0, 0, 0, 1, 24, 1, 2, 1, + 0, 0, 0, 2, 24, 1, 2, 2, + 0, 0, 0, 3, 24, 1, 2, 3, + 0, 0, 0, 4, 24, 1, 2, 4, + ]; + + let parser = Parser::from_ref(&raw); + let mut iter = NlriIter::ipv6_unicast_addpath(parser); + //while let Some(x) = iter.next_with(|e| format!("IPv6!!!: {:?}", e).to_string()) { + // dbg!(x); + //} + + for x in iter.map(|e| format!("IPv6!!!: {:?}", e).to_string()) { + dbg!(x); + } + + } + } From d05afe8af2ab9500346c133c5d4422f9cd4df808 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 4 Mar 2024 15:37:45 +0100 Subject: [PATCH 53/96] Get rid of BasicNlri in new afisafi module --- src/bgp/afisafi.rs | 69 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index daa67c8c..e3af5ccb 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -1,7 +1,7 @@ //use crate::typeenum; // from util::macros use crate::addr::Prefix; -use crate::bgp::message::nlri::{BasicNlri, PathId, parse_prefix}; +use crate::bgp::message::nlri::{PathId, parse_prefix}; use crate::util::parser::ParseError; use crate::bgp::types::Afi; use paste::paste; @@ -53,6 +53,10 @@ paste! { } }} } + + +// For Nlri generic over anything, use literally. Using another name, +// e.g. , does not work. This is a limitation of the macro. macro_rules! afisafi { ( $( @@ -160,16 +164,16 @@ pub trait AfiSafiParse<'a, O, P>: Sized } -/// A type containing a BasicNlri. -pub trait HasBasicNlri { - fn basic_nlri(&self) -> BasicNlri; +/// A type containing nothing more than a (v4 or v6) Prefix. +pub trait IsPrefix { + fn prefix(&self) -> Prefix; // TODO // fn into_routeworkshop() -> RouteWorkshop<_>; } -impl HasBasicNlri for T where T: AfiSafiNlri, B: Into { - fn basic_nlri(&self) -> BasicNlri { +impl IsPrefix for T where T: AfiSafiNlri, B: Into { + fn prefix(&self) -> Prefix { self.nlri().into() } } @@ -197,9 +201,9 @@ afisafi! { } #[derive(Clone, Debug, Hash)] -pub struct Ipv4MulticastNlri(BasicNlri); +pub struct Ipv4MulticastNlri(Prefix); impl AfiSafiNlri for Ipv4MulticastNlri { - type Nlri = BasicNlri; + type Nlri = Prefix; fn nlri(&self) -> Self::Nlri { self.0 } @@ -213,21 +217,21 @@ where type Output = Self; fn parse(parser: &mut Parser<'a, P>) -> Result { Ok( - Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?)) + Self(parse_prefix(parser, Afi::Ipv4)?) ) } } impl fmt::Display for Ipv4MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0.prefix()) + write!(f, "{}", self.0) } } #[derive(Clone, Debug, Hash)] -pub struct Ipv4UnicastNlri(BasicNlri); +pub struct Ipv4UnicastNlri(Prefix); impl AfiSafiNlri for Ipv4UnicastNlri { - type Nlri = BasicNlri; + type Nlri = Prefix; fn nlri(&self) -> Self::Nlri { self.0 } @@ -241,25 +245,25 @@ where type Output = Self; fn parse(parser: &mut Parser<'a, P>) -> Result { Ok( - Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?)) + Self(parse_prefix(parser, Afi::Ipv4)?) ) } } impl fmt::Display for Ipv4UnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0.prefix()) + write!(f, "{}", self.0) } } #[derive(Clone, Debug, Hash)] -pub struct Ipv6UnicastNlri(BasicNlri); +pub struct Ipv6UnicastNlri(Prefix); impl AfiSafiNlri for Ipv6UnicastNlri { type Nlri = Prefix; fn nlri(&self) -> Self::Nlri { - self.0.prefix() + self.0 } } @@ -271,14 +275,14 @@ where type Output = Self; fn parse(parser: &mut Parser<'a, P>) -> Result { Ok( - Self(BasicNlri::new(parse_prefix(parser, Afi::Ipv6)?)) + Self(parse_prefix(parser, Afi::Ipv6)?) ) } } impl fmt::Display for Ipv6UnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0.prefix()) + write!(f, "{}", self.0) } } @@ -307,7 +311,7 @@ where { // XXX not sure how correct this all is, just testing trait bounds etc let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, crate::bgp::types::AfiSafi::Ipv4Unicast)?; - let basic = BasicNlri::new(prefix); + let basic = crate::bgp::message::nlri::BasicNlri::new(prefix); Ok( Self(MplsNlri::new(basic,labels,)) @@ -347,22 +351,6 @@ impl fmt::Display for Ipv4MplsUnicastNlri { // if we split up BasicNlri into an AddPath and a non-AddPath version, the // latter is basically just a addr::Prefix. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct BasicAddpathNlri { - pub prefix: Prefix, - pub path_id: PathId, -} -impl BasicAddpathNlri { - pub fn new(prefix: Prefix, path_id: PathId) -> Self { - Self{ prefix, path_id } - } -} -impl From for BasicNlri { - fn from(b: BasicAddpathNlri) -> Self { - Self::with_path_id(b.prefix, b.path_id) - } -} /* #[derive(Clone, Debug, Hash)] @@ -601,25 +589,24 @@ where mod tests { use super::*; - use crate::bgp::message::nlri::BasicNlri; use crate::addr::Prefix; use std::str::FromStr; #[test] fn test1() { - let b = BasicNlri::new(Prefix::from_str("1.2.3.0/24").unwrap()); - let n = Ipv4UnicastNlri(b); + let p = Prefix::from_str("1.2.3.0/24").unwrap(); + let n = Ipv4UnicastNlri(p); dbg!(&n); let n2 = n.clone().nlri(); dbg!(n2); - let b2 = n.basic_nlri(); + let b2 = n.prefix(); let nlri_type: Nlri<()> = n.into(); dbg!(&nlri_type); - let mc = Ipv4MulticastNlri(b); + let mc = Ipv4MulticastNlri(p); let nlri_type2: Nlri<()> = mc.clone().into(); dbg!(&mc); @@ -724,7 +711,7 @@ mod tests { assert_eq!(iter.count(), 4); let iter = NlriIter::ipv4_unicast_addpath(parser); - for n in iter.map(|e| e.basic_nlri()) { + for n in iter.map(|e| e.prefix()) { dbg!(&n); } } From 18673d5e045e48e397fb79d500680650aec9a083 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 4 Mar 2024 16:24:07 +0100 Subject: [PATCH 54/96] Cleanup --- src/bgp/afisafi.rs | 148 ++++----------------------------------------- 1 file changed, 12 insertions(+), 136 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index e3af5ccb..0254bc80 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -1,5 +1,17 @@ //use crate::typeenum; // from util::macros +// notes: +// - Display impl is not forced as it's not included in the Nlri enum +// generation in the enum (yet) +// - if we remove PathId from BasicNlri/MplsNlri etc, we can't impl From for +// Nlri without losing the PathId. Or, we have to make the Addpath types +// also variants of the Nlri enum, but then that must be possible in the +// macro. +// - the AfiSafiParse impl is not forced in any way by the macro currently. +// update: if we call addpath! from the macro, it is, as is AfiSafiNlri +// - we still miss the enforcement of From for Nlri though + + use crate::addr::Prefix; use crate::bgp::message::nlri::{PathId, parse_prefix}; use crate::util::parser::ParseError; @@ -332,142 +344,6 @@ impl fmt::Display for Ipv4MplsUnicastNlri { } } -// what about a 'custom' Nlri, like an ADD-PATH one? -// -// This needs some more conversion magic and manual typy typy, but seems -// doable so far. -// The main benefit would be in the (yet to be added) fn parse in the -// AfiSafiNlri trait. -// What might be confusing in this particular case though, is that an -// Ipv4UnicastNlri can hold a BasicNlri with a PathId, and likewise, a -// *AddpathNlri can hold a BasicNlri _without_ a PathId. -// So this is really only useful in the FixedNlriIter scenario. -// Or we should introduce a BasicAddpathNlri type or somesuch. -// Maybe it is not that bad of an idea to make the PathId more explicit -// instead of hiding it behind an Option<>: it is crucial to distinguish -// between two ADD-PATH'd announcements. - -// some more thoughts: -// if we split up BasicNlri into an AddPath and a non-AddPath version, the -// latter is basically just a addr::Prefix. - - -/* -#[derive(Clone, Debug, Hash)] -pub struct Ipv4UnicastAddpathNlri(BasicAddpathNlri); -impl AfiSafiNlri for Ipv4UnicastAddpathNlri { - type Nlri = BasicAddpathNlri; - fn nlri(&self) -> Self::Nlri { - self.0 - } -} - -impl AfiSafi for Ipv4UnicastAddpathNlri { - fn afi(&self) -> Afi { Afi::Ipv4} - fn afi_safi(&self) -> AfiSafiType { AfiSafiType::Ipv4Unicast } -} - -impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastAddpathNlri -where - O: Octets, - P: 'a + Octets = O> -{ - type Output = Self; - fn parse(parser: &mut Parser<'a, P>) -> Result { - let path_id = PathId::from_u32(parser.parse_u32_be()?); - let prefix = parse_prefix(parser, Afi::Ipv4)?; - Ok( - Self(BasicAddpathNlri::new(prefix, path_id)) - ) - } -} - -impl AddPath for Ipv4UnicastAddpathNlri { - fn path_id(&self) -> PathId { - self.0.path_id - } -} - -impl From for Ipv4UnicastNlri { - fn from(n: Ipv4UnicastAddpathNlri) -> Self { - Self(n.0.prefix.into()) - } -} -impl From for Nlri { - fn from(n: Ipv4UnicastAddpathNlri) -> Nlri { - Nlri::Ipv4Unicast(n.into()) - } -} -*/ - - -// As basically every NLRI can carry a PathId, perhaps we should not put it in -// the BasicNlri / MplsNlri etc, but in a more generic way. The danger of that -// is, that one can more easily lose it as it's not tied to the Nlri as much. -// This is an attempt, using v6 unicast, to try this alternative. -// -// notes: -// - Display impl is not forced as it's not included in the Nlri enum -// generation in the enum (yet) -// - if we remove PathId from BasicNlri/MplsNlri etc, we can't impl From for -// Nlri without losing the PathId. Or, we have to make the Addpath types -// also variants of the Nlri enum, but then that must be possible in the -// macro. -// - the AfiSafiParse impl is not forced in any way by the macro currently. -// update: if we call addpath! from the macro, it is, as is AfiSafiNlri -// - we still miss the enforcement of From for Nlri though - -/* works -#[derive(Clone, Debug, Hash)] -pub struct Ipv6UnicastAddpathNlri(PathId, Prefix); -impl AfiSafiNlri for Ipv6UnicastAddpathNlri { - type Nlri = Prefix; - fn nlri(&self) -> Self::Nlri { - self.1 - } -} -*/ -/* -#[derive(Clone, Debug, Hash)] -pub struct Ipv6UnicastAddpathNlri(PathId, Ipv6UnicastNlri); -impl AfiSafiNlri for Ipv6UnicastAddpathNlri { - type Nlri = ::Nlri; - fn nlri(&self) -> Self::Nlri { - self.1.nlri() - } -} - -impl AfiSafi for Ipv6UnicastAddpathNlri { - fn afi(&self) -> Afi { self.1.afi() } - fn afi_safi(&self) -> AfiSafiType { self.1.afi_safi() } -} - -impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastAddpathNlri -where - O: Octets, - P: 'a + Octets = O> -{ - type Output = Self; - fn parse(parser: &mut Parser<'a, P>) -> Result { - let path_id = PathId::from_u32(parser.parse_u32_be()?); - let inner = Ipv6UnicastNlri::parse(parser)?; - Ok( - Self(path_id, inner) - ) - } -} - -impl AddPath for Ipv6UnicastAddpathNlri { - fn path_id(&self) -> PathId { - self.0 - } -} -*/ - - -//addpath!(Ipv4MplsUnicast); -//addpath!(Ipv6Unicast); - //------------ Iteration ------------------------------------------------------ // Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. From 2a3e541a2528f67e22fbab8886bda25b6960cadc Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 4 Mar 2024 16:37:04 +0100 Subject: [PATCH 55/96] Call typeenum! macro from afisafi! macro to create Afi enum --- src/bgp/afisafi.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 0254bc80..802753b2 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -1,8 +1,11 @@ -//use crate::typeenum; // from util::macros +use crate::typeenum; // from util::macros + +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; // notes: -// - Display impl is not forced as it's not included in the Nlri enum -// generation in the enum (yet) +// - Display impl is not forced on Addpath structs as it's not included in +// the Nlri enum generation in the enum (yet) // - if we remove PathId from BasicNlri/MplsNlri etc, we can't impl From for // Nlri without losing the PathId. Or, we have to make the Addpath types // also variants of the Nlri enum, but then that must be possible in the @@ -76,14 +79,13 @@ macro_rules! afisafi { ),+ $(,)* ) => { - /* - // not generating this so we can use the existing one in types.rs - // while this is all WIP - #[derive(Debug)] - pub enum Afi { - $( $afi_name ),+ - } - */ + typeenum!( + /// AFI as used in BGP OPEN and UPDATE messages. + #[cfg_attr(feature = "serde", serde(from = "u16"))] + AfiTODORenameMe, u16, + { + $($afi_code => $afi_name),+ + }); paste! { #[derive(Debug)] From 401b755e07141de0bcca653808db41820fbb0a02 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 4 Mar 2024 16:41:59 +0100 Subject: [PATCH 56/96] Fill in all AFI/SAFI combos in macro call, as a TODO list --- src/bgp/afisafi.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index 802753b2..c4b5844e 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -210,8 +210,28 @@ pub trait AddPath: AfiSafiNlri { // - at the least, add a struct for $Afi$SafiNlri , deriving Clone,Debug,Hash // - impl AfiSafiNlri and AfiSafiParse afisafi! { - 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast], - 2 => Ipv6 [ 1 => Unicast ], + 1 => Ipv4 [ + 1 => Unicast, + 2 => Multicast, + 4 => MplsUnicast, + //128 => MplsVpnUnicast, + //132 => RouteTarget, + //133 => FlowSpec, + //134 => FlowSpecVpn, + + ], + 2 => Ipv6 [ + 1 => Unicast, + //2 => Multicast, + //4 => MplsUnicast, + //128 => MplsVpnUnicast, + //133 => FlowSpec, + //134 => FlowSpecVpn, + ], + //25 => L2Vpn [ + // 65 => Vpls, + // 70 => Evpn, + //] } #[derive(Clone, Debug, Hash)] From 655958cb85d892bbd805e3f794b0b8228efb594a Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 4 Mar 2024 16:53:56 +0100 Subject: [PATCH 57/96] Reordering in new afisafi module --- src/bgp/afisafi.rs | 58 +++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index c4b5844e..a595c8db 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -234,16 +234,23 @@ afisafi! { //] } -#[derive(Clone, Debug, Hash)] -pub struct Ipv4MulticastNlri(Prefix); -impl AfiSafiNlri for Ipv4MulticastNlri { + +//------------ Ipv4 ---------------------------------------------------------- + + +// --- Ipv4Unicast + +#[derive(Clone, Debug, Hash, PartialEq)] +pub struct Ipv4UnicastNlri(Prefix); + +impl AfiSafiNlri for Ipv4UnicastNlri { type Nlri = Prefix; fn nlri(&self) -> Self::Nlri { self.0 } } -impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MulticastNlri +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastNlri where O: Octets, P: 'a + Octets = O> @@ -256,22 +263,25 @@ where } } -impl fmt::Display for Ipv4MulticastNlri { +impl fmt::Display for Ipv4UnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } -#[derive(Clone, Debug, Hash)] -pub struct Ipv4UnicastNlri(Prefix); -impl AfiSafiNlri for Ipv4UnicastNlri { +//--- Ipv4Multicast + +#[derive(Clone, Debug, Hash, PartialEq)] +pub struct Ipv4MulticastNlri(Prefix); + +impl AfiSafiNlri for Ipv4MulticastNlri { type Nlri = Prefix; fn nlri(&self) -> Self::Nlri { self.0 } } -impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastNlri +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MulticastNlri where O: Octets, P: 'a + Octets = O> @@ -284,15 +294,17 @@ where } } -impl fmt::Display for Ipv4UnicastNlri { +impl fmt::Display for Ipv4MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } +//------------ Ipv6 ---------------------------------------------------------- +//--- Ipv6Unicast -#[derive(Clone, Debug, Hash)] +#[derive(Clone, Debug, Hash, PartialEq)] pub struct Ipv6UnicastNlri(Prefix); impl AfiSafiNlri for Ipv6UnicastNlri { type Nlri = Prefix; @@ -320,16 +332,16 @@ impl fmt::Display for Ipv6UnicastNlri { } } + + use crate::bgp::message::nlri::MplsNlri; #[derive(Clone, Debug, Hash)] pub struct Ipv4MplsUnicastNlri(MplsNlri); -impl PartialEq> for Ipv4MplsUnicastNlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &Ipv4MplsUnicastNlri) -> bool { - self.0 == other.0 +impl AfiSafiNlri for Ipv4MplsUnicastNlri { + type Nlri = MplsNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() } } @@ -353,10 +365,12 @@ where } } -impl AfiSafiNlri for Ipv4MplsUnicastNlri { - type Nlri = MplsNlri; - fn nlri(&self) -> Self::Nlri { - self.0.clone() +impl PartialEq> for Ipv4MplsUnicastNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4MplsUnicastNlri) -> bool { + self.0 == other.0 } } @@ -385,6 +399,8 @@ impl fmt::Display for Ipv4MplsUnicastNlri { // .. to turn a specific iterator into a generic one, returning the Nlri enum // type. // +// Seems that creating the convenience constructors involves a lot of typy +// typy which could also become part of the afisafi! macro. pub struct NlriIter<'a, O, P, ASP> { From 25ef23da34773b8234f4a21a825ffb98fc872801 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 10:14:03 +0100 Subject: [PATCH 58/96] Include Addpath variants in Nlri enum, impl Display addpath! macro --- src/bgp/afisafi.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index a595c8db..a00e05c5 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -61,11 +61,18 @@ paste! { } } - impl$(<$gen: Clone + Debug + Hash>)? AddPath for [<$nlri AddpathNlri>]$(<$gen>)? { + impl$(<$gen: Clone + Debug + Hash>)? Addpath for [<$nlri AddpathNlri>]$(<$gen>)? { fn path_id(&self) -> PathId { self.0 } } + + impl$(<$gen>)? fmt::Display for [<$nlri AddpathNlri>]$(<$gen>)? { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[{}] ", self.0)?; + fmt::Display::fmt(&self.1, f) + } + } }} } @@ -97,7 +104,8 @@ paste! { #[derive(Clone, Debug, Hash)] pub enum Nlri { $($( - [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]$(<$gen>)?) + [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]$(<$gen>)?), + [<$afi_name $safi_name Addpath>]([<$afi_name $safi_name AddpathNlri>]$(<$gen>)?) ,)+)+ } @@ -106,6 +114,7 @@ paste! { match self { $($( Self::[<$afi_name $safi_name>](..) => AfiSafiType::[<$afi_name $safi_name >], + Self::[<$afi_name $safi_name Addpath>](..) => AfiSafiType::[<$afi_name $safi_name >], )+)+ } } @@ -116,6 +125,9 @@ paste! { match self { $($( Self::[<$afi_name $safi_name>](i) => fmt::Display::fmt(i, f), + Self::[<$afi_name $safi_name Addpath>](i) => { + fmt::Display::fmt(i, f) + } )+)+ } } @@ -171,10 +183,7 @@ pub trait AfiSafiParse<'a, O, P>: Sized where P: 'a + Octets = O> { type Output; // XXX do we actually still need this? - fn parse( - parser: &mut Parser<'a, P> - ) - -> Result; + fn parse(parser: &mut Parser<'a, P>) -> Result; } @@ -193,11 +202,10 @@ impl IsPrefix for T where T: AfiSafiNlri, B: Into { } /// An Nlri containing a Path Id. -pub trait AddPath: AfiSafiNlri { +pub trait Addpath: AfiSafiNlri { fn path_id(&self) -> PathId; } - //------------ Implementations ----------------------------------------------- //afisafi! { @@ -643,7 +651,7 @@ mod tests { let iter = NlriIter::ipv6_unicast_addpath(parser); //assert_eq!(iter.count(), 4); for n in iter { - dbg!(&n); + eprintln!("{n}"); } } From 0f74284fbc1f353938bc1748dce206d7ea81bb62 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 10:25:30 +0100 Subject: [PATCH 59/96] Add From for Nlri enum --- src/bgp/afisafi.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bgp/afisafi.rs b/src/bgp/afisafi.rs index a00e05c5..771e113f 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/afisafi.rs @@ -151,7 +151,12 @@ $($( fn from(n: [<$afi_name $safi_name Nlri>]$(<$gen>)?) -> Self { Nlri::[<$afi_name $safi_name>](n) } + } + impl From<[<$afi_name $safi_name AddpathNlri>]$(<$gen>)?> for Nlri { + fn from(n: [<$afi_name $safi_name AddpathNlri>]$(<$gen>)?) -> Self { + Nlri::[<$afi_name $safi_name Addpath>](n) + } } // Create the Addpath version From 14c8fd7e3cccdb1180972025dbc6f314d86f1837 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 12:03:44 +0100 Subject: [PATCH 60/96] Start reorganizing new afisafi stuff into bgp::nlri module --- src/bgp/mod.rs | 2 +- src/bgp/{ => nlri}/afisafi.rs | 92 +++++++++--------- src/bgp/nlri/common.rs | 83 ++++++++++++++++ src/bgp/nlri/mod.rs | 3 + src/bgp/nlri/mpls.rs | 172 ++++++++++++++++++++++++++++++++++ 5 files changed, 304 insertions(+), 48 deletions(-) rename src/bgp/{ => nlri}/afisafi.rs (95%) create mode 100644 src/bgp/nlri/common.rs create mode 100644 src/bgp/nlri/mod.rs create mode 100644 src/bgp/nlri/mpls.rs diff --git a/src/bgp/mod.rs b/src/bgp/mod.rs index 0bd1b98d..1d9db2a3 100644 --- a/src/bgp/mod.rs +++ b/src/bgp/mod.rs @@ -1,6 +1,6 @@ //! Types and parsing for BGP messages. -pub mod afisafi; +pub mod nlri; // new nlri module pub mod aspath; pub mod communities; pub mod path_attributes; diff --git a/src/bgp/afisafi.rs b/src/bgp/nlri/afisafi.rs similarity index 95% rename from src/bgp/afisafi.rs rename to src/bgp/nlri/afisafi.rs index 771e113f..c868b996 100644 --- a/src/bgp/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -4,16 +4,12 @@ use crate::typeenum; // from util::macros use serde::{Serialize, Deserialize}; // notes: -// - Display impl is not forced on Addpath structs as it's not included in -// the Nlri enum generation in the enum (yet) -// - if we remove PathId from BasicNlri/MplsNlri etc, we can't impl From for -// Nlri without losing the PathId. Or, we have to make the Addpath types -// also variants of the Nlri enum, but then that must be possible in the -// macro. -// - the AfiSafiParse impl is not forced in any way by the macro currently. -// update: if we call addpath! from the macro, it is, as is AfiSafiNlri -// - we still miss the enforcement of From for Nlri though - +// - move in and adapt all remaining stuff from bgp/message/nlri.rs +// - can we do PartialEq impls in macro? +// - eventually rename this to bgp/nlri.rs ? +// - remove bgp/message/nlri.rs +// - pub use Afi/Nlri/etc from bgp::types +// - clean up / remove bgp/workshop/afisafi_nlri.rs use crate::addr::Prefix; use crate::bgp::message::nlri::{PathId, parse_prefix}; @@ -28,6 +24,9 @@ use octseq::{Octets, Parser}; use core::hash::Hash; use core::fmt::Debug; +use super::mpls::*; + + macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => { @@ -313,41 +312,8 @@ impl fmt::Display for Ipv4MulticastNlri { } } -//------------ Ipv6 ---------------------------------------------------------- - -//--- Ipv6Unicast - -#[derive(Clone, Debug, Hash, PartialEq)] -pub struct Ipv6UnicastNlri(Prefix); -impl AfiSafiNlri for Ipv6UnicastNlri { - type Nlri = Prefix; - fn nlri(&self) -> Self::Nlri { - self.0 - } -} - -impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastNlri -where - O: Octets, - P: 'a + Octets = O> -{ - type Output = Self; - fn parse(parser: &mut Parser<'a, P>) -> Result { - Ok( - Self(parse_prefix(parser, Afi::Ipv6)?) - ) - } -} - -impl fmt::Display for Ipv6UnicastNlri { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - +//--- Ipv4MplsUnicast - -use crate::bgp::message::nlri::MplsNlri; #[derive(Clone, Debug, Hash)] pub struct Ipv4MplsUnicastNlri(MplsNlri); @@ -369,11 +335,10 @@ where -> Result { // XXX not sure how correct this all is, just testing trait bounds etc - let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, crate::bgp::types::AfiSafi::Ipv4Unicast)?; - let basic = crate::bgp::message::nlri::BasicNlri::new(prefix); + let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, AfiTODORenameMe::Ipv4)?; Ok( - Self(MplsNlri::new(basic,labels,)) + Self(MplsNlri::new(prefix,labels,)) ) } } @@ -393,6 +358,39 @@ impl fmt::Display for Ipv4MplsUnicastNlri { } } +//------------ Ipv6 ---------------------------------------------------------- + +//--- Ipv6Unicast + +#[derive(Clone, Debug, Hash, PartialEq)] +pub struct Ipv6UnicastNlri(Prefix); +impl AfiSafiNlri for Ipv6UnicastNlri { + type Nlri = Prefix; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + Ok( + Self(parse_prefix(parser, Afi::Ipv6)?) + ) + } +} + +impl fmt::Display for Ipv6UnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + + //------------ Iteration ------------------------------------------------------ // Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. diff --git a/src/bgp/nlri/common.rs b/src/bgp/nlri/common.rs new file mode 100644 index 00000000..6802a1b8 --- /dev/null +++ b/src/bgp/nlri/common.rs @@ -0,0 +1,83 @@ + +use octseq::{Octets, Parser}; +use crate::util::parser::ParseError; +use crate::addr::Prefix; +use super::afisafi::AfiTODORenameMe as Afi; + +use std::net::IpAddr; + +//------------ Types --------------------------------------------------------- +/// Path Identifier for BGP Multiple Paths (RFC7911). +/// +/// Used in all AddpathNlri variants. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct PathId(u32); + + +//------------ Helper functions ---------------------------------------------- + +pub(super) fn parse_prefix(parser: &mut Parser<'_, R>, afi: Afi) + -> Result +{ + let prefix_bits = parser.parse_u8()?; + parse_prefix_for_len(parser, prefix_bits, afi) +} + +pub(super) fn parse_prefix_for_len( + parser: &mut Parser<'_, R>, + prefix_bits: u8, + afi: Afi +) + -> Result +{ + let prefix_bytes = prefix_bits_to_bytes(prefix_bits); + let prefix = match (afi, prefix_bytes) { + (Afi::Ipv4, 0) => { + Prefix::new_v4(0.into(), 0)? + }, + (Afi::Ipv4, _b @ 5..) => { + return Err( + ParseError::form_error("illegal byte size for IPv4 NLRI") + ) + }, + (Afi::Ipv4, _) => { + let mut b = [0u8; 4]; + b[..prefix_bytes].copy_from_slice(parser.peek(prefix_bytes)?); + parser.advance(prefix_bytes)?; + Prefix::new(IpAddr::from(b), prefix_bits).map_err(|e| + ParseError::form_error(e.static_description()) + )? + } + (Afi::Ipv6, 0) => { + Prefix::new_v6(0.into(), 0)? + }, + (Afi::Ipv6, _b @ 17..) => { + return Err( + ParseError::form_error("illegal byte size for IPv6 NLRI") + ) + }, + (Afi::Ipv6, _) => { + let mut b = [0u8; 16]; + b[..prefix_bytes].copy_from_slice(parser.peek(prefix_bytes)?); + parser.advance(prefix_bytes)?; + Prefix::new(IpAddr::from(b), prefix_bits).map_err(|e| + ParseError::form_error(e.static_description()) + )? + }, + (_, _) => { + return Err( + ParseError::form_error("unknown prefix format") + ) + } + }; + Ok(prefix) +} + +fn prefix_bits_to_bytes(bits: u8) -> usize { + if bits != 0 { + (bits as usize - 1) / 8 + 1 + } else { + 0 + } +} diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs new file mode 100644 index 00000000..1bceb6a6 --- /dev/null +++ b/src/bgp/nlri/mod.rs @@ -0,0 +1,3 @@ +pub mod afisafi; +mod common; +mod mpls; diff --git a/src/bgp/nlri/mpls.rs b/src/bgp/nlri/mpls.rs new file mode 100644 index 00000000..acd842c8 --- /dev/null +++ b/src/bgp/nlri/mpls.rs @@ -0,0 +1,172 @@ +use crate::addr::Prefix; +use std::fmt; + +use octseq::{Octets, Parser}; +use crate::util::parser::ParseError; +use super::common::parse_prefix_for_len; +use super::afisafi::AfiTODORenameMe as Afi; + +/// NLRI comprised of a [`Prefix`] and MPLS `Labels`. +#[derive(Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct MplsNlri { + prefix: Prefix, + labels: Labels, +} + +impl MplsNlri { + pub fn new(prefix: Prefix, labels: Labels) -> Self { + Self { prefix, labels } + } + pub fn prefix(&self) -> Prefix { + self.prefix + } +} + +impl MplsNlri { + pub(super) fn parse_labels_and_prefix<'a, R>( parser: &mut Parser<'a, R>, + afi: Afi, + ) -> Result<(Prefix, Labels), ParseError> + where + R: Octets = Octs> + { + let mut prefix_bits = parser.parse_u8()?; + let labels = Labels::::parse(parser)?; + + // Check whether we can safely subtract the labels length from the + // prefix size. If there is an unexpected path id, we might silently + // subtract too much, because there is no 'subtract with overflow' + // warning when built in release mode. + + if u8::try_from(8 * labels.len()) + .map_err(|_| ParseError::form_error("MplsNlri labels too long"))? + > prefix_bits { + return Err(ParseError::ShortInput); + } + + prefix_bits -= 8 * labels.len() as u8; + + let prefix = parse_prefix_for_len( + parser, + prefix_bits, + afi, + )?; + + Ok((prefix, labels)) + } +} + +impl PartialEq> for MplsNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &MplsNlri) -> bool { + self.prefix == other.prefix && self.labels == other.labels + } +} + +impl fmt::Display for MplsNlri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MPLS:{}", self.prefix()) + + } +} + +//------------ Labels -------------------------------------------------------- + +/// MPLS labels, part of [`MplsNlri`] and [`MplsVpnNlri`]. +#[derive(Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct Labels { + octets: Octs +} +impl> Labels { + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.octets.as_ref().len() + } +} + +impl Labels { + // XXX check all this Label stuff again + pub fn skip<'a, R>(parser: &mut Parser<'a, R>) -> Result + where + R: Octets = Octs> + { + let mut res = 0; + let mut stop = false; + let mut buf = [0u8; 3]; + + while !stop { + //20bits label + 3bits rsvd + S bit + parser.parse_buf(&mut buf)?; + res += 3; + + if buf[2] & 0x01 == 0x01 || // actual label with stop bit + buf == [0x80, 0x00, 0x00] || // Compatibility value + buf == [0x00, 0x00, 0x00] // or RFC 8277 2.4 + { + stop = true; + } + } + + Ok(res) + } + + // There are two cases for Labels: + // - in an announcement, it describes one or more MPLS labels + // - in a withdrawal, it's a compatibility value without meaning + // XXX consider splitting up the parsing for this for announcements vs + // withdrawals? Perhaps via another fields in the (currently so-called) + // SessionConfig... + pub fn parse<'a, R>(parser: &mut Parser<'a, R>) -> Result + where + R: Octets = Octs> + ?Sized, + { + let pos = parser.pos(); + + let mut stop = false; + let mut buf = [0u8; 3]; + + while !stop { + //20bits label + 3bits rsvd + S bit + parser.parse_buf(&mut buf)?; + let _lbl = + (buf[0] as u32) << 12 | + (buf[1] as u32) << 4 | + (buf[2] as u32) >> 4; + + if buf[2] & 0x01 == 0x01 || // actual label with stop bit + buf == [0x80, 0x00, 0x00] || // Compatibility value + buf == [0x00, 0x00, 0x00] // or RFC 8277 2.4 + { + stop = true; + } + } + + let len = parser.pos() - pos; + parser.seek(pos)?; + let res = parser.parse_octets(len)?; + Ok( + Labels { octets: res } + ) + } +} + +impl> Eq for Labels { } + +impl PartialEq> for Labels +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Labels) -> bool { + self.octets.as_ref() == other.octets.as_ref() + } +} + +impl> AsRef<[u8]> for Labels { + fn as_ref(&self) -> &[u8] { + self.octets.as_ref() + } +} + From c436c4bd18b3c5143829b3324be9beb433b26bbb Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 14:02:59 +0100 Subject: [PATCH 61/96] Generate NlriIter constructors in macro --- src/bgp/nlri/afisafi.rs | 76 +++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index c868b996..57ca948f 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -158,6 +158,27 @@ $($( } } + + impl<'a, Octs, P> NlriIter<'a, Octs, P, [<$afi_name $safi_name Nlri>]$(<$gen>)?> + where + Octs: Octets, + P: Octets = Octs> + { + pub fn [<$afi_name:lower _ $safi_name:lower>](parser: Parser<'a, P>) -> Self { + NlriIter::<'a, Octs, P, [<$afi_name $safi_name Nlri>]$(<$gen>)?>::new(parser) + } + } + + impl<'a, Octs, P> NlriIter<'a, Octs, P, [<$afi_name $safi_name AddpathNlri>]$(<$gen>)?> + where + Octs: Octets, + P: Octets = Octs> + { + pub fn [<$afi_name:lower _ $safi_name:lower _ addpath>](parser: Parser<'a, P>) -> Self { + NlriIter::<'a, Octs, P, [<$afi_name $safi_name AddpathNlri>]$(<$gen>)?>::new(parser) + } + } + // Create the Addpath version addpath!([<$afi_name $safi_name>]$(<$gen>)?); )+)+ @@ -334,7 +355,6 @@ where fn parse(parser: &mut Parser<'a, P>) -> Result { - // XXX not sure how correct this all is, just testing trait bounds etc let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, AfiTODORenameMe::Ipv4)?; Ok( @@ -433,57 +453,25 @@ where output: std::marker::PhantomData } } - - //pub fn for_afisafi(parser, afisafitype) -> Self { } - - // - // Validate the entire parser so we can safely return items from this - // iterator, instead of returning Option, ParseError> - // - //pub fn validate(&self) { } } -impl<'a, O, P> NlriIter<'a, O, P, Ipv4UnicastNlri> -where - O: Octets, - P: Octets = O> -{ - pub fn ipv4_unicast(parser: Parser<'a, P>) -> Self { - NlriIter::<'a, O, P, Ipv4UnicastNlri>::new(parser) - } -} -impl<'a, O, P> NlriIter<'a, O, P, Ipv4UnicastAddpathNlri> -where - O: Octets, - P: Octets = O> -{ - pub fn ipv4_unicast_addpath(parser: Parser<'a, P>) -> Self { - NlriIter::<'a, O, P, Ipv4UnicastAddpathNlri>::new(parser) - } -} - -impl<'a, O, P> NlriIter<'a, O, P, Ipv6UnicastAddpathNlri> -where - O: Octets, - P: Octets = O> -{ - pub fn ipv6_unicast_addpath(parser: Parser<'a, P>) -> Self { - NlriIter::<'a, O, P, Ipv6UnicastAddpathNlri>::new(parser) - } -} - -impl<'a, O, P> NlriIter<'a, O, P, Ipv4MplsUnicastNlri> +pub fn iter_for_afi_safi<'a, O, P, ASP>( + parser: Parser<'a, P>, +) -> NlriIter<'a, O, P, ASP> where O: Octets, - P: Octets = O> + P: Octets = O>, + ASP: AfiSafiParse<'a, O, P> { - pub fn ipv4_mplsunicast(parser: Parser<'a, P>) -> Self { - NlriIter::<'a, O, P, Ipv4MplsUnicastNlri>::new(parser) - } + NlriIter::<'a, O, P, ASP>::new(parser) } - + // + // Validate the entire parser so we can safely return items from this + // iterator, instead of returning Option, ParseError> + // + //pub fn validate(&self) { } impl<'a, O, P, ASP: AfiSafiParse<'a, O, P>> Iterator for NlriIter<'a, O, P, ASP> From 7f3bb29eccd3e59b2e1b6dce0a1d8761cc2e9b45 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 14:37:09 +0100 Subject: [PATCH 62/96] Add notes on why fn iter on Nlri structs in macro is currently not possible --- src/bgp/nlri/afisafi.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 57ca948f..2d44f9ae 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -158,6 +158,7 @@ $($( } } + //--- NlriIter impl<'a, Octs, P> NlriIter<'a, Octs, P, [<$afi_name $safi_name Nlri>]$(<$gen>)?> where @@ -179,6 +180,39 @@ $($( } } + // Some attempts to add fn iter() to the Nlri structs below. + // Problem is that we can only act on _presence_ of $gen , but not on its + // absence. So when we have the on the struct, the fn iter can not + // define it again, though we need it there for the non structs. + + /* + impl [<$afi_name $safi_name Nlri>] { + pub fn iter1<'a, Octs, P>(parser: Parser<'a, P>) -> NlriIter<'a, Octs, P, Self> + where + Octs: Octets, + P: 'a + Octets = Octs> + { + NlriIter::[<$afi_name:lower _ $safi_name:lower>](parser) + } + } + */ + + /* + // structs only + $( + impl<$gen> [<$afi_name $safi_name Nlri>]<$gen> { + pub fn iter2<'a, P>(parser: Parser<'a, P>) -> NlriIter<'a, Octs, P, Self> + where + Octs: Octets, + P: 'a + Octets = Octs> + { + NlriIter::[<$afi_name:lower _ $safi_name:lower>](parser) + } + } + )? + */ + + // Create the Addpath version addpath!([<$afi_name $safi_name>]$(<$gen>)?); )+)+ From 416ca6c15c5fea79079ff763d25fa316fe5f657e Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 15:00:39 +0100 Subject: [PATCH 63/96] Add Ipv4MplsVpnUnicast --- src/bgp/nlri/afisafi.rs | 38 ++++++++- src/bgp/nlri/mod.rs | 1 + src/bgp/nlri/mpls_vpn.rs | 173 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/bgp/nlri/mpls_vpn.rs diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 2d44f9ae..ef57fd26 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -25,6 +25,7 @@ use core::hash::Hash; use core::fmt::Debug; use super::mpls::*; +use super::mpls_vpn::*; macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => @@ -281,7 +282,7 @@ afisafi! { 1 => Unicast, 2 => Multicast, 4 => MplsUnicast, - //128 => MplsVpnUnicast, + 128 => MplsVpnUnicast, //132 => RouteTarget, //133 => FlowSpec, //134 => FlowSpecVpn, @@ -412,6 +413,41 @@ impl fmt::Display for Ipv4MplsUnicastNlri { } } +//--- Ipv4MplsVpnUnicastNlri + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4MplsVpnUnicastNlri(MplsVpnNlri); + +impl AfiSafiNlri for Ipv4MplsVpnUnicastNlri { + type Nlri = MplsVpnNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MplsVpnUnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + let (labels, rd, prefix) = + parse_labels_rd_prefix(parser, AfiTODORenameMe::Ipv4)?; + + Ok(Self(MplsVpnNlri::new(prefix, labels, rd))) + } +} + +impl fmt::Display for Ipv4MplsVpnUnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + //------------ Ipv6 ---------------------------------------------------------- //--- Ipv6Unicast diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index 1bceb6a6..65a56dcb 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -1,3 +1,4 @@ pub mod afisafi; mod common; mod mpls; +mod mpls_vpn; diff --git a/src/bgp/nlri/mpls_vpn.rs b/src/bgp/nlri/mpls_vpn.rs new file mode 100644 index 00000000..34e42c1b --- /dev/null +++ b/src/bgp/nlri/mpls_vpn.rs @@ -0,0 +1,173 @@ +use crate::addr::Prefix; +use std::fmt; + +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; + +use octseq::{Octets, Parser}; +use crate::util::parser::ParseError; +use super::common::parse_prefix_for_len; +use super::afisafi::AfiTODORenameMe as Afi; +use super::mpls::Labels; + +/// NLRI comprised of a [`BasicNlri`], MPLS `Labels` and a VPN +/// `RouteDistinguisher`. +#[derive(Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct MplsVpnNlri { + prefix: Prefix, + labels: Labels, + rd: RouteDistinguisher, +} + +impl MplsVpnNlri { + pub(super) fn new( + prefix: Prefix, + labels: Labels, + rd: RouteDistinguisher + ) -> Self { + Self { prefix, labels, rd } + } + + pub fn prefix(&self) -> Prefix { + self.prefix + } + + pub fn labels(&self) -> &Labels { + &self.labels + } + + pub fn rd(&self) -> RouteDistinguisher { + self.rd + } +} +pub(super) fn parse_labels_rd_prefix<'a, R, Octs: Octets>( + parser: &mut Parser<'a, R>, + afi: Afi, +) -> Result<(Labels, RouteDistinguisher, Prefix), ParseError> +where + R: Octets = Octs> +{ + let mut prefix_bits = parser.parse_u8()?; + let labels = Labels::::parse(parser)?; + + // 8 for the RouteDistinguisher, plus byte length of labels, + // times 8 to go from bytes to bits + let rd_label_len = u8::try_from(8 * (8 + labels.len())) + .map_err(|_| ParseError::form_error( + "MplsVpnNlri labels/rd too long" + ))?; + + if rd_label_len > prefix_bits { + return Err(ParseError::ShortInput); + } + + let rd = RouteDistinguisher::parse(parser)?; + prefix_bits -= rd_label_len; + + let prefix = parse_prefix_for_len(parser, prefix_bits, afi)?; + + Ok((labels, rd, prefix)) +} + +impl PartialEq> for MplsVpnNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &MplsVpnNlri) -> bool { + self.prefix == other.prefix + && self.labels == other.labels + && self.rd == other.rd + } +} + +impl fmt::Display for MplsVpnNlri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MPLS-VPN:{}", self.prefix()) + + } +} + +//------------ RouteDistinguisher -------------------------------------------- + +/// Route Distinguisher (RD) as defined in RFC4364. +/// +/// Used in [`MplsVpnNlri`], [`VplsNlri`] and [`NextHop`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct RouteDistinguisher { + bytes: [u8; 8] +} + +impl RouteDistinguisher { + pub fn check(parser: &mut Parser) + -> Result<(), ParseError> + { + parser.advance(8)?; + Ok(()) + } + + pub fn parse(parser: &mut Parser<'_, R>) + -> Result + { + let mut b = [0u8; 8]; + b[..8].copy_from_slice(parser.peek(8)?); + parser.advance(8)?; + Ok( + RouteDistinguisher{ bytes: b } + ) + } + + pub fn skip(parser: &mut Parser<'_, R>) + -> Result<(), ParseError> + { + Ok(parser.advance(8)?) + } +} + +impl RouteDistinguisher { + /// Create a new RouteDistinguisher from a slice. + pub fn new(bytes: &[u8]) -> Self { + RouteDistinguisher { bytes: bytes.try_into().expect("parsed before") } + } + + pub fn zeroes() -> Self { + RouteDistinguisher::new(&[0_u8; 8]) + } + + /// Returns the type this RouteDistinguisher. + pub fn typ(&self) -> RouteDistinguisherType { + match self.bytes[0..2] { + [0x00, 0x00] => RouteDistinguisherType::Type0, + [0x00, 0x01] => RouteDistinguisherType::Type1, + [0x00, 0x02] => RouteDistinguisherType::Type2, + _ => RouteDistinguisherType::UnknownType, + } + } + + /// Returns the raw value of this RouteDistinguisher. + pub fn value(&self) -> [u8; 6] { + self.bytes[2..8].try_into().expect("parsed before") + } +} + +impl AsRef<[u8]> for RouteDistinguisher { + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl fmt::Display for RouteDistinguisher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#?}", self.bytes) + } +} + +/// Route Distinguisher types as defined in RFC4364. +#[derive(Eq, PartialEq, Debug)] +pub enum RouteDistinguisherType { + Type0, + Type1, + Type2, + UnknownType, +} From 063eac0541e1e0edb38edfb0f5536427a58db60d Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 15:41:21 +0100 Subject: [PATCH 64/96] Add Ipv4RouteTarget, do not rely on old nlri.rs anymore --- src/bgp/nlri/afisafi.rs | 50 +++++++++++++++++++++++++++++++------ src/bgp/nlri/common.rs | 13 +++++++--- src/bgp/nlri/mod.rs | 1 + src/bgp/nlri/mpls.rs | 2 +- src/bgp/nlri/mpls_vpn.rs | 3 ++- src/bgp/nlri/routetarget.rs | 34 +++++++++++++++++++++++++ 6 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/bgp/nlri/routetarget.rs diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index ef57fd26..f2315426 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -12,9 +12,8 @@ use serde::{Serialize, Deserialize}; // - clean up / remove bgp/workshop/afisafi_nlri.rs use crate::addr::Prefix; -use crate::bgp::message::nlri::{PathId, parse_prefix}; +use super::common::{PathId, parse_prefix}; use crate::util::parser::ParseError; -use crate::bgp::types::Afi; use paste::paste; use std::fmt; @@ -26,6 +25,7 @@ use core::fmt::Debug; use super::mpls::*; use super::mpls_vpn::*; +use super::routetarget::*; macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => @@ -53,7 +53,7 @@ paste! { { type Output = Self; fn parse(parser: &mut Parser<'a, P>) -> Result { - let path_id = PathId::from_u32(parser.parse_u32_be()?); + let path_id = PathId(parser.parse_u32_be()?); let inner = [<$nlri Nlri>]::parse(parser)?; Ok( Self(path_id, inner) @@ -89,7 +89,7 @@ macro_rules! afisafi { typeenum!( /// AFI as used in BGP OPEN and UPDATE messages. #[cfg_attr(feature = "serde", serde(from = "u16"))] - AfiTODORenameMe, u16, + Afi, u16, { $($afi_code => $afi_name),+ }); @@ -283,7 +283,7 @@ afisafi! { 2 => Multicast, 4 => MplsUnicast, 128 => MplsVpnUnicast, - //132 => RouteTarget, + 132 => RouteTarget, //133 => FlowSpec, //134 => FlowSpecVpn, @@ -390,7 +390,7 @@ where fn parse(parser: &mut Parser<'a, P>) -> Result { - let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, AfiTODORenameMe::Ipv4)?; + let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, Afi::Ipv4)?; Ok( Self(MplsNlri::new(prefix,labels,)) @@ -436,7 +436,7 @@ where -> Result { let (labels, rd, prefix) = - parse_labels_rd_prefix(parser, AfiTODORenameMe::Ipv4)?; + parse_labels_rd_prefix(parser, Afi::Ipv4)?; Ok(Self(MplsVpnNlri::new(prefix, labels, rd))) } @@ -448,6 +448,40 @@ impl fmt::Display for Ipv4MplsVpnUnicastNlri { } } +//--- Ipv4RouteTarget + + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4RouteTargetNlri(RouteTargetNlri); + +impl AfiSafiNlri for Ipv4RouteTargetNlri { + type Nlri = RouteTargetNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4RouteTargetNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + + Ok(Self(RouteTargetNlri::parse(parser)?)) + } +} + +impl fmt::Display for Ipv4RouteTargetNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + //------------ Ipv6 ---------------------------------------------------------- //--- Ipv6Unicast @@ -599,7 +633,7 @@ mod tests { #[test] fn addpath() { let n = Ipv4UnicastAddpathNlri( - PathId::from_u32(13), + PathId(13), Ipv4UnicastNlri( Prefix::from_str("1.2.3.0/24").unwrap().into() )); diff --git a/src/bgp/nlri/common.rs b/src/bgp/nlri/common.rs index 6802a1b8..5aafa800 100644 --- a/src/bgp/nlri/common.rs +++ b/src/bgp/nlri/common.rs @@ -2,9 +2,10 @@ use octseq::{Octets, Parser}; use crate::util::parser::ParseError; use crate::addr::Prefix; -use super::afisafi::AfiTODORenameMe as Afi; +use super::afisafi::Afi; use std::net::IpAddr; +use std::fmt; //------------ Types --------------------------------------------------------- /// Path Identifier for BGP Multiple Paths (RFC7911). @@ -12,7 +13,13 @@ use std::net::IpAddr; /// Used in all AddpathNlri variants. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct PathId(u32); +pub struct PathId(pub u32); + +impl fmt::Display for PathId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} //------------ Helper functions ---------------------------------------------- @@ -74,7 +81,7 @@ pub(super) fn parse_prefix_for_len( Ok(prefix) } -fn prefix_bits_to_bytes(bits: u8) -> usize { +pub(super) fn prefix_bits_to_bytes(bits: u8) -> usize { if bits != 0 { (bits as usize - 1) / 8 + 1 } else { diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index 65a56dcb..9c31a39d 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -2,3 +2,4 @@ pub mod afisafi; mod common; mod mpls; mod mpls_vpn; +mod routetarget; diff --git a/src/bgp/nlri/mpls.rs b/src/bgp/nlri/mpls.rs index acd842c8..1d8e3c30 100644 --- a/src/bgp/nlri/mpls.rs +++ b/src/bgp/nlri/mpls.rs @@ -4,7 +4,7 @@ use std::fmt; use octseq::{Octets, Parser}; use crate::util::parser::ParseError; use super::common::parse_prefix_for_len; -use super::afisafi::AfiTODORenameMe as Afi; +use super::afisafi::Afi; /// NLRI comprised of a [`Prefix`] and MPLS `Labels`. #[derive(Copy, Clone, Debug, Hash)] diff --git a/src/bgp/nlri/mpls_vpn.rs b/src/bgp/nlri/mpls_vpn.rs index 34e42c1b..027137da 100644 --- a/src/bgp/nlri/mpls_vpn.rs +++ b/src/bgp/nlri/mpls_vpn.rs @@ -7,7 +7,7 @@ use serde::{Serialize, Deserialize}; use octseq::{Octets, Parser}; use crate::util::parser::ParseError; use super::common::parse_prefix_for_len; -use super::afisafi::AfiTODORenameMe as Afi; +use super::afisafi::Afi; use super::mpls::Labels; /// NLRI comprised of a [`BasicNlri`], MPLS `Labels` and a VPN @@ -41,6 +41,7 @@ impl MplsVpnNlri { self.rd } } + pub(super) fn parse_labels_rd_prefix<'a, R, Octs: Octets>( parser: &mut Parser<'a, R>, afi: Afi, diff --git a/src/bgp/nlri/routetarget.rs b/src/bgp/nlri/routetarget.rs new file mode 100644 index 00000000..9faeef4d --- /dev/null +++ b/src/bgp/nlri/routetarget.rs @@ -0,0 +1,34 @@ +use std::fmt; + +use octseq::{Octets, Parser}; +use crate::util::parser::ParseError; + +use super::common::prefix_bits_to_bytes; + +/// NLRI containing a Route Target membership as defined in RFC4684. +/// +/// **TODO**: implement accessor methods for the contents of this NLRI. +#[derive(Clone, Debug, Hash)] +pub struct RouteTargetNlri { + raw: Octs +} + +impl RouteTargetNlri { + pub fn parse<'a, P>(parser: &mut Parser<'a, P>) + -> Result + where + P: Octets = Octs> + { + let prefix_bits = parser.parse_u8()?; + let prefix_bytes = prefix_bits_to_bytes(prefix_bits); + let raw = parser.parse_octets(prefix_bytes)?; + + Ok(Self{raw}) + } +} + +impl fmt::Display for RouteTargetNlri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ROUTE-TARGET-NLRI") + } +} From b236e14b9b7ae84a43b9d9f01c30d051f3887e33 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 16:03:55 +0100 Subject: [PATCH 65/96] Add fn afi_safis on UpdateMessage --- src/bgp/message/update.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 35be4b10..d4e3d922 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -175,6 +175,32 @@ impl UpdateMessage { impl UpdateMessage { + /// Indicates which address families are present in this message. + /// + /// The tuple consists of four optional [`AfiSafi`] types, describing the + /// presence of, respectively: + /// conventional withdrawals, conventional announcements, + /// multi-protocol withdrawals, multi-protocol announcements. + /// + /// While the conventional announcements/withdrawals will always be IPv4 + /// unicast NLRI, they might include ADD-PATH Path IDs or not. + /// Once we switch over to the new AfiSafiType enum, we can signal PathId + /// presence/absence. + pub fn afi_safis(&self) -> ( + Option, + Option, + Option, + Option, + ) { + ( + (!self.withdrawals.is_empty()).then_some(AfiSafi::Ipv4Unicast), + (!self.announcements.is_empty()).then_some(AfiSafi::Ipv4Unicast), + self.mp_withdrawals().ok().flatten().map(|a| a.afi_safi()), + self.mp_announcements().ok().flatten().map(|a| a.afi_safi()), + ) + + } + /// Returns the conventional withdrawals. pub fn conventional_withdrawals(&self) -> Result, ParseError> { From 4676217e2aa618a0e4c3e4ad2db7bde8b4d857ed Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 16:32:55 +0100 Subject: [PATCH 66/96] Add FlowSpecNlri --- src/bgp/message/update.rs | 1 - src/bgp/nlri/afisafi.rs | 37 +++++++++++++++++-- src/bgp/nlri/flowspec.rs | 75 +++++++++++++++++++++++++++++++++++++++ src/bgp/nlri/mod.rs | 1 + 4 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/bgp/nlri/flowspec.rs diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index d4e3d922..264d7c8e 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -198,7 +198,6 @@ impl UpdateMessage { self.mp_withdrawals().ok().flatten().map(|a| a.afi_safi()), self.mp_announcements().ok().flatten().map(|a| a.afi_safi()), ) - } /// Returns the conventional withdrawals. diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index f2315426..87d15f22 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -26,7 +26,7 @@ use core::fmt::Debug; use super::mpls::*; use super::mpls_vpn::*; use super::routetarget::*; - +use super::flowspec::*; macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => { @@ -284,7 +284,7 @@ afisafi! { 4 => MplsUnicast, 128 => MplsVpnUnicast, 132 => RouteTarget, - //133 => FlowSpec, + 133 => FlowSpec, //134 => FlowSpecVpn, ], @@ -482,6 +482,39 @@ impl fmt::Display for Ipv4RouteTargetNlri { } } +//--- Ipv4FlowSpec + +#[derive(Clone, Debug, Hash)] +pub struct Ipv4FlowSpecNlri(FlowSpecNlri); + +impl AfiSafiNlri for Ipv4FlowSpecNlri { + type Nlri = FlowSpecNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4FlowSpecNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + + Ok(Self(FlowSpecNlri::parse(parser, Afi::Ipv4)?)) + } +} + +impl fmt::Display for Ipv4FlowSpecNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + //------------ Ipv6 ---------------------------------------------------------- //--- Ipv6Unicast diff --git a/src/bgp/nlri/flowspec.rs b/src/bgp/nlri/flowspec.rs new file mode 100644 index 00000000..8accb2ff --- /dev/null +++ b/src/bgp/nlri/flowspec.rs @@ -0,0 +1,75 @@ +use std::fmt; + +use log::debug; +use octseq::{Octets, Parser}; + +use crate::util::parser::ParseError; +use crate::flowspec::Component; +use super::afisafi::Afi; + + +/// NLRI containing a FlowSpec v1 specification. +/// +/// Also see [`crate::flowspec`]. +#[derive(Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct FlowSpecNlri { + #[allow(dead_code)] + afi: Afi, + raw: Octs, +} + +impl FlowSpecNlri { + pub fn parse<'a, R>(parser: &mut Parser<'a, R>, afi: Afi) + -> Result + where + R: Octets = Octs> + { + let len1 = parser.parse_u8()?; + let len: u16 = if len1 >= 0xf0 { + let len2 = parser.parse_u8()? as u16; + (((len1 as u16) << 8) | len2) & 0x0fff + } else { + len1 as u16 + }; + let pos = parser.pos(); + + if usize::from(len) > parser.remaining() { + return Err(ParseError::form_error( + "invalid length of FlowSpec NLRI" + )); + } + + match afi { + Afi::Ipv4 => { + while parser.pos() < pos + len as usize { + Component::parse(parser)?; + } + } + Afi::Ipv6 => { + debug!("FlowSpec v6 not implemented yet, \ + returning unchecked NLRI" + ); + } + _ => { + return Err(ParseError::form_error("illegal AFI for FlowSpec")) + } + } + + parser.seek(pos)?; + let raw = parser.parse_octets(len as usize)?; + + Ok( + FlowSpecNlri { + afi, + raw + } + ) + } +} + +impl fmt::Display for FlowSpecNlri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FLOWSPEC-NLRI") + } +} diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index 9c31a39d..5ca4a8e4 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -3,3 +3,4 @@ mod common; mod mpls; mod mpls_vpn; mod routetarget; +mod flowspec; From 7f44f2b0c87e369724fbafa48ffd52d5a6f6456e Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 17:07:02 +0100 Subject: [PATCH 67/96] Support conversion between (non-)Addpath variants --- src/bgp/nlri/afisafi.rs | 58 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 87d15f22..b5616406 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -12,7 +12,7 @@ use serde::{Serialize, Deserialize}; // - clean up / remove bgp/workshop/afisafi_nlri.rs use crate::addr::Prefix; -use super::common::{PathId, parse_prefix}; +use super::common::{PathId, parse_prefix, prefix_bits_to_bytes}; use crate::util::parser::ParseError; use paste::paste; @@ -159,6 +159,12 @@ $($( } } + impl$(<$gen>)? From<[<$afi_name $safi_name AddpathNlri>]$(<$gen>)?> for [<$afi_name $safi_name Nlri>]$(<$gen>)? { + fn from(n: [<$afi_name $safi_name AddpathNlri>]$(<$gen>)?) -> Self { + n.1 + } + } + //--- NlriIter impl<'a, Octs, P> NlriIter<'a, Octs, P, [<$afi_name $safi_name Nlri>]$(<$gen>)?> @@ -213,6 +219,11 @@ $($( )? */ + impl$(<$gen>)? [<$afi_name $safi_name Nlri>]$(<$gen>)? { + pub fn into_addpath(self, path_id: PathId) -> [<$afi_name $safi_name AddpathNlri>]$(<$gen>)? { + [<$afi_name $safi_name AddpathNlri>](path_id, self) + } + } // Create the Addpath version addpath!([<$afi_name $safi_name>]$(<$gen>)?); @@ -331,6 +342,29 @@ where } } +use octseq::OctetsBuilder; +impl Ipv4UnicastNlri { + pub(crate) fn compose_len(&self) -> usize { + // 1 byte for the length itself + 1 + prefix_bits_to_bytes(self.prefix().len()) + } + + pub(crate) fn compose(&self, target: &mut Target) + -> Result<(), Target::AppendError> { + let len = self.prefix().len(); + target.append_slice(&[len])?; + let prefix_bytes = prefix_bits_to_bytes(len); + match self.prefix().addr() { + std::net::IpAddr::V4(a) => { + target.append_slice(&a.octets()[..prefix_bytes])? + } + _ => unreachable!() + } + Ok(()) + } + +} + impl fmt::Display for Ipv4UnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -793,7 +827,7 @@ mod tests { ]; let parser = Parser::from_ref(&raw); - let mut iter = NlriIter::ipv6_unicast_addpath(parser); + let iter = NlriIter::ipv6_unicast_addpath(parser); //while let Some(x) = iter.next_with(|e| format!("IPv6!!!: {:?}", e).to_string()) { // dbg!(x); //} @@ -804,4 +838,24 @@ mod tests { } + #[test] + fn roundtrip_into_from_addpath() { + let raw = vec![ + 24, 1, 2, 1, + 24, 1, 2, 2, + 24, 1, 2, 3, + 24, 1, 2, 4, + ]; + + let parser = Parser::from_ref(&raw); + let iter = NlriIter::ipv6_unicast(parser); + for (idx, n) in iter.enumerate() { + dbg!( + Ipv6UnicastNlri::from( + dbg!(n.into_addpath(PathId(idx.try_into().unwrap()))) + ) + ); + } + } + } From 82173c75cdc131c31afab9d127912355109de385 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 5 Mar 2024 17:12:29 +0100 Subject: [PATCH 68/96] Add Ipv6{Multicast, MplsUnicast, MplsVpnUnicast, FlowSpec} --- src/bgp/nlri/afisafi.rs | 174 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 4 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index b5616406..69ba8243 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -301,10 +301,10 @@ afisafi! { ], 2 => Ipv6 [ 1 => Unicast, - //2 => Multicast, - //4 => MplsUnicast, - //128 => MplsVpnUnicast, - //133 => FlowSpec, + 2 => Multicast, + 4 => MplsUnicast, + 128 => MplsVpnUnicast, + 133 => FlowSpec, //134 => FlowSpecVpn, ], //25 => L2Vpn [ @@ -581,6 +581,172 @@ impl fmt::Display for Ipv6UnicastNlri { } } +//--- Ipv6Multicast + +#[derive(Clone, Debug, Hash, PartialEq)] +pub struct Ipv6MulticastNlri(Prefix); + +impl AfiSafiNlri for Ipv6MulticastNlri { + type Nlri = Prefix; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6MulticastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + fn parse(parser: &mut Parser<'a, P>) -> Result { + Ok( + Self(parse_prefix(parser, Afi::Ipv6)?) + ) + } +} + +impl fmt::Display for Ipv6MulticastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + + +//--- Ipv6MplsUnicast + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6MplsUnicastNlri(MplsNlri); + +impl AfiSafiNlri for Ipv6MplsUnicastNlri { + type Nlri = MplsNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6MplsUnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + let (prefix, labels) = MplsNlri::parse_labels_and_prefix(parser, Afi::Ipv6)?; + + Ok( + Self(MplsNlri::new(prefix,labels,)) + ) + } +} + +impl PartialEq> for Ipv6MplsUnicastNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv6MplsUnicastNlri) -> bool { + self.0 == other.0 + } +} + +impl fmt::Display for Ipv6MplsUnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +//--- Ipv6MplsVpnUnicastNlri + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6MplsVpnUnicastNlri(MplsVpnNlri); + +impl AfiSafiNlri for Ipv6MplsVpnUnicastNlri { + type Nlri = MplsVpnNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6MplsVpnUnicastNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + let (labels, rd, prefix) = + parse_labels_rd_prefix(parser, Afi::Ipv6)?; + + Ok(Self(MplsVpnNlri::new(prefix, labels, rd))) + } +} + +impl fmt::Display for Ipv6MplsVpnUnicastNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + + +//--- Ipv6FlowSpec + +#[derive(Clone, Debug, Hash)] +pub struct Ipv6FlowSpecNlri(FlowSpecNlri); + +impl AfiSafiNlri for Ipv6FlowSpecNlri { + type Nlri = FlowSpecNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6FlowSpecNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + + Ok(Self(FlowSpecNlri::parse(parser, Afi::Ipv6)?)) + } +} + +impl fmt::Display for Ipv6FlowSpecNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + + +impl Ipv6UnicastAddpathNlri { + pub fn iter<'a, O, P>(parser: Parser<'a, P>) -> NlriIter<'a, O, P, Self> + where + O: Octets, + P: 'a + Octets = O> + { + NlriIter::ipv6_unicast_addpath(parser) + } +} + +impl Ipv4MplsUnicastNlri { + pub fn iter<'a, P>(parser: Parser<'a, P>) -> NlriIter<'a, Octs, P, Self> + where + Octs: Octets, + P: 'a + Octets = Octs> + { + NlriIter::ipv4_mplsunicast(parser) + } +} //------------ Iteration ------------------------------------------------------ From 5f3905d7a7ed9b37512bfcd5e328f2111beb89c5 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 6 Mar 2024 09:40:31 +0100 Subject: [PATCH 69/96] Add L2VpnVpls --- src/bgp/nlri/afisafi.rs | 42 +++++++++++++++++++++++++++++++++--- src/bgp/nlri/mod.rs | 3 ++- src/bgp/nlri/vpls.rs | 48 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 src/bgp/nlri/vpls.rs diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 69ba8243..d1860c1d 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -27,6 +27,7 @@ use super::mpls::*; use super::mpls_vpn::*; use super::routetarget::*; use super::flowspec::*; +use super::vpls::*; macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => { @@ -307,10 +308,10 @@ afisafi! { 133 => FlowSpec, //134 => FlowSpecVpn, ], - //25 => L2Vpn [ - // 65 => Vpls, + 25 => L2Vpn [ + 65 => Vpls, // 70 => Evpn, - //] + ] } @@ -748,6 +749,41 @@ impl Ipv4MplsUnicastNlri { } } +//------------ L2Vpn ---------------------------------------------------------- + +//--- L2VpnVpls + +#[derive(Clone, Debug, Hash)] +pub struct L2VpnVplsNlri(VplsNlri); + +impl AfiSafiNlri for L2VpnVplsNlri { + type Nlri = VplsNlri; + fn nlri(&self) -> Self::Nlri { + self.0 + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for L2VpnVplsNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + + Ok(Self(VplsNlri::parse(parser)?)) + } +} + +impl fmt::Display for L2VpnVplsNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + //------------ Iteration ------------------------------------------------------ // Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index 5ca4a8e4..89fde0a9 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -1,6 +1,7 @@ pub mod afisafi; mod common; +mod flowspec; mod mpls; mod mpls_vpn; mod routetarget; -mod flowspec; +mod vpls; diff --git a/src/bgp/nlri/vpls.rs b/src/bgp/nlri/vpls.rs new file mode 100644 index 00000000..6ddd1b4b --- /dev/null +++ b/src/bgp/nlri/vpls.rs @@ -0,0 +1,48 @@ +use std::fmt; + +use octseq::{Octets, Parser}; + +use crate::util::parser::ParseError; +use super::mpls_vpn::RouteDistinguisher; + +/// VPLS Information as defined in RFC4761. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct VplsNlri { + rd: RouteDistinguisher, + ve_id: u16, + ve_block_offset: u16, + ve_block_size: u16, + raw_label_base: u32, +} + +impl VplsNlri { + pub fn parse(parser: &mut Parser<'_, R>) + -> Result + { + let _len = parser.parse_u16_be()?; + let rd = RouteDistinguisher::parse(parser)?; + let ve_id = parser.parse_u16_be()?; + let ve_block_offset = parser.parse_u16_be()?; + let ve_block_size = parser.parse_u16_be()?; + let label_base_1 = parser.parse_u8()? as u32; + let label_base_2 = parser.parse_u16_be()? as u32; + + Ok( + VplsNlri { + rd, + ve_id, + ve_block_offset, + ve_block_size, + raw_label_base: label_base_1 << 16 | label_base_2, + } + ) + } +} + + +impl fmt::Display for VplsNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VPLS-{}", self.rd) + } +} From 6307b8b1972c9f513448da9e8e65fab34025dff9 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Wed, 6 Mar 2024 09:48:11 +0100 Subject: [PATCH 70/96] Add L2VpnEvpn --- src/bgp/nlri/afisafi.rs | 48 ++++++++++++++++++++++++++++------- src/bgp/nlri/evpn.rs | 55 +++++++++++++++++++++++++++++++++++++++++ src/bgp/nlri/mod.rs | 2 ++ 3 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 src/bgp/nlri/evpn.rs diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index d1860c1d..908f0f7e 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -23,10 +23,11 @@ use octseq::{Octets, Parser}; use core::hash::Hash; use core::fmt::Debug; +use super::evpn::*; +use super::flowspec::*; use super::mpls::*; use super::mpls_vpn::*; use super::routetarget::*; -use super::flowspec::*; use super::vpls::*; macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => @@ -280,15 +281,10 @@ pub trait Addpath: AfiSafiNlri { //------------ Implementations ----------------------------------------------- -//afisafi! { -// 1 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], -// 2 => Ipv6 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast ], -// 25 => L2Vpn [ 65 => Vpls, 70 => Evpn ], -//} - // adding AFI/SAFIs here requires some manual labor: // - at the least, add a struct for $Afi$SafiNlri , deriving Clone,Debug,Hash -// - impl AfiSafiNlri and AfiSafiParse +// - impl AfiSafiNlri, AfiSafiParse and Display + afisafi! { 1 => Ipv4 [ 1 => Unicast, @@ -310,7 +306,7 @@ afisafi! { ], 25 => L2Vpn [ 65 => Vpls, - // 70 => Evpn, + 70 => Evpn, ] } @@ -784,6 +780,40 @@ impl fmt::Display for L2VpnVplsNlri { } } +//--- Evpn + +#[derive(Clone, Debug, Hash)] +pub struct L2VpnEvpnNlri(EvpnNlri); + +impl AfiSafiNlri for L2VpnEvpnNlri { + type Nlri = EvpnNlri; + fn nlri(&self) -> Self::Nlri { + self.0.clone() + } +} + +impl<'a, O, P> AfiSafiParse<'a, O, P> for L2VpnEvpnNlri +where + O: Octets, + P: 'a + Octets = O> +{ + type Output = Self; + + fn parse(parser: &mut Parser<'a, P>) + -> Result + { + + Ok(Self(EvpnNlri::parse(parser)?)) + } +} + +impl fmt::Display for L2VpnEvpnNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + + //------------ Iteration ------------------------------------------------------ // Iterating over NLRI likely mostly happens when ingesting UPDATE PDUs, i.e. diff --git a/src/bgp/nlri/evpn.rs b/src/bgp/nlri/evpn.rs new file mode 100644 index 00000000..3e7bca6a --- /dev/null +++ b/src/bgp/nlri/evpn.rs @@ -0,0 +1,55 @@ +use std::fmt; + +use octseq::{Octets, Parser}; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; + +use crate::typeenum; +use crate::util::parser::ParseError; + +typeenum!( + EvpnRouteType, u8, + { + 1 => EthernetAutoDiscovery, + 2 => MacIpAdvertisement, + 3 => InclusiveMulticastEthernetTag, + 4 => EthernetSegment, + 5 => IpPrefix, + } +); + +/// NLRI containing a EVPN NLRI as defined in RFC7432. +/// +/// **TODO**: implement accessor methods for the contents of this NLRI. +#[derive(Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct EvpnNlri { + #[allow(dead_code)] + route_type: EvpnRouteType, + raw: Octs, +} + +impl EvpnNlri { + pub fn parse<'a, R>(parser: &mut Parser<'a, R>) + -> Result + where + R: Octets = Octs> + { + let route_type = parser.parse_u8()?.into(); + let route_len = parser.parse_u8()?; + let raw = parser.parse_octets(route_len.into())?; + + Ok( + EvpnNlri { + route_type, + raw + } + ) + } +} + +impl fmt::Display for EvpnNlri { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "EVPN-{}", self.route_type) + } +} diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index 89fde0a9..1671592c 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -1,5 +1,7 @@ pub mod afisafi; mod common; + +mod evpn; mod flowspec; mod mpls; mod mpls_vpn; From e84a6af9dd07f5700476c63ce31126cc9a341108 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 19 Mar 2024 09:32:54 +0100 Subject: [PATCH 71/96] Adopt new AfiSafi / Nlri types throughout codebase This commit adapts the codebase to use the types and traits introduced in this branch. With that, several other concepts required changes, or became deprecated and were removed. The new types/traits include: - trait AfiSafi, describing something that is characterized by a certain AFI+SAFI combination; - trait AfiSafiNlri, an NLRI characterized by a certain AFI+SAFI combination; - enum AfiSafiType, describing all AFI+SAFI combinations we support; - enum Nlri, now holding more explicit variants of dedicated types, a la Ipv4UnicastNlri, Ipv6UnicastNlri, etc. For ADD-PATH, dedicated types are generated: Ipv4UnicastAddpathNlri, etc; - On UpdateMessage, `fn typed_announcements()` provides an iterator generic over tpyes implementing AfiSafiParse, thus yielding NLRI of these dedicated types instead of enum variants (which is still available via the `fn announcements()); - UpdateBuilder is now generic over types that implement NlriCompose, making the adding/removing of announcements and withdrawals more straightforward. New generic helper methods include `fn add_announcements_from_pdu`. Other changes: - enum Safi is removed, as an SAFI by itself does not mean anything useful. We keep enum Afi and the new enum AfiSafiType. (The name `AfiSafi` is now used for the new trait); - the UpdateMessage does not hold an entire SessionConfig anymore, but a trimmed down version of the new type PduParseInfo. The SessionConfig is only used in the very first call to `from_octets()` or `parse()`; - MpReachNlri and MpUnreachNlri are not part of the PathAttribute enum anymore, we start treating them differently. Partly because with the new builders being generic, it would mean the enum PathAttribute must carry generic type info, and PaMap as well. All while we most often do not include the MP* attributes in those maps anyway; - the new bgp::nlri modules replaces everyhing that was in bgp::message::nlri Open issues / questions and other remarks: - on UpdateMessage, we had several methods to specifically get unicast announcements. We need to figure out if those are still wanted; - we need to figure out what the Workshop should look like. We can probably get rid of the afisafi_nlri module to a large extent; - the UpdateBuilder now puts all withdrawals and announcements in MP attributes, so nothing goes into the conventional sections in the PDU. Related tests are ignored for now. We need to figure out to what extent we like to support those conventional sections in the builder. --- fuzz/Cargo.lock | 9 +- fuzz/fuzz_targets/parse_update_message.rs | 19 +- src/bgp/message/mod.rs | 6 +- src/bgp/message/nlri.rs | 12 +- src/bgp/message/open.rs | 96 +- src/bgp/message/routerefresh.rs | 23 +- src/bgp/message/update.rs | 908 +++++++-------- src/bgp/message/update_builder.rs | 1226 ++++++++++++--------- src/bgp/nlri/afisafi.rs | 495 ++++++++- src/bgp/nlri/evpn.rs | 6 + src/bgp/nlri/flowspec.rs | 6 + src/bgp/nlri/mod.rs | 15 +- src/bgp/nlri/mpls.rs | 5 + src/bgp/nlri/nexthop.rs | 157 +++ src/bgp/path_attributes.rs | 326 ++++-- src/bgp/types.rs | 220 +--- src/bgp/workshop/afisafi_nlri.rs | 61 +- src/bgp/workshop/mod.rs | 2 +- src/bgp/workshop/route.rs | 53 +- src/bmp/message.rs | 55 +- 20 files changed, 2235 insertions(+), 1465 deletions(-) create mode 100644 src/bgp/nlri/nexthop.rs diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 9e335125..1e41f2bf 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -145,6 +145,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -165,7 +171,7 @@ dependencies = [ [[package]] name = "routecore" -version = "0.4.0-rc1-dev" +version = "0.4.1-dev" dependencies = [ "arbitrary", "bytes", @@ -173,6 +179,7 @@ dependencies = [ "const-str", "log", "octseq", + "paste", ] [[package]] diff --git a/fuzz/fuzz_targets/parse_update_message.rs b/fuzz/fuzz_targets/parse_update_message.rs index 9c72a15b..1d7e1e60 100644 --- a/fuzz/fuzz_targets/parse_update_message.rs +++ b/fuzz/fuzz_targets/parse_update_message.rs @@ -5,23 +5,18 @@ use routecore::bgp::message::{UpdateMessage, SessionConfig}; use routecore::bgp::message::update_builder::UpdateBuilder; fuzz_target!(|data: (&[u8], SessionConfig)| { - if let Ok(upd) = UpdateMessage::from_octets(data.0, data.1) { + if let Ok(upd) = UpdateMessage::from_octets(data.0, &data.1) { if let Ok(pas) = upd.path_attributes() { for pa in pas.into_iter() { - if let Ok(pa) = pa { - let _ = pa.to_owned(); - } + let _ = pa.unwrap().to_owned(); } } - /* - if let Ok(builder) = UpdateBuilder::from_update_message( - &upd, - data.1, - target, - ) { - let _ = builder.into_message(); //.unwrap(); + if let Ok(iter) = upd.announcements() { + iter.count(); + } + if let Ok(iter) = upd.withdrawals() { + iter.count(); } - */ } }); diff --git a/src/bgp/message/mod.rs b/src/bgp/message/mod.rs index cd1e5c5d..d777ee65 100644 --- a/src/bgp/message/mod.rs +++ b/src/bgp/message/mod.rs @@ -1,11 +1,9 @@ pub mod open; pub mod update; pub mod update_builder; -pub mod nlri; pub mod notification; pub mod keepalive; pub mod routerefresh; -//pub mod attr_change_set; use octseq::{Octets, Parser}; use crate::util::parser::ParseError; @@ -21,7 +19,7 @@ use log::debug; use serde::{Serialize, Deserialize}; pub use open::OpenMessage; -pub use update::{UpdateMessage, SessionConfig}; +pub use update::{PduParseInfo, SessionConfig, UpdateMessage}; pub use notification::NotificationMessage; pub use keepalive::KeepaliveMessage; pub use routerefresh::RouteRefreshMessage; @@ -130,7 +128,7 @@ impl Message { return Err(ParseError::StateRequired) }; Ok(Message::Update( - UpdateMessage::from_octets(octets, config)? + UpdateMessage::from_octets(octets, &config)? )) }, MsgType::Notification => diff --git a/src/bgp/message/nlri.rs b/src/bgp/message/nlri.rs index b0d45a96..232c1581 100644 --- a/src/bgp/message/nlri.rs +++ b/src/bgp/message/nlri.rs @@ -3,7 +3,7 @@ use crate::bgp::types::{Afi, AfiSafi, NextHop}; use crate::addr::Prefix; use crate::util::parser::{parse_ipv4addr, parse_ipv6addr, ParseError}; -use crate::bgp::message::update::SessionConfig; +use crate::bgp::message::update::PduParseInfo; use crate::flowspec::Component; use crate::typeenum; use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; @@ -1618,6 +1618,7 @@ pub(crate) fn parse_prefix(parser: &mut Parser<'_, R>, afi: Afi) parse_prefix_for_len(parser, prefix_bits, afi) } +/* impl BasicNlri { pub fn check( parser: &mut Parser, @@ -1736,12 +1737,13 @@ impl From<(Prefix, Option)> for BasicNlri { BasicNlri { prefix: tuple.0, path_id: tuple.1 } } } +*/ impl MplsVpnNlri { pub fn check( parser: &mut Parser, - config: SessionConfig, + config: PduParseInfo, afisafi: AfiSafi, ) -> Result<(), ParseError> { @@ -1774,7 +1776,7 @@ impl MplsVpnNlri { impl MplsVpnNlri { pub fn parse<'a, R>( parser: &mut Parser<'a, R>, - config: SessionConfig, + config: PduParseInfo, afisafi: AfiSafi, ) -> Result where @@ -1916,7 +1918,7 @@ impl> MplsVpnNlri { impl MplsNlri { pub fn check( parser: &mut Parser, - config: SessionConfig, + config: PduParseInfo, afisafi: AfiSafi, ) -> Result<(), ParseError> { if config.rx_addpath(afisafi) { @@ -1960,7 +1962,7 @@ impl fmt::Display for MplsNlri { impl MplsNlri { pub fn parse<'a, R>( parser: &mut Parser<'a, R>, - config: SessionConfig, + config: PduParseInfo, afisafi: AfiSafi, ) -> Result where diff --git a/src/bgp/message/open.rs b/src/bgp/message/open.rs index b17bfbf0..8939cbf7 100644 --- a/src/bgp/message/open.rs +++ b/src/bgp/message/open.rs @@ -1,6 +1,6 @@ use crate::bgp::message::{Header, MsgType}; use crate::asn::Asn; -use crate::bgp::types::{Afi, Safi, AfiSafi, AddpathFamDir, AddpathDirection}; +use crate::bgp::types::{AfiSafi, AddpathFamDir, AddpathDirection}; use crate::typeenum; // from util::macros use crate::util::parser::ParseError; use log::warn; @@ -173,13 +173,6 @@ impl OpenMessage { ) } - // FIXME this should return a AFI/SAFI combination, not a bool - pub fn add_path_capable(&self) -> bool { - self.capabilities().any(|c| - c.typ() == CapabilityType::AddPath - ) - } - /* pub fn addpath_families(&self) -> impl Iterator + '_ { self.capabilities().filter(|c| @@ -208,15 +201,11 @@ impl OpenMessage { for c in c.value().chunks(4) { let mut parser = Parser::from_ref(c); - let afi = parser.parse_u16_be()?.into(); - let safi = parser.parse_u8()?.into(); + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; let dir = AddpathDirection::try_from(parser.parse_u8()?) .map_err(|_| ParseError::Unsupported)?; - res.push(( - AfiSafi::try_from((afi, safi)) - .map_err(|_| ParseError::Unsupported)?, - dir - )); + res.push((AfiSafi::from((afi, safi)), dir)); } } Ok(res) @@ -249,9 +238,9 @@ impl OpenMessage { }).collect::>() } - /// Returns an iterator over `(Afi, Safi)` tuples listed as - /// MultiProtocol Capabilities in the Optional Parameters of this message. - pub fn multiprotocol_ids(&self) -> impl Iterator + '_ { + /// Returns an iterator over `AfiSafi`s listed as MultiProtocol + /// Capabilities in the Optional Parameters of this message. + pub fn multiprotocol_ids(&self) -> impl Iterator + '_ { self.capabilities().filter(|c| c.typ() == CapabilityType::MultiProtocol ).map(|mp_cap| { @@ -260,7 +249,7 @@ impl OpenMessage { mp_cap.value()[1] ]); let safi = mp_cap.value()[3]; - (afi.into(), safi.into()) + (afi, safi).into() }) } @@ -830,15 +819,17 @@ impl> OpenBuilder { self.add_capability(Capability::for_slice(s.to_vec())); } - pub fn add_mp(&mut self, afi: Afi, safi: Safi) { + pub fn add_mp(&mut self, afisafi: AfiSafi) { // code 1 // length n // 2 bytes AFI, rsrvd byte, 1 byte SAFI - let mut s = [0x01, 0x04, 0x00, 0x00, 0x00, safi.into()]; - let a = >::into(afi).to_be_bytes(); - s[2] = a[0]; - s[3] = a[1]; - self.add_capability(Capability::>::for_slice(s.to_vec())) + + let (afi, safi) = afisafi.into(); + let mut s = vec![0x01, 0x04]; + s.extend_from_slice(&afi.to_be_bytes()[..]); + s.extend_from_slice(&[0x00, safi]); + + self.add_capability(Capability::>::for_slice(s)); } pub fn add_addpath(&mut self, afisafi: AfiSafi, dir: AddpathDirection) { @@ -863,10 +854,9 @@ where Infallible: From<::AppendError> ] ); - for (fam, dir) in self.addpath_families.iter() { - let (afi, safi) = fam.split(); - addpath_cap.extend_from_slice(&u16::from(afi).to_be_bytes()); - addpath_cap.extend_from_slice(&[safi.into(), *dir as u8]); + for (afisafi, dir) in self.addpath_families.iter() { + addpath_cap.extend_from_slice(&afisafi.as_bytes()); + addpath_cap.extend_from_slice(&[u8::from(*dir)]); } self.add_capability(Capability::new(addpath_cap)); } @@ -915,9 +905,11 @@ impl OpenBuilder> { mod tests { use super::*; - use crate::bgp::message::Message; + use bytes::Bytes; + use crate::bgp::message::Message; + #[test] fn no_optional_parameters() { // BGP OPEN message, 2-octet ASN 64496, no opt params @@ -1053,21 +1045,23 @@ mod tests { assert_eq!(open.multiprotocol_ids().count(), 15); let protocols = [ - (Afi::Ipv4, Safi::Unicast), - (Afi::Ipv4, Safi::Multicast), - (Afi::Ipv4, Safi::MplsUnicast), - (Afi::Ipv4, Safi::MplsVpnUnicast), - (Afi::Ipv4, Safi::RouteTarget), - (Afi::Ipv4, Safi::FlowSpec), - (Afi::Ipv4, Safi::FlowSpecVpn), - (Afi::Ipv6, Safi::Unicast), - (Afi::Ipv6, Safi::Multicast), - (Afi::Ipv6, Safi::MplsUnicast), - (Afi::Ipv6, Safi::MplsVpnUnicast), - (Afi::Ipv6, Safi::FlowSpec), - (Afi::Ipv6, Safi::FlowSpecVpn), - (Afi::L2Vpn, Safi::Vpls), - (Afi::L2Vpn, Safi::Evpn), + AfiSafi::Ipv4Unicast, + AfiSafi::Ipv4Multicast, + AfiSafi::Ipv4MplsUnicast, + AfiSafi::Ipv4MplsVpnUnicast, + AfiSafi::Ipv4RouteTarget, + AfiSafi::Ipv4FlowSpec, + //AfiSafi::Ipv4FlowSpecVpn, + AfiSafi::Unsupported(1, 134), + AfiSafi::Ipv6Unicast, + AfiSafi::Ipv6Multicast, + AfiSafi::Ipv6MplsUnicast, + AfiSafi::Ipv6MplsVpnUnicast, + AfiSafi::Ipv6FlowSpec, + //AfiSafi::Ipv6FlowSpecVpn, + AfiSafi::Unsupported(2, 134), + AfiSafi::L2VpnVpls, + AfiSafi::L2VpnEvpn, ]; for (id, protocol) in open.multiprotocol_ids().zip( @@ -1137,8 +1131,8 @@ mod builder { open.set_holdtime(180); open.set_bgp_id([1, 2, 3, 4]); - open.add_mp(Afi::Ipv4, Safi::Unicast); - open.add_mp(Afi::Ipv6, Safi::Unicast); + open.add_mp(AfiSafi::Ipv4Unicast); + open.add_mp(AfiSafi::Ipv6Unicast); let res = open.into_message(); @@ -1152,8 +1146,8 @@ mod builder { open.set_holdtime(180); open.set_bgp_id([1, 2, 3, 4]); - open.add_mp(Afi::Ipv4, Safi::Unicast); - open.add_mp(Afi::Ipv6, Safi::Unicast); + open.add_mp(AfiSafi::Ipv4Unicast); + open.add_mp(AfiSafi::Ipv6Unicast); let res = open.into_message(); @@ -1167,8 +1161,8 @@ mod builder { open.set_holdtime(180); open.set_bgp_id([1, 2, 3, 4]); - open.add_mp(Afi::Ipv4, Safi::Unicast); - open.add_mp(Afi::Ipv6, Safi::Unicast); + open.add_mp(AfiSafi::Ipv4Unicast); + open.add_mp(AfiSafi::Ipv6Unicast); open.add_addpath(AfiSafi::Ipv4Unicast, AddpathDirection::SendReceive); open.add_addpath(AfiSafi::Ipv6Unicast, AddpathDirection::SendReceive); diff --git a/src/bgp/message/routerefresh.rs b/src/bgp/message/routerefresh.rs index b524bc36..35334240 100644 --- a/src/bgp/message/routerefresh.rs +++ b/src/bgp/message/routerefresh.rs @@ -1,15 +1,14 @@ use octseq::{Octets, Parser}; use crate::bgp::message::Header; -use crate::bgp::types::{Afi, Safi, RouteRefreshSubtype}; +use crate::bgp::types::{Afi, AfiSafi, RouteRefreshSubtype}; use crate::util::parser::ParseError; /// BGP RouteRefresh message, variant of the [`Message`] enum. #[derive(Clone, Debug)] pub struct RouteRefreshMessage { octets: Octets, - afi: Afi, - safi: Safi, + afisafi: AfiSafi, subtype: RouteRefreshSubtype, } @@ -24,11 +23,12 @@ impl RouteRefreshMessage { )); } } - let afi = parser.parse_u16_be()?.into(); + let afi = parser.parse_u16_be()?; let subtype = parser.parse_u8()?.into(); - let safi = parser.parse_u8()?.into(); + let safi = parser.parse_u8()?; + let afisafi = (afi, safi).into(); - Ok(RouteRefreshMessage { octets, afi, safi, subtype }) + Ok(RouteRefreshMessage { octets, afisafi, subtype }) } pub fn octets(&self) -> &Octs { @@ -39,12 +39,12 @@ impl RouteRefreshMessage { impl RouteRefreshMessage { /// Returns the `Afi` for this Route Refresh message. pub fn afi(&self) -> Afi { - self.afi + self.afisafi.afi() } - /// Returns the `Safi` for this Route Refresh message. - pub fn safi(&self) -> Safi { - self.safi + /// Returns the `AfiSafi` for this Route Refresh message. + pub fn afisafi(&self) -> AfiSafi { + self.afisafi } /// Returns the `RouteRefreshSubtype` for this Route Refresh message. @@ -86,8 +86,7 @@ mod tests { ]; let rr = RouteRefreshMessage::from_octets(&raw).unwrap(); - assert_eq!(rr.afi(), Afi::Ipv4); - assert_eq!(rr.safi(), Safi::Unicast); + assert_eq!(rr.afisafi(), AfiSafi::Ipv4Unicast); assert_eq!(rr.subtype(), RouteRefreshSubtype::Begin); } diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 264d7c8e..70a1f119 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -1,38 +1,36 @@ +use core::ops::Range; +use std::collections::HashMap; use std::fmt::Debug; use std::hash::Hash; +use std::marker::PhantomData; +use std::net::Ipv4Addr; -use crate::bgp::message::Header; use octseq::{Octets, Parser}; +use crate::bgp::message::Header; + use crate::asn::Asn; use crate::bgp::aspath::AsPath; +use crate::bgp::communities::{ + ExtendedCommunity, Ipv6ExtendedCommunity, LargeCommunity, +}; +use crate::bgp::message::MsgType; use crate::bgp::path_attributes::{ - AggregatorInfo, - PathAttributes, PathAttributeType, WireformatPathAttribute + AggregatorInfo, PathAttributes, PathAttributeType, + WireformatPathAttribute, UncheckedPathAttributes, }; -pub use crate::bgp::types::{ - Afi, Safi, LocalPref, MultiExitDisc, NextHop, OriginType, - AfiSafi, AddpathDirection, AddpathFamDir +use crate::bgp::types::{ + LocalPref, MultiExitDisc, NextHop, OriginType, AfiSafi, AddpathDirection, + AddpathFamDir }; -use crate::bgp::message::MsgType; -use crate::bgp::message::nlri::{self, - Nlri, BasicNlri, EvpnNlri, MplsNlri, MplsVpnNlri, VplsNlri, FlowSpecNlri, - RouteTargetNlri, - FixedNlriIter, +use crate::bgp::nlri::afisafi::{ + AfiSafiNlri, AfiSafiParse, NlriIter, NlriEnumIter, Nlri as NlriEnum }; -use core::ops::Range; -use std::marker::PhantomData; -use std::net::Ipv4Addr; use crate::util::parser::ParseError; -use crate::bgp::communities::{ - ExtendedCommunity, Ipv6ExtendedCommunity, - LargeCommunity, -}; - /// BGP UPDATE message, variant of the [`Message`] enum. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -41,7 +39,7 @@ pub struct UpdateMessage { withdrawals: Range, attributes: Range, announcements: Range, - session_config: SessionConfig, + pdu_parse_info: PduParseInfo, } @@ -200,59 +198,96 @@ impl UpdateMessage { ) } - /// Returns the conventional withdrawals. - pub fn conventional_withdrawals(&self) -> Result, ParseError> + /// Returns an iterator over the conventional withdrawals. + /// + /// The withdrawals are always IPv4 Unicast, but can contain Path IDs. + /// Therefore, iterator yields variants of `NlriEnum`. + pub fn conventional_withdrawals(&self) + -> Result< + impl Iterator>, ParseError>> + '_, + ParseError + > { let pp = Parser::with_range(self.octets(), self.withdrawals.clone()); - let iter = Nlris { - parser: pp, - session_config: self.session_config, - afi_safi: AfiSafi::Ipv4Unicast + let (normal_iter, addpath_iter) = match self.pdu_parse_info.conventional_addpath() { + false => (Some(NlriIter::ipv4_unicast(pp)), None), + true => (None, Some(NlriIter::ipv4_unicast_addpath(pp))) }; - Ok(iter) - + Ok(normal_iter.into_iter().flatten() + .map(|n| n.map(NlriEnum::<_>::from)) + .chain( + addpath_iter.into_iter().flatten() + .map(|n| n.map(NlriEnum::<_>::from)) + ) + ) } - /// Returns the withdrawals from the MP_UNREACH_NLRI attribute, if any. - pub fn mp_withdrawals(&self) -> Result>, ParseError> + /// Returns an iterator over the MultiProtocol withdrawals. + pub fn mp_withdrawals(&self) + -> Result>, ParseError> { - if let Some(WireformatPathAttribute::MpUnreachNlri(epa)) = self.path_attributes()?.get( - PathAttributeType::MpUnreachNlri - ){ - let mut parser = epa.value_into_parser(); - let afi = parser.parse_u16_be()?.into(); - let safi = parser.parse_u8()?.into(); - let afi_safi = AfiSafi::try_from((afi, safi)) - .map_err(|_| ParseError::Unsupported)?; - - return Ok(Some(Nlris{ - parser, - session_config: self.session_config, - afi_safi, - })) + let mut unchecked = UncheckedPathAttributes::from_parser( + Parser::with_range(self.octets(), self.attributes.clone()) + ); + if let Some(pa) = unchecked.find(|pa| + pa.type_code() == 15 + ) { + let mut parser = pa.value_into_parser(); + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; + let afi_safi = AfiSafi::from((afi, safi)); + Ok(Some(NlriEnumIter::new(parser, afi_safi))) + } else { + Ok(None) } - - Ok(None) } - + /// Returns a combined iterator of conventional and MP_UNREACH_NLRI. /// /// Note that this iterator might contain NLRI of different AFI/SAFI - /// types. + /// types and yields variants of `NlriEnum`. pub fn withdrawals(&self) -> Result< - impl Iterator>, ParseError>>, + impl Iterator>, ParseError>>, ParseError > { - let mp_iter = self.mp_withdrawals()?.map(|i| i.iter()); - let conventional_iter = self.conventional_withdrawals()?.iter(); + let mp_iter = self.mp_withdrawals()?; + let conventional_iter = self.conventional_withdrawals()?; - Ok(mp_iter.into_iter().flatten().chain(conventional_iter)) + Ok(mp_iter.into_iter().flatten() + .chain(conventional_iter) + ) + } + /// Returns an iterator yielding withdrawals of a specific NLRI type. + pub fn typed_withdrawals<'a, O, ASP>(&'a self) + -> Result>, ParseError> + where + O: Octets, + Octs: Octets = O>, + ASP: AfiSafiNlri + AfiSafiParse<'a, O, Octs> + { + if ASP::afi_safi() == AfiSafi::Ipv4Unicast && !self.withdrawals.is_empty() { + return Ok(Some(NlriIter::<_, _, ASP>::new( + Parser::with_range(self.octets(), self.withdrawals.clone()) + ))); + } + if let Some(pa) = self.unchecked_path_attributes().find(|pa| + pa.type_code() == 15 + ) { + let mut parser = pa.value_into_parser(); + let _afi = parser.parse_u16_be()?; + let _safi = parser.parse_u8()?; + + Ok(Some(NlriIter::<_, _, ASP>::new(parser))) + } else { + Ok(None) + } } + /// Creates a vec of all withdrawals in this message. /// @@ -264,10 +299,10 @@ impl UpdateMessage { /// For more fine-grained control, consider using the /// `unicast_withdrawals` method. pub fn withdrawals_vec(&self) - -> Result>>, ParseError> + -> Result>>, ParseError> { - let conv = self.conventional_withdrawals()?.iter(); - let mp = self.mp_withdrawals()?.map(|mp| mp.iter()); + let conv = self.conventional_withdrawals()?; + let mp = self.mp_withdrawals()?; conv.chain(mp.into_iter().flatten()).collect() } @@ -284,48 +319,55 @@ impl UpdateMessage { { let pp = Parser::with_range(self.octets(), self.attributes.clone()); - Ok(PathAttributes::new(pp, self.session_config)) + Ok(PathAttributes::new(pp, self.pdu_parse_info)) + } + + fn unchecked_path_attributes(&self) -> UncheckedPathAttributes { + let pp = Parser::with_range(self.octets(), self.attributes.clone()); + UncheckedPathAttributes::from_parser(pp) } /// Returns the conventional announcements. pub fn conventional_announcements(&self) - -> Result, ParseError> + -> Result< + impl Iterator>, ParseError>>, + ParseError> { let pp = Parser::with_range(self.octets(), self.announcements.clone()); - let iter = Nlris { - parser: pp, - session_config: self.session_config, - afi_safi: AfiSafi::Ipv4Unicast, - }; + let (normal_iter, addpath_iter) = + match self.pdu_parse_info.conventional_addpath() { + false => (Some(NlriIter::ipv4_unicast(pp)), None), + true => (None, Some(NlriIter::ipv4_unicast_addpath(pp))) + } + ; - Ok(iter) + Ok(normal_iter.into_iter().flatten() + .map(|n| n.map(NlriEnum::<_>::from)) + .chain( + addpath_iter.into_iter().flatten() + .map(|n| n.map(NlriEnum::<_>::from)) + )) } /// Returns the announcements from the MP_UNREACH_NLRI attribute, if any. - pub fn mp_announcements(&self) -> Result>, ParseError> + pub fn mp_announcements(&self) + -> Result>, ParseError> { - if let Some(WireformatPathAttribute::MpReachNlri(epa)) = self.path_attributes()?.get( - PathAttributeType::MpReachNlri - ){ - let mut parser = epa.value_into_parser(); - let afi = parser.parse_u16_be()?.into(); - let safi = parser.parse_u8()?.into(); - let afi_safi = AfiSafi::try_from((afi, safi)) - .map_err(|_| ParseError::Unsupported)?; + if let Some(pa) = self.unchecked_path_attributes().find(|pa| + pa.type_code() == 14 + ) { + let mut parser = pa.value_into_parser(); + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; + let afi_safi = AfiSafi::from((afi, safi)); NextHop::skip(&mut parser)?; parser.advance(1)?; // 1 reserved byte - let res = Nlris{ - parser, - session_config: self.session_config, - afi_safi, - }; - - return Ok(Some(res)) + Ok(Some(NlriEnumIter::new(parser, afi_safi))) + } else { + Ok(None) } - - Ok(None) } /// Returns a combined iterator of conventional and MP_REACH_NLRI. @@ -353,16 +395,51 @@ impl UpdateMessage { /// types. pub fn announcements(&self) -> Result< - impl Iterator>, ParseError>>, + impl Iterator>, ParseError>>, ParseError > { - let mp_iter = self.mp_announcements()?.map(|i| i.iter()); - let conventional_iter = self.conventional_announcements()?.iter(); + let mp_iter = self.mp_announcements()?; + let conventional_iter = self.conventional_announcements()?; Ok(mp_iter.into_iter().flatten().chain(conventional_iter)) } + + /// Returns an iterator yielding announcements of a specific NLRI type. + pub fn typed_announcements<'a, O, ASP>(&'a self) + -> Result>, ParseError> + where + O: Octets, + Octs: Octets = O>, + ASP: AfiSafiNlri + AfiSafiParse<'a, O, Octs> + { + // If the requested announcements are of type Ipv4Unicast, and the + // conventional announcements range is non-zero, return that. + if ASP::afi_safi() == AfiSafi::Ipv4Unicast && + !self.announcements.is_empty() + { + return Ok(Some(NlriIter::<_, _, ASP>::new( + Parser::with_range(self.octets(), self.announcements.clone()) + ))); + } + + // Otherwise, look in the attributes for an MP_REACH_NLRI. + if let Some(pa) = self.unchecked_path_attributes().find(|pa| + pa.type_code() == 14 + ) { + let mut parser = pa.value_into_parser(); + let _afi = parser.parse_u16_be()?; + let _safi = parser.parse_u8()?; + NextHop::skip(&mut parser)?; + parser.advance(1)?; // 1 reserved byte + + Ok(Some(NlriIter::<_, _, ASP>::new(parser))) + } else { + Ok(None) + } + } + /// Creates a vec of all announcements in this message. /// /// If any of the NLRI, in the conventional part or the MP_REACH_NLRI @@ -373,32 +450,34 @@ impl UpdateMessage { /// For more fine-grained control, consider using the /// `unicast_announcements` method. pub fn announcements_vec(&self) - -> Result>>, ParseError> + -> Result>>, ParseError> { - let conv = self.conventional_announcements()?.iter(); - let mp = self.mp_announcements()?.map(|mp| mp.iter()); + let conv = self.conventional_announcements()?; + let mp = self.mp_announcements()?; conv.chain(mp.into_iter().flatten()).collect() } + /* /// Returns a combined iterator of conventional and unicast MP_REACH_NLRI. /// /// If at any point an error occurs, the iterator returns that error and /// fuses itself, i.e. any following call to `next()` will return None. pub fn unicast_announcements(&self) -> Result< - impl Iterator> + '_, + impl Iterator> + '_, ParseError > { + let mp_iter = self.mp_announcements()?.filter(|nlris| matches!( nlris.afi_safi(), AfiSafi::Ipv4Unicast | AfiSafi::Ipv6Unicast ) - ).map(|nlris| nlris.iter()); + ); - let conventional_iter = self.conventional_announcements()?.iter(); + let conventional_iter = self.conventional_announcements()?; Ok(mp_iter.into_iter().flatten().chain(conventional_iter) .map(|n| @@ -452,7 +531,6 @@ impl UpdateMessage { conv.chain(mp.into_iter().flatten()).collect() } - /// Returns a combined iterator of conventional and unicast /// MP_UNREACH_NLRI. /// @@ -524,6 +602,7 @@ impl UpdateMessage { conv.chain(mp.into_iter().flatten()).collect() } +*/ pub fn has_conventional_nlri(&self) -> bool { !self.announcements.is_empty() @@ -531,30 +610,25 @@ impl UpdateMessage { pub fn has_mp_nlri(&self) -> Result { Ok( - self.path_attributes()? - .get(PathAttributeType::MpReachNlri).is_some() + self.unchecked_path_attributes().any(|pa| pa.type_code() == 14) ) } - /// Returns `Option<(Afi, Safi)>` if this UPDATE represents the End-of-RIB + /// Returns `Option<(AfiSafi)>` if this UPDATE represents the End-of-RIB /// marker for a AFI/SAFI combination. - pub fn is_eor(&self) -> Result, ParseError> { + pub fn is_eor(&self) -> Result, ParseError> { // Conventional BGP if self.length() == 23 { // minimum length for a BGP UPDATE indicates EOR // (no announcements, no withdrawals) - return Ok(Some((Afi::Ipv4, Safi::Unicast))); + return Ok(Some(AfiSafi::Ipv4Unicast)); } // Based on MP_UNREACH_NLRI - - let mut pas = self.path_attributes()?; - if let Some(Ok(WireformatPathAttribute::MpUnreachNlri(epa))) = pas.next() { - let mut pa = epa.value_into_parser(); - if pa.remaining() == 3 && pas.next().is_none() { - let afi = pa.parse_u16_be()?.into(); - let safi = pa.parse_u8()?.into(); - return Ok(Some((afi, safi))) + if let Ok(Some(mut iter)) = self.mp_withdrawals() { + let res = iter.afi_safi(); + if iter.next().is_none() { + return Ok(Some(res)) } } @@ -598,8 +672,10 @@ impl UpdateMessage { { if let Some(WireformatPathAttribute::AsPath(epa)) = self.path_attributes()?.get(PathAttributeType::AsPath) { let mut p = epa.value_into_parser(); - Ok(Some(AsPath::new(p.parse_octets(p.remaining())?, - epa.session_config().has_four_octet_asn())?)) + Ok(Some( + AsPath::new(p.parse_octets(p.remaining())?, + epa.pdu_parse_info().has_four_octet_asn())? + )) } else { Ok(None) } @@ -618,14 +694,13 @@ impl UpdateMessage { /// Returns NextHop information from the MP_REACH_NLRI, if any. pub fn mp_next_hop(&self) -> Result, ParseError> { - if let Some(WireformatPathAttribute::MpReachNlri(epa)) = self.path_attributes()?.get( - PathAttributeType::MpReachNlri - ){ - let mut p = epa.value_into_parser(); - let afi = p.parse_u16_be()?.into(); - let safi = p.parse_u8()?.into(); - let afi_safi = AfiSafi::try_from((afi, safi)) - .map_err(|_| ParseError::Unsupported)?; + if let Some(pa) = self.unchecked_path_attributes().find(|pa| + pa.type_code() == 14 + ) { + let mut p = pa.value_into_parser(); + let afi = p.parse_u16_be()?; + let safi = p.parse_u8()?; + let afi_safi = AfiSafi::from((afi, safi)); Ok(Some(NextHop::parse(&mut p, afi_safi)?)) } else { Ok(None) @@ -633,14 +708,13 @@ impl UpdateMessage { } fn mp_next_hop_tuple(&self) -> Result, ParseError> { - if let Some(WireformatPathAttribute::MpReachNlri(epa)) = self.path_attributes()?.get( - PathAttributeType::MpReachNlri - ){ - let mut p = epa.value_into_parser(); - let afi = p.parse_u16_be()?.into(); - let safi = p.parse_u8()?.into(); - let afi_safi = AfiSafi::try_from((afi, safi)) - .map_err(|_| ParseError::Unsupported)?; + if let Some(pa) = self.unchecked_path_attributes().find(|pa| + pa.type_code() == 14 + ) { + let mut p = pa.value_into_parser(); + let afi = p.parse_u16_be()?; + let safi = p.parse_u8()?; + let afi_safi = AfiSafi::from((afi, safi)); Ok(Some((afi_safi, NextHop::parse(&mut p, afi_safi)?))) } else { Ok(None) @@ -756,7 +830,7 @@ impl UpdateMessage { // the same as in the fn parse in path_attributes.rs use crate::util::parser::parse_ipv4addr; let mut pa = epa.value_into_parser(); - let asn = if self.session_config.has_four_octet_asn() { + let asn = if self.pdu_parse_info.has_four_octet_asn() { Asn::from_u32(pa.parse_u32_be()?) } else { Asn::from_u32(pa.parse_u16_be()?.into()) @@ -902,18 +976,24 @@ impl UpdateMessage { /// As parsing of BGP UPDATE messages requires stateful information /// signalled by the BGP OPEN messages, this function requires a /// [`SessionConfig`]. - pub fn from_octets(octets: Octs, config: SessionConfig) + pub fn from_octets(octets: Octs, config: &SessionConfig) -> Result { let mut parser = Parser::from_ref(&octets); - let UpdateMessage{withdrawals, attributes, announcements, ..} = UpdateMessage::<_>::parse(&mut parser, config)?; + let UpdateMessage{ + withdrawals, + attributes, + announcements, + pdu_parse_info, + .. + } = UpdateMessage::<_>::parse(&mut parser, config)?; let res = Self { octets, withdrawals: (withdrawals.start +19..withdrawals.end + 19), attributes: (attributes.start +19..attributes.end + 19), announcements: (announcements.start +19..announcements.end + 19), - session_config: config + pdu_parse_info } ; @@ -926,7 +1006,7 @@ impl UpdateMessage { /// but will not be included in the resulting `Octs`. pub fn parse<'a, R: Octets>( parser: &mut Parser<'a, R>, - config: SessionConfig + session_config: &SessionConfig ) -> Result>, ParseError> where R: Octets = Octs>, @@ -947,25 +1027,21 @@ impl UpdateMessage { let withdrawals_len = parser.parse_u16_be()?; let withdrawals_start = parser.pos() - start_pos; if withdrawals_len > 0 { - let mut wdraw_parser = parser.parse_parser( - withdrawals_len.into() - )?; - while wdraw_parser.remaining() > 0 { - // conventional withdrawals are always IPv4 - BasicNlri::check( - &mut wdraw_parser, - config, - AfiSafi::Ipv4Unicast - )?; + let wdraw_parser = parser.parse_parser(withdrawals_len.into())?; + + if session_config.rx_addpath(AfiSafi::Ipv4Unicast) { + NlriIter::ipv4_unicast_addpath(wdraw_parser).validate()?; + } else { + NlriIter::ipv4_unicast(wdraw_parser).validate()?; } } + let withdrawals_end = parser.pos() - start_pos; - let withdrawals = if withdrawals_start == withdrawals_end { - 0..0 - } else { - withdrawals_start..withdrawals_end - }; + let withdrawals = withdrawals_start..withdrawals_end; + // To create PduParseInfo later on: + let mut mp_reach_afisafi = None; + let mut mp_unreach_afisafi = None; let attributes_len = parser.parse_u16_be()?; let attributes_start = parser.pos() - start_pos; @@ -975,34 +1051,65 @@ impl UpdateMessage { )?; // XXX this calls `validate` on every attribute, do we want to // error on that level here? - for pa in PathAttributes::new(pas_parser, config) { + for pa in PathAttributes::new(pas_parser, PduParseInfo::default()) { pa?; } + + let pas = UncheckedPathAttributes::from_parser(pas_parser); + for pa in pas { + match pa.type_code() { + 14 => { + // MP_REACH_NLRI + let mut tmp_parser = pa.value_into_parser(); + let afi = tmp_parser.parse_u16_be()?; + let safi = tmp_parser.parse_u8()?; + let afisafi = (afi, safi).into(); + mp_reach_afisafi = Some(afisafi); + } + 15 => { + // MP_UNREACH_NLRI + let mut tmp_parser = pa.value_into_parser(); + let afi = tmp_parser.parse_u16_be()?; + let safi = tmp_parser.parse_u8()?; + let afisafi = (afi, safi).into(); + mp_unreach_afisafi = Some(afisafi); + } + _ => { + // Any other attribute else we want to validate or + // peek in here already? + } + } + } } + let attributes_end = parser.pos() - start_pos; - let attributes = if attributes_start == attributes_end { - 0..0 - } else { - attributes_start..attributes_end - }; + let attributes = attributes_start..attributes_end; + if usize::from(attributes_len) != attributes.len() { + return Err(ParseError::form_error("invalid attributes length")); + } let announcements_start = parser.pos() - start_pos; - while parser.pos() < start_pos + header.length() as usize - 19 { - // conventional announcements are always IPv4 - BasicNlri::check( - parser, - config, - AfiSafi::Ipv4Unicast - )?; + + // Subtracting the 19 here is safe as we check for a minimal length of + // 19 above. + if announcements_start > header.length() as usize - 19 { + return Err(ParseError::form_error("invalid path attributes section")); } - let end_pos = parser.pos() - start_pos; + let ann_parser = parser.parse_parser( + header.length() as usize - 19 - (announcements_start) + )?; - let announcements = if announcements_start == end_pos { - 0..0 + if session_config.rx_addpath(AfiSafi::Ipv4Unicast) { + NlriIter::ipv4_unicast_addpath(ann_parser).validate()?; } else { - announcements_start..end_pos - }; + NlriIter::ipv4_unicast(ann_parser).validate()?; + } + + + let end_pos = parser.pos() - start_pos; + + let announcements = announcements_start..end_pos; if end_pos != (header.length() as usize) - 19 { @@ -1013,12 +1120,18 @@ impl UpdateMessage { parser.seek(start_pos)?; + let ppi = PduParseInfo::from_session_config( + session_config, + mp_reach_afisafi, + mp_unreach_afisafi + ); + Ok(UpdateMessage { octets: parser.parse_octets((header.length() - 19).into())?, withdrawals, attributes, announcements, - session_config: config + pdu_parse_info: ppi }) } @@ -1040,11 +1153,12 @@ 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, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct SessionConfig { pub four_octet_asn: FourOctetAsn, + ipv4unicast_in_mp: bool, addpath_fams: SessionAddpaths, } @@ -1052,66 +1166,140 @@ pub struct SessionConfig { #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -struct SessionAddpaths([Option; 16]); -impl SessionAddpaths { - const fn new() -> Self { - Self([None; 16]) +pub struct PduParseInfo { + four_octet_asn: bool, + pdu_addpaths: PduAddpaths, +} + +impl PduParseInfo { + pub fn new() -> Self { + Self::modern() } - const fn new_all_enabled() -> Self { - Self([Some(AddpathDirection::SendReceive); 16]) + pub fn modern() -> Self { + Self { four_octet_asn: true, pdu_addpaths: PduAddpaths::default() } } - fn set(&mut self, afi_safi: AfiSafi, dir: AddpathDirection) { - self.0[afi_safi as usize] = Some(dir); + pub fn legacy() -> Self { + Self { four_octet_asn: false, pdu_addpaths: PduAddpaths::default() } } - fn get(&self, afi_safi: AfiSafi) -> Option { - self.0[afi_safi as usize] + + pub fn from_session_config( + sc: &SessionConfig, + mp_reach_afisafi: Option, + mp_unreach_afisafi: Option, + ) -> Self { + Self { + four_octet_asn: sc.has_four_octet_asn(), + pdu_addpaths: PduAddpaths { + conventional: sc.rx_addpath(AfiSafi::Ipv4Unicast), + mp_reach: mp_reach_afisafi.map(|fam| sc.rx_addpath(fam)) + .unwrap_or(false), + mp_unreach: mp_unreach_afisafi.map(|fam| sc.rx_addpath(fam)) + .unwrap_or(false), + } + } } - fn enabled_addpaths(&self) - -> impl Iterator + '_ + pub fn has_four_octet_asn(&self) -> bool { + self.four_octet_asn + } + + pub fn conventional_addpath(&self) -> bool { + self.pdu_addpaths.conventional() + } + + pub fn mp_reach_addpath(&self) -> bool { + self.pdu_addpaths.mp_reach() + } + + pub fn mp_unreach_addpath(&self) -> bool { + self.pdu_addpaths.mp_unreach() + } +} + +impl Default for PduParseInfo { + fn default() -> Self { + Self { four_octet_asn: true, pdu_addpaths: PduAddpaths::default() } + } +} + + + +#[derive(Copy, Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +struct PduAddpaths { + conventional: bool, + mp_reach: bool, + mp_unreach: bool, +} + +impl PduAddpaths { + #[allow(dead_code)] + pub(crate) fn new(conventional: bool, mp_reach: bool, mp_unreach: bool) + -> Self { - self.0.iter() - .enumerate() - .filter_map(|(idx, apd)| apd.map(|apd| (idx, apd))) + Self { conventional, mp_reach, mp_unreach } } - fn inverse(&self) -> Self { - let mut res = [None; 16]; - for (i, apd) in self.0.iter().enumerate() { - if apd.is_none() { - res[i] = Some(AddpathDirection::SendReceive); - } - } - Self(res) + pub(crate) fn conventional(self) -> bool { + self.conventional } - // used for parsing retries - fn inverse_fam(&mut self, afi_safi: AfiSafi) { - if self.0[afi_safi as usize].is_some() { - self.0[afi_safi as usize] = None; - } else { - self.0[afi_safi as usize] = Some(AddpathDirection::Receive); - } + pub(crate) fn mp_reach(self) -> bool { + self.mp_reach + } + + pub(crate) fn mp_unreach(self) -> bool { + self.mp_unreach } } +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +struct SessionAddpaths(HashMap); +impl SessionAddpaths { + fn new() -> Self { + Self(HashMap::new()) + } + + fn set(&mut self, afisafi: AfiSafi, dir: AddpathDirection) { + self.0.insert(afisafi, dir); + } + + fn get(&self, afisafi: AfiSafi) -> Option { + self.0.get(&afisafi).copied() + } + + fn enabled_addpaths(&self) + -> impl Iterator + '_ + { + self.0.iter().map(|(&k, &v)| (k, v)) + } +} impl SessionConfig { - pub const fn modern() -> Self { + pub fn modern() -> Self { Self { four_octet_asn: FourOctetAsn::Enabled, + ipv4unicast_in_mp: true, addpath_fams: SessionAddpaths::new(), } } pub fn legacy() -> Self { Self { four_octet_asn: FourOctetAsn::Disabled, + ipv4unicast_in_mp: false, addpath_fams: SessionAddpaths::new(), } } + pub fn ipv4_unicast_mp(&self) -> bool { + self.ipv4unicast_in_mp + } + pub fn has_four_octet_asn(&self) -> bool { matches!(self.four_octet_asn, FourOctetAsn::Enabled) } @@ -1157,7 +1345,7 @@ impl SessionConfig { } pub fn enabled_addpaths(&self) - -> impl Iterator + '_ + -> impl Iterator + '_ { self.addpath_fams.enabled_addpaths() } @@ -1166,10 +1354,7 @@ impl SessionConfig { self.addpath_fams = SessionAddpaths::new() } - pub fn enable_all_addpaths(&mut self) { - self.addpath_fams = SessionAddpaths::new_all_enabled() - } - + /* pub fn inverse_addpaths(&mut self) { self.addpath_fams = self.addpath_fams.inverse(); } @@ -1177,6 +1362,7 @@ impl SessionConfig { pub fn inverse_addpath(&mut self, fam: AfiSafi) { self.addpath_fams.inverse_fam(fam); } + */ } /// Indicates whether this session is Four Octet capable. @@ -1318,178 +1504,6 @@ impl Iterator for LargeCommunityIter { } } -/// Represents the announced NLRI in a BGP UPDATE message. -#[derive(Debug)] -pub struct Nlris<'a, Octs: Octets> { - parser: Parser<'a, Octs>, - session_config: SessionConfig, - afi_safi: AfiSafi, -} - -impl<'a, Octs: Octets> Nlris<'a, Octs> { - pub fn new( - parser: Parser<'a, Octs>, - session_config: SessionConfig, - afi_safi: AfiSafi, - ) -> Nlris<'a, Octs> { - Nlris { parser, session_config, afi_safi } - } - - pub fn iter(&self) -> NlriIter<'a, Octs> { - NlriIter { - parser: self.parser, - session_config: self.session_config, - afi_safi: self.afi_safi, - } - } - - /// Returns the AfiSafi for these NLRI. - pub fn afi_safi(&self) -> AfiSafi { - self.afi_safi - } - - /// Returns the Afi for these NLRI. - pub fn afi(&self) -> Afi { - self.afi_safi.afi() - } - - /// Returns the Safi for these NLRI. - pub fn safi(&self) -> Safi { - self.afi_safi.safi() - } -} - -/// Iterator over the reachable NLRIs. -/// -/// Returns items of the enum [`Nlri`], thus both conventional and -/// BGP MultiProtocol (RFC4760) NLRIs. -/// -/// If at any point an error occurs, the iterator returns that error and fuses -/// itself, i.e. any following call to `next()` will return None. -pub struct NlriIter<'a, Octs> { - parser: Parser<'a, Octs>, - session_config: SessionConfig, - afi_safi: AfiSafi, -} - -impl<'a, Octs: Octets> NlriIter<'a, Octs> { - pub fn afi_safi(&self) -> AfiSafi { - self.afi_safi - } - - fn into_parser(self) -> Parser<'a, Octs> { - self.parser - } - - fn get_nlri(&mut self) -> Result>, ParseError> { - use AfiSafi::*; - let res = match self.afi_safi { - Ipv4Unicast | Ipv6Unicast => { - Nlri::Unicast(BasicNlri::parse( - &mut self.parser, - self.session_config, - self.afi_safi - - )?) - } - Ipv4Multicast | Ipv6Multicast => { - Nlri::Multicast(BasicNlri::parse( - &mut self.parser, - self.session_config, - self.afi_safi, - )?) - } - Ipv4MplsVpnUnicast | Ipv6MplsVpnUnicast => { - Nlri::MplsVpn(MplsVpnNlri::parse( - &mut self.parser, - self.session_config, - self.afi_safi, - )?) - }, - Ipv4MplsUnicast | Ipv6MplsUnicast => { - Nlri::Mpls(MplsNlri::parse( - &mut self.parser, - self.session_config, - self.afi_safi, - )?) - }, - L2VpnVpls => { - Nlri::Vpls(VplsNlri::parse( - &mut self.parser - )?) - }, - Ipv4FlowSpec | Ipv6FlowSpec => { - Nlri::FlowSpec(FlowSpecNlri::parse( - &mut self.parser, - self.afi_safi.afi() - )?) - }, - Ipv4RouteTarget => { - Nlri::RouteTarget(RouteTargetNlri::parse( - &mut self.parser - )?) - }, - L2VpnEvpn => { - Nlri::Evpn(EvpnNlri::parse( - &mut self.parser - )?) - }, - /* not a thing anymore since we match on AfiSafi variants instead - * of arbitrary combinations of (Afi::, Safi::) variants. - _ => { - debug!("trying to iterate over NLRI \ - for unknown AFI/SAFI combination {:?}", - self.afisafi - ); - - // As all the NLRI in the iterator are of the same AFI/SAFI - // type, we will not be able to make sense of anything in this - // blob of NLRI. We advance the parser so the next call to - // next() on this iterator will return None, and be done with - // it. - self.parser.advance(self.parser.remaining())?; - return Err(ParseError::Unsupported) - } - */ - }; - Ok(res) - } -} - -impl<'a, Octs: Octets> Iterator for NlriIter<'a, Octs> { - type Item = Result>, ParseError>; - - fn next(&mut self) -> Option { - if self.parser.remaining() == 0 { - return None; - } - match self.get_nlri() { - Ok(n) => Some(Ok(n)), - Err(e) => { - // Whenever an error occurred, e.g. because the NLRI could not - // be parsed, we return the error and 'fuse' the iterator by - // advancing the parser, ensuring the next call to `next()` - // returns a None. - self.parser.advance(self.parser.remaining()).ok()?; - Some(Err(e)) - } - } - } -} - -impl<'a, Octs: Octets> TryFrom> - for FixedNlriIter<'a, Octs, nlri::Ipv4Unicast> -{ - type Error = &'static str; - fn try_from(iter: NlriIter<'a, Octs>) -> Result { - if iter.afi_safi() == AfiSafi::Ipv4Unicast { - return Ok(FixedNlriIter::new(&mut iter.into_parser())) - } - Err("can not convert into FixedNlriIter for Ipv4Unicast") - } -} - - //--- Tests ------------------------------------------------------------------ #[cfg(test)] @@ -1504,9 +1518,10 @@ mod tests { ExtendedCommunitySubType, Tag, Wellknown, }; - use crate::bgp::message::{Message, nlri::{ - PathId, RouteDistinguisher - }}; + + use crate::bgp::nlri::afisafi::{AfiSafiNlri, Nlri, Ipv4UnicastNlri, Ipv4MulticastNlri}; + use crate::bgp::types::{PathId, RouteDistinguisher}; + use crate::bgp::message::Message; use crate::addr::Prefix; @@ -1648,7 +1663,12 @@ mod tests { let mut nlri_iter = update.announcements().unwrap(); let nlri1 = nlri_iter.next().unwrap(); - assert_eq!(nlri1.unwrap(), Nlri::unicast_from_str("10.10.10.2/32").unwrap()); + + let unwrapped = nlri1.unwrap(); + let tmp_n = Ipv4UnicastNlri::try_from(Prefix::from_str("10.10.10.2/32").unwrap()).unwrap(); + let tmp_n2 = Nlri::<&[u8]>::from(tmp_n); + + assert_eq!(unwrapped, tmp_n2); assert!(nlri_iter.next().is_none()); } @@ -1676,7 +1696,7 @@ mod tests { let mut parser = Parser::from_ref(&bytes); let update = UpdateMessage::parse( &mut parser, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); update.print_pcap(); @@ -1685,7 +1705,7 @@ mod tests { let update = UpdateMessage::parse( &mut parser, - SessionConfig::modern() + &SessionConfig::modern(), ).unwrap(); update.print_pcap(); @@ -1711,7 +1731,7 @@ mod tests { while parser.remaining() > 0 && n < MAX { if let Err(e) = UpdateMessage::<_>::parse( - &mut parser, SessionConfig::modern() + &mut parser, &SessionConfig::modern(), ) { eprintln!("failed to parse: {e}"); } @@ -1740,7 +1760,7 @@ mod tests { let update: UpdateMessage<_> = Message::from_octets( &buf, Some(SessionConfig::modern()) - ).unwrap().try_into().unwrap(); + ).unwrap().try_into().unwrap(); assert_eq!(update.total_path_attribute_len(), 27); assert_eq!(update.announcements().unwrap().count(), 2); @@ -1776,7 +1796,7 @@ mod tests { ]; let update = UpdateMessage::from_octets( &buf, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); assert_eq!(update.withdrawn_routes_len(), 0); @@ -1796,7 +1816,7 @@ mod tests { "2001:db8:ffff:3::/64", ].map(|p| Nlri::unicast_from_str(p).unwrap()); - assert!(prefixes.into_iter().eq( + assert!(prefixes.clone().into_iter().eq( update.announcements().unwrap().map(|n| n.unwrap()) )); @@ -1804,17 +1824,6 @@ mod tests { update.announcements_vec().unwrap().into_iter() )); - assert!(update.unicast_announcements().unwrap() - .map(|b| Nlri::<&[u8]>::Unicast(b.unwrap())) - .eq(prefixes) - ); - - assert!( - update.unicast_announcements_vec().unwrap().into_iter() - .map(Nlri::<&[u8]>::Unicast) - .eq(prefixes) - ); - assert!(update.find_next_hop(AfiSafi::Ipv6Multicast).is_err()); } @@ -2048,10 +2057,10 @@ mod tests { let nlri1 = upd.announcements().unwrap().next().unwrap(); assert_eq!( nlri1.unwrap(), - Nlri::<&[u8]>::Unicast(BasicNlri::with_path_id( - Prefix::from_str("198.51.100.0/25").unwrap(), - PathId::from_u32(1) - )) + Nlri::<&[u8]>::from( + Ipv4UnicastNlri::from_str("198.51.100.0/25").unwrap() + .into_addpath(PathId(1)) + ) ); assert!(upd.communities().unwrap().is_some()); @@ -2231,14 +2240,18 @@ mod tests { let sc = SessionConfig::modern(); let upd: UpdateMessage<_> = Message::from_octets(&buf, Some(sc)) .unwrap().try_into().unwrap(); - assert_eq!(upd.mp_announcements().unwrap().unwrap().afi(), Afi::Ipv4); - assert_eq!(upd.mp_announcements().unwrap().unwrap().safi(), Safi::Multicast); + assert_eq!( + upd.mp_announcements().unwrap().unwrap().afi_safi(), + AfiSafi::Ipv4Multicast + ); let prefixes = [ "198.51.100.0/26", "198.51.100.64/26", "198.51.100.128/26", "198.51.100.192/26", - ].map(|p| Nlri::<&[u8]>::Multicast(Prefix::from_str(p).unwrap().into())); + ].map(|p| Nlri::<&[u8]>::Ipv4Multicast( + Ipv4MulticastNlri::from_str(p).unwrap() + )); assert!(prefixes.into_iter().eq(upd.announcements().unwrap().map(|n| n.unwrap()))); } @@ -2255,8 +2268,10 @@ mod tests { let sc = SessionConfig::modern(); let upd: UpdateMessage<_> = Message::from_octets(&buf, Some(sc)) .unwrap().try_into().unwrap(); - assert_eq!(upd.mp_withdrawals().unwrap().unwrap().afi(), Afi::Ipv4); - assert_eq!(upd.mp_withdrawals().unwrap().unwrap().safi(), Safi::Multicast); + assert_eq!( + upd.mp_withdrawals().unwrap().unwrap().afi_safi(), + AfiSafi::Ipv4Multicast + ); assert_eq!(upd.mp_withdrawals().unwrap().iter().count(), 1); } @@ -2283,25 +2298,26 @@ mod tests { 0x01 ]; - use crate::bgp::message::nlri::EvpnRouteType; + use crate::bgp::nlri::evpn::EvpnRouteType; - let upd = UpdateMessage::from_octets(&buf, SessionConfig::modern()) + let upd = UpdateMessage::from_octets(&buf, &SessionConfig::modern()) .unwrap(); for n in upd.announcements().unwrap() { println!("{:?}", n.unwrap()); } let mut announcements = upd.announcements().unwrap(); - if let Some(Ok(Nlri::Evpn(e))) = announcements.next() { - assert_eq!( - e.route_type(), - EvpnRouteType::EthernetAutoDiscovery - ) + if let Some(Ok(Nlri::L2VpnEvpn(e))) = announcements.next() { + assert_eq!( + e.nlri().route_type(), + EvpnRouteType::EthernetAutoDiscovery + ) } else { panic!() } - if let Some(Ok(Nlri::Evpn(e))) = announcements.next() { - assert_eq!( - e.route_type(), - EvpnRouteType::MacIpAdvertisement) + if let Some(Ok(Nlri::L2VpnEvpn(e))) = announcements.next() { + assert_eq!( + e.nlri().route_type(), + EvpnRouteType::MacIpAdvertisement + ) } else { panic!() } assert!(announcements.next().is_none()); @@ -2351,17 +2367,20 @@ mod tests { //let update: UpdateMessage<_> = parse_msg(&buf); let update = UpdateMessage::from_octets( &buf, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); - assert_eq!(update.mp_announcements().unwrap().unwrap().iter().count(), 1); - assert!(update.mp_announcements().unwrap().unwrap().iter().next().unwrap().is_err()); + assert_eq!(update.mp_announcements().unwrap().unwrap().count(), 1); + assert!(update.mp_announcements().unwrap().unwrap().next().unwrap().is_err()); // We expect only the two conventional announcements here: - assert_eq!(update.unicast_announcements().unwrap().count(), 2); + //assert_eq!(update.unicast_announcements().unwrap().count(), 2); } + // this test used to return 4 NLRI: the good one and the first invalid one + // from MP, and the two valid conventional ones. + #[ignore = "we need to figure out how we deal with invalid NLRI"] #[test] fn invalid_nlri_length_in_announcements() { // botched BGP UPDATE message containing MP_REACH_NLRI path attribute, @@ -2404,7 +2423,7 @@ mod tests { let update = UpdateMessage::from_octets( &buf, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); assert!(matches!( @@ -2412,14 +2431,20 @@ mod tests { Err(ParseError::Form(..)) )); + /* assert!(matches!( update.unicast_announcements_vec(), Err(ParseError::Form(..)) )); + */ + for a in update.announcements().unwrap() { + dbg!(a.unwrap()); + } assert_eq!(update.announcements().unwrap().count(), 4); - assert_eq!(update.unicast_announcements().unwrap().count(), 4); + //assert_eq!(update.unicast_announcements().unwrap().count(), 4); + /* assert!( update.unicast_announcements().unwrap().eq( [ @@ -2430,8 +2455,10 @@ mod tests { ] ) ); + */ } + #[ignore = "does not Err anymore, we need to figure out what we want"] #[test] fn unknown_afi_safi_withdrawals() { // botched BGP UPDATE with 4 MP_UNREACH_NLRI @@ -2454,10 +2481,11 @@ mod tests { ]; assert!( - UpdateMessage::from_octets(&buf, SessionConfig::modern()).is_err() + UpdateMessage::from_octets(&buf, &SessionConfig::modern()).is_err() ); } + #[ignore = "see others wrt invalid NLRI"] #[test] fn invalid_withdrawals() { // botched BGP UPDATE with 4 MP_UNREACH_NLRI @@ -2481,7 +2509,7 @@ mod tests { ]; assert!( - UpdateMessage::from_octets(&buf, SessionConfig::modern()).is_err() + UpdateMessage::from_octets(&buf, &SessionConfig::modern()).is_err() ); /* @@ -2534,12 +2562,12 @@ mod tests { let mut parser = Parser::from_ref(&bytes); let update = UpdateMessage::parse( &mut parser, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); let update2 = UpdateMessage::from_octets( parser.peek_all(), - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); assert_eq!(update.fmt_pcap_string(), update2.fmt_pcap_string()); @@ -2564,21 +2592,21 @@ mod tests { let upd = UpdateMessage::from_octets( &raw, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); if let Ok(Some(NextHop::Unicast(a))) = upd.mp_next_hop() { assert_eq!(a, Ipv4Addr::from_str("10.7.8.8").unwrap()); } else { panic!("wrong"); } - let mut ann = upd.mp_announcements().unwrap().unwrap().iter(); - if let Some(Ok(Nlri::Mpls(n1))) = ann.next() { + let mut ann = upd.mp_announcements().unwrap().unwrap(); + if let Some(Ok(Nlri::Ipv4MplsUnicast(n1))) = ann.next() { assert_eq!( - n1.basic().prefix(), + n1.nlri().prefix(), Prefix::from_str("10.0.0.9/32").unwrap() ); assert_eq!( - n1.labels().as_ref(), + n1.nlri().labels().as_ref(), &[0x01, 0xf4, 0x01] // single label: [2012] //Labels::from(..), ); @@ -2619,7 +2647,7 @@ mod tests { let upd = UpdateMessage::from_octets( &raw, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); if let Ok(Some(NextHop::MplsVpnUnicast(rd, a))) = upd.mp_next_hop() { assert_eq!(rd, RouteDistinguisher::new(&[0; 8])); @@ -2627,19 +2655,19 @@ mod tests { } else { panic!("wrong"); } - let mut ann = upd.mp_announcements().unwrap().unwrap().iter(); - if let Some(Ok(Nlri::MplsVpn(n1))) = ann.next() { + let mut ann = upd.mp_announcements().unwrap().unwrap(); + if let Some(Ok(Nlri::Ipv6MplsVpnUnicast(n1))) = ann.next() { assert_eq!( - n1.basic().prefix(), + n1.nlri().prefix(), Prefix::from_str("fc00::1/128").unwrap() ); assert_eq!( - n1.labels().as_ref(), + n1.nlri().labels().as_ref(), &[0x00, 0x7d, 0xc1] // single label: [2012] //Labels::from([2012]), ); assert_eq!( - n1.rd(), + n1.nlri().rd(), //RouteDistinguisher::from_str("100:1".unwrap()) RouteDistinguisher::new(&[0, 0, 0, 100, 0, 0, 0, 1]) ); @@ -2670,15 +2698,16 @@ mod tests { let upd = UpdateMessage::from_octets( &raw, - SessionConfig::modern() + &SessionConfig::modern() ).unwrap(); assert_eq!( - upd.mp_announcements().unwrap().unwrap().iter().count(), + upd.mp_announcements().unwrap().unwrap().count(), 3 ); } + #[ignore = "from_octets currently does not validate the attributes"] #[test] fn invalid_mp_unreach_nlri() { let raw = vec![ @@ -2687,12 +2716,14 @@ mod tests { 2, 133, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 255, 255, 255, 254 ]; + print_pcap(&raw); assert!( - UpdateMessage::from_octets(&raw, SessionConfig::modern()).is_err() + UpdateMessage::from_octets(&raw, &SessionConfig::modern()).is_err() ); } + #[ignore = "this could/should now happen on PduParseInfo level?"] #[test] fn session_addpaths() { let mut aps = SessionAddpaths::new(); @@ -2710,12 +2741,14 @@ mod tests { assert_eq!(aps.enabled_addpaths().count(), 2); - let inv_aps = aps.inverse(); - assert_eq!(inv_aps.enabled_addpaths().count(), 16 - 2); + //let inv_aps = aps.inverse(); + //assert_eq!(inv_aps.enabled_addpaths().count(), 16 - 2); } + #[ignore = "see previous"] #[test] fn session_config_addpaths() { + /* let mut sc = SessionConfig::modern(); sc.add_addpath_rxtx(AfiSafi::Ipv4Unicast); sc.add_addpath_rxtx(AfiSafi::Ipv6MplsUnicast); @@ -2727,5 +2760,6 @@ mod tests { assert_eq!(sc.enabled_addpaths().count(), 14); sc.inverse_addpath(AfiSafi::Ipv4Unicast); assert_eq!(sc.enabled_addpaths().count(), 15); + */ } } diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 6d610e57..5efa702f 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -2,42 +2,36 @@ use std::fmt; use std::net::{IpAddr, Ipv6Addr}; use bytes::BytesMut; -use octseq::{FreezeBuilder, Octets, OctetsBuilder, OctetsFrom, OctetsInto, ShortBuf}; +use octseq::{FreezeBuilder, Octets, OctetsBuilder, OctetsFrom, ShortBuf}; use log::warn; use crate::bgp::aspath::HopPath; use crate::bgp::communities::StandardCommunity; -use crate::bgp::message::{Header, MsgType, SessionConfig, UpdateMessage}; -use crate::bgp::message::nlri::Nlri; -use crate::bgp::message::update::{Afi, Safi, AfiSafi, NextHop}; +use crate::bgp::message::{Header, MsgType, UpdateMessage, SessionConfig}; +use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiParse, NlriCompose}; +use crate::bgp::path_attributes::{Attribute, PaMap, PathAttributeType}; +use crate::bgp::types::{AfiSafi, NextHop}; use crate::util::parser::ParseError; -//use rotonda_fsm::bgp::session::AgreedConfig; - -use crate::bgp::path_attributes::{ - PaMap, PathAttributeType -}; - //------------ UpdateBuilder ------------------------------------------------- #[derive(Debug)] -pub struct UpdateBuilder { +pub struct UpdateBuilder { target: Target, //config: AgreedConfig, //FIXME this lives in rotonda_fsm, but that //depends on routecore. Sounds like a depedency hell. - announcements: Vec>>, - withdrawals: Vec>>, - addpath_enabled: Option, // for conventional nlri (unicast v4) - // data structure that stores all path attributes, by default not the NLRI - // (but it could). + announcements: Option>, + withdrawals: Option>, attributes: PaMap, } -impl UpdateBuilder { +impl UpdateBuilder { const MAX_PDU: usize = 4096; // XXX should come from NegotiatedConfig } -impl UpdateBuilder -where Target: octseq::Truncate +impl UpdateBuilder +where + A: AfiSafiNlri + NlriCompose, + Target: OctetsBuilder + octseq::Truncate, { pub fn from_target(mut target: Target) -> Result { @@ -49,29 +43,39 @@ where Target: octseq::Truncate Ok(UpdateBuilder { target, - announcements: Vec::new(), - withdrawals: Vec::new(), - addpath_enabled: None, + announcements: None, + withdrawals: None, attributes: PaMap::empty(), }) } pub fn from_attributes_builder( attributes: PaMap, - ) -> UpdateBuilder> { - let mut res = UpdateBuilder::>::new_vec(); + ) -> UpdateBuilder, A> { + let mut res = UpdateBuilder::, A>::new_vec(); res.attributes = attributes; res } + pub fn from_workshop( + ws: crate::bgp::workshop::route::RouteWorkshop + ) -> UpdateBuilder, A> { + + let mut res = Self::from_attributes_builder(ws.attributes().clone()); + let nlri = ws.nlri(); + let _ = res.add_announcement(nlri.clone()); + + res + } + /// Creates an UpdateBuilder with Path Attributes from an UpdateMessage /// /// pub fn from_update_message<'a, Octs: 'a + Octets>( pdu: &'a UpdateMessage, - _session_config: SessionConfig, + _session_config: &SessionConfig, target: Target - ) -> Result, ComposeError> + ) -> Result, ComposeError> where Vec: OctetsFrom> { @@ -87,101 +91,41 @@ where Target: octseq::Truncate //--- Withdrawals - pub fn add_withdrawal( - &mut self, - withdrawal: &Nlri - ) -> Result<(), ComposeError> - where - Vec: OctetsFrom, - T: Octets, + pub fn add_withdrawal(&mut self, withdrawal: A) + -> Result<(), ComposeError> { - match *withdrawal { - Nlri::Unicast(b) => { - if b.is_v4() { - if let Some(addpath_enabled) = self.addpath_enabled { - if addpath_enabled != b.is_addpath() { - return Err(ComposeError::IllegalCombination) - } - } else { - self.addpath_enabled = Some(b.is_addpath()); - } - - self.withdrawals.push( - <&Nlri as OctetsInto>>>::try_octets_into(withdrawal).map_err(|_| ComposeError::todo() )? - ); - } else { - // Nlri::Unicast only holds IPv4 and IPv6, so this must be - // IPv6. - - // Check if we have an attribute in self.attributes - // Again, like with Communities, we cant rely on - // entry().or_insert(), because that does not do a max pdu - // length check and it does not allow us to error out. - - if !self - .attributes - .contains::() - { - self.attributes.set( - MpUnreachNlriBuilder::new( - Afi::Ipv6, - Safi::Unicast, - b.is_addpath(), - ) - ); - } - - let mut builder = self - .attributes - .take::() - .unwrap(); // Just added it, so we know it is there. - - if !builder.valid_combination( - Afi::Ipv6, Safi::Unicast, b.is_addpath() - ) { - // We are already constructing a - // MP_UNREACH_NLRI but for a different - // AFI,SAFI than the prefix in `withdrawal`, - // or we are mixing addpath with non-addpath. - return Err(ComposeError::IllegalCombination); - } + if let Some(ref mut w) = self.withdrawals.as_mut() { + w.add_withdrawal(withdrawal); + } else { + self.withdrawals = Some(MpUnreachNlriBuilder::from(withdrawal)); + } - builder.add_withdrawal(withdrawal); - self.attributes.set::(builder); - } - } - _ => todo!(), // TODO - }; Ok(()) } + pub fn withdrawals_from_iter(&mut self, withdrawals: I) -> Result<(), ComposeError> - where I: IntoIterator>> + where I: IntoIterator { - withdrawals.into_iter().try_for_each(|w| self.add_withdrawal(&w) )?; + withdrawals.into_iter().try_for_each(|w| self.add_withdrawal(w) )?; Ok(()) } + - pub fn append_withdrawals(&mut self, withdrawals: &mut Vec>) + + pub fn append_withdrawals(&mut self, withdrawals: Vec) -> Result<(), ComposeError> - where - Vec: OctetsFrom, - T: Octets, { - for (idx, w) in withdrawals.iter().enumerate() { - if let Err(e) = self.add_withdrawal(w) { - withdrawals.drain(..idx); - return Err(e); - } + for w in withdrawals { + self.add_withdrawal(w)?; } + Ok(()) } //--- Path Attributes - - pub fn set_aspath(&mut self , aspath: HopPath) -> Result<(), ComposeError> { @@ -202,123 +146,107 @@ where Target: octseq::Truncate Ok(()) } - pub fn set_mp_nexthop( - &mut self, - nexthop: NextHop, - ) -> Result<(), ComposeError> { - let mut builder = self - .attributes - .take::() - .ok_or(ComposeError::IllegalCombination)?; - builder.set_nexthop(nexthop)?; - self.attributes.set(builder); - Ok(()) + pub fn set_nexthop(&mut self, nexthop: NextHop) + -> Result<(), ComposeError> + { + self.set_mp_nexthop(nexthop) } - pub fn set_nexthop_unicast(&mut self, addr: IpAddr) -> Result<(), ComposeError> { - match addr { - IpAddr::V4(a) => { - self.attributes.set(crate::bgp::types::ConventionalNextHop(a)); - Ok(()) - }, - IpAddr::V6(_) => self.set_mp_nexthop(NextHop::Unicast(addr)) + pub fn set_mp_nexthop(&mut self, nexthop: NextHop) + -> Result<(), ComposeError> + { + if let Some(ref mut a) = self.announcements.as_mut() { + a.set_nexthop(nexthop)?; + } else { + self.announcements = + Some(MpReachNlriBuilder::for_nexthop(nexthop)) + ; } + + Ok(()) } pub fn set_nexthop_ll_addr(&mut self, addr: Ipv6Addr) -> Result<(), ComposeError> { - // We could/should check for addr.is_unicast_link_local() once that - // lands in stable. - if let Some(mut builder) = - self.attributes.take::() - { - match builder.get_nexthop() { - NextHop::Unicast(a) if a.is_ipv6() => {} - NextHop::Ipv6LL(_, _) => {} - _ => return Err(ComposeError::IllegalCombination), - } - - builder.set_nexthop_ll_addr(addr); - self.attributes.set(builder); + if let Some(ref mut a) = self.announcements.as_mut() { + a.set_nexthop_ll_addr(addr); } else { - self.attributes.set( - MpReachNlriBuilder::new( - Afi::Ipv6, - Safi::Unicast, - NextHop::Ipv6LL(0.into(), addr), - false, - ) - ); + let nexthop = NextHop::Ipv6LL(Ipv6Addr::from(0), addr); + self.announcements = + Some(MpReachNlriBuilder::for_nexthop(nexthop)) + ; } Ok(()) } - + //--- Announcements - pub fn add_announcement(&mut self, announcement: &Nlri) + pub fn add_announcement(&mut self, announcement: A) -> Result<(), ComposeError> - where - Vec: OctetsFrom, - T: Octets, { - match announcement { - Nlri::Unicast(b) if b.is_v4() => { - // These go in the conventional NLRI part at the end of the - // PDU. - if let Some(addpath_enabled) = self.addpath_enabled { - if addpath_enabled != b.is_addpath() { - return Err(ComposeError::IllegalCombination) - } - } else { - self.addpath_enabled = Some(b.is_addpath()); - } - - self.announcements.push( - <&Nlri as OctetsInto>>>::try_octets_into(announcement).map_err(|_| ComposeError::todo() ).unwrap() - ); - } - n => { - if !self.attributes.contains::() { - self.attributes.set( - MpReachNlriBuilder::new_for_nlri(n) - ); - } - - let mut builder = self - .attributes - .take::() - .unwrap(); // Just added it, so we know it is there. - - if !builder.valid_combination(n) { - // We are already constructing a - // MP_UNREACH_NLRI but for a different - // AFI,SAFI than the prefix in `announcement`, - // or we are mixing addpath with non-addpath. - self.attributes.set(builder); - return Err(ComposeError::IllegalCombination); - } - - builder.add_announcement(announcement); - self.attributes.set(builder); - } + if let Some(ref mut a) = self.announcements.as_mut() { + a.add_announcement(announcement); + } else { + self.announcements = + Some(MpReachNlriBuilder::for_nlri(announcement)) + ; } + Ok(()) + } + pub fn announcements_from_iter(&mut self, announcements: I) + -> Result<(), ComposeError> + where I: IntoIterator + { + announcements.into_iter().try_for_each(|w| self.add_announcement(w))?; Ok(()) } - pub fn announcements_from_iter( - &mut self, announcements: I - ) -> Result<(), ComposeError> + pub fn add_announcements_from_pdu<'a, Octs, O>( + &mut self, + source: &'a UpdateMessage, + _session_config: &SessionConfig + ) where - I: IntoIterator>, - Vec: OctetsFrom, - T: Octets, + A: AfiSafiNlri + NlriCompose + AfiSafiParse<'a, O, Octs, Output = A>, + Octs: Octets = O>, + O: Octets, { - announcements.into_iter().try_for_each(|w| self.add_announcement(&w) )?; - Ok(()) + if source.announcements().is_ok_and(|i| i.count() == 0) { + return; + } + if let Some(ref mut a) = self.announcements.as_mut() { + a.add_announcements_from_pdu::(source, _session_config); + } else { + let mut a = MpReachNlriBuilder::new(); + a.add_announcements_from_pdu::(source, _session_config); + self.announcements = Some(a); + } + } + + pub fn add_withdrawals_from_pdu<'a, Octs, O>( + &mut self, + source: &'a UpdateMessage, + _session_config: &SessionConfig + ) + where + A: AfiSafiNlri + NlriCompose + AfiSafiParse<'a, O, Octs, Output = A>, + Octs: Octets = O>, + O: Octets, + { + if source.withdrawals().is_ok_and(|i| i.count() == 0) { + return; + } + if let Some(ref mut w) = self.withdrawals.as_mut() { + w.add_withdrawals_from_pdu::(source, _session_config); + } else { + let mut w = MpUnreachNlriBuilder::new(); + w.add_withdrawals_from_pdu::(source, _session_config); + self.withdrawals = Some(w); + } } //--- Standard communities @@ -340,24 +268,28 @@ where Target: octseq::Truncate } } -impl UpdateBuilder +impl UpdateBuilder +where + A: Clone + AfiSafiNlri + NlriCompose { - pub fn into_message(self) -> + pub fn into_message(self, session_config: &SessionConfig) -> Result::Octets>, ComposeError> where Target: OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate, - ::Octets: Octets, + ::Octets: Octets { self.is_valid()?; - // FIXME this SessionConfig::modern should come from self Ok(UpdateMessage::from_octets( - self.finish().map_err(|_| ShortBuf)?, SessionConfig::modern() + self.finish(session_config).map_err(|_| ShortBuf)?, session_config, )?) } pub fn attributes(&self) -> &PaMap { &self.attributes } + + // What is this suppose to do? + // It basically creates a copy of the PaMap, not an UpdateBuilder? pub fn from_map(&self, map: &mut PaMap) -> PaMap { let mut pab = PaMap::empty(); pab.merge_upsert(map); @@ -367,61 +299,72 @@ impl UpdateBuilder // Check whether the combination of NLRI and attributes would produce a // valid UPDATE pdu. fn is_valid(&self) -> Result<(), ComposeError> { - // If we have builders for MP_(UN)REACH_NLRI, they should carry - // prefixes. - - if let Some(pa) = self.attributes.get::() { - if pa.is_empty() { - return Err(ComposeError::EmptyMpReachNlri); - } + // If we have a builder for MP_REACH_NLRI, it should carry prefixes. + if self.announcements.as_ref() + .is_some_and(|b| b.announcements.is_empty()) + { + return Err(ComposeError::EmptyMpReachNlri); } - if let Some(pa) = self.attributes.get::() { - // FIXME an empty MP_UNREACH_NLRI can be valid when signaling - // EoR, but then it has to be the only path attribute. - if pa.is_empty() { - return Err(ComposeError::EmptyMpUnreachNlri); - } + if self.withdrawals.as_ref().is_some_and(|b| b.is_empty()) && + ( + self.announcements.as_ref().is_some_and(|b| !b.is_empty()) + || !self.attributes.is_empty() + ) + { + // Empty MP_UNREACH but other announcements/attributes + // present, so this is not a valid EoR + return Err(ComposeError::EmptyMpUnreachNlri); } Ok(()) } - fn calculate_pdu_length(&self) -> usize { + fn calculate_pdu_length(&self, _session_config: &SessionConfig) -> usize { // Marker, length and type. let mut res: usize = 16 + 2 + 1; + // TODO we must check where the ipv4 unicast withdrawals/announcements + // for this session should go: MP attributes or conventional PDU + // sections? + // For now, we do everything in MP. + + // Withdrawals, 2 bytes for length + N bytes for NLRI: - res += 2 + self.withdrawals.iter() - .fold(0, |sum, w| sum + w.compose_len()); + res += 2; // + self.withdrawals.iter() .fold(0, |sum, w| sum + w.compose_len()); // Path attributes, 2 bytes for length + N bytes for attributes: res += 2 + self.attributes.bytes_len(); - // Announcements, no length bytes: - res += self.announcements.iter() - .fold(0, |sum, a| sum + a.compose_len()); + // Manually add in the MP attributes: + if let Some(mp_reach) = &self.announcements { + res += mp_reach.compose_len(); + } + + if let Some(mp_unreach) = &self.withdrawals { + res += mp_unreach.compose_len(); + } res } - fn larger_than(&self, max: usize) -> bool { + fn larger_than(&self, max: usize, session_config: &SessionConfig) -> bool { // TODO add more 'quick returns' here, e.g. for MpUnreachNlri or // conventional withdrawals/announcements. - if let Some(b) = self.attributes.get::() { + if let Some(b) = &self.announcements { if b.announcements.len() * 2 > max { return true; } } - self.calculate_pdu_length() > max + self.calculate_pdu_length(session_config) > max } /// Compose the PDU, returns the builder if it exceeds the max PDU size. /// /// Note that `UpdateBuilder` implements `IntoIterator`, which is perhaps /// more appropriate for many use cases. - pub fn take_message(mut self) -> ( + pub fn take_message(mut self, session_config: &SessionConfig) -> ( Result::Octets>, ComposeError>, Option ) @@ -429,8 +372,8 @@ impl UpdateBuilder Target: Clone + OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate, ::Octets: Octets { - if !self.larger_than(Self::MAX_PDU) { - return (self.into_message(), None) + if !self.larger_than(Self::MAX_PDU, session_config) { + return (self.into_message(session_config), None) } @@ -455,6 +398,13 @@ impl UpdateBuilder // First naive approach: we split off at most 450 NLRI. In the extreme // case of AddPathed /32's this would still fit in a 4096 PDU. + // TODO we need to see if, for this session, there is anything that + // should go into the conventional withdrawals. + // Note that because we are making the builders generic over (a + // single) AfiSafiNlri, these parts are probably prone to change + // anyway. + + /* if !self.withdrawals.is_empty() { //let withdrawal_len = self.withdrawals.iter() // .fold(0, |sum, w| sum + w.compose_len()); @@ -467,12 +417,13 @@ impl UpdateBuilder return (builder.into_message(), Some(self)); } + */ // Scenario 2: many withdrawals in MP_UNREACH_NLRI // At this point, we have no conventional withdrawals anymore. - let maybe_pdu = if let Some(mut unreach_builder) = - self.attributes.take::() + let maybe_pdu = if let Some(ref mut unreach_builder) = + self.withdrawals { let mut split_at = 0; if !unreach_builder.withdrawals.is_empty() { @@ -487,29 +438,32 @@ impl UpdateBuilder } let this_batch = unreach_builder.split(split_at); - self.attributes.set(unreach_builder); let mut builder = - Self::from_target(self.target.clone()).unwrap(); - builder.attributes.set(this_batch); + UpdateBuilder::from_target(self.target.clone()).unwrap(); + builder.withdrawals = Some(this_batch); - Some(builder.into_message()) + Some(builder.into_message(session_config)) } else { None } } else { None }; + // Bit of a clumsy workaround as we can not return Some(self) from // within the if let ... self.attributes.get_mut above if let Some(pdu) = maybe_pdu { return (pdu, Some(self)) } + // TODO: like with conventional withdrawals, handle this case for + // sessions where ipv4 unicast does not go into MP attributes. // Scenario 3: many conventional announcements // Similar but different to the scenario of many withdrawals, as // announcements are tied to the attributes. At this point, we know we // have no conventional withdrawals left, and no MP_UNREACH_NLRI. + /* if !self.announcements.is_empty() { let split_at = std::cmp::min(self.announcements.len() / 2, 450); let this_batch = self.announcements.drain(..split_at); @@ -520,6 +474,7 @@ impl UpdateBuilder return (builder.into_message(), Some(self)); } + */ // Scenario 4: many MP_REACH_NLRI announcements // This is tricky: we need to split the MP_REACH_NLRI attribute, and @@ -531,8 +486,8 @@ impl UpdateBuilder // sets. The first PDUs take longer to construct than the later ones. // Flamegraph currently hints at the fn split on MpReachNlriBuilder. - let maybe_pdu = if let Some(mut reach_builder) = - self.attributes.remove::() + let maybe_pdu = if let Some(ref mut reach_builder) = + self.announcements { let mut split_at = 0; @@ -555,12 +510,13 @@ impl UpdateBuilder } let this_batch = reach_builder.split(split_at); - let mut builder = Self::from_target(self.target.clone()).unwrap(); + let mut builder = Self::from_target( + self.target.clone() + ).unwrap(); builder.attributes = self.attributes.clone(); - self.attributes.set(reach_builder); - builder.attributes.set(this_batch); + builder.announcements = Some(this_batch); - Some(builder.into_message()) + Some(builder.into_message(session_config)) } else { None } @@ -579,7 +535,7 @@ impl UpdateBuilder // those without altering the information that the user intends to // convey. So, we error out. - let pdu_len = self.calculate_pdu_length(); + let pdu_len = self.calculate_pdu_length(session_config); (Err(ComposeError::PduTooLarge(pdu_len)), None) } @@ -589,7 +545,7 @@ impl UpdateBuilder /// /// Note that `UpdateBuilder` implements `IntoIterator`, which is perhaps /// more appropriate for many use cases. - pub fn into_messages(self) -> Result< + pub fn into_messages(self, session_config: &SessionConfig) -> Result< Vec::Octets>>, ComposeError > @@ -603,7 +559,9 @@ impl UpdateBuilder if remainder.is_none() { return Ok(res); } - let (pdu, new_remainder) = remainder.take().unwrap().take_message(); + let (pdu, new_remainder) = + remainder.take().unwrap().take_message(session_config) + ; match pdu { Ok(pdu) => { res.push(pdu); @@ -617,17 +575,33 @@ impl UpdateBuilder } } - fn finish(mut self) + pub fn into_pdu_iter(self, session_config: &SessionConfig) + -> PduIterator<'_, Target, A> + { + PduIterator { builder: Some(self), session_config } + } + + fn finish(mut self, session_config: &SessionConfig) -> Result<::Octets, Target::AppendError> where - Target: OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate + Target: OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate, + ::Octets: Octets { - let total_pdu_len = self.calculate_pdu_length(); + let total_pdu_len = self.calculate_pdu_length(session_config); let mut header = Header::for_slice_mut(self.target.as_mut()); header.set_length(u16::try_from(total_pdu_len).unwrap()); // `withdrawals_len` is checked to be <= 4096 or <= 65535 // so it will always fit in a u16. + // + // TODO handle the case where ipv4 unicast does not go into MP. + // For now, we put everything in MP_UNREACH_NLRI, leaving the + // conventional withdrawals section empty, i.e. length 0 + + self.target.append_slice(&0_u16.to_be_bytes())?; + + // + /* let withdrawals_len = self.withdrawals.iter() .fold(0, |sum, w| sum + w.compose_len()); self.target.append_slice( @@ -642,27 +616,48 @@ impl UpdateBuilder _ => todo!(), } } + */ + // TODO we need checks for things like the total attributes length. + // These happened in other places (in cumbersome ways), but are now + // gone. + let mut attributes_len = self.attributes.bytes_len(); - // We can do these unwraps because of the checks in the add/append - // methods. - // attributes_len` is checked to be <= 4096 or <= 65535 - // so it will always fit in a u16. - let attributes_len = self.attributes.bytes_len(); + if let Some(ref mp_reach_builder) = self.announcements { + attributes_len += mp_reach_builder.compose_len(); + } + + if let Some(ref mp_unreach_builder) = self.withdrawals { + attributes_len += mp_unreach_builder.compose_len(); + } + let _ = self.target.append_slice( &u16::try_from(attributes_len).unwrap().to_be_bytes() ); + if let Some(mp_reach_builder) = self.announcements { + mp_reach_builder.compose(&mut self.target)?; + } + + if let Some(mp_unreach_builder) = self.withdrawals { + mp_unreach_builder.compose(&mut self.target)?; + } + self.attributes .attributes() .iter() .try_for_each(|(_tc, pa)| pa.compose(&mut self.target))?; + // XXX Here, in the conventional NLRI field at the end of the PDU, we // write IPv4 Unicast announcements. But what if we have agreed to do // MP for v4/unicast, should these announcements go in the // MP_REACH_NLRI attribute then instead? + // + // TODO see note at conventional withdrawals. For now, everything goes + // into MP. + /* for a in &self.announcements { match a { Nlri::Unicast(b) if b.is_v4() => { @@ -671,17 +666,20 @@ impl UpdateBuilder _ => unreachable!(), } } + */ Ok(self.target.freeze()) } } -pub struct PduIterator { - builder: Option> +pub struct PduIterator<'a, Target, A> { + builder: Option>, + session_config: &'a SessionConfig, } -impl Iterator for PduIterator +impl<'a, Target, A> Iterator for PduIterator<'a, Target, A> where + A: AfiSafiNlri + NlriCompose, Target: Clone + OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate, ::Octets: Octets { @@ -692,36 +690,45 @@ where fn next(&mut self) -> Option { self.builder.as_ref()?; - let (res, remainder) = self.builder.take().unwrap().take_message(); + let (res, remainder) = + self.builder.take().unwrap().take_message(self.session_config) + ; self.builder = remainder; Some(res) } } -impl IntoIterator for UpdateBuilder + +// XXX impl IntoIterator seems untrivial because we need to squeeze in a +// lifetime for the &SessionConfig, which the trait does not facilitate. +// The fn into_pdu_iterator()->PduIterator on UpdateBuilder provides +// similar functionality for now. +/* +impl IntoIterator for UpdateBuilder where Target: Clone + OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate, - ::Octets: Octets + ::Octets: Octets, + for<'a> Self: 'a { type Item = Result< UpdateMessage<::Octets>, ComposeError >; - type IntoIter = PduIterator; + type IntoIter = PduIterator<'a, Target, A>; fn into_iter(self) -> Self::IntoIter { PduIterator { builder: Some(self) } } - } +*/ -impl UpdateBuilder> { +impl UpdateBuilder, A> { pub fn new_vec() -> Self { - Self::from_target(Vec::with_capacity(23)).unwrap() + UpdateBuilder::from_target(Vec::with_capacity(23)).unwrap() } } -impl UpdateBuilder { +impl UpdateBuilder { pub fn new_bytes() -> Self { Self::from_target(BytesMut::new()).unwrap() } @@ -745,16 +752,144 @@ impl UpdateBuilder { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct MpReachNlriBuilder { - announcements: Vec>>, - len: usize, // size of value, excluding path attribute flags+type_code+len - extended: bool, - afi: Afi, - safi: Safi, +pub struct MpReachNlriBuilder { + announcements: Vec, nexthop: NextHop, - addpath_enabled: bool, } +impl MpReachNlriBuilder { + pub fn add_announcements_from_pdu<'a, Octs, O>( + &mut self, + source: &'a UpdateMessage, + _session_config: &SessionConfig + ) + where + A: AfiSafiNlri + NlriCompose + AfiSafiParse<'a, O, Octs, Output = A>, + Octs: Octets = O>, + O: Octets, + { + if let Ok(Some(iter)) = source.typed_announcements::<_, A>() { + for a in iter { + self.add_announcement(a.unwrap()); + } + } + } +} + +impl MpReachNlriBuilder { + pub fn new() -> Self { + Self { + announcements: Vec::new(), + nexthop: NextHop::new(A::afi_safi()) + } + } + + pub(crate) fn is_empty(&self) -> bool { + self.announcements.is_empty() + } + + pub fn for_nlri(nlri: A) -> Self { + Self { + announcements: vec![nlri], + nexthop: NextHop::new(A::afi_safi()) + } + } + + pub fn for_nexthop(nexthop: NextHop) -> Self { + Self { + announcements: Vec::new(), + nexthop, + } + } + + pub fn for_nlri_and_nexthop(nlri: A, nexthop: NextHop) -> Self { + Self { + announcements: vec![nlri], + nexthop, + } + } + + + pub fn afi_safi(&self) -> AfiSafi { + A::afi_safi() + } + + pub fn add_announcement(&mut self, nlri: A) { + self.announcements.push(nlri); + } + + pub fn set_nexthop(&mut self, nexthop: NextHop) -> Result<(), ComposeError> { + + // TODO check whether nexthop is valid for the AfiSafi this builder is + // generic over + //if !::nh_valid(&nexthop) { + //{ + // return Err(ComposeError::IllegalCombination); + //} + + self.nexthop = nexthop; + Ok(()) + } + + pub fn set_nexthop_ll_addr(&mut self, addr: Ipv6Addr) { + match self.nexthop { + NextHop::Unicast(IpAddr::V6(a)) => { + self.nexthop = NextHop::Ipv6LL(a, addr); + } + NextHop::Ipv6LL(a, _ll) => { + self.nexthop = NextHop::Ipv6LL(a, addr); + } + _ => unreachable!() + } + } + + + pub(crate) fn get_nexthop(&self) -> &NextHop { + &self.nexthop + } + + //--- Composing related + + pub(crate) fn split(&mut self, n: usize) -> Self { + let this_batch = self.announcements.drain(..n).collect(); + MpReachNlriBuilder { + announcements: this_batch, + ..*self + } + } + + pub fn value_len(&self) -> usize { + 2 + 1 + 1 // afi, safi + 1 reserved byte + + self.nexthop.compose_len() + + self.announcements.iter().fold(0, |sum, w| sum + w.compose_len()) + } + + pub fn compose_value( + &self, + target: &mut Target + ) -> Result<(), Target::AppendError> + { + target.append_slice(&A::afi_safi().as_bytes())?; + self.nexthop.compose(target)?; + + // Reserved byte: + target.append_slice(&[0x00])?; + + for a in &self.announcements { + a.compose(target)? + } + + Ok(()) + } +} + +impl Default for MpReachNlriBuilder { + fn default() -> Self { + Self::new() + } +} + +/* impl MpReachNlriBuilder { // MP_REACH_NLRI and MP_UNREACH_NLRI can only occur once (like any path // attribute), and can carry only a single tuple of (AFI, SAFI). @@ -769,8 +904,7 @@ impl MpReachNlriBuilder { // byte (1) and then space for at least an IPv6 /48 announcement (7) pub(crate) fn new( - afi: Afi, - safi: Safi, + afisafi: AfiSafi, nexthop: NextHop, addpath_enabled: bool, ) -> Self { @@ -779,8 +913,7 @@ impl MpReachNlriBuilder { // 3 bytes for AFI+SAFI, nexthop len, reserved byte len: 3 + nexthop.compose_len() + 1, extended: false, - afi, - safi, + afisafi, nexthop, addpath_enabled } @@ -798,10 +931,9 @@ impl MpReachNlriBuilder { where T: Octets, Vec: OctetsFrom { - let (afi, safi) = nlri.afi_safi().split(); let addpath_enabled = nlri.is_addpath(); let nexthop = NextHop::new(nlri.afi_safi()); - Self::new(afi, safi, nexthop, addpath_enabled) + Self::new(nlri.afi_safi(), nexthop, addpath_enabled) } pub(crate) fn value_len(&self) -> usize { @@ -823,9 +955,7 @@ impl MpReachNlriBuilder { } pub(crate) fn set_nlri(&mut self, nlri: Nlri>) -> Result<(), ComposeError> { - let (afi, safi) = nlri.afi_safi().split(); - self.afi = afi; - self.safi = safi; + self.afisafi = nlri.afi_safi(); self.addpath_enabled = nlri.is_addpath(); self.announcements = vec![nlri]; Ok(()) @@ -861,7 +991,7 @@ impl MpReachNlriBuilder { fn valid_combination( &self, nlri: &Nlri ) -> bool { - AfiSafi::try_from((self.afi, self.safi)) == Ok(nlri.afi_safi()) + self.afisafi == nlri.afi_safi() && (self.announcements.is_empty() || self.addpath_enabled == nlri.is_addpath()) } @@ -886,8 +1016,9 @@ impl MpReachNlriBuilder { target: &mut Target ) -> Result<(), Target::AppendError> { - target.append_slice(&u16::from(self.afi).to_be_bytes())?; - target.append_slice(&[self.safi.into()])?; + //target.append_slice(&u16::from(self.afi).to_be_bytes())?; + //target.append_slice(&[self.safi.into()])?; + target.append_slice(&self.afisafi.as_bytes())?; self.nexthop.compose(target)?; // Reserved byte: @@ -916,6 +1047,7 @@ impl MpReachNlriBuilder { } } +*/ // **NB:** this is bgp::message::update::NextHop impl NextHop { @@ -928,7 +1060,7 @@ impl NextHop { NextHop::MplsVpnUnicast(_rd, IpAddr::V4(_)) => 8 + 4, NextHop::MplsVpnUnicast(_rd, IpAddr::V6(_)) => 8 + 16, NextHop::Empty => 0, // FlowSpec - NextHop::Unimplemented(_afi, _safi) => { + NextHop::Unimplemented(_afisafi) => { warn!( "unexpected compose_len called on NextHop::Unimplemented \ returning usize::MAX, this will cause failure." @@ -960,7 +1092,7 @@ impl NextHop { target.append_slice(&a.octets())?; } NextHop::Empty => { }, - NextHop::Unimplemented(_afi, _safi) => todo!(), + NextHop::Unimplemented(_afisafi) => todo!(), } Ok(()) @@ -982,22 +1114,44 @@ impl NextHop { // add_withdrawal should check whether the remote side is able to receive // path ids if the Nlri passed to add_withdrawal contains Some(PathId). // -#[derive(Clone, Debug, Eq, Hash, PartialEq)] + +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct MpUnreachNlriBuilder { - withdrawals: Vec>>, - afi: Afi, - safi: Safi, - addpath_enabled: bool, +pub struct MpUnreachNlriBuilder { + withdrawals: Vec, } -impl MpUnreachNlriBuilder { - pub(crate) fn new(afi: Afi, safi: Safi, addpath_enabled: bool) -> Self { - MpUnreachNlriBuilder { - withdrawals: vec![], - afi, - safi, - addpath_enabled +impl MpUnreachNlriBuilder +where +{ + pub fn add_withdrawals_from_pdu<'a, Octs, O>( + &mut self, + source: &'a UpdateMessage, + _session_config: &SessionConfig + ) + where + A: AfiSafiNlri + NlriCompose + AfiSafiParse<'a, O, Octs, Output = A>, + Octs: Octets = O>, + O: Octets, + { + if let Ok(Some(iter)) = source.typed_withdrawals::<_, A>() { + for w in iter { + self.add_withdrawal(w.unwrap()); + } + } + } +} + +impl MpUnreachNlriBuilder { + pub fn new() -> Self { + Self { + withdrawals: Vec::new(), + } + } + + pub fn from(withdrawal: A) -> Self { + Self { + withdrawals: vec![withdrawal], } } @@ -1005,7 +1159,6 @@ impl MpUnreachNlriBuilder { let this_batch = self.withdrawals.drain(..n).collect(); MpUnreachNlriBuilder { withdrawals: this_batch, - ..*self } } @@ -1017,45 +1170,17 @@ impl MpUnreachNlriBuilder { self.withdrawals.is_empty() } - pub(crate) fn valid_combination( - &self, afi: Afi, safi: Safi, is_addpath: bool - ) -> bool { - self.afi == afi - && self.safi == safi - && (self.withdrawals.is_empty() - || self.addpath_enabled == is_addpath) - } - - pub(crate) fn add_withdrawal(&mut self, withdrawal: &Nlri) - where - Vec: OctetsFrom, - T: Octets, - - { - self.withdrawals.push( - <&Nlri as OctetsInto>>>::try_octets_into(withdrawal).map_err(|_| ComposeError::todo() ).unwrap() - ); + pub fn add_withdrawal(&mut self, nlri: A) { + self.withdrawals.push(nlri); } pub(crate) fn compose_value(&self, target: &mut Target) -> Result<(), Target::AppendError> { - target.append_slice(&u16::from(self.afi).to_be_bytes())?; - target.append_slice(&[self.safi.into()])?; + target.append_slice(&A::afi_safi().as_bytes())?; for w in &self.withdrawals { - match w { - Nlri::Unicast(b) if b.is_v4() => { - unreachable!() - } - Nlri::Unicast(b) | Nlri::Multicast(b) => b.compose(target)?, - //Nlri::Mpls(m) => m.compose(target)?, - //Nlri::MplsVpn(m) => m.compose(target)?, - //Nlri::Vpls(v) => v.compose(target)?, - Nlri::FlowSpec(f) => f.compose(target)?, - //Nlri::RouteTarget(r) => r.compose(target)?, - _ => todo!() - } + w.compose(target)?; } Ok(()) @@ -1141,6 +1266,7 @@ pub enum ComposeError{ } impl ComposeError { + #[allow(dead_code)] fn todo() -> Self { ComposeError::Todo } @@ -1207,18 +1333,28 @@ impl fmt::Display for ComposeError { #[cfg(test)] mod tests { - use std::collections::BTreeSet; - use std::net::Ipv4Addr; + //use std::collections::BTreeSet; + use std::fs::File; + use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; + use memmap2::Mmap; use octseq::Parser; - use crate::bgp::path_attributes::AttributeHeader; - use crate::{addr::Prefix, bgp::message::update::OriginType}; + + //use crate::bgp::path_attributes::AttributeHeader; + use crate::addr::Prefix; use crate::asn::Asn; - //use crate::bgp::communities::Wellknown; - use crate::bgp::message::nlri::BasicNlri; - use crate::bgp::message::update::AfiSafi; + use crate::bgp::aspath::HopPath; + use crate::bgp::communities::{StandardCommunity, Tag}; + use crate::bgp::nlri::afisafi::{ + Ipv4UnicastNlri, + Ipv4MulticastNlri, + Ipv6UnicastNlri, + Ipv4FlowSpecNlri, + }; + use crate::bgp::types::{AfiSafi, OriginType, PathId}; + use super::*; fn print_pcap>(buf: T) { @@ -1234,30 +1370,30 @@ mod tests { fn empty_nlri_iterators() { let mut builder = UpdateBuilder::new_vec(); builder.add_withdrawal( - &Nlri::unicast_from_str("2001:db8::/32").unwrap() + Ipv6UnicastNlri::from_str("2001:db8::/32").unwrap() ).unwrap(); - let msg = builder.into_message().unwrap(); + let msg = builder.into_message(&SessionConfig::modern()).unwrap(); print_pcap(msg.as_ref()); assert_eq!(msg.withdrawals().unwrap().count(), 1); let mut builder2 = UpdateBuilder::new_vec(); builder2.add_withdrawal( - &Nlri::unicast_from_str("10.0.0.0/8").unwrap() + Ipv4UnicastNlri::from_str("10.0.0.0/8").unwrap() ).unwrap(); - let msg2 = builder2.into_message().unwrap(); + let msg2 = builder2.into_message(&SessionConfig::modern()).unwrap(); print_pcap(msg2.as_ref()); assert_eq!(msg2.withdrawals().unwrap().count(), 1); } #[test] fn build_empty() { - let builder = UpdateBuilder::new_vec(); - let msg = builder.finish().unwrap(); + let builder: UpdateBuilder<_, Ipv4UnicastNlri> = UpdateBuilder::new_vec(); + let msg = builder.finish(&SessionConfig::modern()).unwrap(); //print_pcap(&msg); assert!( - UpdateMessage::from_octets(msg, SessionConfig::modern()).is_ok() + UpdateMessage::from_octets(msg, &SessionConfig::modern()).is_ok() ); } @@ -1274,27 +1410,31 @@ mod tests { "10.0.0.0/7", "10.0.0.0/8", "10.0.0.0/9", - ].map(|s| Nlri::unicast_from_str(s).unwrap()) + ].map(|s| Ipv4UnicastNlri::from_str(s).unwrap()) .into_iter() .collect::>(); - let _ = builder.append_withdrawals(&mut withdrawals.clone()); - let msg = builder.finish().unwrap(); + let _ = builder.append_withdrawals(withdrawals.clone()); + let msg = builder.finish(&SessionConfig::modern()).unwrap(); + + if let Err(e) = UpdateMessage::from_octets(&msg, &SessionConfig::modern()) { + dbg!(e); + } assert!( - UpdateMessage::from_octets(&msg, SessionConfig::modern()) + UpdateMessage::from_octets(&msg, &SessionConfig::modern()) .is_ok() ); print_pcap(&msg); let mut builder2 = UpdateBuilder::new_vec(); - for w in &withdrawals { + for w in withdrawals { builder2.add_withdrawal(w).unwrap(); } - let msg2 = builder2.finish().unwrap(); + let msg2 = builder2.finish(&SessionConfig::modern()).unwrap(); assert!( - UpdateMessage::from_octets(&msg2, SessionConfig::modern()) + UpdateMessage::from_octets(&msg2, &SessionConfig::modern()) .is_ok() ); print_pcap(&msg2); @@ -1302,6 +1442,7 @@ mod tests { assert_eq!(msg, msg2); } + /* #[test] fn build_withdrawals_from_iter() { let mut builder = UpdateBuilder::new_vec(); @@ -1323,26 +1464,28 @@ mod tests { let msg = builder.into_message().unwrap(); print_pcap(msg); } + */ #[test] fn take_message_many_withdrawals() { let mut builder = UpdateBuilder::new_vec(); - let mut prefixes: Vec>> = vec![]; - for i in 1..1500_u32 { + let mut prefixes: Vec = vec![]; + for i in 1..3000_u32 { prefixes.push( - Nlri::Unicast( + Ipv4UnicastNlri::try_from( Prefix::new_v4( Ipv4Addr::from((i << 10).to_be_bytes()), 22 - ).unwrap().into() - ) + ).unwrap() + ).unwrap() ); } + let prefixes_len = prefixes.len(); builder.withdrawals_from_iter(prefixes).unwrap(); let mut w_cnt = 0; - let remainder = if let (pdu1, Some(remainder)) = builder.take_message() { + let remainder = if let (pdu1, Some(remainder)) = builder.take_message(&SessionConfig::modern()) { match pdu1 { Ok(pdu) => { w_cnt += pdu.withdrawals().unwrap().count(); @@ -1351,10 +1494,10 @@ mod tests { Err(e) => panic!("{}", e) } } else { - panic!("wrong"); + panic!("wrong 1"); }; - let remainder2 = if let (pdu2, Some(remainder2)) = remainder.take_message() { + let remainder2 = if let (pdu2, Some(remainder2)) = remainder.take_message(&SessionConfig::modern()) { match pdu2 { Ok(pdu) => { w_cnt += pdu.withdrawals().unwrap().count(); @@ -1363,10 +1506,10 @@ mod tests { Err(e) => panic!("{}", e) } } else { - panic!("wrong"); + panic!("wrong 2"); }; - if let (pdu3, None) = remainder2.take_message() { + if let (pdu3, None) = remainder2.take_message(&SessionConfig::modern()) { match pdu3 { Ok(pdu) => { w_cnt += pdu.withdrawals().unwrap().count(); @@ -1374,7 +1517,7 @@ mod tests { Err(e) => panic!("{}", e) } } else { - panic!("wrong"); + panic!("wrong 3"); }; assert_eq!(w_cnt, prefixes_len); @@ -1383,19 +1526,19 @@ mod tests { #[test] fn take_message_many_withdrawals_2() { let mut builder = UpdateBuilder::new_vec(); - let mut prefixes: Vec>> = vec![]; + let mut prefixes: Vec = vec![]; for i in 1..1500_u32 { prefixes.push( - Nlri::Unicast( + Ipv4UnicastNlri::try_from( Prefix::new_v4( Ipv4Addr::from((i << 10).to_be_bytes()), 22 - ).unwrap().into() - ) + ).unwrap() + ).unwrap() ); } let prefixes_len = prefixes.len(); - builder.append_withdrawals(&mut prefixes).unwrap(); + builder.append_withdrawals(prefixes).unwrap(); let mut w_cnt = 0; let mut remainder = Some(builder); @@ -1403,7 +1546,7 @@ mod tests { if remainder.is_none() { break } - let (pdu, new_remainder) = remainder.take().unwrap().take_message(); + let (pdu, new_remainder) = remainder.take().unwrap().take_message(&SessionConfig::modern()); match pdu { Ok(pdu) => { w_cnt += pdu.withdrawals().unwrap().count(); @@ -1419,22 +1562,22 @@ mod tests { #[test] fn into_messages_many_withdrawals() { let mut builder = UpdateBuilder::new_vec(); - let mut prefixes: Vec>> = vec![]; + let mut prefixes: Vec = vec![]; for i in 1..1500_u32 { prefixes.push( - Nlri::Unicast( + Ipv4UnicastNlri::try_from( Prefix::new_v4( Ipv4Addr::from((i << 10).to_be_bytes()), 22 - ).unwrap().into() - ) + ).unwrap() + ).unwrap() ); } let prefixes_len = prefixes.len(); - builder.append_withdrawals(&mut prefixes).unwrap(); + builder.append_withdrawals(prefixes).unwrap(); let mut w_cnt = 0; - for pdu in builder.into_messages().unwrap() { + for pdu in builder.into_messages(&SessionConfig::modern()).unwrap() { w_cnt += pdu.withdrawals().unwrap().count(); } @@ -1444,24 +1587,24 @@ mod tests { #[test] fn into_messages_many_announcements() { let mut builder = UpdateBuilder::new_vec(); - let mut prefixes: Vec>> = vec![]; + let mut prefixes: Vec = vec![]; for i in 1..1500_u32 { prefixes.push( - Nlri::Unicast( + Ipv4UnicastNlri::try_from( Prefix::new_v4( Ipv4Addr::from((i << 10).to_be_bytes()), 22 - ).unwrap().into() - ) + ).unwrap() + ).unwrap() ); } let prefixes_len = prefixes.len(); - prefixes.iter().by_ref().for_each(|p| - builder.add_announcement(p).unwrap() - ); + for p in prefixes { + builder.add_announcement(p).unwrap(); + } let mut w_cnt = 0; - for pdu in builder.into_messages().unwrap() { + for pdu in builder.into_messages(&SessionConfig::modern()).unwrap() { w_cnt += pdu.announcements().unwrap().count(); } @@ -1474,29 +1617,26 @@ mod tests { let prefixes_num = 1024; for i in 0..prefixes_num { builder.add_withdrawal( - &Nlri::unicast_from_str(&format!("2001:db:{:04}::/48", i)) + Ipv6UnicastNlri::from_str(&format!("2001:db:{:04}::/48", i)) .unwrap() ).unwrap(); } let mut w_cnt = 0; - for pdu in builder.into_messages().unwrap() { + for pdu in builder.into_messages(&SessionConfig::modern()).unwrap() { w_cnt += pdu.withdrawals().unwrap().count(); } assert_eq!(w_cnt, prefixes_num); } - use crate::bgp::communities::{StandardCommunity, Tag}; #[test] fn into_messages_many_announcements_mp() { let mut builder = UpdateBuilder::new_vec(); let prefixes_num = 1_000_000; for i in 0..prefixes_num { builder.add_announcement( - &Nlri::<&[u8]>::Unicast(BasicNlri::new(Prefix::new_v6((i << 96).into(), 32).unwrap())) - //&Nlri::unicast_from_str(&format!("2001:db:{:04}::/48", i)) - // .unwrap() + Ipv6UnicastNlri::try_from(Prefix::new_v6((i << 96).into(), 32).unwrap()).unwrap() ).unwrap(); } builder.attributes.set(crate::bgp::types::LocalPref(123)); @@ -1506,10 +1646,10 @@ mod tests { }); let mut a_cnt = 0; - for pdu in builder { + for pdu in builder.into_pdu_iter(&SessionConfig::modern()) { //eprint!("."); let pdu = pdu.unwrap(); - assert!(pdu.as_ref().len() <= UpdateBuilder::<()>::MAX_PDU); + assert!(pdu.as_ref().len() <= UpdateBuilder::<(), Ipv4UnicastNlri>::MAX_PDU); a_cnt += pdu.announcements().unwrap().count(); assert!(pdu.local_pref().unwrap().is_some()); assert!(pdu.multi_exit_disc().unwrap().is_some()); @@ -1523,9 +1663,8 @@ mod tests { #[test] fn build_withdrawals_basic_v4_addpath() { - use crate::bgp::message::nlri::PathId; let mut builder = UpdateBuilder::new_vec(); - let mut withdrawals = [ + let withdrawals = [ "0.0.0.0/0", "10.2.1.0/24", "10.2.2.0/24", @@ -1534,16 +1673,16 @@ mod tests { "10.0.0.0/7", "10.0.0.0/8", "10.0.0.0/9", - ].iter().enumerate().map(|(idx, s)| Nlri::<&[u8]>::Unicast(BasicNlri { - prefix: Prefix::from_str(s).unwrap(), - path_id: Some(PathId::from_u32(idx.try_into().unwrap()))}) + ].iter().enumerate().map(|(idx, s)| + Ipv4UnicastNlri::from_str(s).unwrap() + .into_addpath(PathId(idx.try_into().unwrap())) ).collect::>(); - let _ = builder.append_withdrawals(&mut withdrawals); - let msg = builder.finish().unwrap(); + let _ = builder.append_withdrawals(withdrawals); + let msg = builder.finish(&SessionConfig::modern()).unwrap(); let mut sc = SessionConfig::modern(); sc.add_addpath_rxtx(AfiSafi::Ipv4Unicast); assert!( - UpdateMessage::from_octets(&msg, sc) + UpdateMessage::from_octets(&msg, &sc) .is_ok() ); print_pcap(&msg); @@ -1552,49 +1691,52 @@ mod tests { #[test] fn build_withdrawals_basic_v6_single() { let mut builder = UpdateBuilder::new_vec(); - let mut withdrawals = vec![ - Nlri::unicast_from_str("2001:db8::/32").unwrap() + let withdrawals = vec![ + Ipv6UnicastNlri::from_str("2001:db8::/32").unwrap() ]; - let _ = builder.append_withdrawals(&mut withdrawals); + let _ = builder.append_withdrawals(withdrawals); - let msg = builder.finish().unwrap(); + let msg = builder.finish(&SessionConfig::modern()).unwrap(); println!("msg raw len: {}", &msg.len()); print_pcap(&msg); - UpdateMessage::from_octets(&msg, SessionConfig::modern()).unwrap(); + UpdateMessage::from_octets(&msg, &SessionConfig::modern()).unwrap(); } #[test] fn build_withdrawals_basic_v6_from_iter() { let mut builder = UpdateBuilder::new_vec(); - let mut withdrawals: Vec>> = vec![]; + let mut withdrawals = vec![]; for i in 1..512_u128 { withdrawals.push( - Nlri::Unicast( + Ipv6UnicastNlri::try_from( Prefix::new_v6( Ipv6Addr::from((i << 64).to_be_bytes()), 64 - ).unwrap().into() - ) + ).unwrap() + ).unwrap() ); } let _ = builder.withdrawals_from_iter(withdrawals); - let raw = builder.finish().unwrap(); + let raw = builder.finish(&SessionConfig::modern()).unwrap(); print_pcap(&raw); - UpdateMessage::from_octets(&raw, SessionConfig::modern()).unwrap(); + UpdateMessage::from_octets(&raw, &SessionConfig::modern()).unwrap(); } + /* not possible anymore with the new typed builders #[test] fn build_mixed_withdrawals() { let mut builder = UpdateBuilder::new_vec(); builder.add_withdrawal( - &Nlri::unicast_from_str("10.0.0.0/8").unwrap() + //&Nlri::unicast_from_str("10.0.0.0/8").unwrap() + Ipv4UnicastNlri::from_str("10.0.0.0/8").unwrap() ).unwrap(); builder.add_withdrawal( - &Nlri::unicast_from_str("2001:db8::/32").unwrap() + //&Nlri::unicast_from_str("2001:db8::/32").unwrap() + Ipv6UnicastNlri::from_str("2001:db8::/32").unwrap() ).unwrap(); let msg = builder.into_message().unwrap(); print_pcap(msg.as_ref()); @@ -1604,7 +1746,6 @@ mod tests { #[test] fn build_mixed_addpath_conventional() { - use crate::bgp::message::nlri::PathId; let mut builder = UpdateBuilder::new_vec(); builder.add_withdrawal( &Nlri::unicast_from_str("10.0.0.0/8").unwrap() @@ -1620,7 +1761,6 @@ mod tests { #[test] fn build_mixed_addpath_mp() { - use crate::bgp::message::nlri::PathId; let mut builder = UpdateBuilder::new_vec(); builder.add_withdrawal( &Nlri::unicast_from_str("2001:db8::/32").unwrap() @@ -1636,7 +1776,6 @@ mod tests { #[test] fn build_mixed_addpath_ok() { - use crate::bgp::message::nlri::PathId; let mut builder = UpdateBuilder::new_vec(); builder.add_withdrawal( &Nlri::unicast_from_str("10.0.0.0/8").unwrap() @@ -1650,6 +1789,7 @@ mod tests { assert!(res.is_ok()); print_pcap(builder.finish().unwrap()); } + */ #[test] fn build_conv_mp_mix() { @@ -1678,26 +1818,25 @@ mod tests { ]; - let upd = UpdateMessage::from_octets(&buf, SessionConfig::modern()).unwrap(); + let upd = UpdateMessage::from_octets(&buf, &SessionConfig::modern()).unwrap(); print_pcap(upd.as_ref()); assert!(upd.has_conventional_nlri() && upd.has_mp_nlri().unwrap()); - assert_eq!(upd.unicast_announcements().unwrap().count(), 7); + assert_eq!(upd.announcements().unwrap().count(), 7); } #[test] fn build_announcements_conventional() { - use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); let prefixes = [ "1.0.1.0/24", "1.0.2.0/24", "1.0.3.0/24", "1.0.4.0/24", - ].map(|p| Nlri::unicast_from_str(p).unwrap()); + ].map(|p| Ipv4UnicastNlri::from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); - builder.set_nexthop_unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into()).unwrap(); + builder.set_nexthop(NextHop::Unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into())).unwrap(); let path = HopPath::from([ Asn::from_u32(123); 70 ]); @@ -1705,7 +1844,7 @@ mod tests { //builder.set_aspath::>(path.to_as_path().unwrap()).unwrap(); builder.set_aspath(path).unwrap(); - let raw = builder.finish().unwrap(); + let raw = builder.finish(&SessionConfig::modern()).unwrap(); print_pcap(raw); //let pdu = builder.into_message().unwrap(); @@ -1714,17 +1853,16 @@ mod tests { #[test] fn build_announcements_mp() { - use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); let prefixes = [ "2001:db8:1::/48", "2001:db8:2::/48", "2001:db8:3::/48", - ].map(|p| Nlri::unicast_from_str(p).unwrap()); + ].map(|p| Ipv6UnicastNlri::from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); - builder.set_nexthop_unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into()).unwrap(); + builder.set_nexthop(NextHop::Unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into())).unwrap(); let path = HopPath::from([ Asn::from_u32(100), Asn::from_u32(200), @@ -1733,18 +1871,15 @@ mod tests { //builder.set_aspath::>(path.to_as_path().unwrap()).unwrap(); builder.set_aspath(path).unwrap(); - let raw = builder.finish().unwrap(); + let raw = builder.finish(&SessionConfig::modern()).unwrap(); print_pcap(raw); } - #[ignore = "currently the MP nexthop can't be set this way"] #[test] fn build_announcements_mp_missing_nlri() { - use crate::bgp::aspath::HopPath; - - let mut builder = UpdateBuilder::new_vec(); + let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::new_vec(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); - builder.set_nexthop_unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into()).unwrap(); + builder.set_nexthop(NextHop::Unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into())).unwrap(); let path = HopPath::from([ Asn::from_u32(100), Asn::from_u32(200), @@ -1753,22 +1888,20 @@ mod tests { builder.set_aspath(path).unwrap(); assert!(matches!( - builder.into_message(), - Err(ComposeError::EmptyMpReachNlri) + builder.into_message(&SessionConfig::modern()), + Err(ComposeError::EmptyMpReachNlri) )); } #[test] fn build_announcements_mp_link_local() { - use crate::bgp::aspath::HopPath; - let mut builder = UpdateBuilder::new_vec(); let prefixes = [ "2001:db8:1::/48", "2001:db8:2::/48", "2001:db8:3::/48", - ].map(|p| Nlri::unicast_from_str(p).unwrap()); + ].map(|p| Ipv6UnicastNlri::from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); @@ -1785,15 +1918,13 @@ mod tests { //let unchecked = builder.finish().unwrap(); //print_pcap(unchecked); - let msg = builder.into_message().unwrap(); + let msg = builder.into_message(&SessionConfig::modern()).unwrap(); msg.print_pcap(); } #[test] fn build_announcements_mp_ll_no_nlri() { - use crate::bgp::aspath::HopPath; - - let mut builder = UpdateBuilder::new_vec(); + let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::new_vec(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); //builder.set_nexthop("2001:db8::1".parse().unwrap()).unwrap(); builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap(); @@ -1805,24 +1936,23 @@ mod tests { builder.set_aspath(path).unwrap(); assert!(matches!( - builder.into_message(), - Err(ComposeError::EmptyMpReachNlri) + builder.into_message(&SessionConfig::modern()), + Err(ComposeError::EmptyMpReachNlri) )); } #[test] fn build_standard_communities() { - use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); let prefixes = [ "1.0.1.0/24", "1.0.2.0/24", "1.0.3.0/24", "1.0.4.0/24", - ].map(|p| Nlri::unicast_from_str(p).unwrap()); + ].map(|p| Ipv4UnicastNlri::from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); - builder.set_nexthop_unicast("1.2.3.4".parse::().unwrap().into()).unwrap(); + builder.set_nexthop(NextHop::Unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into())).unwrap(); let path = HopPath::from([ Asn::from_u32(100), Asn::from_u32(200), @@ -1838,24 +1968,23 @@ mod tests { builder.add_community(format!("AS999:{n}").parse().unwrap()).unwrap(); } - builder.into_message().unwrap(); + builder.into_message(&SessionConfig::modern()).unwrap(); //let raw = builder.finish().unwrap(); //print_pcap(&raw); } #[test] fn build_other_attributes() { - use crate::bgp::aspath::HopPath; let mut builder = UpdateBuilder::new_vec(); let prefixes = [ "1.0.1.0/24", "1.0.2.0/24", "1.0.3.0/24", "1.0.4.0/24", - ].map(|p| Nlri::unicast_from_str(p).unwrap()); + ].map(|p| Ipv4UnicastNlri::from_str(p).unwrap()); builder.announcements_from_iter(prefixes).unwrap(); builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp)); - builder.set_nexthop_unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into()).unwrap(); + builder.set_nexthop(NextHop::Unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into())).unwrap(); let path = HopPath::from([ Asn::from_u32(100), Asn::from_u32(200), @@ -1867,7 +1996,7 @@ mod tests { builder.attributes.set(crate::bgp::types::MultiExitDisc(1234)); builder.attributes.set(crate::bgp::types::LocalPref(9876)); - let msg = builder.into_message().unwrap(); + let msg = builder.into_message(&SessionConfig::modern()).unwrap(); msg.print_pcap(); } @@ -1883,18 +2012,27 @@ mod tests { 0x01, 0x20, 0x0a, 0x0a, 0x0a, 0x02 ]; let sc = SessionConfig::modern(); - let upd = UpdateMessage::from_octets(&raw, sc).unwrap(); + let upd = UpdateMessage::from_octets(&raw, &sc).unwrap(); let target = BytesMut::new(); - let mut builder = UpdateBuilder::from_update_message(&upd, sc, target).unwrap(); + let mut builder = UpdateBuilder::from_update_message(&upd, &sc, target).unwrap(); + assert_eq!(builder.attributes.len(), 4); builder.add_announcement( - &Nlri::unicast_from_str("10.10.10.2/32").unwrap() + Ipv4UnicastNlri::from_str("10.10.10.2/32").unwrap() ).unwrap(); - let upd2 = builder.into_message().unwrap(); - assert_eq!(&raw, upd2.as_ref()); + let upd2 = builder.into_message(&SessionConfig::modern()).unwrap(); + + // doesn't work as we put everything in MP attributes + //assert_eq!(&raw, upd2.as_ref()); + + assert!( + upd.typed_announcements::<_, Ipv4UnicastNlri>().unwrap().unwrap().eq( + upd2.typed_announcements::<_, Ipv4UnicastNlri>().unwrap().unwrap() + ) + ); } #[test] @@ -1913,12 +2051,12 @@ mod tests { 0x01, 0x20, 0x0a, 0x0a, 0x0a, 0x02 ]; let sc = SessionConfig::modern(); - let upd = UpdateMessage::from_octets(&raw, sc).unwrap(); + let upd = UpdateMessage::from_octets(&raw, &sc).unwrap(); for pa in upd.clone().path_attributes().unwrap() { eprintln!("{:?}", pa.unwrap().to_owned().unwrap()); } let target = BytesMut::new(); - let mut builder = UpdateBuilder::from_update_message(&upd, sc, target).unwrap(); + let mut builder = UpdateBuilder::from_update_message(&upd, &sc, target).unwrap(); assert_eq!(builder.attributes.len(), 4); @@ -1930,16 +2068,16 @@ mod tests { assert_eq!(builder.attributes.len(), 4); builder.add_announcement( - &Nlri::unicast_from_str("10.10.10.2/32").unwrap() + Ipv4UnicastNlri::from_str("10.10.10.2/32").unwrap() ).unwrap(); - let upd2 = builder.into_message().unwrap(); - assert_eq!(&raw, upd2.as_ref()); + let upd2 = builder.into_message(&SessionConfig::modern()).unwrap(); + assert_eq!(upd2.origin(), Ok(Some(OriginType::Igp))); } #[test] fn build_ordered_attributes() { - let mut builder = UpdateBuilder::new_vec(); + let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::new_vec(); builder.add_community( StandardCommunity::from_str("AS1234:999").unwrap() ).unwrap(); @@ -1950,7 +2088,7 @@ mod tests { assert_eq!(builder.attributes.len(), 2); - let pdu = builder.into_message().unwrap(); + let pdu = builder.into_message(&SessionConfig::modern()).unwrap(); let mut prev_type_code = 0_u8; for pa in pdu.path_attributes().unwrap() { let type_code = pa.unwrap().type_code(); @@ -1961,11 +2099,14 @@ mod tests { } // TODO also do fn check(raw: Bytes) + // XXX this will not work anymore as we put conventional NLRI in MP + // attributes.. but we can check a subset of things perhaps. fn parse_build_compare(raw: &[u8]) { + let sc = SessionConfig::modern(); let original = - match UpdateMessage::from_octets(&raw, sc) { - Ok(msg) => msg,// UpdateMessage::from_octets(&raw, sc) { + match UpdateMessage::from_octets(raw, &sc) { + Ok(msg) => msg, Err(_e) => { //TODO get the ShortInput ones (and retry with a different //SessionConfig) @@ -1977,12 +2118,65 @@ mod tests { }; let target = BytesMut::new(); - let mut builder = UpdateBuilder::from_update_message( - &original, sc, target - ).unwrap(); + let (_unreach_afisafi, reach_afisafi) = match original.afi_safis() { + (_, _, Some(mp_u), Some(mp_r)) => (mp_u, mp_r), + (_, _, None, Some(mp_r)) => (mp_r, mp_r), + (_, _, Some(mp_u), None) => (mp_u, mp_u), + (Some(c_w), Some(c_a), _, _) => (c_w, c_a), + (Some(c_w), None, _, _) => (c_w, c_w), + (None, Some(c_a), _, _) => (c_a, c_a), + (None, None, None, None) => + { + // conventional IPv4 End-of-RIB + (AfiSafi::Ipv4Unicast, AfiSafi::Ipv4Unicast) + } + }; - for w in original.withdrawals().unwrap() { - builder.add_withdrawal(&w.unwrap()).unwrap(); + let composed = match reach_afisafi { + AfiSafi::Ipv4Unicast => { + let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::from_update_message(&original, &sc, target).unwrap(); + builder.add_announcements_from_pdu(&original, &sc); + builder.add_withdrawals_from_pdu(&original, &sc); + builder.into_message(&sc) + } + AfiSafi::Ipv4Multicast => { + let mut builder = UpdateBuilder::<_, Ipv4MulticastNlri>::from_update_message(&original, &sc, target).unwrap(); + builder.add_announcements_from_pdu(&original, &sc); + builder.add_withdrawals_from_pdu(&original, &sc); + builder.into_message(&sc) + } + AfiSafi::Ipv4MplsUnicast => todo!(), + AfiSafi::Ipv4MplsVpnUnicast => todo!(), + AfiSafi::Ipv4RouteTarget => todo!(), + AfiSafi::Ipv4FlowSpec => { + let mut builder = UpdateBuilder::<_, Ipv4FlowSpecNlri<_>>::from_update_message(&original, &sc, target).unwrap(); + builder.add_announcements_from_pdu(&original, &sc); + builder.add_withdrawals_from_pdu(&original, &sc); + builder.into_message(&sc) + } + AfiSafi::Ipv6Unicast => { + let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::from_update_message(&original, &sc, target).unwrap(); + builder.add_announcements_from_pdu(&original, &sc); + builder.add_withdrawals_from_pdu(&original, &sc); + builder.into_message(&sc) + } + + AfiSafi::Ipv6Multicast => todo!(), + AfiSafi::Ipv6MplsUnicast => todo!(), + AfiSafi::Ipv6MplsVpnUnicast => todo!(), + AfiSafi::Ipv6FlowSpec => todo!(), + AfiSafi::L2VpnVpls => todo!(), + AfiSafi::L2VpnEvpn => todo!(), + AfiSafi::Unsupported(_, _) => todo!(), + }; + //let mut builder = UpdateBuilder::<_, reach_afisafi>::from_update_message( + // &original, &sc, target + //).unwrap(); + + /* + //for w in original.withdrawals().unwrap() { + for w in original.typed_withdrawals().unwrap().unwrap() { + builder.add_withdrawal(w.unwrap()).unwrap(); } for a in original.announcements().unwrap() { @@ -2009,7 +2203,9 @@ mod tests { let calculated_len = builder.calculate_pdu_length(); - let composed = match builder.take_message() { + */ + /* + let composed = match composed { (Ok(msg), None) => msg, (Err(e), _) => { print_pcap(raw); @@ -2019,8 +2215,47 @@ mod tests { unimplemented!("builder returning remainder") } }; + */ + let composed = match composed { + Ok(msg) => msg, + Err(e) => { + print_pcap(raw); + panic!("error: {e}"); + } + }; + + if std::panic::catch_unwind(|| { + assert_eq!( + original.announcements().unwrap().count(), + composed.announcements().unwrap().count(), + ); + assert_eq!( + original.withdrawals().unwrap().count(), + composed.withdrawals().unwrap().count(), + ); + + // In case of an original with conventional withdrawals and + // announcements, we end up with 2 extra attributes (MP + // REACH/UNREACH), so allow a margin of 2 here. + assert!( + composed.path_attributes().unwrap().count().abs_diff( + original.path_attributes().unwrap().count() + ) <= 2 + ); + + assert_eq!(original.origin(), composed.origin()); + assert_eq!(original.multi_exit_disc(), composed.multi_exit_disc()); + assert_eq!(original.local_pref(), composed.local_pref()); + }).is_err() { + eprintln!("--"); + print_pcap(raw); + print_pcap(composed.as_ref()); + + eprintln!("--"); + panic!("tmp"); + } - assert_eq!(composed.as_ref().len(), calculated_len); + //assert_eq!(composed.as_ref().len(), calculated_len); // XXX there are several possible reasons why our composed pdu // differs from the original input, especially if the attributes @@ -2029,6 +2264,7 @@ mod tests { //assert_eq!(raw, composed.as_ref()); + /* // compare as much as possible: #[allow(clippy::blocks_in_conditions)] if std::panic::catch_unwind(|| { @@ -2098,6 +2334,7 @@ mod tests { } else { eprint!("×"); } + */ } #[test] @@ -2150,11 +2387,8 @@ mod tests { } - use std::fs::File; - use memmap2::Mmap; - - #[test] #[ignore] + #[test] fn parse_build_compare_bulk() { let filename = "examples/raw_bgp_updates"; let file = File::open(filename).unwrap(); @@ -2351,63 +2585,25 @@ mod tests { ]; let sc = SessionConfig::modern(); - let original = UpdateMessage::from_octets(&raw, sc).unwrap(); - let mut builder = UpdateBuilder::from_update_message( + let original = UpdateMessage::from_octets(&raw, &sc).unwrap(); + // XXX unsure about Ipv4UnicastNlri here + let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::from_update_message( &original, - sc, + &sc, Vec::new() ).unwrap(); - for w in original.withdrawals().unwrap() { - builder.add_withdrawal(&w.unwrap()).unwrap(); + for w in original.typed_withdrawals::<_, Ipv4UnicastNlri>().unwrap().unwrap() { + builder.add_withdrawal(w.unwrap()).unwrap(); } - for a in original.announcements().unwrap() { - builder.add_announcement(&a.unwrap()).unwrap(); + for a in original.typed_announcements::<_, Ipv4UnicastNlri>().unwrap().unwrap() { + builder.add_announcement(a.unwrap()).unwrap(); } - let composed = builder.into_message().unwrap(); + let composed = builder.into_message(&SessionConfig::modern()).unwrap(); assert_eq!(original.path_attributes().unwrap().count(), 4); assert_eq!(composed.path_attributes().unwrap().count(), 3); } -/* - #[test] - #[allow(deprecated)] - fn build_acs() { - use crate::bgp::aspath::HopPath; - use crate::bgp::message::nlri::PathId; - - let builder = UpdateBuilder::new_vec(); - let mut acs = AttrChangeSet::empty(); - - // ORIGIN - acs.origin_type.set(OriginType::Igp); - - // AS_PATH - let mut hp = HopPath::new(); - hp.prepend(Asn::from_u32(100)); - hp.prepend(Asn::from_u32(101)); - acs.as_path.set(hp.to_as_path().unwrap()); - - // NEXT_HOP - acs.next_hop.set(NextHop::Ipv4(Ipv4Addr::from_str("192.0.2.1").unwrap())); - - - // now for some NLRI - // XXX currently ACS only holds one single Nlri - acs.nlri.set(Nlri::Unicast(BasicNlri{ - prefix: Prefix::from_str("1.2.0.0/25").unwrap(), - path_id: Some(PathId::from_u32(123)) - })); - - acs.standard_communities.set(vec![ - Wellknown::NoExport.into(), - Wellknown::Blackhole.into(), - ]); - - let _msg = builder.build_acs(acs).unwrap(); - //print_pcap(&msg); - } -*/ } diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 908f0f7e..27dff6ac 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -1,3 +1,8 @@ +use core::hash::Hash; +use core::str::FromStr; +use std::fmt::{self, Debug}; + + use crate::typeenum; // from util::macros #[cfg(feature = "serde")] @@ -16,12 +21,8 @@ use super::common::{PathId, parse_prefix, prefix_bits_to_bytes}; use crate::util::parser::ParseError; use paste::paste; -use std::fmt; -use octseq::{Octets, Parser}; - -use core::hash::Hash; -use core::fmt::Debug; +use octseq::{Octets, OctetsBuilder, Parser}; use super::evpn::*; use super::flowspec::*; @@ -34,6 +35,7 @@ macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => { paste! { + #[allow(clippy::derived_hash_with_manual_eq)] #[derive(Clone, Debug, Hash)] pub struct [<$nlri AddpathNlri>]$(<$gen>)?(PathId, [<$nlri Nlri>]$(<$gen>)?); impl$(<$gen: Clone + Debug + Hash>)? AfiSafiNlri for [<$nlri AddpathNlri>]$(<$gen>)? { @@ -44,8 +46,8 @@ paste! { } impl$(<$gen>)? AfiSafi for [<$nlri AddpathNlri>]$(<$gen>)? { - fn afi(&self) -> Afi { self.1.afi() } - fn afi_safi(&self) -> AfiSafiType { self.1.afi_safi() } + fn afi() -> Afi { <[<$nlri Nlri>]$(<$gen>)? as AfiSafi>::afi() } + fn afi_safi() -> AfiSafiType { <[<$nlri Nlri>]$(<$gen>)? as AfiSafi>::afi_safi() } } impl<'a, Octs, P> AfiSafiParse<'a, Octs, P> for [<$nlri AddpathNlri>]$(<$gen>)? @@ -75,6 +77,7 @@ paste! { fmt::Display::fmt(&self.1, f) } } + }} } @@ -97,9 +100,116 @@ macro_rules! afisafi { }); paste! { - #[derive(Debug)] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub enum AfiSafiType { $( $( [<$afi_name $safi_name>] ,)+)+ + Unsupported(u16, u8), + } + + /* + impl TryFrom<(u16, u8)> for AfiSafiType { + type Error = &'static str; + fn try_from(t: (u16, u8)) -> Result { + match t { + $($( + ($afi_code, $safi_code) => Self::[<$afi_name $safi_name>], + )+)+ + _ => Err("unsupported AFI+SAFI combination") + } + } + } + */ + + impl From<(u16, u8)> for AfiSafiType { + fn from(t: (u16, u8)) -> Self { + match t { + $($( + ($afi_code, $safi_code) => Self::[<$afi_name $safi_name>], + )+)+ + _ => Self::Unsupported(t.0, t.1) + } + } + } + + impl From for (u16, u8) { + fn from(afisafi: AfiSafiType) -> (u16, u8) { + match afisafi { + $($( + AfiSafiType::[<$afi_name $safi_name>] => ($afi_code, $safi_code), + )+)+ + AfiSafiType::Unsupported(a, s) => (a, s) + } + } + } + + /* + impl From for [u8; 3] { + fn from(afisafi: AfiSafiType) -> [u8; 3] { + match afisafi { + $($( + AfiSafiType::[<$afi_name $safi_name>] => concat!($afi_code.to_be_bytes().into(), $safi_code), + )+)+ + AfiSafiType::Unsupported(a, s) => [a.to_be_bytes(), s] + } + } + } + */ + + /* + impl AsRef<[u8]> for AfiSafiType { + fn as_ref(&self) -> &[u8] { + match self { + $($( + Self::[<$afi_name $safi_name>] => &[$afi_code, $safi_code), + )+)+ + Self::Unsupported(a, s) => (a, s) + } + } + } + */ + + impl AfiSafiType { + pub const fn afi(self) -> Afi { + match self { + $($( + Self::[<$afi_name $safi_name>] => Afi::$afi_name, + )+)+ + Self::Unsupported(a, _s) => Afi::Unimplemented(a) + } + } + + pub const fn as_bytes(self) -> [u8; 3] { + match self { + $($( + Self::[<$afi_name $safi_name>] => { + let afi = $afi_code.to_be_bytes(); + [afi[0], afi[1], $safi_code] + } + )+)+ + AfiSafiType::Unsupported(a, s) => { + let afi = a.to_be_bytes(); + [afi[0], afi[1], s] + } + } + } + } + + + impl fmt::Display for AfiSafiType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + $($( + Self::[<$afi_name $safi_name>] => { + write!(f, stringify!([<$afi_name $safi_name>])) + } + )+)+ + Self::Unsupported(a, s) => { + write!(f, "UnsupportedAfiSafi({}, {})", a, s) + } + } + } } // this enforces these derives on all *Nlri structs. @@ -143,8 +253,8 @@ $($( // $Afi$SafiNlri struct, along with all its basic or exotic data fields // and whatnot. We can not do that in a generic way in this macro. impl$(<$gen>)? AfiSafi for [<$afi_name $safi_name Nlri>]$(<$gen>)? { - fn afi(&self) -> Afi { Afi::$afi_name } - fn afi_safi(&self) -> AfiSafiType { + fn afi() -> Afi { Afi::$afi_name } + fn afi_safi() -> AfiSafiType { AfiSafiType::[<$afi_name $safi_name>] } } @@ -234,12 +344,87 @@ $($( }} } +//--- Trait implementations for macro generated types + +impl PartialEq> for Nlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Nlri) -> bool { + use Nlri::*; + match (self, other) { + (Ipv4Unicast(p1), Ipv4Unicast(p2)) => p1 == p2, + (Ipv4UnicastAddpath(p1), Ipv4UnicastAddpath(p2)) => p1 == p2, + (Ipv4Multicast(p1), Ipv4Multicast(p2)) => p1 == p2, + (Ipv4MulticastAddpath(_), Ipv4MulticastAddpath(_)) => todo!(), + (Ipv4MplsUnicast(_), Ipv4MplsUnicast(_)) => todo!(), + (Ipv4MplsUnicastAddpath(_), Ipv4MplsUnicastAddpath(_)) => todo!(), + (Ipv4MplsVpnUnicast(_), Ipv4MplsVpnUnicast(_)) => todo!(), + (Ipv4MplsVpnUnicastAddpath(_), Ipv4MplsVpnUnicastAddpath(_)) => todo!(), + (Ipv4RouteTarget(_), Ipv4RouteTarget(_)) => todo!(), + (Ipv4RouteTargetAddpath(_), Ipv4RouteTargetAddpath(_)) => todo!(), + (Ipv4FlowSpec(_), Ipv4FlowSpec(_)) => todo!(), + (Ipv4FlowSpecAddpath(_), Ipv4FlowSpecAddpath(_)) => todo!(), + (Ipv6Unicast(p1), Ipv6Unicast(p2)) => p1 == p2, + (Ipv6UnicastAddpath(_), Ipv6UnicastAddpath(_)) => todo!(), + (Ipv6Multicast(_), Ipv6Multicast(_)) => todo!(), + (Ipv6MulticastAddpath(_), Ipv6MulticastAddpath(_)) => todo!(), + (Ipv6MplsUnicast(_), Ipv6MplsUnicast(_)) => todo!(), + (Ipv6MplsUnicastAddpath(_), Ipv6MplsUnicastAddpath(_)) => todo!(), + (Ipv6MplsVpnUnicast(_), Ipv6MplsVpnUnicast(_)) => todo!(), + (Ipv6MplsVpnUnicastAddpath(_), Ipv6MplsVpnUnicastAddpath(_)) => todo!(), + (Ipv6FlowSpec(_), Ipv6FlowSpec(_)) => todo!(), + (Ipv6FlowSpecAddpath(_), Ipv6FlowSpecAddpath(_)) => todo!(), + (L2VpnVpls(_), L2VpnVpls(_)) => todo!(), + (L2VpnVplsAddpath(_), L2VpnVplsAddpath(_)) => todo!(), + (L2VpnEvpn(_), L2VpnEvpn(_)) => todo!(), + (L2VpnEvpnAddpath(_), L2VpnEvpnAddpath(_)) => todo!(), + _ => false + } + + /* + match (self, other) { + (Ipv4Unicast(s), Ipv4Unicast(o)) | + //(Multicast(s), Nlri::Multicast(o)) => s == o, + //(Mpls(s), Nlri::Mpls(o)) => s == o, + //(MplsVpn(s), Nlri::MplsVpn(o)) => s == o, + //(Vpls(s), Nlri::Vpls(o)) => s == o, + //(FlowSpec(s), Nlri::FlowSpec(o)) => s == o, + //(RouteTarget(s), Nlri::RouteTarget(o)) => s == o, + + _ => todo!(), // TMP while refactoring + //_ => false + } + */ + } +} + +// XXX While Nlri<()> might make more sense, it clashes with trait bounds +// like Vec: OctetsFrom elsewhere, as, From<()> is not implemented for +// Vec. Similarly, () is not AsRef<[u8]>. +impl Nlri<&[u8]> { + /// Creates a `Nlri::Unicast` for `prefix`. + /// + /// This returns the error thrown by `Prefix::from_str` if `prefix` does + /// not represent a valid IPv6 or IPv4 prefix. + pub fn unicast_from_str(prefix: &str) + -> Result, ::Err> + { + let p = Prefix::from_str(prefix)?; + if p.is_v4() { + Ok(Nlri::Ipv4Unicast(Ipv4UnicastNlri(p))) + } else { + Ok(Nlri::Ipv6Unicast(Ipv6UnicastNlri(p))) + } + } +} + //------------ Traits --------------------------------------------------------- /// A type characterized by an AFI and SAFI. pub trait AfiSafi { - fn afi(&self) -> Afi; - fn afi_safi(&self) -> AfiSafiType; + fn afi() -> Afi; + fn afi_safi() -> AfiSafiType; } /// A type representing an NLRI for a certain AFI+SAFI. @@ -255,10 +440,17 @@ pub trait AfiSafiNlri: AfiSafi + Clone + Hash + Debug { pub trait AfiSafiParse<'a, O, P>: Sized where P: 'a + Octets = O> { - type Output; // XXX do we actually still need this? + type Output: AfiSafi; // XXX do we actually still need this? fn parse(parser: &mut Parser<'a, P>) -> Result; } +pub trait NlriCompose: AfiSafiNlri { + fn compose(&self, target: &mut Target) + -> Result<(), Target::AppendError>; + + fn compose_len(&self) -> usize { todo!() } +} + /// A type containing nothing more than a (v4 or v6) Prefix. pub trait IsPrefix { @@ -286,7 +478,7 @@ pub trait Addpath: AfiSafiNlri { // - impl AfiSafiNlri, AfiSafiParse and Display afisafi! { - 1 => Ipv4 [ + 1_u16 => Ipv4 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast, @@ -296,7 +488,7 @@ afisafi! { //134 => FlowSpecVpn, ], - 2 => Ipv6 [ + 2_u16 => Ipv6 [ 1 => Unicast, 2 => Multicast, 4 => MplsUnicast, @@ -304,7 +496,7 @@ afisafi! { 133 => FlowSpec, //134 => FlowSpecVpn, ], - 25 => L2Vpn [ + 25_u16 => L2Vpn [ 65 => Vpls, 70 => Evpn, ] @@ -326,6 +518,25 @@ impl AfiSafiNlri for Ipv4UnicastNlri { } } +impl FromStr for Ipv4UnicastNlri { + type Err = &'static str; + fn from_str(s: &str) -> Result { + let p = Prefix::from_str(s).map_err(|_| "could not parse prefix")?; + p.try_into() + } +} + +impl TryFrom for Ipv4UnicastNlri { + type Error = &'static str; + fn try_from(p: Prefix) -> Result { + if p.is_v4() { + Ok( Self(p) ) + } else { + Err("prefix is not IPv4") + } + } +} + impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4UnicastNlri where O: Octets, @@ -339,14 +550,13 @@ where } } -use octseq::OctetsBuilder; -impl Ipv4UnicastNlri { - pub(crate) fn compose_len(&self) -> usize { +impl NlriCompose for Ipv4UnicastNlri { + fn compose_len(&self) -> usize { // 1 byte for the length itself 1 + prefix_bits_to_bytes(self.prefix().len()) } - pub(crate) fn compose(&self, target: &mut Target) + fn compose(&self, target: &mut Target) -> Result<(), Target::AppendError> { let len = self.prefix().len(); target.append_slice(&[len])?; @@ -359,7 +569,26 @@ impl Ipv4UnicastNlri { } Ok(()) } +} +impl NlriCompose for Ipv4UnicastAddpathNlri { + fn compose_len(&self) -> usize { + 4 + self.1.compose_len() + } + + fn compose(&self, target: &mut Target) + -> Result<(), Target::AppendError> + { + target.append_slice(&self.0.0.to_be_bytes())?; + self.1.compose(target) + } + +} + +impl PartialEq for Ipv4UnicastAddpathNlri { + fn eq(&self, other: &Ipv4UnicastAddpathNlri) -> bool { + self.0 == other.0 + } } impl fmt::Display for Ipv4UnicastNlri { @@ -380,6 +609,25 @@ impl AfiSafiNlri for Ipv4MulticastNlri { } } +impl FromStr for Ipv4MulticastNlri { + type Err = &'static str; + fn from_str(s: &str) -> Result { + let p = Prefix::from_str(s).map_err(|_| "err")?; + p.try_into() + } +} + +impl TryFrom for Ipv4MulticastNlri { + type Error = &'static str; + fn try_from(p: Prefix) -> Result { + if p.is_v4() { + Ok( Self(p) ) + } else { + Err("prefix is not IPv4") + } + } +} + impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv4MulticastNlri where O: Octets, @@ -393,6 +641,27 @@ where } } +impl NlriCompose for Ipv4MulticastNlri { + fn compose_len(&self) -> usize { + // 1 byte for the length itself + 1 + prefix_bits_to_bytes(self.prefix().len()) + } + + fn compose(&self, target: &mut Target) + -> Result<(), Target::AppendError> { + let len = self.prefix().len(); + target.append_slice(&[len])?; + let prefix_bytes = prefix_bits_to_bytes(len); + match self.prefix().addr() { + std::net::IpAddr::V4(a) => { + target.append_slice(&a.octets()[..prefix_bytes])? + } + _ => unreachable!() + } + Ok(()) + } +} + impl fmt::Display for Ipv4MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -438,6 +707,15 @@ where Octs: AsRef<[u8]>, } } +impl PartialEq> for Ipv4MplsUnicastAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4MplsUnicastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv4MplsUnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -540,6 +818,32 @@ where } } +impl NlriCompose for Ipv4FlowSpecNlri { + fn compose_len(&self) -> usize { + let value_len = self.0.raw().as_ref().len(); + let len_len = if value_len >= 240 { 2 } else { 1 } ; + len_len + value_len + } + + fn compose(&self, target: &mut Target) + -> Result<(), Target::AppendError> { + let len = self.0.raw().as_ref().len(); + if len >= 240 { + todo!(); //FIXME properly encode into 0xfnnn for 239 < len < 4095 + /* + target.append_slice( + &u16::try_from(self.compose_len()).unwrap_or(u16::MAX) + .to_be_bytes() + )?; + */ + } else { + // We know len < 255 so we can safely unwrap. + target.append_slice(&[u8::try_from(len).unwrap()])?; + } + target.append_slice(self.0.raw().as_ref()) + } +} + impl fmt::Display for Ipv4FlowSpecNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -558,6 +862,24 @@ impl AfiSafiNlri for Ipv6UnicastNlri { self.0 } } +impl FromStr for Ipv6UnicastNlri { + type Err = &'static str; + fn from_str(s: &str) -> Result { + let p = Prefix::from_str(s).map_err(|_| "could not parse prefix")?; + p.try_into() + } +} + +impl TryFrom for Ipv6UnicastNlri { + type Error = &'static str; + fn try_from(p: Prefix) -> Result { + if p.is_v6() { + Ok( Self(p) ) + } else { + Err("prefix is not IPv6") + } + } +} impl<'a, O, P> AfiSafiParse<'a, O, P> for Ipv6UnicastNlri where @@ -572,6 +894,27 @@ where } } +impl NlriCompose for Ipv6UnicastNlri { + fn compose_len(&self) -> usize { + // 1 byte for the length itself + 1 + prefix_bits_to_bytes(self.prefix().len()) + } + + fn compose(&self, target: &mut Target) + -> Result<(), Target::AppendError> { + let len = self.prefix().len(); + target.append_slice(&[len])?; + let prefix_bytes = prefix_bits_to_bytes(len); + match self.prefix().addr() { + std::net::IpAddr::V6(a) => { + target.append_slice(&a.octets()[..prefix_bytes])? + } + _ => unreachable!() + } + Ok(()) + } +} + impl fmt::Display for Ipv6UnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -849,13 +1192,30 @@ where P: Octets = O>, ASP: AfiSafiParse<'a, O, P> { - fn new(parser: Parser<'a, P>) -> Self { + pub fn new(parser: Parser<'a, P>) -> Self { NlriIter { parser, asp: std::marker::PhantomData, output: std::marker::PhantomData } } + + pub fn afi_safi(&self) -> AfiSafiType { + ASP::Output::afi_safi() + } + + // + // Validate the entire parser so we can safely return items from this + // iterator, instead of returning Option, ParseError> + // + pub fn validate(&self) -> Result<(), ParseError> { + let mut parser = self.parser; + while parser.remaining() > 0 { + // TODO replace this ::parse with a cheaper ::check, if available + ASP::parse(&mut parser)?; + } + Ok(()) + } } @@ -870,24 +1230,76 @@ where NlriIter::<'a, O, P, ASP>::new(parser) } - // - // Validate the entire parser so we can safely return items from this - // iterator, instead of returning Option, ParseError> - // - //pub fn validate(&self) { } - - impl<'a, O, P, ASP: AfiSafiParse<'a, O, P>> Iterator for NlriIter<'a, O, P, ASP> where P: Octets = O> { - type Item = ASP::Output; + type Item = Result; fn next(&mut self) -> Option { if self.parser.remaining() == 0 { return None; } - Some(ASP::parse(&mut self.parser).unwrap()) + Some(ASP::parse(&mut self.parser)) + } +} + +/// Generic iterator returning Nlri enum variants instead of specific Nlri +/// structs. +pub struct NlriEnumIter<'a, P> { + parser: Parser<'a, P>, + afisafi: AfiSafiType, +} +impl<'a, P> NlriEnumIter<'a, P> { + pub fn new(parser: Parser<'a, P>, afisafi: AfiSafiType) -> Self { + Self { parser, afisafi } + } + + pub fn afi_safi(&self) -> AfiSafiType { + self.afisafi + } +} + +impl<'a, O, P> Iterator for NlriEnumIter<'a, P> +where + O: Octets, + P: Octets = O>, +{ + type Item = Result, ParseError>; + + fn next(&mut self) -> Option { + if self.parser.remaining() == 0 { + return None + } + + let res = match self.afisafi { + AfiSafiType::Ipv4Unicast => Ipv4UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4Unicast), + AfiSafiType::Ipv4Multicast => Ipv4MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv4Multicast), + AfiSafiType::Ipv4MplsUnicast => Ipv4MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicast), + AfiSafiType::Ipv4MplsVpnUnicast => Ipv4MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicast), + AfiSafiType::Ipv4RouteTarget => Ipv4RouteTargetNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTarget), + AfiSafiType::Ipv4FlowSpec => Ipv4FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpec), + AfiSafiType::Ipv6Unicast => Ipv6UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6Unicast), + AfiSafiType::Ipv6Multicast => Ipv6MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv6Multicast), + AfiSafiType::Ipv6MplsUnicast => Ipv6MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicast), + AfiSafiType::Ipv6MplsVpnUnicast => Ipv6MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicast), + AfiSafiType::Ipv6FlowSpec => Ipv6FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpec), + AfiSafiType::L2VpnVpls => L2VpnVplsNlri::parse(&mut self.parser).map(Nlri::L2VpnVpls), + AfiSafiType::L2VpnEvpn => L2VpnEvpnNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpn), + AfiSafiType::Unsupported(_, _) => { return None } + }; + Some(res) + } +} + +impl<'a, O, P, ASP> From> for NlriEnumIter<'a, P> +where + O: Octets, + ASP: AfiSafiParse<'a, O, P>, + P: Octets = O>, +{ + fn from(iter: NlriIter<'a, O, P, ASP>) -> Self { + Self { parser: iter.parser, afisafi: iter.afi_safi() } } } @@ -912,21 +1324,16 @@ mod tests { fn test1() { let p = Prefix::from_str("1.2.3.0/24").unwrap(); let n = Ipv4UnicastNlri(p); - dbg!(&n); - let n2 = n.clone().nlri(); - dbg!(n2); + let _n2 = n.clone().nlri(); - let b2 = n.prefix(); + let _b2 = n.prefix(); - let nlri_type: Nlri<()> = n.into(); - dbg!(&nlri_type); + let _nlri_type: Nlri<()> = n.into(); let mc = Ipv4MulticastNlri(p); - let nlri_type2: Nlri<()> = mc.clone().into(); - dbg!(&mc); + let _nlri_type2: Nlri<()> = mc.clone().into(); - dbg!(nlri_type2); } #[test] @@ -1008,7 +1415,9 @@ mod tests { let v4_iter = NlriIter::ipv4_unicast(parser); - for n in v4_iter.map(Nlri::<_>::from).chain(mpls_iter.map(Nlri::<_>::from)) { + for n in NlriEnumIter::from(v4_iter) + .chain(NlriEnumIter::from(mpls_iter)) + { dbg!(&n); } } @@ -1027,7 +1436,7 @@ mod tests { assert_eq!(iter.count(), 4); let iter = NlriIter::ipv4_unicast_addpath(parser); - for n in iter.map(|e| e.prefix()) { + for n in iter.map(|e| e.unwrap().prefix()) { dbg!(&n); } } @@ -1045,7 +1454,7 @@ mod tests { let iter = NlriIter::ipv6_unicast_addpath(parser); //assert_eq!(iter.count(), 4); for n in iter { - eprintln!("{n}"); + eprintln!("{}", n.unwrap()); } } @@ -1084,7 +1493,7 @@ mod tests { for (idx, n) in iter.enumerate() { dbg!( Ipv6UnicastNlri::from( - dbg!(n.into_addpath(PathId(idx.try_into().unwrap()))) + dbg!(n.unwrap().into_addpath(PathId(idx.try_into().unwrap()))) ) ); } diff --git a/src/bgp/nlri/evpn.rs b/src/bgp/nlri/evpn.rs index 3e7bca6a..be4370d8 100644 --- a/src/bgp/nlri/evpn.rs +++ b/src/bgp/nlri/evpn.rs @@ -29,6 +29,12 @@ pub struct EvpnNlri { raw: Octs, } +impl EvpnNlri { + pub fn route_type(&self) -> EvpnRouteType { + self.route_type + } +} + impl EvpnNlri { pub fn parse<'a, R>(parser: &mut Parser<'a, R>) -> Result diff --git a/src/bgp/nlri/flowspec.rs b/src/bgp/nlri/flowspec.rs index 8accb2ff..53aaf1d5 100644 --- a/src/bgp/nlri/flowspec.rs +++ b/src/bgp/nlri/flowspec.rs @@ -19,6 +19,12 @@ pub struct FlowSpecNlri { raw: Octs, } +impl FlowSpecNlri { + pub(crate) fn raw(&self) -> &Octs { + &self.raw + } +} + impl FlowSpecNlri { pub fn parse<'a, R>(parser: &mut Parser<'a, R>, afi: Afi) -> Result diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index 1671592c..df32a9e2 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -1,9 +1,10 @@ pub mod afisafi; -mod common; +pub mod nexthop; +pub mod common; -mod evpn; -mod flowspec; -mod mpls; -mod mpls_vpn; -mod routetarget; -mod vpls; +pub(crate) mod evpn; +pub(crate) mod flowspec; +pub(crate) mod mpls; +pub(crate) mod mpls_vpn; +pub(crate) mod routetarget; +pub(crate) mod vpls; diff --git a/src/bgp/nlri/mpls.rs b/src/bgp/nlri/mpls.rs index 1d8e3c30..998e2293 100644 --- a/src/bgp/nlri/mpls.rs +++ b/src/bgp/nlri/mpls.rs @@ -21,6 +21,10 @@ impl MplsNlri { pub fn prefix(&self) -> Prefix { self.prefix } + + pub fn labels(&self) -> &Labels { + &self.labels + } } impl MplsNlri { @@ -65,6 +69,7 @@ where Octs: AsRef<[u8]>, } } + impl fmt::Display for MplsNlri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "MPLS:{}", self.prefix()) diff --git a/src/bgp/nlri/nexthop.rs b/src/bgp/nlri/nexthop.rs new file mode 100644 index 00000000..595b80af --- /dev/null +++ b/src/bgp/nlri/nexthop.rs @@ -0,0 +1,157 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use octseq::{Octets, Parser}; +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; + +use crate::bgp::types::RouteDistinguisher; +use crate::util::parser::{parse_ipv4addr, parse_ipv6addr, ParseError}; + +use super::afisafi::AfiSafiType as AfiSafi; + +/// Conventional and BGP-MP Next Hop variants. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum NextHop { + Unicast(IpAddr), + Multicast(IpAddr), + Ipv6LL(Ipv6Addr, Ipv6Addr), // is this always unicast? + MplsVpnUnicast(RouteDistinguisher, IpAddr), + Empty, // FlowSpec + Unimplemented(AfiSafi), +} + +impl NextHop { + pub fn new(afisafi: AfiSafi) -> Self { + use AfiSafi::*; + match afisafi { + Ipv4Unicast => Self::Unicast(Ipv4Addr::from(0).into()), + Ipv6Unicast => Self::Unicast(Ipv6Addr::from(0).into()), + Ipv4Multicast => Self::Multicast(Ipv4Addr::from(0).into()), + Ipv6Multicast => Self::Multicast(Ipv6Addr::from(0).into()), + + Ipv4MplsUnicast => Self::Unicast(Ipv4Addr::from(0).into()), + Ipv6MplsUnicast => Self::Unicast(Ipv6Addr::from(0).into()), + + Ipv4MplsVpnUnicast => Self::MplsVpnUnicast( + RouteDistinguisher::zeroes(), + Ipv4Addr::from(0).into() + ), + Ipv6MplsVpnUnicast => Self::MplsVpnUnicast( + RouteDistinguisher::zeroes(), + Ipv6Addr::from(0).into() + ), + + Ipv4RouteTarget => Self::Unicast(Ipv4Addr::from(0).into()), + + Ipv4FlowSpec | Ipv6FlowSpec => Self::Empty, + + L2VpnVpls => Self::Unicast(Ipv4Addr::from(0).into()), + L2VpnEvpn => Self::Unicast(Ipv4Addr::from(0).into()), + AfiSafi::Unsupported(_, _) => Self::Unimplemented(afisafi) + } + } +} + +impl std::fmt::Display for NextHop { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Unicast(ip) | Self::Multicast(ip) => write!(f, "{}", ip), + Self::Ipv6LL(ip1, ip2) => write!(f, "{} {} ", ip1, ip2), + Self::MplsVpnUnicast(rd, ip) => write!(f, "rd {} {}", rd, ip), + Self::Empty => write!(f, "empty"), + Self::Unimplemented(afisafi) => { + write!(f, "unimplemented for AfiSafi {}", afisafi) + } + } + } +} + + + +//--- NextHop in MP_REACH_NLRI ----------------------------------------------- +impl NextHop { + pub fn parse(parser: &mut Parser<'_, R>, afisafi: AfiSafi) + -> Result + { + use AfiSafi::*; + let len = parser.parse_u8()?; + + macro_rules! error { + () => { return Err(ParseError::Unsupported) } + } + + let res = match afisafi { + + Ipv4Unicast | + Ipv4Multicast | + Ipv4RouteTarget | + L2VpnVpls | + L2VpnEvpn + => { + match len { + 4 => NextHop::Unicast(parse_ipv4addr(parser)?.into()), + _ => error!() + } + } + Ipv6Unicast => { + match len { + 16 => NextHop::Unicast(parse_ipv6addr(parser)?.into()), + 32 => NextHop::Ipv6LL( + parse_ipv6addr(parser)?, + parse_ipv6addr(parser)? + ), + _ => error!() + } + } + Ipv6Multicast => { + match len { + 16 => NextHop::Unicast(parse_ipv6addr(parser)?.into()), + _ => error!() + } + } + // RFC4684: the nexthop for MPLS can be of the other AFI than the + // NLRI themselves are. + Ipv4MplsUnicast | Ipv6MplsUnicast => { + match len { + 4 => NextHop::Unicast(parse_ipv4addr(parser)?.into()), + 16 => NextHop::Unicast(parse_ipv6addr(parser)?.into()), + _ => error!() + } + } + Ipv4MplsVpnUnicast => { + match len { + 12 => NextHop::MplsVpnUnicast( + RouteDistinguisher::parse(parser)?, + parse_ipv4addr(parser)?.into() + ), + _ => error!() + } + } + Ipv6MplsVpnUnicast => { + match len { + 24 => NextHop::MplsVpnUnicast( + RouteDistinguisher::parse(parser)?, + parse_ipv6addr(parser)?.into() + ), + _ => error!() + } + } + + Ipv4FlowSpec | Ipv6FlowSpec => Self::Empty, + + AfiSafi::Unsupported(_, _) => error!() + }; + + Ok(res) + } + + pub fn skip(parser: &mut Parser<'_, R>) + -> Result<(), ParseError> + { + let len = parser.parse_u8()?; + parser.advance(len.into())?; + Ok(()) + } +} + diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index df838e4b..4f55bf8f 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -7,9 +7,8 @@ use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; use crate::asn::Asn; use crate::bgp::message::{ - SessionConfig, + PduParseInfo, UpdateMessage, - nlri::FixedNlriIter, update_builder::ComposeError, }; use crate::bgp::message::update_builder::{ @@ -18,7 +17,7 @@ use crate::bgp::message::update_builder::{ StandardCommunitiesList }; use crate::bgp::communities::StandardCommunity; -use crate::bgp::types::{Afi, Safi, AfiSafi}; +use crate::bgp::nlri::afisafi::NlriCompose; use crate::util::parser::{ParseError, parse_ipv4addr}; @@ -126,8 +125,8 @@ impl PaMap { let mut pa_map = Self::empty(); for pa in pdu.path_attributes()? { if let Ok(pa) = pa { - if pa.type_code() != MpReachNlriBuilder::TYPE_CODE - && pa.type_code() != MpUnreachNlriBuilder::TYPE_CODE + if pa.type_code() != MpReachNlriBuilder::<()>::TYPE_CODE + && pa.type_code() != MpUnreachNlriBuilder::<()>::TYPE_CODE { if let PathAttributeType::Invalid(n) = pa.type_code().into() { warn!("invalid PA {}:\n{}", n, pdu.fmt_pcap_string()); @@ -452,25 +451,29 @@ macro_rules! path_attributes { #[derive(Debug)] pub struct EncodedPathAttribute<'a, Octs: Octets> { parser: Parser<'a, Octs>, - session_config: SessionConfig, + pdu_parse_info: PduParseInfo, } impl<'a, Octs: Octets> EncodedPathAttribute<'a, Octs> { fn new( parser: Parser<'a, Octs>, - session_config: SessionConfig + ppi: PduParseInfo, ) -> Self { - Self { parser, session_config } + Self { parser, pdu_parse_info: ppi } } - pub fn session_config(&self) -> SessionConfig { - self.session_config + pub fn pdu_parse_info(&self) -> PduParseInfo { + self.pdu_parse_info } - fn flags(&self) -> Flags { + pub fn flags(&self) -> Flags { self.parser.peek_all()[0].into() } - fn length(&self) -> usize { + pub fn type_code(&self) -> u8 { + self.parser.peek_all()[1] + } + + pub fn length(&self) -> usize { if self.flags().is_extended_length() { let raw = self.parser.peek(4).unwrap(); u16::from_be_bytes([raw[2], raw[3]]).into() @@ -511,7 +514,7 @@ macro_rules! path_attributes { impl<'a, Octs: Octets> WireformatPathAttribute<'a, Octs> { - fn parse(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + fn parse(parser: &mut Parser<'a, Octs>, ppi: PduParseInfo) -> Result, ParseError> { let start_pos = parser.pos(); @@ -528,7 +531,7 @@ macro_rules! path_attributes { $( $type_code => { if let Err(e) = <$data>::validate( - flags.into(), &mut pp, sc + flags.into(), &mut pp, ppi ) { debug!("failed to parse path attribute: {e}"); if $type_code == 14 { @@ -548,7 +551,7 @@ macro_rules! path_attributes { } else { pp.seek(start_pos)?; WireformatPathAttribute::$name( - EncodedPathAttribute::new(pp, sc) + EncodedPathAttribute::new(pp, ppi) ) } } @@ -576,13 +579,11 @@ macro_rules! path_attributes { { match self { $( - //WireformatPathAttribute::$name(p, sc) => { WireformatPathAttribute::$name(epa) => { Ok(PathAttribute::$name( - //$name::parse(&mut p.clone(), *sc)? <$data>::parse( &mut epa.value_into_parser(), - epa.session_config() + epa.pdu_parse_info() )? )) } @@ -751,8 +752,8 @@ path_attributes!( 8 => StandardCommunities(crate::bgp::message::update_builder::StandardCommunitiesList), Flags::OPT_TRANS, 9 => OriginatorId(crate::bgp::types::OriginatorId), Flags::OPT_NON_TRANS, 10 => ClusterList(crate::bgp::path_attributes::ClusterIds), Flags::OPT_NON_TRANS, - 14 => MpReachNlri(crate::bgp::message::update_builder::MpReachNlriBuilder), Flags::OPT_NON_TRANS, - 15 => MpUnreachNlri(crate::bgp::message::update_builder::MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, + //14 => MpReachNlri(crate::bgp::message::update_builder::MpReachNlriBuilder), Flags::OPT_NON_TRANS, + //15 => MpUnreachNlri(crate::bgp::message::update_builder::MpUnreachNlriBuilder), Flags::OPT_NON_TRANS, 16 => ExtendedCommunities(crate::bgp::path_attributes::ExtendedCommunitiesList), Flags::OPT_TRANS, 17 => As4Path(crate::bgp::types::As4Path), Flags::OPT_TRANS, 18 => As4Aggregator(crate::bgp::types::As4Aggregator), Flags::OPT_TRANS, @@ -918,11 +919,11 @@ pub trait Attribute: AttributeHeader + Clone { fn validate( flags: Flags, parser: &mut Parser<'_, Octs>, - sc: SessionConfig + ppi: PduParseInfo ) -> Result<(), ParseError>; - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, ppi: PduParseInfo) -> Result where Self: Sized, @@ -936,7 +937,7 @@ pub trait Attribute: AttributeHeader + Clone { #[derive(Debug)] pub struct PathAttributes<'a, Octs> { pub parser: Parser<'a, Octs>, - pub session_config: SessionConfig, + pub pdu_parse_info: PduParseInfo, } impl<'a, Octs> Clone for PathAttributes<'a, Octs> { @@ -948,10 +949,10 @@ impl<'a, Octs> Clone for PathAttributes<'a, Octs> { impl<'a, Octs> Copy for PathAttributes<'a, Octs> { } impl<'a, Octs: Octets> PathAttributes<'a, Octs> { - pub fn new(parser: Parser<'_, Octs>, session_config: SessionConfig) + pub fn new(parser: Parser<'_, Octs>, pdu_parse_info: PduParseInfo) -> PathAttributes<'_, Octs> { - PathAttributes { parser, session_config } + PathAttributes { parser, pdu_parse_info } } pub fn get(&self, pat: PathAttributeType) @@ -979,7 +980,7 @@ impl<'a, Octs: Octets> Iterator for PathAttributes<'a, Octs> { let res = WireformatPathAttribute::parse( &mut self.parser, - self.session_config + self.pdu_parse_info ); Some(res) } @@ -997,6 +998,37 @@ macro_rules! check_len_exact { } } +pub struct UncheckedPathAttributes<'a, Octs> { + parser: Parser<'a, Octs>, +} + +impl<'a, Octs> UncheckedPathAttributes<'a, Octs> { + pub fn from_parser(parser: Parser<'a, Octs>) -> Self { + Self { parser } + } +} + +impl<'a, Octs: Octets> Iterator for UncheckedPathAttributes<'a, Octs> { + type Item = EncodedPathAttribute<'a, Octs>; + fn next(&mut self) -> Option { + if self.parser.remaining() == 0 { + return None + } + let pos = self.parser.pos(); + let flags = self.parser.parse_u8().ok()?; + let _type_code = self.parser.parse_u8().ok()?; + let (header_len, len) = match flags & 0x10 == 0x10 { + true => (4, self.parser.parse_u16_be().ok()? as usize), + false => (3, self.parser.parse_u8().ok()? as usize), + }; + + let _ = self.parser.seek(pos); + let pp = self.parser.parse_parser(header_len + len).ok()?; + Some(EncodedPathAttribute::new(pp, PduParseInfo::default())) + } +} + + //--- Origin impl Attribute for crate::bgp::types::Origin { @@ -1008,7 +1040,7 @@ impl Attribute for crate::bgp::types::Origin { target.append_slice(&[self.0.into()]) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'_, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'_, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(parser.parse_u8()?.into())) @@ -1017,7 +1049,7 @@ impl Attribute for crate::bgp::types::Origin { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 1, "ORIGIN") } @@ -1038,13 +1070,13 @@ impl Attribute for crate::bgp::aspath::HopPath { ) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, ppi: PduParseInfo) -> Result { // XXX reusing the old/existing AsPath here for the time being let asp = crate::bgp::aspath::AsPath::new( parser.peek_all().to_vec(), - sc.has_four_octet_asn() + ppi.has_four_octet_asn() ).map_err(|_| ParseError::form_error("invalid AS_PATH"))?; Ok(asp.to_hop_path()) @@ -1053,9 +1085,9 @@ impl Attribute for crate::bgp::aspath::HopPath { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - session_config: SessionConfig + pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { - let asn_size = if session_config.has_four_octet_asn() { + let asn_size = if pdu_parse_info.has_four_octet_asn() { 4 } else { 2 @@ -1085,7 +1117,7 @@ impl Attribute for crate::bgp::types::ConventionalNextHop { target.append_slice(&self.0.octets()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(parse_ipv4addr(parser)?)) @@ -1094,7 +1126,7 @@ impl Attribute for crate::bgp::types::ConventionalNextHop { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 4, "NEXT_HOP") } @@ -1111,7 +1143,7 @@ impl Attribute for crate::bgp::types::MultiExitDisc { target.append_slice(&self.0.to_be_bytes()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(parser.parse_u32_be()?)) @@ -1120,7 +1152,7 @@ impl Attribute for crate::bgp::types::MultiExitDisc { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 4, "MULTI_EXIT_DISC") } @@ -1137,7 +1169,7 @@ impl Attribute for crate::bgp::types::LocalPref { target.append_slice(&self.0.to_be_bytes()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(parser.parse_u32_be()?)) @@ -1146,7 +1178,7 @@ impl Attribute for crate::bgp::types::LocalPref { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 4, "LOCAL_PREF") } @@ -1163,7 +1195,7 @@ impl Attribute for crate::bgp::types::AtomicAggregate { Ok(()) } - fn parse<'a, Octs: 'a + Octets>(_parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(_parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self) @@ -1172,7 +1204,7 @@ impl Attribute for crate::bgp::types::AtomicAggregate { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 0, "ATOMIC_AGGREGATE") } @@ -1222,10 +1254,10 @@ impl Attribute for AggregatorInfo { target.append_slice(&self.address().octets()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, ppi: PduParseInfo) -> Result { - let asn = if sc.has_four_octet_asn() { + let asn = if ppi.has_four_octet_asn() { Asn::from_u32(parser.parse_u32_be()?) } else { Asn::from_u32(parser.parse_u16_be()?.into()) @@ -1238,12 +1270,12 @@ impl Attribute for AggregatorInfo { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - session_config: SessionConfig + pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { //if flags != Self::FLAGS.into() { // return Err(ParseError::form_error("invalid flags")); //} - if session_config.has_four_octet_asn() { + if pdu_parse_info.has_four_octet_asn() { check_len_exact!(parser, 8, "AGGREGATOR")?; } else { check_len_exact!(parser, 6, "AGGREGATOR")?; @@ -1274,7 +1306,7 @@ impl Attribute for crate::bgp::message::update_builder::StandardCommunitiesList Ok(()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let mut builder = StandardCommunitiesList::with_capacity( @@ -1290,7 +1322,7 @@ impl Attribute for crate::bgp::message::update_builder::StandardCommunitiesList fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { if parser.remaining() % 4 != 0 { return Err(ParseError::form_error( @@ -1319,7 +1351,7 @@ impl Attribute for crate::bgp::types::OriginatorId { target.append_slice(&self.0.octets()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(parse_ipv4addr(parser)?)) @@ -1328,7 +1360,7 @@ impl Attribute for crate::bgp::types::OriginatorId { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 4, "ORIGINATOR_ID") } @@ -1376,7 +1408,7 @@ impl Attribute for ClusterIds { Ok(()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let mut cluster_ids = Vec::with_capacity(parser.remaining() / 4); @@ -1389,7 +1421,7 @@ impl Attribute for ClusterIds { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { if parser.remaining() % 4 != 0 { return Err(ParseError::form_error( @@ -1401,7 +1433,23 @@ impl Attribute for ClusterIds { } //--- MpReachNlri -impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { +impl AttributeHeader for MpReachNlriBuilder { + const FLAGS: u8 = Flags::OPT_NON_TRANS; + const TYPE_CODE: u8 = 14; +} +impl FromAttribute for MpReachNlriBuilder { + fn from_attribute(_value: PathAttribute) -> Option> { + None + } + + fn attribute_type() -> Option { + None + //Some(PathAttributeType::MpReachNlriBuilder + } +} + + +impl Attribute for MpReachNlriBuilder { fn value_len(&self) -> usize { self.value_len() } @@ -1412,13 +1460,18 @@ impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { self.compose_value(target) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) - -> Result + fn parse<'a, Octs: 'a + Octets>( + _parser: &mut Parser<'a, Octs>, + //sc: SessionConfig, + _ppi: PduParseInfo, + ) -> Result where Vec: OctetsFrom> { - let afi: Afi = parser.parse_u16_be()?.into(); - let safi: Safi = parser.parse_u8()?.into(); + todo!() + /* + let afi = parser.parse_u16_be()?; + let safi= parser.parse_u8()?; let afisafi = AfiSafi::try_from((afi, safi)) .map_err(|_| ParseError::Unsupported)?; let nexthop = crate::bgp::types::NextHop::parse(parser, afisafi)?; @@ -1431,16 +1484,22 @@ impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { let afisafi = AfiSafi::try_from((afi, safi)) .map_err(|_| ParseError::Unsupported)?; - let mut builder = MpReachNlriBuilder::new( - afi, - safi, + let mut builder = MpReachNlriBuilder::::new( + afisafi, nexthop, - sc.rx_addpath(afisafi) + //sc.rx_addpath(afisafi), + ppi.mp_reach_addpath(), ); + todo!() + */ + // TODO figure out how much sense it actually makes to create an + // MpReachNlriBuilder here. + + /* let nlri_iter = crate::bgp::message::update::Nlris::new( *parser, - sc, + ppi, afisafi ).iter(); @@ -1449,12 +1508,13 @@ impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { } Ok(builder) + */ } fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - session_config: SessionConfig + _ppi: PduParseInfo ) -> Result<(), ParseError> { // We only check for the bare minimum here, as most checks are // better done upon (creation of the) Nlri iterator based on the @@ -1464,10 +1524,12 @@ impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { "length for MP_REACH_NLRI less than minimum" )) } + Ok(()) - let afi: Afi = parser.parse_u16_be()?.into(); - let safi: Safi = parser.parse_u8()?.into(); + /* + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; let afisafi = AfiSafi::try_from((afi, safi)) .map_err(|_| ParseError::Unsupported)?; let _nexthop = crate::bgp::types::NextHop::parse(parser, afisafi)?; @@ -1477,7 +1539,7 @@ impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { //} parser.advance(1)?; // reserved byte - let expect_path_id = session_config.rx_addpath(afisafi); + let expect_path_id = session_config.mp_reach_addpath(); use AfiSafi::*; match (afisafi, expect_path_id) { @@ -1528,11 +1590,18 @@ impl Attribute for crate::bgp::message::update_builder::MpReachNlriBuilder { Ok(()) } } + */ } } + //--- MpUnreachNlri -impl Attribute for crate::bgp::message::update_builder::MpUnreachNlriBuilder { +impl AttributeHeader for MpUnreachNlriBuilder { + const FLAGS: u8 = Flags::OPT_NON_TRANS; + const TYPE_CODE: u8 = 15; +} + +impl Attribute for MpUnreachNlriBuilder { fn value_len(&self) -> usize { self.value_len() } @@ -1543,25 +1612,33 @@ impl Attribute for crate::bgp::message::update_builder::MpUnreachNlriBuilder { self.compose_value(target) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(_parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result where Vec: OctetsFrom> { - let afi: Afi = parser.parse_u16_be()?.into(); - let safi: Safi = parser.parse_u8()?.into(); + todo!() + /* + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; let afisafi = AfiSafi::try_from((afi, safi)) .map_err(|_| ParseError::Unsupported)?; let mut builder = MpUnreachNlriBuilder::new( - afi, - safi, - sc.rx_addpath(afisafi) + afisafi, + //sc.rx_addpath(afisafi), + ppi.mp_unreach_addpath(), ); + todo!() + */ + // TODO figure out how much sense it makes to actually make an + // MpUnreachBuilder here. + + /* let nlri_iter = crate::bgp::message::update::Nlris::new( *parser, - sc, + ppi, afisafi, ).iter(); @@ -1569,12 +1646,13 @@ impl Attribute for crate::bgp::message::update_builder::MpUnreachNlriBuilder { builder.add_withdrawal(&nlri?); } Ok(builder) + */ } fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { // We only check for the bare minimum here, as most checks are // better done upon (creation of the) Nlri iterator based on the @@ -1584,14 +1662,16 @@ impl Attribute for crate::bgp::message::update_builder::MpUnreachNlriBuilder { "length for MP_UNREACH_NLRI less than minimum" )) } + Ok(()) + /* - let afi: Afi = parser.parse_u16_be()?.into(); - let safi: Safi = parser.parse_u8()?.into(); + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; let afisafi = AfiSafi::try_from((afi, safi)) .map_err(|_| ParseError::Unsupported)?; - let expect_path_id = session_config.rx_addpath(afisafi); + let expect_path_id = session_config.mp_unreach_addpath(); use AfiSafi::*; match (afisafi, expect_path_id) { @@ -1642,6 +1722,7 @@ impl Attribute for crate::bgp::message::update_builder::MpUnreachNlriBuilder { Ok(()) } } + */ } } @@ -1689,7 +1770,7 @@ impl Attribute for crate::bgp::path_attributes::ExtendedCommunitiesList { Ok(()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let mut communities = Vec::with_capacity(parser.remaining() / 8); @@ -1704,7 +1785,7 @@ impl Attribute for crate::bgp::path_attributes::ExtendedCommunitiesList { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { if parser.remaining() % 8 != 0 { return Err(ParseError::form_error( @@ -1730,13 +1811,13 @@ impl Attribute for crate::bgp::types::As4Path { ) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, ppi: PduParseInfo) -> Result { // XXX Same as with AsPath, reusing the old/existing As4Path here let asp = crate::bgp::aspath::AsPath::new( parser.peek_all().to_vec(), - sc.has_four_octet_asn() + ppi.has_four_octet_asn() ).map_err(|_| ParseError::form_error("invalid AS4_PATH"))?; Ok(Self(asp.to_hop_path())) } @@ -1744,7 +1825,7 @@ impl Attribute for crate::bgp::types::As4Path { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { while parser.remaining() > 0 { let segment_type = parser.parse_u8()?; @@ -1773,7 +1854,7 @@ impl Attribute for crate::bgp::types::As4Aggregator { target.append_slice(&self.0.address().octets()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let asn = Asn::from_u32(parser.parse_u32_be()?); @@ -1784,7 +1865,7 @@ impl Attribute for crate::bgp::types::As4Aggregator { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { check_len_exact!(parser, 8, "AS4_AGGREGATOR") } @@ -1802,7 +1883,7 @@ impl Attribute for crate::bgp::types::Connector { target.append_slice(&self.0.octets()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(parse_ipv4addr(parser)?)) @@ -1811,7 +1892,7 @@ impl Attribute for crate::bgp::types::Connector { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _sc: SessionConfig + _ppi: PduParseInfo ) -> Result<(), ParseError> { @@ -1844,7 +1925,7 @@ impl Attribute for AsPathLimitInfo { target.append_slice(&self.attacher.to_raw()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let info = AsPathLimitInfo { @@ -1858,7 +1939,7 @@ impl Attribute for AsPathLimitInfo { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _sc: SessionConfig + _ppi: PduParseInfo ) -> Result<(), ParseError> { @@ -1909,7 +1990,7 @@ impl Attribute for Ipv6ExtendedCommunitiesList { Ok(()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let mut communities = Vec::with_capacity(parser.remaining() / 20); @@ -1924,7 +2005,7 @@ impl Attribute for Ipv6ExtendedCommunitiesList { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { if parser.remaining() % 20 != 0 { return Err(ParseError::form_error( @@ -1980,7 +2061,7 @@ impl Attribute for LargeCommunitiesList { Ok(()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let mut communities = Vec::with_capacity(parser.remaining() / 12); @@ -1995,7 +2076,7 @@ impl Attribute for LargeCommunitiesList { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _session_config: SessionConfig + _pdu_parse_info: PduParseInfo ) -> Result<(), ParseError> { if parser.remaining() % 12 != 0 { return Err(ParseError::form_error( @@ -2017,7 +2098,7 @@ impl Attribute for crate::bgp::types::Otc { target.append_slice(&self.0.to_raw()) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { Ok(Self(Asn::from_u32(parser.parse_u32_be()?))) @@ -2026,7 +2107,7 @@ impl Attribute for crate::bgp::types::Otc { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _sc: SessionConfig + _ppi: PduParseInfo ) -> Result<(), ParseError> { @@ -2061,7 +2142,7 @@ impl Attribute for AttributeSet { target.append_slice(&self.attributes) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let origin = Asn::from_u32(parser.parse_u32_be()?); @@ -2072,7 +2153,7 @@ impl Attribute for AttributeSet { fn validate( _flags: Flags, parser: &mut Parser<'_, Octs>, - _sc: SessionConfig + _ppi: PduParseInfo ) -> Result<(), ParseError> { @@ -2113,7 +2194,7 @@ impl Attribute for ReservedRaw { target.append_slice(&self.raw) } - fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _sc: SessionConfig) + fn parse<'a, Octs: 'a + Octets>(parser: &mut Parser<'a, Octs>, _ppi: PduParseInfo) -> Result { let raw = parser.peek_all().to_vec(); @@ -2123,7 +2204,7 @@ impl Attribute for ReservedRaw { fn validate( _flags: Flags, _parser: &mut Parser<'_, Octs>, - _sc: SessionConfig + _ppi: PduParseInfo ) -> Result<(), ParseError> { @@ -2171,11 +2252,11 @@ impl UnimplementedPathAttribute { #[cfg(test)] mod tests { + use super::*; + use crate::asn::Asn; use crate::bgp::communities::Wellknown; - use crate::bgp::message::nlri::Nlri; - use crate::bgp::message::update::NextHop; use crate::bgp::aspath::HopPath; use crate::bgp::types::OriginType; @@ -2184,7 +2265,7 @@ mod tests { use super::PathAttribute as PA; fn check(raw: Vec, owned: PathAttribute) { let mut parser = Parser::from_ref(&raw); - let sc = SessionConfig::modern(); + let sc = PduParseInfo::modern(); let pa = WireformatPathAttribute::parse(&mut parser, sc) .unwrap(); assert_eq!(owned, pa.to_owned().unwrap()); @@ -2264,6 +2345,8 @@ mod tests { ClusterIds::new(vec![[10, 0, 0, 3].into()]).into() ); + /* + // MpBuilders are not variants of PathAttribute anymore... check( vec![ 0x80, 0x0e, 0x1c, @@ -2275,14 +2358,13 @@ mod tests { 0x30, 0x20, 0x01, 0x0d, 0xb8, 0xaa, 0xbb ], { - let mut builder = MpReachNlriBuilder::new( - Afi::Ipv6, - Safi::Unicast, + let mut builder = MpReachNlriBuilder::::for_nexthop( NextHop::Unicast("2001:db8::1234".parse().unwrap()), - false // no addpath ); builder.add_announcement( - &Nlri::unicast_from_str("2001:db8:aabb::/48").unwrap() + Ipv6UnicastNlri::try_from( + Prefix::from_str("2001:db8:aabb::/48").unwrap() + ).unwrap() ); builder.into() @@ -2299,8 +2381,6 @@ mod tests { 0x00, 0x03 ], { - use crate::addr::Prefix; - use std::str::FromStr; let mut builder = MpUnreachNlriBuilder::new( Afi::Ipv6, Safi::Multicast, @@ -2322,6 +2402,7 @@ mod tests { builder.into() } ); + */ check( vec![ @@ -2422,7 +2503,7 @@ mod tests { 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0xff // MED ]; let pas = PathAttributes::new( - Parser::from_ref(&raw), SessionConfig::modern() + Parser::from_ref(&raw), PduParseInfo::modern() ); //for _ in 0..4 { // let pa = pas.next(); @@ -2441,7 +2522,7 @@ mod tests { 0xc0, 254, 0x04, 0x01, 0x02, 0x03, 0x04 ]; let mut parser = Parser::from_ref(&raw); - let sc = SessionConfig::modern(); + let sc = PduParseInfo::modern(); let pa = WireformatPathAttribute::parse(&mut parser, sc); if let Ok(WireformatPathAttribute::Unimplemented(u)) = pa { @@ -2463,7 +2544,7 @@ mod tests { ]; let mut parser = Parser::from_ref(&raw); let pa = WireformatPathAttribute::parse( - &mut parser, SessionConfig::modern() + &mut parser, PduParseInfo::modern() ).unwrap(); assert!(matches!(pa, WireformatPathAttribute::Invalid(_,_,_))); } @@ -2505,7 +2586,7 @@ mod tests { ]; let pa = WireformatPathAttribute::parse( - &mut Parser::from_ref(&raw), SessionConfig::modern() + &mut Parser::from_ref(&raw), PduParseInfo::modern() ).unwrap(); let mut owned = pa.to_owned().unwrap(); if let PathAttribute::AsPath(ref mut asp) = owned { @@ -2520,4 +2601,27 @@ mod tests { assert!(composed != raw); } + #[test] + fn pamap() { + use crate::bgp::types::LocalPref as LP; + use crate::bgp::types::MultiExitDisc as MED; + + let mut pamap = PaMap::empty(); + pamap.set(LP(100)); + pamap.set::(MED(12)); + + let _lp1 = pamap.remove::(); + //let _lp2 = pamap.take::(); + dbg!(&pamap); + + //pamap.set(NH::new(AfiSafi::Ipv4Unicast)); + //let nh = pamap.get::(); + //dbg!(nh); + + dbg!(&pamap); + + let asp = HopPath::new(); + pamap.set(asp); + dbg!(&pamap); + } } diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 33830b8f..9176a2c4 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -1,158 +1,22 @@ -use crate::asn::Asn; -use crate::typeenum; // from util::macros use std::fmt; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::Ipv4Addr; -use crate::bgp::message::nlri::RouteDistinguisher; #[cfg(feature = "serde")] use serde::{Serialize, Deserialize}; +use crate::asn::Asn; +pub use crate::bgp::nlri::afisafi::Afi; +pub use crate::bgp::nlri::common::PathId; +pub use crate::bgp::nlri::mpls_vpn::RouteDistinguisher; +pub use crate::bgp::nlri::nexthop::NextHop; +use crate::typeenum; // from util::macros + use super::aspath::HopPath; use super::path_attributes::AggregatorInfo; -typeenum!( -/// AFI as used in BGP OPEN and UPDATE messages. -#[cfg_attr(feature = "serde", serde(from = "u16"))] - Afi, u16, - { - 1 => Ipv4, - 2 => Ipv6, - 25 => L2Vpn - }); - -typeenum!( -/// SAFI as used in BGP OPEN and UPDATE messages. -#[cfg_attr(feature = "serde", serde(from = "u8"))] - Safi, u8, - { - 1 => Unicast, - 2 => Multicast, - 4 => MplsUnicast, - 65 => Vpls, - 70 => Evpn, - 128 => MplsVpnUnicast, - 132 => RouteTarget, - 133 => FlowSpec, - 134 => FlowSpecVpn - }); - -/// Valid/supported pair of `AFI` and `SAFI`. -/// -/// Not all combinations of the `AFI` and `SAFI` variants make sense. This -/// enum explicitly comprises combinations which are described in standards -/// documents. -#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum AfiSafi { - Ipv4Unicast, - Ipv6Unicast, - Ipv4Multicast, - Ipv6Multicast, - - Ipv4MplsUnicast, - Ipv6MplsUnicast, - - Ipv4MplsVpnUnicast, - Ipv6MplsVpnUnicast, - - Ipv4RouteTarget, - - Ipv4FlowSpec, - Ipv6FlowSpec, - - L2VpnVpls, - L2VpnEvpn, -} - -impl TryFrom<(Afi, Safi)> for AfiSafi { - type Error = &'static str; - fn try_from(t: (Afi, Safi)) -> Result { - - use AfiSafi::*; - match t { - (Afi::Ipv4, Safi::Unicast) => Ok(Ipv4Unicast), - (Afi::Ipv6, Safi::Unicast) => Ok(Ipv6Unicast), - - (Afi::Ipv4, Safi::Multicast) => Ok(Ipv4Multicast), - (Afi::Ipv6, Safi::Multicast) => Ok(Ipv6Multicast), - - (Afi::Ipv4, Safi::MplsUnicast) => Ok(Ipv4MplsUnicast), - (Afi::Ipv6, Safi::MplsUnicast) => Ok(Ipv6MplsUnicast), - - (Afi::Ipv4, Safi::MplsVpnUnicast) => Ok(Ipv4MplsVpnUnicast), - (Afi::Ipv6, Safi::MplsVpnUnicast) => Ok(Ipv6MplsVpnUnicast), - - (Afi::Ipv4, Safi::RouteTarget) => Ok(Ipv4RouteTarget), - - (Afi::Ipv4, Safi::FlowSpec) => Ok(Ipv4FlowSpec), - (Afi::Ipv6, Safi::FlowSpec) => Ok(Ipv6FlowSpec), - - (Afi::L2Vpn, Safi::Vpls) => Ok(L2VpnVpls), - (Afi::L2Vpn, Safi::Evpn) => Ok(L2VpnEvpn), - _ => Err("unsupported Afi/Safi combination") - } - } -} - -impl AfiSafi { - pub fn afi(&self) -> Afi { - self.split().0 - } - - pub fn safi(&self) -> Safi { - self.split().1 - } - - pub fn split(&self) -> (Afi, Safi) { - match self { - Self::Ipv4Unicast => (Afi::Ipv4, Safi::Unicast), - Self::Ipv6Unicast => (Afi::Ipv6, Safi::Unicast), - Self::Ipv4Multicast => (Afi::Ipv4, Safi::Multicast), - Self::Ipv6Multicast => (Afi::Ipv6, Safi::Multicast), - Self::Ipv4MplsUnicast => (Afi::Ipv4, Safi::MplsUnicast), - Self::Ipv6MplsUnicast => (Afi::Ipv6, Safi::MplsUnicast), - - Self::Ipv4MplsVpnUnicast => (Afi::Ipv4, Safi::MplsVpnUnicast), - Self::Ipv6MplsVpnUnicast => (Afi::Ipv6, Safi::MplsVpnUnicast), - - Self::Ipv4RouteTarget => (Afi::Ipv4, Safi::RouteTarget), - - Self::Ipv4FlowSpec => (Afi::Ipv4, Safi::FlowSpec), - Self::Ipv6FlowSpec => (Afi::Ipv6, Safi::FlowSpec), - - Self::L2VpnVpls => (Afi::L2Vpn, Safi::Vpls), - Self::L2VpnEvpn => (Afi::L2Vpn, Safi::Evpn), - } - } -} - -impl fmt::Display for AfiSafi { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Ipv4Unicast => write!(f, "Ipv4Unicast"), - Self::Ipv6Unicast => write!(f, "Ipv6Unicast"), - Self::Ipv4Multicast => write!(f, "Ipv4Multicast"), - Self::Ipv6Multicast => write!(f, "Ipv6Multicast"), - - Self::Ipv4MplsUnicast => write!(f, "Ipv4MplsUnicast"), - Self::Ipv6MplsUnicast => write!(f, "Ipv6MplsUnicast"), - - Self::Ipv4MplsVpnUnicast => write!(f, "Ipv4MplsVpnUnicast"), - Self::Ipv6MplsVpnUnicast => write!(f, "Ipv6MplsVpnUnicast"), - - Self::Ipv4RouteTarget => write!(f, "Ipv4RouteTarget"), - - Self::Ipv4FlowSpec => write!(f, "Ipv4FlowSpec"), - Self::Ipv6FlowSpec => write!(f, "Ipv6FlowSpec"), - - Self::L2VpnVpls => write!(f, "L2VpnVpls"), - Self::L2VpnEvpn => write!(f, "L2VpnEvpn"), - } - - } -} +pub use crate::bgp::nlri::afisafi::AfiSafiType as AfiSafi; #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] @@ -215,6 +79,16 @@ impl TryFrom for AddpathDirection { } +impl From for u8 { + fn from(apd: AddpathDirection) -> u8 { + match apd { + AddpathDirection::Receive => 1, + AddpathDirection::Send => 2, + AddpathDirection::SendReceive => 3, + } + } +} + typeenum!( /// BGP Origin types as used in BGP UPDATE messages. @@ -339,62 +213,6 @@ impl std::fmt::Display for Otc { } } -/// Conventional and BGP-MP Next Hop variants. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum NextHop { - Unicast(IpAddr), - Multicast(IpAddr), - Ipv6LL(Ipv6Addr, Ipv6Addr), // is this always unicast? - MplsVpnUnicast(RouteDistinguisher, IpAddr), - Empty, // FlowSpec - Unimplemented(Afi, Safi), -} - -impl NextHop { - pub fn new(afisafi: AfiSafi) -> Self { - use AfiSafi::*; - match afisafi { - Ipv4Unicast => Self::Unicast(Ipv4Addr::from(0).into()), - Ipv6Unicast => Self::Unicast(Ipv6Addr::from(0).into()), - Ipv4Multicast => Self::Multicast(Ipv4Addr::from(0).into()), - Ipv6Multicast => Self::Multicast(Ipv6Addr::from(0).into()), - - Ipv4MplsUnicast => Self::Unicast(Ipv4Addr::from(0).into()), - Ipv6MplsUnicast => Self::Unicast(Ipv6Addr::from(0).into()), - - Ipv4MplsVpnUnicast => Self::MplsVpnUnicast( - RouteDistinguisher::zeroes(), - Ipv4Addr::from(0).into() - ), - Ipv6MplsVpnUnicast => Self::MplsVpnUnicast( - RouteDistinguisher::zeroes(), - Ipv6Addr::from(0).into() - ), - - Ipv4RouteTarget => Self::Unicast(Ipv4Addr::from(0).into()), - - Ipv4FlowSpec | Ipv6FlowSpec => Self::Empty, - - L2VpnVpls => Self::Unicast(Ipv4Addr::from(0).into()), - L2VpnEvpn => Self::Unicast(Ipv4Addr::from(0).into()), - } - } -} - -impl std::fmt::Display for NextHop { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Unicast(ip) | Self::Multicast(ip) => write!(f, "{}", ip), - Self::Ipv6LL(ip1, ip2) => write!(f, "{} {} ", ip1, ip2), - Self::MplsVpnUnicast(rd, ip) => write!(f, "rd {} {}", rd, ip), - Self::Empty => write!(f, "empty"), - Self::Unimplemented(afi, safi) => write!(f, "unimplemented for AFI {} /SAFI {}", afi, safi), - } - } -} - - /// Conventional NextHop only, this gets stored in the /// `PathAttribute::ConventionalNextHop` variant. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/src/bgp/workshop/afisafi_nlri.rs b/src/bgp/workshop/afisafi_nlri.rs index 89721a0c..f50c4ea5 100644 --- a/src/bgp/workshop/afisafi_nlri.rs +++ b/src/bgp/workshop/afisafi_nlri.rs @@ -5,19 +5,20 @@ use octseq::Octets; use crate::{ addr::Prefix, bgp::{message::{ - nlri::{BasicNlri, FlowSpecNlri, Nlri}, + //nlri::{BasicNlri, FlowSpecNlri, Nlri}, update::AfiSafi, update_builder::ComposeError, }, path_attributes::PaMap, ParseError}, }; +use crate::bgp::nlri::afisafi::Nlri; use super::route::RouteWorkshop; //------------ AfiSafiNlri --------------------------------------------------- -pub trait AfiSafiNlri: Clone + Hash + Debug { +pub trait AfiSafiNlri: Clone + Hash + Debug { type Nlri; fn nlri(&self) -> Self::Nlri; - fn afi_safi() -> AfiSafi; + fn afi_safi(&self) -> AfiSafi; } @@ -26,7 +27,7 @@ pub trait AfiSafiNlri: Clone + Hash + Debug { pub trait HasBasicNlri { fn basic_nlri(&self) -> BasicNlri; fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri; + -> RouteWorkshop where M: AfiSafiNlri; } @@ -35,14 +36,14 @@ pub trait HasBasicNlri { #[derive(Clone, Debug, Hash)] pub struct Ipv4UnicastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv4UnicastNlri { +impl AfiSafiNlri for Ipv4UnicastNlri { type Nlri = BasicNlri; fn nlri(&self) -> Self::Nlri { self.0 } - fn afi_safi() -> AfiSafi { + fn afi_safi(&self) -> AfiSafi { AfiSafi::Ipv4Unicast } } @@ -53,8 +54,8 @@ impl HasBasicNlri for Ipv4UnicastNlri { } fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri { - RouteWorkshop::::from_pa_map(nlri, pa.clone()) + -> RouteWorkshop where M: AfiSafiNlri { + RouteWorkshop::::from_pa_map(nlri, pa.clone()) } } @@ -81,14 +82,14 @@ impl TryFrom> #[derive(Clone, Debug, Hash)] pub struct Ipv6UnicastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv6UnicastNlri { +impl AfiSafiNlri for Ipv6UnicastNlri { type Nlri = BasicNlri; fn nlri(&self) -> Self::Nlri { self.0 } - fn afi_safi() -> AfiSafi { + fn afi_safi(&self) -> AfiSafi { AfiSafi::Ipv6Unicast } } @@ -108,8 +109,8 @@ impl HasBasicNlri for Ipv6UnicastNlri { } fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri { - RouteWorkshop::::from_pa_map(nlri, pa.clone()) + -> RouteWorkshop where M: AfiSafiNlri { + RouteWorkshop::::from_pa_map(nlri, pa.clone()) } } @@ -137,14 +138,14 @@ impl TryFrom> #[derive(Clone, Debug, Hash)] pub struct Ipv4MulticastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv4MulticastNlri { +impl AfiSafiNlri for Ipv4MulticastNlri { type Nlri = BasicNlri; fn nlri(&self) -> Self::Nlri { self.0 } - fn afi_safi() -> AfiSafi { + fn afi_safi(&self) -> AfiSafi { AfiSafi::Ipv4Multicast } } @@ -155,8 +156,8 @@ impl HasBasicNlri for Ipv4MulticastNlri { } fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri { - RouteWorkshop::::from_pa_map(nlri, pa.clone()) + -> RouteWorkshop where M: AfiSafiNlri { + RouteWorkshop::::from_pa_map(nlri, pa.clone()) } } @@ -184,14 +185,14 @@ impl TryFrom> #[derive(Clone, Debug, Hash)] pub struct Ipv6MulticastNlri(pub BasicNlri); -impl AfiSafiNlri for Ipv6MulticastNlri { +impl AfiSafiNlri for Ipv6MulticastNlri { type Nlri = BasicNlri; fn nlri(&self) -> Self::Nlri { self.0 } - fn afi_safi() -> AfiSafi { + fn afi_safi(&self) -> AfiSafi { AfiSafi::Ipv6Multicast } } @@ -220,7 +221,7 @@ impl TryFrom> #[derive(Clone, Debug, Hash)] pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); -impl AfiSafiNlri +impl AfiSafiNlri for Ipv4FlowSpecNlri { type Nlri = Ipv4FlowSpecNlri; @@ -229,7 +230,7 @@ impl AfiSafiNlri Ipv4FlowSpecNlri(self.0.clone()) } - fn afi_safi() -> AfiSafi { + fn afi_safi(&self) -> AfiSafi { AfiSafi::Ipv4FlowSpec } } @@ -245,4 +246,22 @@ impl TryFrom>> for Ipv4UnicastNlri { } Err(ParseError::Unsupported) } -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + + use super::*; + use std::str::FromStr; + + #[test] + fn basic() { + let basic: BasicNlri = Prefix::from_str("10.1.1.0/24").unwrap().into(); + let nlri = Nlri::<()>::Unicast(basic); + let pamap = PaMap::empty(); + let ws = RouteWorkshop::from_pa_map(nlri, pamap.clone()); + let ws2 = RouteWorkshop::from_pa_map(basic, pamap); + dbg!(ws); + dbg!(ws2); + } +} diff --git a/src/bgp/workshop/mod.rs b/src/bgp/workshop/mod.rs index 62eacc4e..acbdde97 100644 --- a/src/bgp/workshop/mod.rs +++ b/src/bgp/workshop/mod.rs @@ -1,2 +1,2 @@ pub mod route; -pub mod afisafi_nlri; \ No newline at end of file +//pub mod afisafi_nlri; diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 266feb45..144d3807 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -1,22 +1,26 @@ use std::fmt::Debug; use std::hash::Hash; -use std::marker::PhantomData; +//use std::marker::PhantomData; use octseq::{Octets, OctetsFrom}; -use serde::Serialize; use crate::bgp::communities::Community; -use crate::bgp::message::update_builder::{ComposeError, MpReachNlriBuilder}; +use crate::bgp::message::update_builder::{ComposeError, /*MpReachNlriBuilder*/}; use crate::bgp::message::UpdateMessage; use crate::bgp::path_attributes::{FromAttribute, PaMap}; use crate::bgp::{ - message::{nlri::Nlri, update_builder::StandardCommunitiesList}, + message::{ + //nlri::Nlri, + update_builder::StandardCommunitiesList + }, path_attributes::{ ExtendedCommunitiesList, Ipv6ExtendedCommunitiesList, LargeCommunitiesList, PathAttribute, }, }; +use crate::bgp::nlri::afisafi::Nlri; + //------------ TypedRoute ---------------------------------------------------- @@ -29,7 +33,8 @@ pub enum TypedRoute { //------------ Route --------------------------------------------------------- -#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize)] +#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Route(N, PaMap); impl Route { @@ -76,19 +81,20 @@ impl From>> for PathAttribute { //------------ The Workshop -------------------------------------------------- -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)] -pub struct RouteWorkshop(N, PaMap, PhantomData); +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct RouteWorkshop(N, PaMap); -impl RouteWorkshop { +impl RouteWorkshop { pub fn new(nlri: N) -> Self { - Self(nlri, PaMap::empty(), PhantomData) + Self(nlri, PaMap::empty()) } pub fn from_pa_map(nlri: N, pa_map: PaMap) -> Self { - Self(nlri, pa_map, PhantomData) + Self(nlri, pa_map) } - pub fn from_update_pdu( + pub fn from_update_pdu( nlri: N, pdu: &UpdateMessage, ) -> Result @@ -96,7 +102,7 @@ impl RouteWorkshop { for<'a> Vec: OctetsFrom>, { PaMap::from_update_pdu(pdu) - .map(|r| Self(nlri, r, PhantomData)) + .map(|r| Self(nlri, r)) } pub fn nlri(&self) -> &N { @@ -110,7 +116,7 @@ impl RouteWorkshop { WA::store(value, &mut self.1) } - pub fn get_attr>( + pub fn get_attr>( &self, ) -> Option { self.1.get::().or_else(|| A::retrieve(&self.1)) @@ -162,9 +168,20 @@ impl_workshop!( crate::bgp::path_attributes::ClusterIds crate::bgp::message::update_builder::StandardCommunitiesList crate::bgp::types::Otc - crate::bgp::message::update_builder::MpReachNlriBuilder + //crate::bgp::message::update_builder::MpReachNlriBuilder ); +/* +impl WorkshopAttribute for crate::bgp::message::update_builder::MpReachNlriBuilder { + fn store(local_attrs: Self, attrs: &mut PaMap) -> + Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } + fn retrieve(_attrs: &PaMap) -> + Option { None } +} +*/ + + + //------------ WorkshopAttribute --------------------------------------------- pub trait WorkshopAttribute: FromAttribute { @@ -246,9 +263,10 @@ impl FromAttribute for Vec { } //------------ NlriWorkshop -------------------------------------------------- -impl FromAttribute for crate::bgp::message::nlri::Nlri> { } +impl FromAttribute for Nlri> { } -impl WorkshopAttribute for crate::bgp::message::nlri::Nlri> { +/* +impl WorkshopAttribute for Nlri> { fn retrieve(attrs: &PaMap) -> Option where Self: Sized { @@ -266,11 +284,13 @@ impl WorkshopAttribute for crate::bgp::message::nlri::Nlri WorkshopAttribute for crate::bgp::types::NextHop { fn retrieve(attrs: &PaMap) -> Option { if let Some(next_hop) = @@ -297,3 +317,4 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { } } } +*/ diff --git a/src/bmp/message.rs b/src/bmp/message.rs index 25cd7254..d5e80cf3 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -6,7 +6,7 @@ use crate::asn::Asn; use crate::bgp::message::{Message as BgpMsg, OpenMessage as BgpOpen, UpdateMessage as BgpUpdate, NotificationMessage as BgpNotification}; -use crate::bgp::types::{Afi, Safi}; +use crate::bgp::types::{Afi, AfiSafi}; use crate::bgp::message::update::{SessionConfig, FourOctetAsn}; use crate::bgp::message::open::CapabilityType; use crate::util::parser::ParseError; @@ -590,7 +590,7 @@ impl RouteMonitoring { /// Return the encapsulated /// [BGP UPDATE message](`crate::bgp::MessageUpdate`). - pub fn bgp_update(&self, config: SessionConfig) + pub fn bgp_update(&self, config: &SessionConfig) -> Result>, ParseError> { let mut parser = Parser::from_ref( @@ -943,28 +943,27 @@ impl PeerUpNotification { conf } - - pub fn supported_protocols(&self) -> Vec<(Afi, Safi)> { + pub fn supported_protocols(&self) -> Vec { let mut v = Vec::new(); let mut res = Vec::new(); let (sent, rcvd) = self.bgp_open_sent_rcvd(); sent.capabilities() .filter(|c| c.typ() == CapabilityType::MultiProtocol) .for_each(|c| { - let afi: Afi = u16::from_be_bytes([c.value()[0], c.value()[1]]).into(); - let safi: Safi = c.value()[3].into(); - v.push((afi, safi)); - //println!("sent cap: {:?} == {}/{}", c.value(), afi, safi) + let afi = u16::from_be_bytes([c.value()[0], c.value()[1]]); + let safi = c.value()[3]; + let afisafi = (afi, safi).into(); + v.push(afisafi); }); rcvd.capabilities() .filter(|c| c.typ() == CapabilityType::MultiProtocol) .for_each(|c| { - let afi: Afi = u16::from_be_bytes([c.value()[0], c.value()[1]]).into(); - let safi: Safi = c.value()[3].into(); - if v.contains(&(afi, safi)) { - res.push((afi, safi)); - } - //println!("rcvd cap: {:?} == {}/{}", c.value(), afi, safi) + let afi = u16::from_be_bytes([c.value()[0], c.value()[1]]); + let safi = c.value()[3]; + let afisafi = (afi, safi).into(); + if v.contains(&afisafi) { + res.push(afisafi); + } }); res } @@ -1254,16 +1253,16 @@ pub enum Stat { Type6(u32), Type7(u64), Type8(u64), - Type9(Afi,Safi,u64), - Type10(Afi,Safi,u64), + Type9(Afi,u8,u64), + Type10(Afi,u8,u64), Type11(u32), Type12(u32), Type13(u32), // RFC 8671, Adj-RIB-Out Type14(u64), Type15(u64), - Type16(Afi,Safi,u64), - Type17(Afi,Safi,u64), + Type16(Afi,u8,u64), + Type17(Afi,u8,u64), Unimplemented(u16,u16) // type,len } @@ -1332,12 +1331,12 @@ impl <'a>StatIter<'a> { res } - fn _take_afi_safi_u64(&mut self) -> (Afi, Safi, u64) { + fn _take_afi_safi_u64(&mut self) -> (Afi, u8, u64) { let afi: Afi = u16::from_be_bytes( self.octets[self.pos + 4 .. self.pos + 4 + 2].try_into().unwrap() ).into(); - let safi: Safi = self.octets[self.pos + 4 + 2].into(); + let safi = self.octets[self.pos + 4 + 2]; let v = u64::from_be_bytes( self.octets[self.pos + 4 + 3 .. self.pos + 4 + 3 + 8] @@ -1676,10 +1675,10 @@ mod tests { use bytes::Bytes; use std::str::FromStr; use crate::addr::Prefix; - use crate::bgp::types::{Afi, Safi}; + use crate::bgp::types::Afi; use crate::bgp::path_attributes::AttributeHeader; use crate::bgp::types::{ConventionalNextHop, MultiExitDisc}; - use crate::bgp::message::nlri::Nlri; + use crate::bgp::nlri::afisafi::{AfiSafiNlri, Nlri}; use crate::bgp::message::update::{FourOctetAsn, SessionConfig}; // Helper for generating a .pcap, pass output to `text2pcap`. @@ -1785,7 +1784,7 @@ mod tests { assert_eq!(bmp.common_header().length(), 103); let config = SessionConfig::modern(); - let bgp_update = bmp.bgp_update(config).unwrap(); + let bgp_update = bmp.bgp_update(&config).unwrap(); //-- from here on, this actually tests the bgp parsing functionality // rather than the bmp one, but let's leave it for now --------------- @@ -1821,9 +1820,9 @@ mod tests { // NLRI let mut nlris = bgp_update.announcements().unwrap(); - if let Some(Ok(Nlri::Unicast(n1))) = nlris.next() { + if let Some(Ok(Nlri::Ipv4Unicast(n1))) = nlris.next() { assert_eq!( - n1.prefix(), + n1.nlri(), Prefix::from_str("10.10.10.2/32").unwrap() ); } else { @@ -1877,8 +1876,8 @@ mod tests { Type8(28), Type14(139540), Type15(139540), - Type16(Afi::Ipv6, Safi::Unicast, 139540), - Type17(Afi::Ipv6, Safi::Unicast, 139540), + Type16(Afi::Ipv6, 1, 139540), + Type17(Afi::Ipv6, 1, 139540), ]; for (s1, s2) in bmp.stats().zip(stats.iter()) { @@ -2035,7 +2034,7 @@ mod tests { assert_eq!( bmp.supported_protocols(), - vec![(Afi::Ipv4, Safi::Unicast)] + vec![(AfiSafi::Ipv4Unicast)] ); } From b1f47174523ab5472a633e33ef1d15ca68c165c6 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Thu, 21 Mar 2024 14:45:41 +0100 Subject: [PATCH 72/96] Remove unnecessary type arguments --- 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 70a1f119..c15635a3 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -216,10 +216,10 @@ impl UpdateMessage { }; Ok(normal_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::<_>::from)) + .map(|n| n.map(NlriEnum::from)) .chain( addpath_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::<_>::from)) + .map(|n| n.map(NlriEnum::from)) ) ) } @@ -343,10 +343,10 @@ impl UpdateMessage { ; Ok(normal_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::<_>::from)) + .map(|n| n.map(NlriEnum::from)) .chain( addpath_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::<_>::from)) + .map(|n| n.map(NlriEnum::from)) )) } From b0d704bc6f1dd620372b57cfa3221d54a2401633 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 13:48:24 +0100 Subject: [PATCH 73/96] Reintroduction of BasicNlri etc --- src/bgp/message/update.rs | 8 +- src/bgp/nlri/afisafi.rs | 52 +++++- src/bgp/workshop/route.rs | 359 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 413 insertions(+), 6 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index c15635a3..26653ac9 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -429,8 +429,12 @@ impl UpdateMessage { pa.type_code() == 14 ) { let mut parser = pa.value_into_parser(); - let _afi = parser.parse_u16_be()?; - let _safi = parser.parse_u8()?; + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; + if AfiSafi::from((afi, safi)) != ASP::afi_safi() { + return Ok(None); + //return Err(ParseError::form_error("different AFI+SAFI than requested")); + } NextHop::skip(&mut parser)?; parser.advance(1)?; // 1 reserved byte diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 27dff6ac..fab206ca 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -232,7 +232,7 @@ paste! { } } - impl fmt::Display for Nlri<()> { + impl fmt::Display for Nlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { $($( @@ -429,7 +429,7 @@ pub trait AfiSafi { /// A type representing an NLRI for a certain AFI+SAFI. pub trait AfiSafiNlri: AfiSafi + Clone + Hash + Debug { - type Nlri; //: AfiSafi; + type Nlri; fn nlri(&self) -> Self::Nlri; // TODO @@ -453,24 +453,58 @@ pub trait NlriCompose: AfiSafiNlri { /// A type containing nothing more than a (v4 or v6) Prefix. -pub trait IsPrefix { +pub trait IsPrefix: AfiSafiNlri { fn prefix(&self) -> Prefix; + fn path_id(&self) -> Option { + None + } // TODO // fn into_routeworkshop() -> RouteWorkshop<_>; } +// with this blanket impl we can't distinguish addpath from non-addpath +/* impl IsPrefix for T where T: AfiSafiNlri, B: Into { fn prefix(&self) -> Prefix { self.nlri().into() } } +*/ + +macro_rules! is_prefix { + ($nlri:ident) => { paste! { + impl IsPrefix for [<$nlri Nlri>] { + fn prefix(&self) -> Prefix { + self.nlri().into() + } + } + impl IsPrefix for [<$nlri AddpathNlri>] { + fn prefix(&self) -> Prefix { + self.nlri().into() + } + fn path_id(&self) -> Option { + Some(::path_id(&self)) + } + } + }} +} +is_prefix!(Ipv4Unicast); +is_prefix!(Ipv4Multicast); +is_prefix!(Ipv6Unicast); +is_prefix!(Ipv6Multicast); /// An Nlri containing a Path Id. pub trait Addpath: AfiSafiNlri { fn path_id(&self) -> PathId; } +pub trait IsAddpathPrefix { + fn prefix(&self) -> Prefix; + fn path_id(&self) -> PathId; +} + + //------------ Implementations ----------------------------------------------- // adding AFI/SAFIs here requires some manual labor: @@ -1313,6 +1347,16 @@ where } } +impl<'a, O, P> NlriEnumIter<'a, P> +where + O: Octets, + P: Octets = O>, +{ + pub fn next_with::Item) -> T>(&mut self, fmap: F) -> Option { + self.next().map(fmap) + } +} + #[cfg(test)] mod tests { @@ -1386,7 +1430,7 @@ mod tests { #[test] fn display() { - let n: Nlri<_> = Ipv4UnicastNlri(Prefix::from_str("1.2.3.0/24").unwrap().into()).into(); + let n: Nlri<()> = Ipv4UnicastNlri(Prefix::from_str("1.2.3.0/24").unwrap().into()).into(); eprintln!("{}", n); } diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 144d3807..82d6006c 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -318,3 +318,362 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { } } */ + + + +//----------------------------------------------------------------------------- + +use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiParse}; + +fn pdu_into_rws<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> Vec +where + Octs: 'a + Octets = R>, + R: Hash + Clone + Debug, + Vec: From>, + T: From>>, + //Nlri//: AfiSafiNlri // + Hash + Debug +{ + + let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + + let mut res = Vec::new(); + for a in pdu.announcements().unwrap() { + res.push( + T::from( + RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) + ) + ); + } + + res +} + +fn pdu_into_typed_rws<'a, Octs, T, R, AFN>(pdu: &'a UpdateMessage) -> Vec +where + Octs: 'a + Octets = R>, + + //R: Hash + Clone + Debug + Octets, + T: From>, + AFN: AfiSafiNlri + AfiSafiParse<'a, R, Octs, Output = AFN>, + + R: Octets, + Vec: OctetsFrom, +{ + + let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + + let mut res = Vec::new(); + if let Ok(Some(iter)) = pdu.typed_announcements::<_, AFN>() { + for a in iter { + res.push( + T::from( + RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) + ) + ); + } + } else { + eprintln!("empty or invalid NLRI iter"); + } + + res +} + + + +fn pdu_into_rws_iter<'a, Octs, T, R>(pdu: &'a UpdateMessage) +-> impl Iterator + '_ +where + Octs: 'a + Octets = R>, + R: Hash + Clone + Debug, + Vec: From>, + T: From>>, + //Nlri//: AfiSafiNlri // + Hash + Debug +{ + + let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + + pdu.announcements().unwrap().map(move |a| + T::from( + RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) + ) + ) +} + + +fn pdu_into_rws_basic_iter<'a, Octs, R>(pdu: &'a UpdateMessage) +-> impl Iterator> + '_ +where + Octs: 'a + Octets = R>, + R: Hash + Clone + Debug, + Vec: From>, +{ + + let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + + pdu.announcements().unwrap().filter_map(move |a| + a.ok().map(|n| BasicNlri::try_from(n).ok()).map(|a| + RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) + )) +} + + + + +// TODO vec/iter for withdrawals, append to existing functions? or return +// tuple of (announcements, withdrawals) ? + +//------------ BasicNlri again ------------------------------------------------ + +use crate::addr::Prefix; +use std::fmt; +use crate::bgp::nlri::afisafi::{AfiSafiType, Addpath, IsPrefix}; +use crate::bgp::types::PathId; + + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct BasicNlri { + ty: AfiSafiType, + prefix: Prefix, + path_id: Option +} + +impl BasicNlri { + pub fn prefix(&self) -> Prefix { + self.prefix + } + + pub fn path_id(&self) -> Option { + self.path_id + } + + pub fn get_type(&self) -> AfiSafiType { + self.ty + } +} + +impl fmt::Display for BasicNlri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.prefix) + } +} + +impl From for BasicNlri { + fn from(n: N) -> BasicNlri { + BasicNlri { + ty: N::afi_safi(), + prefix: n.prefix(), + path_id: n.path_id(), + } + } +} + + +impl From> for RouteWorkshop +where N: IsPrefix +{ + fn from(value: RouteWorkshop) -> Self { + RouteWorkshop(value.0.into(), value.1) + } +} + + +impl TryFrom> for BasicNlri { + type Error = &'static str; + + fn try_from(n: Nlri) -> Result { + match n { + Nlri::Ipv4Unicast(_) => todo!(), + Nlri::Ipv4UnicastAddpath(_) => todo!(), + Nlri::Ipv4Multicast(_) => todo!(), + Nlri::Ipv4MulticastAddpath(_) => todo!(), + Nlri::Ipv6Unicast(_) => todo!(), + Nlri::Ipv6UnicastAddpath(_) => todo!(), + Nlri::Ipv6Multicast(_) => todo!(), + Nlri::Ipv6MulticastAddpath(_) => todo!(), + _ => Err("NLRI not basic"), + } + } +} + + +#[allow(unused_imports)] +#[cfg(test)] +mod tests { + use super::*; + use crate::bgp::message::update::SessionConfig; + + use crate::bgp::nlri::afisafi::{ + Ipv4UnicastNlri, + Ipv6UnicastNlri, + Ipv6UnicastAddpathNlri, + Ipv4FlowSpecNlri, + }; + + + #[test] + fn pdu_into_rws_vec() { + + // UPDATE with 5 ipv6 nlri + let raw = vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + //0x00, 0x88, + 0x00, 0x88 + 6, + 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, + 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, + 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, + 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, + 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, + 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, + 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, + 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, + 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 16, 1, 2, + 16, 10, 20 + + ]; + let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) + .unwrap(); + + //let res: Vec> = pdu_into_rws(&pdu); + let res: Vec> = pdu_into_rws(&pdu); + assert_eq!(res.len(), 7); + for rws in res { + println!("{}", rws.nlri()); + } + } + + #[test] + fn pdu_into_rws_iter_test() { + + // UPDATE with 5 ipv6 nlri + 2 conventional + let raw = vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + //0x00, 0x88, + 0x00, 0x88 + 6, + 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, + 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, + 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, + 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, + 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, + 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, + 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, + 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, + 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 16, 1, 2, + 16, 10, 20 + + ]; + let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) + .unwrap(); + + assert_eq!(pdu_into_rws_iter::<_, RouteWorkshop<_>, _>(&pdu).count(), 7); + } + + #[test] + fn pdu_into_rws_typed() { + + // UPDATE with 5 ipv6 nlri + 2 conventional + let raw = vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + //0x00, 0x88, + 0x00, 0x88 + 6, + 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, + 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, + 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, + 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, + 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, + 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, + 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, + 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, + 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 16, 1, 2, + 16, 10, 20 + + ]; + let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) + .unwrap(); + + let res = pdu_into_typed_rws::<_, RouteWorkshop, _, Ipv6UnicastNlri>(&pdu); + for rws in &res { + println!("{}", rws.nlri()); + } + assert_eq!(res.len(), 5); + + let res = pdu_into_typed_rws::<_, RouteWorkshop, _, Ipv4UnicastNlri>(&pdu); + for rws in &res { + println!("{}", rws.nlri()); + } + assert_eq!(res.len(), 2); + + let res = pdu_into_typed_rws::<_, RouteWorkshop<_>, _, Ipv4FlowSpecNlri<_>>(&pdu); + for rws in &res { + println!("{}", rws.nlri()); + } + assert_eq!(res.len(), 0); + } + + #[test] + fn pdu_into_basic() { + + // UPDATE with 5 ipv6 nlri + 2 conventional + let raw = vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + //0x00, 0x88, + 0x00, 0x88 + 6, + 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, + 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, + 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, + 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, + 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, + 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, + 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, + 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, + 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 16, 1, 2, + 16, 10, 20 + + ]; + let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) + .unwrap(); + + let res = pdu_into_rws_basic_iter(&pdu); + for rws in res { + println!("{}", rws.nlri()); + } + let res = pdu_into_rws_basic_iter(&pdu); + assert_eq!(res.count(), 7); + + } + + + +} From 431debfcf890abea65146eb376296d3064bce107 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:09:44 +0100 Subject: [PATCH 74/96] Switch over to inetnum crate --- Cargo.toml | 1 + src/bgp/aspath.rs | 14 ++++++++++---- src/bgp/communities.rs | 2 +- src/bgp/message/mod.rs | 2 +- src/bgp/message/nlri.rs | 2 +- src/bgp/message/open.rs | 2 +- src/bgp/message/update.rs | 8 ++++---- src/bgp/message/update_builder.rs | 4 ++-- src/bgp/nlri/afisafi.rs | 4 ++-- src/bgp/nlri/common.rs | 2 +- src/bgp/nlri/mpls.rs | 2 +- src/bgp/nlri/mpls_vpn.rs | 2 +- src/bgp/path_attributes.rs | 4 ++-- src/bgp/types.rs | 2 +- src/bgp/workshop/route.rs | 2 +- src/bmp/message.rs | 4 ++-- src/flowspec.rs | 2 +- src/lib.rs | 4 ++-- 18 files changed, 35 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bb12873f..a444614f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["routing", "bgp"] license = "BSD-3-Clause" [dependencies] +inetnum = { version = "0.1.0", features = ["arbitrary", "serde"] } arbitrary = { version = "1", optional = true, features = ["derive"] } bytes = { version = "1.2", optional = true } chrono = { version = "0.4.20", optional = true, default-features = false } diff --git a/src/bgp/aspath.rs b/src/bgp/aspath.rs index e7561c22..4a40e08f 100644 --- a/src/bgp/aspath.rs +++ b/src/bgp/aspath.rs @@ -13,7 +13,7 @@ use core::hash::Hash; use std::slice::SliceIndex; use std::{error, fmt}; -use crate::asn::{Asn, LargeAsnError}; +use inetnum::asn::{Asn, LargeAsnError}; #[cfg(feature = "serde")] use serde::ser::SerializeSeq; @@ -244,7 +244,9 @@ impl HopPath { )?; head.iter().try_for_each(|h| { match h { - Hop::Asn(asn) => asn.compose(target), + Hop::Asn(asn) => { + target.append_slice(&asn.to_raw()) + } _ => unreachable!() } })?; @@ -259,7 +261,9 @@ impl HopPath { )?; c.iter().try_for_each(|h| { match h { - Hop::Asn(asn) => asn.compose(target), + Hop::Asn(asn) => { + target.append_slice(&asn.to_raw()) + } _ => unreachable!() } })?; @@ -845,7 +849,9 @@ impl> Segment { target.append_slice(self.octets.as_ref())?; } else { - self.asns().try_for_each(|asn| asn.compose(target))?; + self.asns().try_for_each(|asn| + target.append_slice(&asn.to_raw()) + )?; } Ok(()) } diff --git a/src/bgp/communities.rs b/src/bgp/communities.rs index 5e73f412..17a13799 100644 --- a/src/bgp/communities.rs +++ b/src/bgp/communities.rs @@ -93,7 +93,7 @@ use std::fmt::{self, Display, Error, Formatter}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; -use crate::asn::{Asn, Asn16, ParseAsnError}; +use inetnum::asn::{Asn, Asn16, ParseAsnError}; #[cfg(feature = "serde")] use serde::{Serialize, Serializer}; diff --git a/src/bgp/message/mod.rs b/src/bgp/message/mod.rs index d777ee65..b9edd6b0 100644 --- a/src/bgp/message/mod.rs +++ b/src/bgp/message/mod.rs @@ -10,7 +10,7 @@ use crate::util::parser::ParseError; use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::io::Read; -use crate::addr::PrefixError; +use inetnum::addr::PrefixError; use crate::typeenum; // from util::macros use log::debug; diff --git a/src/bgp/message/nlri.rs b/src/bgp/message/nlri.rs index 232c1581..e0f08f8d 100644 --- a/src/bgp/message/nlri.rs +++ b/src/bgp/message/nlri.rs @@ -1,6 +1,6 @@ use crate::bgp::types::{Afi, AfiSafi, NextHop}; -use crate::addr::Prefix; +use inetnum::addr::Prefix; use crate::util::parser::{parse_ipv4addr, parse_ipv6addr, ParseError}; use crate::bgp::message::update::PduParseInfo; diff --git a/src/bgp/message/open.rs b/src/bgp/message/open.rs index 8939cbf7..00ccadac 100644 --- a/src/bgp/message/open.rs +++ b/src/bgp/message/open.rs @@ -1,5 +1,5 @@ use crate::bgp::message::{Header, MsgType}; -use crate::asn::Asn; +use inetnum::asn::Asn; use crate::bgp::types::{AfiSafi, AddpathFamDir, AddpathDirection}; use crate::typeenum; // from util::macros use crate::util::parser::ParseError; diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 26653ac9..e995e0dc 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -9,7 +9,7 @@ use octseq::{Octets, Parser}; use crate::bgp::message::Header; -use crate::asn::Asn; +use inetnum::asn::Asn; use crate::bgp::aspath::AsPath; use crate::bgp::communities::{ ExtendedCommunity, Ipv6ExtendedCommunity, LargeCommunity, @@ -469,7 +469,7 @@ impl UpdateMessage { /// fuses itself, i.e. any following call to `next()` will return None. pub fn unicast_announcements(&self) -> Result< - impl Iterator> + '_, + impl Iterator> + '_, ParseError > { @@ -1526,7 +1526,7 @@ mod tests { use crate::bgp::nlri::afisafi::{AfiSafiNlri, Nlri, Ipv4UnicastNlri, Ipv4MulticastNlri}; use crate::bgp::types::{PathId, RouteDistinguisher}; use crate::bgp::message::Message; - use crate::addr::Prefix; + use inetnum::addr::Prefix; @@ -2090,7 +2090,7 @@ mod tests { ExtendedCommunitySubType::OtherSubType(0x06)) ); - use crate::asn::Asn16; + use inetnum::asn::Asn16; assert_eq!(ext_comm1.as2(), Some(Asn16::from_u16(0))); let ext_comm2 = ext_comms.next().unwrap(); diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 5efa702f..891d8cb8 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -1343,8 +1343,8 @@ mod tests { //use crate::bgp::path_attributes::AttributeHeader; - use crate::addr::Prefix; - use crate::asn::Asn; + use inetnum::addr::Prefix; + use inetnum::asn::Asn; use crate::bgp::aspath::HopPath; use crate::bgp::communities::{StandardCommunity, Tag}; use crate::bgp::nlri::afisafi::{ diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index fab206ca..56f94ab3 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -16,7 +16,7 @@ use serde::{Serialize, Deserialize}; // - pub use Afi/Nlri/etc from bgp::types // - clean up / remove bgp/workshop/afisafi_nlri.rs -use crate::addr::Prefix; +use inetnum::addr::Prefix; use super::common::{PathId, parse_prefix, prefix_bits_to_bytes}; use crate::util::parser::ParseError; use paste::paste; @@ -1361,7 +1361,7 @@ where mod tests { use super::*; - use crate::addr::Prefix; + use inetnum::addr::Prefix; use std::str::FromStr; #[test] diff --git a/src/bgp/nlri/common.rs b/src/bgp/nlri/common.rs index 5aafa800..b728dfcf 100644 --- a/src/bgp/nlri/common.rs +++ b/src/bgp/nlri/common.rs @@ -1,7 +1,7 @@ use octseq::{Octets, Parser}; use crate::util::parser::ParseError; -use crate::addr::Prefix; +use inetnum::addr::Prefix; use super::afisafi::Afi; use std::net::IpAddr; diff --git a/src/bgp/nlri/mpls.rs b/src/bgp/nlri/mpls.rs index 998e2293..69841d48 100644 --- a/src/bgp/nlri/mpls.rs +++ b/src/bgp/nlri/mpls.rs @@ -1,4 +1,4 @@ -use crate::addr::Prefix; +use inetnum::addr::Prefix; use std::fmt; use octseq::{Octets, Parser}; diff --git a/src/bgp/nlri/mpls_vpn.rs b/src/bgp/nlri/mpls_vpn.rs index 027137da..c9a338f6 100644 --- a/src/bgp/nlri/mpls_vpn.rs +++ b/src/bgp/nlri/mpls_vpn.rs @@ -1,4 +1,4 @@ -use crate::addr::Prefix; +use inetnum::addr::Prefix; use std::fmt; #[cfg(feature = "serde")] diff --git a/src/bgp/path_attributes.rs b/src/bgp/path_attributes.rs index 4f55bf8f..10104b13 100644 --- a/src/bgp/path_attributes.rs +++ b/src/bgp/path_attributes.rs @@ -5,7 +5,7 @@ use std::net::Ipv4Addr; use log::{debug, warn}; use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; -use crate::asn::Asn; +use inetnum::asn::Asn; use crate::bgp::message::{ PduParseInfo, UpdateMessage, @@ -2255,7 +2255,7 @@ mod tests { use super::*; - use crate::asn::Asn; + use inetnum::asn::Asn; use crate::bgp::communities::Wellknown; use crate::bgp::aspath::HopPath; use crate::bgp::types::OriginType; diff --git a/src/bgp/types.rs b/src/bgp/types.rs index 9176a2c4..24dd6e62 100644 --- a/src/bgp/types.rs +++ b/src/bgp/types.rs @@ -5,7 +5,7 @@ use std::net::Ipv4Addr; #[cfg(feature = "serde")] use serde::{Serialize, Deserialize}; -use crate::asn::Asn; +use inetnum::asn::Asn; pub use crate::bgp::nlri::afisafi::Afi; pub use crate::bgp::nlri::common::PathId; pub use crate::bgp::nlri::mpls_vpn::RouteDistinguisher; diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 82d6006c..9117dc87 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -424,7 +424,7 @@ where //------------ BasicNlri again ------------------------------------------------ -use crate::addr::Prefix; +use inetnum::addr::Prefix; use std::fmt; use crate::bgp::nlri::afisafi::{AfiSafiType, Addpath, IsPrefix}; use crate::bgp::types::PathId; diff --git a/src/bmp/message.rs b/src/bmp/message.rs index d5e80cf3..78bc6269 100644 --- a/src/bmp/message.rs +++ b/src/bmp/message.rs @@ -4,7 +4,7 @@ //! providing access to its contents based on the underlying `bytes` buffer //! without allocating. -use crate::asn::Asn; +use inetnum::asn::Asn; use crate::bgp::message::{Message as BgpMsg, OpenMessage as BgpOpen, UpdateMessage as BgpUpdate, NotificationMessage as BgpNotification}; use crate::bgp::types::{Afi, AfiSafi}; use crate::bgp::message::update::{SessionConfig, FourOctetAsn}; @@ -1674,7 +1674,7 @@ mod tests { use super::*; use bytes::Bytes; use std::str::FromStr; - use crate::addr::Prefix; + use inetnum::addr::Prefix; use crate::bgp::types::Afi; use crate::bgp::path_attributes::AttributeHeader; use crate::bgp::types::{ConventionalNextHop, MultiExitDisc}; diff --git a/src/flowspec.rs b/src/flowspec.rs index eae022b2..8d07decd 100644 --- a/src/flowspec.rs +++ b/src/flowspec.rs @@ -1,6 +1,6 @@ //! FlowSpec v1 parsing. -use crate::addr::Prefix; +use inetnum::addr::Prefix; use crate::bgp::types::Afi; use crate::util::parser::ParseError; use log::debug; diff --git a/src/lib.rs b/src/lib.rs index 28273170..fd01ccc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! A library for IP routing primitives. -pub mod addr; -pub mod asn; +//pub mod addr; +//pub mod asn; #[cfg(feature = "bgp")] pub mod bgp; pub mod bgpsec; From 22758a442685e2f3e1f787d1299136966859a063 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:09:57 +0100 Subject: [PATCH 75/96] Fix tests --- src/bgp/message/update_builder.rs | 10 +++++++--- src/bgp/workshop/route.rs | 16 ++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index 891d8cb8..d5618637 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -2597,13 +2597,17 @@ mod tests { builder.add_withdrawal(w.unwrap()).unwrap(); } - for a in original.typed_announcements::<_, Ipv4UnicastNlri>().unwrap().unwrap() { - builder.add_announcement(a.unwrap()).unwrap(); + if let Ok(Some(iter)) = + original.typed_announcements::<_, Ipv4UnicastNlri>() + { + for a in iter { + builder.add_announcement(a.unwrap()).unwrap(); + } } let composed = builder.into_message(&SessionConfig::modern()).unwrap(); assert_eq!(original.path_attributes().unwrap().count(), 4); - assert_eq!(composed.path_attributes().unwrap().count(), 3); + assert_eq!(composed.path_attributes().unwrap().count(), 2); } } diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 9117dc87..e19d9b3d 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -483,14 +483,14 @@ impl TryFrom> for BasicNlri { fn try_from(n: Nlri) -> Result { match n { - Nlri::Ipv4Unicast(_) => todo!(), - Nlri::Ipv4UnicastAddpath(_) => todo!(), - Nlri::Ipv4Multicast(_) => todo!(), - Nlri::Ipv4MulticastAddpath(_) => todo!(), - Nlri::Ipv6Unicast(_) => todo!(), - Nlri::Ipv6UnicastAddpath(_) => todo!(), - Nlri::Ipv6Multicast(_) => todo!(), - Nlri::Ipv6MulticastAddpath(_) => todo!(), + Nlri::Ipv4Unicast(n) => Ok(n.into()), + Nlri::Ipv4UnicastAddpath(n) => Ok(n.into()), + Nlri::Ipv4Multicast(n) => Ok(n.into()), + Nlri::Ipv4MulticastAddpath(n) => Ok(n.into()), + Nlri::Ipv6Unicast(n) => Ok(n.into()), + Nlri::Ipv6UnicastAddpath(n) => Ok(n.into()), + Nlri::Ipv6Multicast(n) => Ok(n.into()), + Nlri::Ipv6MulticastAddpath(n) => Ok(n.into()), _ => Err("NLRI not basic"), } } From e4190fa725a123c5ed7a7ae7846d7460ba69d535 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:10:57 +0100 Subject: [PATCH 76/96] Clippy --- src/bgp/workshop/route.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index e19d9b3d..1168db44 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -325,7 +325,7 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiParse}; -fn pdu_into_rws<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> Vec +pub fn pdu_into_rws<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> Vec where Octs: 'a + Octets = R>, R: Hash + Clone + Debug, @@ -348,7 +348,7 @@ where res } -fn pdu_into_typed_rws<'a, Octs, T, R, AFN>(pdu: &'a UpdateMessage) -> Vec +pub fn pdu_into_typed_rws<'a, Octs, T, R, AFN>(pdu: &'a UpdateMessage) -> Vec where Octs: 'a + Octets = R>, @@ -380,7 +380,7 @@ where -fn pdu_into_rws_iter<'a, Octs, T, R>(pdu: &'a UpdateMessage) +pub fn pdu_into_rws_iter<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> impl Iterator + '_ where Octs: 'a + Octets = R>, @@ -400,7 +400,7 @@ where } -fn pdu_into_rws_basic_iter<'a, Octs, R>(pdu: &'a UpdateMessage) +pub fn pdu_into_rws_basic_iter<'a, Octs, R>(pdu: &'a UpdateMessage) -> impl Iterator> + '_ where Octs: 'a + Octets = R>, @@ -426,7 +426,7 @@ where use inetnum::addr::Prefix; use std::fmt; -use crate::bgp::nlri::afisafi::{AfiSafiType, Addpath, IsPrefix}; +use crate::bgp::nlri::afisafi::{AfiSafiType, IsPrefix}; use crate::bgp::types::PathId; From 15afa312b6e73369eaf1347265a6ecb0cb10be57 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:15:50 +0100 Subject: [PATCH 77/96] Comment out everything BasicNlri related --- src/bgp/workshop/route.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 1168db44..9a37efcc 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -171,16 +171,6 @@ impl_workshop!( //crate::bgp::message::update_builder::MpReachNlriBuilder ); -/* -impl WorkshopAttribute for crate::bgp::message::update_builder::MpReachNlriBuilder { - fn store(local_attrs: Self, attrs: &mut PaMap) -> - Result<(), ComposeError> { attrs.set(local_attrs); Ok(()) } - fn retrieve(_attrs: &PaMap) -> - Option { None } -} -*/ - - //------------ WorkshopAttribute --------------------------------------------- @@ -400,6 +390,8 @@ where } +// XXX to be moved to roto +/* pub fn pdu_into_rws_basic_iter<'a, Octs, R>(pdu: &'a UpdateMessage) -> impl Iterator> + '_ where @@ -415,13 +407,12 @@ where RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) )) } +*/ - -// TODO vec/iter for withdrawals, append to existing functions? or return -// tuple of (announcements, withdrawals) ? - +// XXX to be moved to roto +/* //------------ BasicNlri again ------------------------------------------------ use inetnum::addr::Prefix; @@ -496,6 +487,8 @@ impl TryFrom> for BasicNlri { } } +*/ + #[allow(unused_imports)] #[cfg(test)] @@ -584,6 +577,8 @@ mod tests { assert_eq!(pdu_into_rws_iter::<_, RouteWorkshop<_>, _>(&pdu).count(), 7); } + //XXX to be moved to roto + /* #[test] fn pdu_into_rws_typed() { @@ -674,6 +669,7 @@ mod tests { } +*/ } From 7e0feec42e53f791d0ccdd1bc1b0d22467ae61a8 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:22:31 +0100 Subject: [PATCH 78/96] Remove unused afisafi_nlri.rs from bgp::workshop --- src/bgp/workshop/afisafi_nlri.rs | 267 ------------------------------- src/bgp/workshop/mod.rs | 1 - 2 files changed, 268 deletions(-) delete mode 100644 src/bgp/workshop/afisafi_nlri.rs diff --git a/src/bgp/workshop/afisafi_nlri.rs b/src/bgp/workshop/afisafi_nlri.rs deleted file mode 100644 index f50c4ea5..00000000 --- a/src/bgp/workshop/afisafi_nlri.rs +++ /dev/null @@ -1,267 +0,0 @@ -use std::{fmt::Debug, hash::Hash}; - -use octseq::Octets; - -use crate::{ - addr::Prefix, - bgp::{message::{ - //nlri::{BasicNlri, FlowSpecNlri, Nlri}, - update::AfiSafi, update_builder::ComposeError, - }, path_attributes::PaMap, ParseError}, -}; -use crate::bgp::nlri::afisafi::Nlri; - -use super::route::RouteWorkshop; - -//------------ AfiSafiNlri --------------------------------------------------- - -pub trait AfiSafiNlri: Clone + Hash + Debug { - type Nlri; - fn nlri(&self) -> Self::Nlri; - fn afi_safi(&self) -> AfiSafi; -} - - -//------------ HasBasicNlri -------------------------------------------------- - -pub trait HasBasicNlri { - fn basic_nlri(&self) -> BasicNlri; - fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri; -} - - -//------------ Ipv4UnicastNlri ----------------------------------------------- - -#[derive(Clone, Debug, Hash)] -pub struct Ipv4UnicastNlri(pub BasicNlri); - -impl AfiSafiNlri for Ipv4UnicastNlri { - type Nlri = BasicNlri; - - fn nlri(&self) -> Self::Nlri { - self.0 - } - - fn afi_safi(&self) -> AfiSafi { - AfiSafi::Ipv4Unicast - } -} - -impl HasBasicNlri for Ipv4UnicastNlri { - fn basic_nlri(&self) -> BasicNlri { - self.0 - } - - fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri { - RouteWorkshop::::from_pa_map(nlri, pa.clone()) - } -} - -impl TryFrom> - for Ipv4UnicastNlri { - type Error = ComposeError; - - fn try_from(value: crate::bgp::message::nlri::Nlri) - -> Result { - if let Nlri::Unicast(n) = value { - if n.prefix.is_v4() { - Ok(Ipv4UnicastNlri(n)) - } else { - Err(ComposeError::InvalidAttribute) - } - } else { - Err(ComposeError::InvalidAttribute) - } - } -} - -//------------ Ipv6UnicastNlri ----------------------------------------------- - -#[derive(Clone, Debug, Hash)] -pub struct Ipv6UnicastNlri(pub BasicNlri); - -impl AfiSafiNlri for Ipv6UnicastNlri { - type Nlri = BasicNlri; - - fn nlri(&self) -> Self::Nlri { - self.0 - } - - fn afi_safi(&self) -> AfiSafi { - AfiSafi::Ipv6Unicast - } -} - -impl From for Ipv6UnicastNlri { - fn from(value: Prefix) -> Self { - Self(BasicNlri { - prefix: value, - path_id: None, - }) - } -} - -impl HasBasicNlri for Ipv6UnicastNlri { - fn basic_nlri(&self) -> BasicNlri { - self.0 - } - - fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri { - RouteWorkshop::::from_pa_map(nlri, pa.clone()) - } -} - -impl TryFrom> - for Ipv6UnicastNlri { - type Error = ComposeError; - - fn try_from(value: crate::bgp::message::nlri::Nlri) - -> Result { - if let Nlri::Unicast(n) = value { - if !n.prefix.is_v4() { - Ok(Ipv6UnicastNlri(n)) - } else { - Err(ComposeError::InvalidAttribute) - } - } else { - Err(ComposeError::InvalidAttribute) - } - } -} - - -//------------ Ipv4MulticastNlri --------------------------------------------- - -#[derive(Clone, Debug, Hash)] -pub struct Ipv4MulticastNlri(pub BasicNlri); - -impl AfiSafiNlri for Ipv4MulticastNlri { - type Nlri = BasicNlri; - - fn nlri(&self) -> Self::Nlri { - self.0 - } - - fn afi_safi(&self) -> AfiSafi { - AfiSafi::Ipv4Multicast - } -} - -impl HasBasicNlri for Ipv4MulticastNlri { - fn basic_nlri(&self) -> BasicNlri { - self.0 - } - - fn make_route_with_nlri(nlri: M, pa: &PaMap) - -> RouteWorkshop where M: AfiSafiNlri { - RouteWorkshop::::from_pa_map(nlri, pa.clone()) - } -} - -impl TryFrom> - for Ipv4MulticastNlri { - type Error = ComposeError; - - fn try_from(value: crate::bgp::message::nlri::Nlri) - -> Result { - if let Nlri::Multicast(n) = value { - if n.prefix.is_v4() { - Ok(Ipv4MulticastNlri(n)) - } else { - Err(ComposeError::InvalidAttribute) - } - } else { - Err(ComposeError::InvalidAttribute) - } - } -} - - -//------------ Ipv6MulticastNlri --------------------------------------------- - -#[derive(Clone, Debug, Hash)] -pub struct Ipv6MulticastNlri(pub BasicNlri); - -impl AfiSafiNlri for Ipv6MulticastNlri { - type Nlri = BasicNlri; - - fn nlri(&self) -> Self::Nlri { - self.0 - } - - fn afi_safi(&self) -> AfiSafi { - AfiSafi::Ipv6Multicast - } -} - -impl TryFrom> - for Ipv6MulticastNlri { - type Error = ComposeError; - - fn try_from(value: crate::bgp::message::nlri::Nlri) - -> Result { - if let Nlri::Multicast(n) = value { - if !n.prefix.is_v4() { - Ok(Ipv6MulticastNlri(n)) - } else { - Err(ComposeError::InvalidAttribute) - } - } else { - Err(ComposeError::InvalidAttribute) - } - } -} - - -//------------ Ipv4FlowSpecNlri ---------------------------------------------- - -#[derive(Clone, Debug, Hash)] -pub struct Ipv4FlowSpecNlri(pub FlowSpecNlri); - -impl AfiSafiNlri - for Ipv4FlowSpecNlri -{ - type Nlri = Ipv4FlowSpecNlri; - - fn nlri(&self) -> Self::Nlri { - Ipv4FlowSpecNlri(self.0.clone()) - } - - fn afi_safi(&self) -> AfiSafi { - AfiSafi::Ipv4FlowSpec - } -} - -impl TryFrom>> for Ipv4UnicastNlri { - type Error = ParseError; - - fn try_from(val: Nlri>) -> Result { - if let Nlri::Unicast(b) = val { - if b.prefix.is_v4() { - return Ok(Ipv4UnicastNlri(b)); - } - } - Err(ParseError::Unsupported) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use std::str::FromStr; - - #[test] - fn basic() { - let basic: BasicNlri = Prefix::from_str("10.1.1.0/24").unwrap().into(); - let nlri = Nlri::<()>::Unicast(basic); - let pamap = PaMap::empty(); - let ws = RouteWorkshop::from_pa_map(nlri, pamap.clone()); - let ws2 = RouteWorkshop::from_pa_map(basic, pamap); - dbg!(ws); - dbg!(ws2); - } -} diff --git a/src/bgp/workshop/mod.rs b/src/bgp/workshop/mod.rs index acbdde97..b9bb57fe 100644 --- a/src/bgp/workshop/mod.rs +++ b/src/bgp/workshop/mod.rs @@ -1,2 +1 @@ pub mod route; -//pub mod afisafi_nlri; From accd569ea2c0d99e9bed878a2100e8259af65a0c Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:24:43 +0100 Subject: [PATCH 79/96] Remove old, unused bgp::message::nlri module --- src/bgp/message/nlri.rs | 2374 --------------------------------------- src/bgp/mod.rs | 2 +- 2 files changed, 1 insertion(+), 2375 deletions(-) delete mode 100644 src/bgp/message/nlri.rs diff --git a/src/bgp/message/nlri.rs b/src/bgp/message/nlri.rs deleted file mode 100644 index e0f08f8d..00000000 --- a/src/bgp/message/nlri.rs +++ /dev/null @@ -1,2374 +0,0 @@ -use crate::bgp::types::{Afi, AfiSafi, NextHop}; - -use inetnum::addr::Prefix; - -use crate::util::parser::{parse_ipv4addr, parse_ipv6addr, ParseError}; -use crate::bgp::message::update::PduParseInfo; -use crate::flowspec::Component; -use crate::typeenum; -use octseq::{Octets, OctetsBuilder, OctetsFrom, Parser}; -use log::debug; - -use std::fmt; -use std::net::IpAddr; -use std::str::FromStr; - -#[cfg(feature = "serde")] -use serde::{Serialize, Deserialize}; - - -//------------ FixedNlriIter ------------------------------------------------- -// -// This is an alternative iterator for parsing wireformat encoded NLRI. Where -// the 'normal' iterator will match on afi/safi every time next() is called, -// the FixedNlriIter is generic over a type implementing AfiSafiParse. That -// trait has explicit implementations for every afi/safi pair, so the whole -// matching is lifted up one level. -// The normal iterator is nicer in terms of API, the fixed one might be a bit -// more performant in certain cases. - -pub struct FixedNlriIter<'a, T, AS> { - parser: Parser<'a, T>, - afisafi: std::marker::PhantomData, -} - -impl<'a, T: 'a + Octets, AS: AfiSafiParse > FixedNlriIter<'a, T, AS> { - pub fn new(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter { parser: *parser, afisafi: std::marker::PhantomData} - } - - pub fn validate(&mut self) -> Result<(), ParseError> { - let pos = self.parser.pos(); - while self.parser.remaining() > 0 { - self.skip_nlri()? - } - self.parser.seek(pos)?; - Ok(()) - } - - fn get_nlri(&mut self) -> Result>, ParseError> { - AS::parse_nlri(&mut self.parser) - } - - fn skip_nlri(&mut self) -> Result<(), ParseError> { - AS::skip_nlri(&mut self.parser) - } -} - -//------------ Ipv4Unicast --------------------------------------------------- - -pub(crate) struct Ipv4Unicast; -impl AfiSafiParse for Ipv4Unicast { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - Ok( - BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4Unicast> { - pub(crate) fn ipv4unicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4UnicastAddPath -------------------------------------------- - -pub(crate) struct Ipv4UnicastAddPath; -impl AfiSafiParse for Ipv4UnicastAddPath { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - let path_id = PathId::parse(parser)?; - Ok( - BasicNlri::with_path_id( - parse_prefix(parser, Afi::Ipv4)?, - path_id - ) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix_addpath(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4UnicastAddPath> { - pub(crate) fn ipv4unicast_addpath(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6Unicast --------------------------------------------------- - -pub(crate) struct Ipv6Unicast; -impl AfiSafiParse for Ipv6Unicast { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - Ok( - BasicNlri::new(parse_prefix(parser, Afi::Ipv6)?) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6Unicast> { - pub(crate) fn ipv6unicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6UnicastAddPath -------------------------------------------- - -pub(crate) struct Ipv6UnicastAddPath; -impl AfiSafiParse for Ipv6UnicastAddPath { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - let path_id = PathId::parse(parser)?; - Ok( - BasicNlri::with_path_id( - parse_prefix(parser, Afi::Ipv6)?, - path_id - ) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix_addpath(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6UnicastAddPath> { - pub(crate) fn ipv6unicast_addpath(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4Multicast ------------------------------------------------- - -pub(crate) struct Ipv4Multicast; -impl AfiSafiParse for Ipv4Multicast { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - Ok( - BasicNlri::new(parse_prefix(parser, Afi::Ipv4)?) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4Multicast> { - pub(crate) fn ipv4multicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4MulticastAddPath ------------------------------------------ - -pub(crate) struct Ipv4MulticastAddPath; -impl AfiSafiParse for Ipv4MulticastAddPath { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - let path_id = PathId::parse(parser)?; - Ok( - BasicNlri::with_path_id( - parse_prefix(parser, Afi::Ipv4)?, - path_id - ) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix_addpath(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4MulticastAddPath> { - pub(crate) fn ipv4multicast_addpath(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6Multicast ------------------------------------------------- - -pub(crate) struct Ipv6Multicast; -impl AfiSafiParse for Ipv6Multicast { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - Ok( - BasicNlri::new(parse_prefix(parser, Afi::Ipv6)?) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6Multicast> { - pub(crate) fn ipv6multicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6MulticastAddPath ------------------------------------------ - -pub(crate) struct Ipv6MulticastAddPath; -impl AfiSafiParse for Ipv6MulticastAddPath { - type Item = BasicNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - let path_id = PathId::parse(parser)?; - Ok( - BasicNlri::with_path_id( - parse_prefix(parser, Afi::Ipv6)?, - path_id - ) - ) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - skip_prefix_addpath(parser) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6MulticastAddPath> { - pub(crate) fn ipv6multicast_addpath(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4MplsUnicast ----------------------------------------------- - -pub(crate) struct Ipv4MplsUnicast; -impl AfiSafiParse for Ipv4MplsUnicast { - type Item = MplsNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsNlri::parse_no_addpath(parser, AfiSafi::Ipv4MplsUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - MplsNlri::skip_labels_and_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4MplsUnicast> { - pub(crate) fn ipv4mpls_unicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4MplsUnicastAddPath ---------------------------------------- - -pub(crate) struct Ipv4MplsUnicastAddPath; -impl AfiSafiParse for Ipv4MplsUnicastAddPath { - type Item = MplsNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsNlri::parse_addpath(parser, AfiSafi::Ipv4MplsUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - parser.advance(4)?; - MplsNlri::skip_labels_and_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4MplsUnicastAddPath> { - pub(crate) fn ipv4mpls_unicast_addpath(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6MplsUnicast ----------------------------------------------- - -pub(crate) struct Ipv6MplsUnicast; -impl AfiSafiParse for Ipv6MplsUnicast { - type Item = MplsNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsNlri::parse_no_addpath(parser, AfiSafi::Ipv6MplsUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - MplsNlri::skip_labels_and_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6MplsUnicast> { - pub(crate) fn ipv6mpls_unicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6MplsUnicastAddPath ---------------------------------------- - -pub(crate) struct Ipv6MplsUnicastAddPath; -impl AfiSafiParse for Ipv6MplsUnicastAddPath { - type Item = MplsNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsNlri::parse_addpath(parser, AfiSafi::Ipv6MplsUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - parser.advance(4)?; - MplsNlri::skip_labels_and_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6MplsUnicastAddPath> { - pub(crate) fn ipv6mpls_unicast_addpath(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4MplsVpnUnicast -------------------------------------------- - -pub(crate) struct Ipv4MplsVpnUnicast; -impl AfiSafiParse for Ipv4MplsVpnUnicast { - type Item = MplsVpnNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsVpnNlri::parse_no_addpath(parser, AfiSafi::Ipv4MplsVpnUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - MplsVpnNlri::skip_labels_rd_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4MplsVpnUnicast> { - pub(crate) fn ipv4mpls_vpn_unicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv4MplsVpnUnicastAddPath ------------------------------------- - -pub(crate) struct Ipv4MplsVpnUnicastAddPath; -impl AfiSafiParse for Ipv4MplsVpnUnicastAddPath { - type Item = MplsVpnNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsVpnNlri::parse_addpath(parser, AfiSafi::Ipv4MplsVpnUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - parser.advance(4)?; - MplsVpnNlri::skip_labels_rd_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4MplsVpnUnicastAddPath> { - pub(crate) fn ipv4mpls_vpn_unicast_addpath(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6MplsVpnUnicast -------------------------------------------- - -pub(crate) struct Ipv6MplsVpnUnicast; -impl AfiSafiParse for Ipv6MplsVpnUnicast { - type Item = MplsVpnNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsVpnNlri::parse_no_addpath(parser, AfiSafi::Ipv6MplsVpnUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - MplsVpnNlri::skip_labels_rd_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6MplsVpnUnicast> { - pub(crate) fn ipv6mpls_vpn_unicast(parser: &mut Parser<'a, T>) -> Self { - FixedNlriIter::::new(parser) - } -} - -//------------ Ipv6MplsVpnUnicastAddPath ------------------------------------- - -pub(crate) struct Ipv6MplsVpnUnicastAddPath; -impl AfiSafiParse for Ipv6MplsVpnUnicastAddPath { - type Item = MplsVpnNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - MplsVpnNlri::parse_addpath(parser, AfiSafi::Ipv6MplsVpnUnicast) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - parser.advance(4)?; - MplsVpnNlri::skip_labels_rd_prefix(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6MplsUnicastAddPath> { - pub(crate) fn ipv6mpls_vpn_unicast_addpath(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - - -//------------ Ipv4RouteTarget ------------------------------------- - -pub(crate) struct Ipv4RouteTarget; -impl AfiSafiParse for Ipv4RouteTarget { - type Item = RouteTargetNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - RouteTargetNlri::parse_no_addpath(parser) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - RouteTargetNlri::::skip(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4RouteTarget> { - pub(crate) fn ipv4route_target(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - - -//------------ Ipv4FlowSpec ------------------------------------- - -pub(crate) struct Ipv4FlowSpec; -impl AfiSafiParse for Ipv4FlowSpec { - type Item = FlowSpecNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - FlowSpecNlri::parse(parser, Afi::Ipv4) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - FlowSpecNlri::::skip(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv4FlowSpec> { - pub(crate) fn ipv4flowspec(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - - -//------------ Ipv6FlowSpec ------------------------------------- - -pub(crate) struct Ipv6FlowSpec; -impl AfiSafiParse for Ipv6FlowSpec { - type Item = FlowSpecNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - FlowSpecNlri::parse(parser, Afi::Ipv6) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - FlowSpecNlri::::skip(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, Ipv6FlowSpec> { - pub(crate) fn ipv6flowspec(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - -//------------ L2VpnVpls ------------------------------------- - -pub(crate) struct L2VpnVpls; -impl AfiSafiParse for L2VpnVpls { - type Item = VplsNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - VplsNlri::parse(parser) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - VplsNlri::skip(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, L2VpnVpls> { - pub(crate) fn l2vpn_vpls(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - -//------------ L2VpnEvpn ------------------------------------- - -pub(crate) struct L2VpnEvpn; -impl AfiSafiParse for L2VpnEvpn { - type Item = EvpnNlri; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError> { - EvpnNlri::parse(parser) - } - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError> - { - EvpnNlri::::skip(parser)?; - Ok(()) - } -} - -impl<'a, T: 'a + Octets> FixedNlriIter<'a, T, L2VpnEvpn> { - pub(crate) fn l2vpn_evpn(parser: &mut Parser<'a, T>) - -> Self - { - FixedNlriIter::::new(parser) - } -} - -//------------ AfiSafiParse -------------------------------------------------- - -pub trait AfiSafiParse { - type Item; - fn parse_nlri<'a, Octs: Octets>( - parser: &mut Parser<'a, Octs> - ) -> Result>, ParseError>; - - fn skip_nlri(parser: &mut Parser<'_, Octs>) - -> Result<(), ParseError>; -} - -//------------ Iterator ------------------------------------------------------ - -impl<'a, T: Octets, AS: AfiSafiParse> Iterator for FixedNlriIter<'a, T, AS> { - type Item = Result>, ParseError>; - - fn next(&mut self) -> Option { - if self.parser.remaining() == 0 { - return None; - } - Some(self.get_nlri()) - } -} - -//--- NextHop in MP_REACH_NLRI ----------------------------------------------- -impl NextHop { - pub fn parse(parser: &mut Parser<'_, R>, afisafi: AfiSafi) - -> Result - { - use AfiSafi::*; - let len = parser.parse_u8()?; - - macro_rules! error { - () => { return Err(ParseError::Unsupported) } - } - - let res = match afisafi { - - Ipv4Unicast | - Ipv4Multicast | - Ipv4RouteTarget | - L2VpnVpls | - L2VpnEvpn - => { - match len { - 4 => NextHop::Unicast(parse_ipv4addr(parser)?.into()), - _ => error!() - } - } - Ipv6Unicast => { - match len { - 16 => NextHop::Unicast(parse_ipv6addr(parser)?.into()), - 32 => NextHop::Ipv6LL( - parse_ipv6addr(parser)?, - parse_ipv6addr(parser)? - ), - _ => error!() - } - } - Ipv6Multicast => { - match len { - 16 => NextHop::Unicast(parse_ipv6addr(parser)?.into()), - _ => error!() - } - } - // RFC4684: the nexthop for MPLS can be of the other AFI than the - // NLRI themselves are. - Ipv4MplsUnicast | Ipv6MplsUnicast => { - match len { - 4 => NextHop::Unicast(parse_ipv4addr(parser)?.into()), - 16 => NextHop::Unicast(parse_ipv6addr(parser)?.into()), - _ => error!() - } - } - Ipv4MplsVpnUnicast => { - match len { - 12 => NextHop::MplsVpnUnicast( - RouteDistinguisher::parse(parser)?, - parse_ipv4addr(parser)?.into() - ), - _ => error!() - } - } - Ipv6MplsVpnUnicast => { - match len { - 24 => NextHop::MplsVpnUnicast( - RouteDistinguisher::parse(parser)?, - parse_ipv6addr(parser)?.into() - ), - _ => error!() - } - } - - Ipv4FlowSpec | Ipv6FlowSpec => Self::Empty, - }; - - Ok(res) - } - - pub fn skip(parser: &mut Parser<'_, R>) - -> Result<(), ParseError> - { - let len = parser.parse_u8()?; - parser.advance(len.into())?; - Ok(()) - } -} - - -//--- NLRI ------------------------------------------------------------------- - -/// Path Identifier for BGP Multiple Paths (RFC7911). -/// -/// Optionally used in [`BasicNlri`]. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct PathId(u32); - -impl PathId { - pub fn check(parser: &mut Parser) - -> Result<(), ParseError> - { - parser.advance(4)?; - Ok(()) - } - - pub fn parse(parser: &mut Parser<'_, R>) - -> Result - { - Ok(PathId(parser.parse_u32_be()?)) - } - - pub fn from_u32(id: u32) -> Self { - PathId(id) - } - - pub fn to_raw(self) -> [u8; 4] { - self.0.to_be_bytes() - } -} - -impl From for u32 { - fn from(value: PathId) -> Self { - value.0 - } -} - -impl fmt::Display for PathId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// MPLS labels, part of [`MplsNlri`] and [`MplsVpnNlri`]. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct Labels { - octets: Octs -} - -impl PartialEq> for Labels -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &Labels) -> bool { - self.octets.as_ref() == other.octets.as_ref() - } -} - -impl> Eq for Labels { } - - -impl> Labels { - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.octets.as_ref().len() - } -} - -impl> AsRef<[u8]> for Labels { - fn as_ref(&self) -> &[u8] { - self.octets.as_ref() - } -} - -impl Labels { - // XXX check all this Label stuff again - fn skip<'a, R>(parser: &mut Parser<'a, R>) -> Result - where - R: Octets = Octs> - { - let mut res = 0; - let mut stop = false; - let mut buf = [0u8; 3]; - - while !stop { - //20bits label + 3bits rsvd + S bit - parser.parse_buf(&mut buf)?; - res += 3; - - if buf[2] & 0x01 == 0x01 || // actual label with stop bit - buf == [0x80, 0x00, 0x00] || // Compatibility value - buf == [0x00, 0x00, 0x00] // or RFC 8277 2.4 - { - stop = true; - } - } - - Ok(res) - } - // There are two cases for Labels: - // - in an announcement, it describes one or more MPLS labels - // - in a withdrawal, it's a compatibility value without meaning - // XXX consider splitting up the parsing for this for announcements vs - // withdrawals? Perhaps via another fields in the (currently so-called) - // SessionConfig... - fn parse<'a, R>(parser: &mut Parser<'a, R>) -> Result - where - R: Octets = Octs> + ?Sized, - { - let pos = parser.pos(); - - let mut stop = false; - let mut buf = [0u8; 3]; - - while !stop { - //20bits label + 3bits rsvd + S bit - parser.parse_buf(&mut buf)?; - let _lbl = - (buf[0] as u32) << 12 | - (buf[1] as u32) << 4 | - (buf[2] as u32) >> 4; - - if buf[2] & 0x01 == 0x01 || // actual label with stop bit - buf == [0x80, 0x00, 0x00] || // Compatibility value - buf == [0x00, 0x00, 0x00] // or RFC 8277 2.4 - { - stop = true; - } - } - - let len = parser.pos() - pos; - parser.seek(pos)?; - let res = parser.parse_octets(len)?; - Ok( - Labels { octets: res } - ) - } -} - -/// Route Distinguisher (RD) as defined in RFC4364. -/// -/// Used in [`MplsVpnNlri`], [`VplsNlri`] and [`NextHop`]. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RouteDistinguisher { - bytes: [u8; 8] -} - -impl RouteDistinguisher { - pub fn check(parser: &mut Parser) - -> Result<(), ParseError> - { - parser.advance(8)?; - Ok(()) - } - - pub fn parse(parser: &mut Parser<'_, R>) - -> Result - { - let mut b = [0u8; 8]; - b[..8].copy_from_slice(parser.peek(8)?); - parser.advance(8)?; - Ok( - RouteDistinguisher{ bytes: b } - ) - } - - pub fn skip(parser: &mut Parser<'_, R>) - -> Result<(), ParseError> - { - Ok(parser.advance(8)?) - } -} - -impl RouteDistinguisher { - /// Create a new RouteDistinguisher from a slice. - pub fn new(bytes: &[u8]) -> Self { - RouteDistinguisher { bytes: bytes.try_into().expect("parsed before") } - } - - pub fn zeroes() -> Self { - RouteDistinguisher::new(&[0_u8; 8]) - } - - /// Returns the type this RouteDistinguisher. - pub fn typ(&self) -> RouteDistinguisherType { - match self.bytes[0..2] { - [0x00, 0x00] => RouteDistinguisherType::Type0, - [0x00, 0x01] => RouteDistinguisherType::Type1, - [0x00, 0x02] => RouteDistinguisherType::Type2, - _ => RouteDistinguisherType::UnknownType, - } - } - - /// Returns the raw value of this RouteDistinguisher. - pub fn value(&self) -> [u8; 6] { - self.bytes[2..8].try_into().expect("parsed before") - } -} - -impl AsRef<[u8]> for RouteDistinguisher { - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -impl fmt::Display for RouteDistinguisher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#?}", self.bytes) - } -} - -/// Route Distinguisher types as defined in RFC4364. -#[derive(Eq, PartialEq, Debug)] -pub enum RouteDistinguisherType { - Type0, - Type1, - Type2, - UnknownType, -} - -/// NLRI comprised of a [`Prefix`] and an optional [`PathId`]. -/// -/// The `BasicNlri` is extended in [`MplsNlri`] and [`MplsVpnNlri`]. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct BasicNlri { - pub prefix: Prefix, - pub path_id: Option, -} - -/// NLRI comprised of a [`BasicNlri`] and MPLS `Labels`. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct MplsNlri { - basic: BasicNlri, - labels: Labels, -} - -impl PartialEq> for MplsNlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &MplsNlri) -> bool { - self.basic == other.basic && self.labels == other.labels - } -} - -/// NLRI comprised of a [`BasicNlri`], MPLS `Labels` and a VPN -/// `RouteDistinguisher`. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct MplsVpnNlri { - basic: BasicNlri, - labels: Labels, - rd: RouteDistinguisher, -} - -impl MplsVpnNlri { - pub fn basic(&self) -> BasicNlri { - self.basic - } - - pub fn labels(&self) -> &Labels { - &self.labels - } - - pub fn rd(&self) -> RouteDistinguisher { - self.rd - } -} - -impl PartialEq> for MplsVpnNlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &MplsVpnNlri) -> bool { - self.basic == other.basic - && self.labels == other.labels - && self.rd == other.rd - } -} - -/// VPLS Information as defined in RFC4761. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct VplsNlri { - rd: RouteDistinguisher, - ve_id: u16, - ve_block_offset: u16, - ve_block_size: u16, - raw_label_base: u32, -} - -/// NLRI containing a FlowSpec v1 specification. -/// -/// Also see [`crate::flowspec`]. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct FlowSpecNlri { - #[allow(dead_code)] - afi: Afi, - raw: Octs, -} - -impl PartialEq> for FlowSpecNlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &FlowSpecNlri) -> bool { - self.raw.as_ref() == other.raw.as_ref() - } -} - -/// NLRI containing a Route Target membership as defined in RFC4684. -/// -/// **TODO**: implement accessor methods for the contents of this NLRI. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct RouteTargetNlri { - #[allow(dead_code)] - raw: Octs, -} - -impl PartialEq> for RouteTargetNlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &RouteTargetNlri) -> bool { - self.raw.as_ref() == other.raw.as_ref() - } -} - -/// NLRI containing a EVPN NLRI as defined in RFC7432. -/// -/// **TODO**: implement accessor methods for the contents of this NLRI. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct EvpnNlri { - #[allow(dead_code)] - route_type: EvpnRouteType, - raw: Octs, -} - -impl EvpnNlri { - pub fn route_type(&self) -> EvpnRouteType { - self.route_type - } -} - -impl> EvpnNlri { - fn compose_len(&self) -> usize { - 1 + self.raw.as_ref().len() - } -} - -typeenum!( - EvpnRouteType, u8, - { - 1 => EthernetAutoDiscovery, - 2 => MacIpAdvertisement, - 3 => InclusiveMulticastEthernetTag, - 4 => EthernetSegment, - 5 => IpPrefix, - } -); - - -impl PartialEq> for EvpnNlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &EvpnNlri) -> bool { - self.raw.as_ref() == other.raw.as_ref() - } -} - -/// Conventional and BGP-MP NLRI variants. -#[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub enum Nlri { // (AFIs , SAFIs): - Unicast(BasicNlri), // (v4/v6, unicast) - Multicast(BasicNlri), // (v4/v6, multicast) - Mpls(MplsNlri), // (v4/v6, mpls unicast) - MplsVpn(MplsVpnNlri), // (v4/v6, mpls vpn unicast) - Vpls(VplsNlri), // (l2vpn, vpls) - FlowSpec(FlowSpecNlri), // (v4/v6, flowspec) - RouteTarget(RouteTargetNlri), // (v4, route target) - Evpn(EvpnNlri), -} -// XXX some thoughts on if and how we need to redesign Nlri: -// -// Looking at which enum variants represent which afis/safis, currently only -// the Basic variant represents more than one SAFI, i.e. unicast and -// multicast. The other variants represent a single SAFI, and many of them do -// that for both v4 and v6. -// -// This means that when an Iterator is created, the user -// determines the SAFI by matching on the Nlri variant coming from next(), but -// for the BasicNlri it is uncertain whether we deal with unicast or -// multicast. -// -// It would be nice if the user could query the UpdateMessage to find out what -// AFI/SAFI is in the announcements/withdrawals. Currently, `fn nlris()` and -// `fn withdrawals()` return a struct with getters for both afi and safi, and -// a `fn iter()` to get an iterator over the actual NLRI. But because nlris() -// needs an overhaul to (correctly) return both conventional and MP_* NLRI, it -// might actually return an iterator over _two_ AFI/SAFI tuples: one -// conventional v4/unicast, and one MP tuple. -// -// The AFI could be derived from the Prefix in all cases but FlowSpec. For -// the Vpls and RouteTarget variants, there can only be one (valid) AFI, i.e. -// l2vpn and v4, respectively. -// -// We also need to take into account the creation of messages, i.e. adding -// NLRI for announcement/withdrawal to an instance of UpdateBuilder. The -// UpdateBuilder needs to be able to determine from the NLRI which AFI/SAFI it -// is dealing with. Currently a BasicNlri could be both unicast and multicast, -// but there is no way to know which one. That must be fixed. -// -// Questions: -// - are we ok with deriving the AFI from the Prefix, or do we want to -// explicitly embed the AFI in the variant? (NB that would add 4 variants for -// now, possibly more later). -// - should we simply add a Multicast variant (and perhaps rename Basic to -// Unicast)? -// - should we remove methods from the enum level to force a user to pattern -// match on the exact variant? e.g. calling Nlri.prefix() hides the exact -// variant from the user, possibly causing confusion. They might unknowingly -// storing MplsVpn prefixes thinking it was a 'Basic unicast' thing. - - -impl fmt::Display for Nlri { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Nlri::Unicast(b) => { - write!(f, "{}", b.prefix())?; - if let Some(path_id) = b.path_id() { - write!(f, " (path_id {})", path_id)? - } - } - Nlri::Multicast(b) => { - write!(f, "{} (mcast)", b.prefix())?; - if let Some(path_id) = b.path_id() { - write!(f, " (path_id {})", path_id)? - } - } - Nlri::Mpls(m) => { - write!(f, "MPLS-{}-{:?}", - m.basic.prefix(), m.labels.as_ref() - )? - } - Nlri::MplsVpn(m) => { - write!(f, "MPLSVPN-{}-{:?}-{:?}", - m.basic.prefix(), m.labels.as_ref(), m.rd - )? - } - Nlri::Vpls(n) => write!(f, "VPLS-{:?}", n.rd)?, - Nlri::FlowSpec(_) => write!(f, "FlowSpec-NLRI")?, - Nlri::RouteTarget(r) => { - write!(f, "RouteTarget-NLRI-{:?}", r.raw.as_ref())? - } - Nlri::Evpn(r) => { - write!(f, "Evpn-NLRI-{:?}", r.raw.as_ref())? - } - } - Ok(()) - } -} - -impl OctetsFrom> for Nlri - where - SrcOcts: Octets, - Octs: OctetsFrom, -{ - type Error = Octs::Error; - - fn try_octets_from(source: Nlri) -> Result { - match source { - Nlri::Unicast(b) => Ok(Nlri::Unicast(b)), - Nlri::Multicast(b) => Ok(Nlri::Multicast(b)), - Nlri::Mpls(m) => Ok(Nlri::Mpls(MplsNlri { - basic: m.basic, - labels: Labels{ octets: Octs::try_octets_from(m.labels.octets)? }, - })), - Nlri::MplsVpn(m) => Ok(Nlri::MplsVpn(MplsVpnNlri { - basic: m.basic, - labels: Labels{ octets: Octs::try_octets_from(m.labels.octets)? }, - rd: m.rd - })), - Nlri::Vpls(v) => Ok(Nlri::Vpls(v)), - Nlri::RouteTarget(r) => Ok(Nlri::RouteTarget(RouteTargetNlri{ - raw: Octs::try_octets_from(r.raw)? - })), - Nlri::FlowSpec(m) => Ok(Nlri::FlowSpec(FlowSpecNlri { - afi: m.afi, - raw: Octs::try_octets_from(m.raw)? - })), - Nlri::Evpn(e) => Ok(Nlri::Evpn( EvpnNlri{ - route_type: e.route_type, - raw: Octs::try_octets_from(e.raw)? - })), - } - } -} - -impl<'a, SrcOcts: 'a + Octets> OctetsFrom<&'a Nlri> for Nlri> - where Vec: OctetsFrom, -{ - type Error = as OctetsFrom>::Error; - - fn try_octets_from(source: &'a Nlri) -> Result { - match source { - Nlri::Unicast(b) => Ok(Nlri::Unicast(*b)), - Nlri::Multicast(b) => Ok(Nlri::Multicast(*b)), - Nlri::Mpls(m) => Ok(Nlri::Mpls(MplsNlri { - basic: m.basic, - labels: Labels{ octets: m.labels.as_ref().to_vec() }, - })), - Nlri::MplsVpn(m) => Ok(Nlri::MplsVpn(MplsVpnNlri { - basic: m.basic, - labels: Labels{ octets: m.labels.as_ref().to_vec() }, - rd: m.rd - })), - Nlri::Vpls(v) => Ok(Nlri::Vpls(*v)), - Nlri::FlowSpec(m) => Ok(Nlri::FlowSpec(FlowSpecNlri{ - afi: m.afi, - raw: m.raw.as_ref().to_vec() - })), - Nlri::RouteTarget(r) => Ok(Nlri::RouteTarget(RouteTargetNlri{ - raw: r.raw.as_ref().to_vec(), - })), - Nlri::Evpn(e) => Ok(Nlri::Evpn( EvpnNlri{ - route_type: e.route_type, - raw: e.raw.as_ref().to_vec(), - })), - } - } -} - -impl OctetsFrom> for FlowSpecNlri - where Octs: OctetsFrom, -{ - type Error = Octs::Error; - - fn try_octets_from( - source: FlowSpecNlri - ) -> Result { - Ok(FlowSpecNlri { afi: source.afi, raw: Octs::try_octets_from(source.raw)? } ) - } -} - -/* -impl<'a, Octs, SrcOcts: 'a + Octets> OctetsFrom<&'a FlowSpecNlri> for FlowSpecNlri - where Octs: OctetsFrom, -{ - type Error = Octs::Error; - - fn try_octets_from( - source: &'a FlowSpecNlri - ) -> Result { - Ok(FlowSpecNlri { afi: source.afi, raw: Octs::try_octets_from(source.raw)? } ) - } -} -*/ - -impl<'a, SrcOcts: 'a + Octets> OctetsFrom<&'a FlowSpecNlri> for FlowSpecNlri> - where Vec: OctetsFrom, - Vec: From -{ - type Error = as OctetsFrom>::Error; - - fn try_octets_from( - source: &'a FlowSpecNlri - ) -> Result { - Ok(FlowSpecNlri { afi: source.afi, raw: source.raw.as_ref().to_vec() } ) - } -} - -impl PartialEq> for Nlri -where Octs: AsRef<[u8]>, - Other: AsRef<[u8]> -{ - fn eq(&self, other: &Nlri) -> bool { - match (self, other) { - (Self::Unicast(s), Nlri::Unicast(o)) | - (Self::Multicast(s), Nlri::Multicast(o)) => s == o, - (Self::Mpls(s), Nlri::Mpls(o)) => s == o, - (Self::MplsVpn(s), Nlri::MplsVpn(o)) => s == o, - (Self::Vpls(s), Nlri::Vpls(o)) => s == o, - (Self::FlowSpec(s), Nlri::FlowSpec(o)) => s == o, - (Self::RouteTarget(s), Nlri::RouteTarget(o)) => s == o, - _ => false - } - } -} - -impl> Eq for Nlri { } - -impl Nlri { - /// Returns true if this NLRI contains a Path Id. - pub fn is_addpath(&self) -> bool { - match self { - Self::Unicast(b) | Self::Multicast(b) => b.is_addpath(), - Self::Mpls(m) => m.basic.is_addpath(), - Self::MplsVpn(m) => m.basic.is_addpath(), - Self::Vpls(_) | Self::FlowSpec(_) | Self::RouteTarget(_) => false, - Self::Evpn(_) => false, - } - } -} - -impl Nlri { - /// Returns the `AfiSafi` for this Nlri. - pub fn afi_safi(&self) -> AfiSafi { - use AfiSafi::*; - match self { - Self::Unicast(b) => { - if b.is_v4() { - Ipv4Unicast - } else { - Ipv6Unicast - } - } - Self::Multicast(b) => { - if b.is_v4() { - Ipv4Multicast - } else { - Ipv6Multicast - } - } - Self::Mpls(n) => { - if n.basic.is_v4() { - Ipv4MplsUnicast - } else { - Ipv6MplsUnicast - } - } - Self::MplsVpn(n) => { - if n.basic.is_v4() { - Ipv4MplsVpnUnicast - } else { - Ipv6MplsVpnUnicast - } - } - Self::Vpls(_) => L2VpnVpls, - Self::FlowSpec(n) => { - if n.afi == Afi::Ipv4 { - Ipv4FlowSpec - } else { - Ipv6FlowSpec - } - } - Self::RouteTarget(_) => Ipv4RouteTarget, - Self::Evpn(_) => L2VpnEvpn, - } - - } - -} - -impl> Nlri { - - /// Returns the MPLS [`Labels`], if any. - /// - /// Applicable to MPLS and MPLS-VPN NLRI. - pub fn labels(&self) -> Option<&Labels> { - match &self { - Nlri::Mpls(n) => Some(&n.labels), - Nlri::MplsVpn(n) => Some(&n.labels), - _ => None - } - } - - /// Returns the RouteDistinguisher, if any. - /// - /// Applicable to MPLS VPN and VPLS NLRI. - pub fn rd(&self) -> Option { - match self { - Nlri::MplsVpn(n) => Some(n.rd), - Nlri::Vpls(n) => Some(n.rd), - _ => None - } - } - - // VPLS specific methods - - /// Returns the VPLS VE ID. - pub fn ve_id(&self) -> Option { - match self { - Nlri::Vpls(n) => Some(n.ve_id), - _ => None - } - - } - - /// Returns the VPLS VE Block Offset. - pub fn ve_block_offset(&self) -> Option { - match self { - Nlri::Vpls(n) => Some(n.ve_block_offset), - _ => None - } - } - - /// Returns the VPLS VE Block Size. - pub fn ve_block_size(&self) -> Option { - match self { - Nlri::Vpls(n) => Some(n.ve_block_size), - _ => None - } - } - - /// Returns the VPLS Label Base. - pub fn raw_label_base(&self) -> Option { - match self { - Nlri::Vpls(n) => Some(n.raw_label_base), - _ => None - } - } - - //--- Compose methods - - pub fn compose_len(&self) -> usize { - match self { - Nlri::Unicast(b) | Nlri::Multicast(b) => b.compose_len(), - Nlri::Mpls(m) => m.compose_len(), - Nlri::MplsVpn(m) => m.compose_len(), - Nlri::Vpls(v) => v.compose_len(), - Nlri::FlowSpec(f) => f.compose_len(), - Nlri::RouteTarget(r) => r.compose_len(), - Nlri::Evpn(e) => e.compose_len(), - } - } -} - - -// XXX While Nlri<()> might make more sense, it clashes with trait bounds -// like Vec: OctetsFrom elsewhere, as, From<()> is not implemented for -// Vec. Similarly, () is not AsRef<[u8]>. -impl Nlri<&[u8]> { - /// Creates a `Nlri::Unicast` for `prefix`. - /// - /// This returns the error thrown by `Prefix::from_str` if `prefix` does - /// not represent a valid IPv6 or IPv4 prefix. - pub fn unicast_from_str(prefix: &str) - -> Result, ::Err> - { - Ok( - Nlri::Unicast(BasicNlri::new( - Prefix::from_str(prefix)? - )) - ) - } -} - -// Calculate the number of bytes we need to parse for a certain prefix length -// given in bits. -fn prefix_bits_to_bytes(bits: u8) -> usize { - if bits != 0 { - (bits as usize - 1) / 8 + 1 - } else { - 0 - } -} - -fn check_prefix( - parser: &mut Parser, - prefix_bits: u8, - afi: Afi -) -> Result<(), ParseError> { - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - match (afi, prefix_bytes) { - (Afi::Ipv4, 0) => { }, - (Afi::Ipv4, _b @ 5..) => { - return Err( - ParseError::form_error("illegal byte size for IPv4 NLRI") - ) - }, - (Afi::Ipv4, _) => { parser.advance(prefix_bytes)?; } - (Afi::Ipv6, 0) => { }, - (Afi::Ipv6, _b @ 17..) => { - return Err( - ParseError::form_error("illegal byte size for IPv6 NLRI") - ) - }, - (Afi::Ipv6, _) => { parser.advance(prefix_bytes)?; }, - (_, _) => { - return Err( - ParseError::form_error("unknown prefix format") - ) - } - }; - - Ok(()) -} - -fn parse_prefix_for_len( - parser: &mut Parser<'_, R>, - prefix_bits: u8, - afi: Afi -) - -> Result -{ - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - let prefix = match (afi, prefix_bytes) { - (Afi::Ipv4, 0) => { - Prefix::new_v4(0.into(), 0)? - }, - (Afi::Ipv4, _b @ 5..) => { - return Err( - ParseError::form_error("illegal byte size for IPv4 NLRI") - ) - }, - (Afi::Ipv4, _) => { - let mut b = [0u8; 4]; - b[..prefix_bytes].copy_from_slice(parser.peek(prefix_bytes)?); - parser.advance(prefix_bytes)?; - Prefix::new(IpAddr::from(b), prefix_bits).map_err(|e| - ParseError::form_error(e.static_description()) - )? - } - (Afi::Ipv6, 0) => { - Prefix::new_v6(0.into(), 0)? - }, - (Afi::Ipv6, _b @ 17..) => { - return Err( - ParseError::form_error("illegal byte size for IPv6 NLRI") - ) - }, - (Afi::Ipv6, _) => { - let mut b = [0u8; 16]; - b[..prefix_bytes].copy_from_slice(parser.peek(prefix_bytes)?); - parser.advance(prefix_bytes)?; - Prefix::new(IpAddr::from(b), prefix_bits).map_err(|e| - ParseError::form_error(e.static_description()) - )? - }, - (_, _) => { - return Err( - ParseError::form_error("unknown prefix format") - ) - } - }; - Ok(prefix) -} - -fn skip_prefix(parser: &mut Parser<'_, R>) - -> Result<(), ParseError> -{ - let prefix_bits = parser.parse_u8()?; - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - Ok(parser.advance(prefix_bytes)?) -} - -fn skip_prefix_for_len(parser: &mut Parser<'_, R>, prefix_bits: u8) - -> Result<(), ParseError> -{ - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - Ok(parser.advance(prefix_bytes)?) -} - -fn skip_prefix_addpath(parser: &mut Parser<'_, R>) - -> Result<(), ParseError> -{ - parser.advance(4)?; - let prefix_bits = parser.parse_u8()?; - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - Ok(parser.advance(prefix_bytes)?) -} - -pub(crate) fn parse_prefix(parser: &mut Parser<'_, R>, afi: Afi) - -> Result -{ - let prefix_bits = parser.parse_u8()?; - parse_prefix_for_len(parser, prefix_bits, afi) -} - -/* -impl BasicNlri { - pub fn check( - parser: &mut Parser, - config: SessionConfig, - afisafi: AfiSafi, - ) -> Result<(), ParseError> { - if config.rx_addpath(afisafi) { - PathId::check(parser)? - } - - let prefix_bits = parser.parse_u8()?; - check_prefix(parser, prefix_bits, afisafi.afi())?; - - Ok(()) - } - - pub fn parse( - parser: &mut Parser<'_, R>, - config: SessionConfig, - afisafi: AfiSafi, - ) -> Result { - let path_id = if config.rx_addpath(afisafi) { - Some(PathId::parse(parser)?) - } else { - None - }; - - let prefix_bits = parser.parse_u8()?; - let prefix = parse_prefix_for_len( - parser, - prefix_bits, - afisafi.afi() - )?; - - Ok( - BasicNlri { - prefix, - path_id, - } - ) - } - - pub fn new(prefix: Prefix) -> BasicNlri { - BasicNlri { prefix, path_id: None } - } - - pub fn with_path_id(prefix: Prefix, path_id: PathId) -> BasicNlri { - BasicNlri { prefix, path_id: Some(path_id) } - } - - pub fn prefix(&self) -> Prefix { - self.prefix - } - - /// Returns the PathId for AddPath enabled prefixes, if some. - pub fn path_id(&self) -> Option { - self.path_id - } - - /// Returns true if this NLRI contains a Path Id. - pub fn is_addpath(&self) -> bool { - self.path_id.is_some() - } - - pub(crate) fn compose_len(&self) -> usize { - let mut res = if self.path_id.is_some() { - 4 - } else { - 0 - }; - // 1 byte for the length itself - res += 1 + prefix_bits_to_bytes(self.prefix.len()); - res - } - - pub(crate) fn compose(&self, target: &mut Target) - -> Result<(), Target::AppendError> { - let len = self.prefix.len(); - if let Some(path_id) = self.path_id { - target.append_slice(&path_id.to_raw())?; - } - - target.append_slice(&[len])?; - let prefix_bytes = prefix_bits_to_bytes(len); - - match self.prefix.addr() { - IpAddr::V4(a) => { - target.append_slice(&a.octets()[..prefix_bytes])?; - } - IpAddr::V6(a) => { - target.append_slice(&a.octets()[..prefix_bytes])?; - } - } - Ok(()) - } - - pub fn is_v4(&self) -> bool { - self.prefix.is_v4() - } -} - -impl From for BasicNlri { - fn from(prefix: Prefix) -> BasicNlri { - BasicNlri { prefix, path_id: None } - } -} - -impl From<(Prefix, PathId)> for BasicNlri { - fn from(tuple: (Prefix, PathId)) -> BasicNlri { - BasicNlri { prefix: tuple.0, path_id: Some(tuple.1) } - } -} - -impl From<(Prefix, Option)> for BasicNlri { - fn from(tuple: (Prefix, Option)) -> BasicNlri { - BasicNlri { prefix: tuple.0, path_id: tuple.1 } - } -} -*/ - - -impl MplsVpnNlri { - pub fn check( - parser: &mut Parser, - config: PduParseInfo, - afisafi: AfiSafi, - ) -> Result<(), ParseError> - { - if config.rx_addpath(afisafi) { - parser.advance(4)?; - } - - let mut prefix_bits = parser.parse_u8()?; - let labels = Labels::parse(parser)?; - - // Check whether we can safely subtract the labels length from the - // prefix size. If there is an unexpected path id, we might silently - // subtract too much, because there is no 'subtract with overflow' - // warning when built in release mode. - if 8 * labels.len() as u8 > prefix_bits { - return Err(ParseError::ShortInput); - } - - prefix_bits -= 8 * labels.len() as u8; - - RouteDistinguisher::check(parser)?; - prefix_bits -= 8 * 8_u8; - - check_prefix(parser, prefix_bits, afisafi.afi())?; - - Ok(()) - } -} - -impl MplsVpnNlri { - pub fn parse<'a, R>( - parser: &mut Parser<'a, R>, - config: PduParseInfo, - afisafi: AfiSafi, - ) -> Result - where - R: Octets = Octs> - { - let path_id = if config.rx_addpath(afisafi) { - Some(PathId::parse(parser)?) - } else { - None - }; - - let mut prefix_bits = parser.parse_u8()?; - let labels = Labels::parse(parser)?; - - // Check whether we can safely subtract both the labels length and the - // route Distinguisher length from the prefix size. If there is an - // unexpected path id, we might silently subtract too much, because - // there is no 'subtract with overflow' warning when built in release - // mode. - - if - u8::try_from(8 * (8 + labels.len())) - .map_err(|_| ParseError::form_error( - "MplsVpnNlri labels/rd too long" - ))? > prefix_bits - { - return Err(ParseError::ShortInput); - } - - prefix_bits -= 8 * labels.len() as u8; - - let rd = RouteDistinguisher::parse(parser)?; - - prefix_bits -= 8*8; - - let prefix = parse_prefix_for_len(parser, prefix_bits, afisafi.afi())?; - - let basic = BasicNlri{ prefix, path_id }; - Ok(MplsVpnNlri{ basic, labels, rd }) - } - - pub fn parse_no_addpath<'a, R>( - parser: &mut Parser<'a, R>, - afisafi: AfiSafi, - ) -> Result - where - R: Octets = Octs> - { - let (labels, rd, prefix) = - Self::parse_labels_rd_prefix(parser, afisafi)?; - - let basic = BasicNlri::new(prefix); - - Ok(MplsVpnNlri{basic, labels, rd}) - } - - pub fn parse_addpath<'a, R>( - parser: &mut Parser<'a, R>, - afisafi: AfiSafi, - ) -> Result - where - R: Octets = Octs> - { - let path_id = PathId::parse(parser)?; - let (labels, rd, prefix) = - Self::parse_labels_rd_prefix(parser, afisafi)?; - - let basic = BasicNlri::with_path_id(prefix, path_id); - - Ok(MplsVpnNlri{basic, labels, rd}) - } - - fn parse_labels_rd_prefix<'a, R>( - parser: &mut Parser<'a, R>, - afisafi: AfiSafi, - ) -> Result<(Labels, RouteDistinguisher, Prefix), ParseError> - where - R: Octets = Octs> - { - let mut prefix_bits = parser.parse_u8()?; - let labels = Labels::::parse(parser)?; - - // 8 for the RouteDistinguisher, plus byte length of labels, - // times 8 to go from bytes to bits - let rd_label_len = u8::try_from(8 * (8 + labels.len())) - .map_err(|_| ParseError::form_error( - "MplsVpnNlri labels/rd too long" - ))?; - - if rd_label_len > prefix_bits { - return Err(ParseError::ShortInput); - } - - let rd = RouteDistinguisher::parse(parser)?; - prefix_bits -= rd_label_len; - - let prefix = parse_prefix_for_len( - parser, - prefix_bits, - afisafi.afi() - )?; - - Ok((labels, rd, prefix)) - } - - fn skip_labels_rd_prefix<'a, R>( - parser: &mut Parser<'a, R>, - ) -> Result<(), ParseError> - where - R: Octets = Octs> - { - let mut prefix_bits = parser.parse_u8()?; - let labels_len = Labels::::skip(parser)?; - - let rd_label_len = u8::try_from(8 * (8 + labels_len)) - .map_err(|_| ParseError::form_error( - "MplsVpnNlri labels/rd too long" - ))?; - - if rd_label_len > prefix_bits { - return Err(ParseError::ShortInput); - } - - RouteDistinguisher::skip(parser)?; - prefix_bits -= rd_label_len; - - skip_prefix_for_len(parser, prefix_bits)?; - - Ok(()) - } -} - -impl> MplsVpnNlri { - fn compose_len(&self) -> usize { - self.basic.compose_len() + self.labels.len() + self.rd.bytes.len() - } -} - -impl MplsNlri { - pub fn check( - parser: &mut Parser, - config: PduParseInfo, - afisafi: AfiSafi, - ) -> Result<(), ParseError> { - if config.rx_addpath(afisafi) { - PathId::check(parser)? - } - - let mut prefix_bits = parser.parse_u8()?; - let labels = Labels::parse(parser)?; - - // Check whether we can safely subtract the labels length from the - // prefix size. If there is an unexpected path id, we might silently - // subtract too much, because there is no 'subtract with overflow' - // warning when built in release mode. - if 8 * labels.len() as u8 > prefix_bits { - return Err(ParseError::ShortInput); - } - - prefix_bits -= 8 * labels.len() as u8; - check_prefix(parser, prefix_bits, afisafi.afi())?; - Ok(()) - } -} - -impl MplsNlri { - pub fn new(basic: BasicNlri, labels: Labels) -> Self { - Self { basic, labels } - } - pub fn basic(&self) -> BasicNlri { - self.basic - } -} - -impl fmt::Display for MplsNlri { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "MPLS:{}", self.basic().prefix()) - - } -} - - -impl MplsNlri { - pub fn parse<'a, R>( - parser: &mut Parser<'a, R>, - config: PduParseInfo, - afisafi: AfiSafi, - ) -> Result - where - R: Octets = Octs> - { - let path_id = if config.rx_addpath(afisafi) { - Some(PathId::parse(parser)?) - } else { - None - }; - - let (prefix, labels) = Self::parse_labels_and_prefix(parser, afisafi)?; - let basic = BasicNlri { prefix, path_id }; - - Ok( - MplsNlri { - basic, - labels, - } - ) - } - - pub fn parse_no_addpath<'a, R>( - parser: &mut Parser<'a, R>, - afisafi: AfiSafi, - ) -> Result - where - R: Octets = Octs> - { - let (prefix, labels) = Self::parse_labels_and_prefix(parser, afisafi)?; - let basic = BasicNlri::new(prefix); - - Ok( - MplsNlri { - basic, - labels, - } - ) - } - - pub fn parse_addpath<'a, R>( - parser: &mut Parser<'a, R>, - afisafi: AfiSafi, - ) -> Result - where - R: Octets = Octs> - { - let path_id = PathId::parse(parser)?; - let (prefix, labels) = Self::parse_labels_and_prefix(parser, afisafi)?; - let basic = BasicNlri::with_path_id(prefix, path_id); - - Ok( - MplsNlri { - basic, - labels, - } - ) - } - - pub fn parse_labels_and_prefix<'a, R>( - parser: &mut Parser<'a, R>, - afisafi: AfiSafi, - ) -> Result<(Prefix, Labels), ParseError> - where - R: Octets = Octs> - { - let mut prefix_bits = parser.parse_u8()?; - let labels = Labels::::parse(parser)?; - - // Check whether we can safely subtract the labels length from the - // prefix size. If there is an unexpected path id, we might silently - // subtract too much, because there is no 'subtract with overflow' - // warning when built in release mode. - - if u8::try_from(8 * labels.len()) - .map_err(|_| ParseError::form_error("MplsNlri labels too long"))? - > prefix_bits { - return Err(ParseError::ShortInput); - } - - prefix_bits -= 8 * labels.len() as u8; - - let prefix = parse_prefix_for_len( - parser, - prefix_bits, - afisafi.afi() - )?; - - Ok((prefix, labels)) - } - - fn skip_labels_and_prefix<'a, R>( - parser: &mut Parser<'a, R>, - ) -> Result<(), ParseError> - where - R: Octets = Octs> - { - let mut prefix_bits = parser.parse_u8()?; - let labels_len = Labels::::skip(parser)?; - - // Check whether we can safely subtract the labels length from the - // prefix size. If there is an unexpected path id, we might silently - // subtract too much, because there is no 'subtract with overflow' - // warning when built in release mode. - - if u8::try_from(8 * labels_len) - .map_err(|_| ParseError::form_error("MplsNlri labels too long"))? - > prefix_bits { - return Err(ParseError::ShortInput); - } - - prefix_bits -= 8 * labels_len as u8; - - skip_prefix_for_len(parser, prefix_bits)?; - - Ok(()) - } - - - -} - -impl> MplsNlri { - fn compose_len(&self) -> usize { - self.basic.compose_len() + self.labels.len() - } - pub fn labels(&self) -> &Labels { - &self.labels - } -} - -impl VplsNlri { - pub fn skip(parser: &mut Parser) - -> Result<(), ParseError> - { - parser.advance(2)?; // length, u16 - RouteDistinguisher::check(parser)?; - // ve id/block offset/block size, label base - parser.advance(2 + 2 + 2 + 1 + 2)?; - - Ok(()) - } - - pub fn parse(parser: &mut Parser<'_, R>) - -> Result - { - let _len = parser.parse_u16_be()?; - let rd = RouteDistinguisher::parse(parser)?; - let ve_id = parser.parse_u16_be()?; - let ve_block_offset = parser.parse_u16_be()?; - let ve_block_size = parser.parse_u16_be()?; - let label_base_1 = parser.parse_u8()? as u32; - let label_base_2 = parser.parse_u16_be()? as u32; - - Ok( - VplsNlri { - rd, - ve_id, - ve_block_offset, - ve_block_size, - raw_label_base: label_base_1 << 16 | label_base_2, - } - ) - } - - fn compose_len(&self) -> usize { - 8 + 2 + 2 + 2 + 4 - } -} - -impl FlowSpecNlri { - pub fn check(parser: &mut Parser, afi: Afi) - -> Result<(), ParseError> - { - let len1 = parser.parse_u8()?; - let len: u16 = if len1 >= 0xf0 { - let len2 = parser.parse_u8()? as u16; - (((len1 as u16) << 8) | len2) & 0x0fff - } else { - len1 as u16 - }; - let mut pp = parser.parse_parser(len.into())?; - match afi { - Afi::Ipv4 => { - while pp.remaining() > 0 { - Component::parse(&mut pp)?; - } - Ok(()) - } - Afi::Ipv6 => { - debug!("FlowSpec v6 not implemented yet"); - Ok(()) - } - _ => Err(ParseError::form_error("illegal AFI for FlowSpec")) - - } - } - - fn skip(parser: &mut Parser) -> Result<(), ParseError> { - let len1 = parser.parse_u8()?; - let len: u16 = if len1 >= 0xf0 { - let len2 = parser.parse_u8()? as u16; - (((len1 as u16) << 8) | len2) & 0x0fff - } else { - len1 as u16 - }; - - Ok(parser.advance(len.into())?) - } -} - -impl FlowSpecNlri { - pub fn parse<'a, R>(parser: &mut Parser<'a, R>, afi: Afi) - -> Result - where - R: Octets = Octs> - { - let len1 = parser.parse_u8()?; - let len: u16 = if len1 >= 0xf0 { - let len2 = parser.parse_u8()? as u16; - (((len1 as u16) << 8) | len2) & 0x0fff - } else { - len1 as u16 - }; - let pos = parser.pos(); - - if usize::from(len) > parser.remaining() { - return Err(ParseError::form_error( - "invalid length of FlowSpec NLRI" - )); - } - - match afi { - Afi::Ipv4 => { - while parser.pos() < pos + len as usize { - Component::parse(parser)?; - } - } - Afi::Ipv6 => { - debug!("FlowSpec v6 not implemented yet, \ - returning unchecked NLRI" - ); - } - _ => { - return Err(ParseError::form_error("illegal AFI for FlowSpec")) - } - } - - parser.seek(pos)?; - let raw = parser.parse_octets(len as usize)?; - - Ok( - FlowSpecNlri { - afi, - raw - } - ) - } - - pub(crate) fn compose(&self, target: &mut Target) - -> Result<(), Target::AppendError> { - let len = self.raw.as_ref().len(); - if len >= 240 { - todo!(); //FIXME properly encode into 0xfnnn for 239 < len < 4095 - /* - target.append_slice( - &u16::try_from(self.compose_len()).unwrap_or(u16::MAX) - .to_be_bytes() - )?; - */ - } else { - // We know len < 255 so we can safely unwrap. - target.append_slice(&[u8::try_from(len).unwrap()])?; - } - target.append_slice(self.raw.as_ref()) - } -} - - -impl> FlowSpecNlri { - pub(crate) fn compose_len(&self) -> usize { - let value_len = self.raw.as_ref().len(); - let len_len = if value_len >= 240 { 2 } else { 1 } ; - len_len + value_len - } -} - -impl RouteTargetNlri { - pub fn _check(parser: &mut Parser) - -> Result<(), ParseError> - { - let prefix_bits = parser.parse_u8()?; - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - parser.advance(prefix_bytes)?; - - Ok(()) - } -} - -impl RouteTargetNlri { - pub fn parse<'a, R>(parser: &mut Parser<'a, R>) - -> Result - where - R: Octets = Octs> - { - let prefix_bits = parser.parse_u8()?; - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - let raw = parser.parse_octets(prefix_bytes)?; - - Ok( - RouteTargetNlri { - raw - } - ) - } - - pub fn parse_no_addpath<'a, R>(parser: &mut Parser<'a, R>) - -> Result - where - R: Octets = Octs> - { - Self::parse(parser) - } - - fn skip(parser: &mut Parser) -> Result<(), ParseError> - where - R: AsRef<[u8]> - { - let prefix_bits = parser.parse_u8()?; - let prefix_bytes = prefix_bits_to_bytes(prefix_bits); - parser.advance(prefix_bytes)?; - - Ok(()) - } -} - -impl> RouteTargetNlri { - fn compose_len(&self) -> usize { - self.raw.as_ref().len() - } -} - -impl EvpnNlri { - pub fn parse<'a, R>(parser: &mut Parser<'a, R>) - -> Result - where - R: Octets = Octs> - { - let route_type = parser.parse_u8()?.into(); - let route_len = parser.parse_u8()?; - let raw = parser.parse_octets(route_len.into())?; - - Ok( - EvpnNlri { - route_type, - raw - } - ) - } - - fn skip(parser: &mut Parser) -> Result<(), ParseError> - where - R: AsRef<[u8]> - { - parser.advance(1)?; // Route Type - let len = parser.parse_u8()?; - parser.advance(len.into())?; // Length in bytes - Ok(()) - } -} - -//------------ Tests ---------------------------------------------------------- - -#[cfg(test)] -mod tests { - - use super::*; - use std::str::FromStr; - - #[test] - fn compose_len() { - fn test(p: (&str, Option), expected_len: usize) { - let prefix = Prefix::from_str(p.0).unwrap(); - let b: BasicNlri = (prefix, p.1).into(); - assert_eq!(b.compose_len(), expected_len); - } - - [ - (("10.0.0.0/24", None), 4 ), - (("10.0.0.0/23", None), 4 ), - (("10.0.0.0/25", None), 5 ), - (("0.0.0.0/0", None), 1 ), - - (("10.0.0.0/24", Some(PathId(1))), 4 + 4 ), - (("10.0.0.0/23", Some(PathId(1))), 4 + 4 ), - (("10.0.0.0/25", Some(PathId(1))), 5 + 4 ), - (("0.0.0.0/0", Some(PathId(1))), 1 + 4 ), - - (("2001:db8::/32", None), 5 ), - (("::/0", None), 1 ), - - (("2001:db8::/32", Some(PathId(1))), 5 + 4 ), - (("::/0", Some(PathId(1))), 1 + 4 ), - - ].iter().for_each(|e| test(e.0, e.1)); - - } -} - diff --git a/src/bgp/mod.rs b/src/bgp/mod.rs index 1d9db2a3..e2fb9f9a 100644 --- a/src/bgp/mod.rs +++ b/src/bgp/mod.rs @@ -1,6 +1,6 @@ //! Types and parsing for BGP messages. -pub mod nlri; // new nlri module +pub mod nlri; pub mod aspath; pub mod communities; pub mod path_attributes; From 7539ee099aebb37420ee6a508444838f4b991627 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:27:10 +0100 Subject: [PATCH 80/96] Remove unused {addr,asn} modules as we use the inetnum crate now --- src/addr.rs | 1423 --------------------------------------------------- src/asn.rs | 767 --------------------------- src/lib.rs | 2 - 3 files changed, 2192 deletions(-) delete mode 100644 src/addr.rs delete mode 100644 src/asn.rs diff --git a/src/addr.rs b/src/addr.rs deleted file mode 100644 index bf96312a..00000000 --- a/src/addr.rs +++ /dev/null @@ -1,1423 +0,0 @@ -//! IP address resources. - -use std::{error, fmt}; -use std::cmp::Ordering; -use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr}; -use std::num::ParseIntError; -use std::str::FromStr; - - -//------------ Bits ---------------------------------------------------------- - -/// The value of an IP address. -/// -/// This private type holds the content of an IP address. It is big enough to -/// hold either an IPv4 and IPv6 address as it keeps the address internally -/// as a 128 bit unsigned integer. IPv6 addresses are kept in all bits in host -/// byte order while IPv4 addresses are kept in the upper four bytes and are -/// right-padded with zero bits. This makes it possible to count prefix -/// lengths the same way for both addresses, i.e., starting from the top of -/// the raw integer. -/// -/// There is no way of distinguishing between IPv4 and IPv6 from just a value -/// of this type. This information needs to be carried separately. -#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -struct Bits(u128); - -impl Bits { - /// Creates a new address from 128 raw bits in host byte order. - pub fn new(bits: u128) -> Self { - Bits(bits) - } - - /// Creates a new address value for an IPv4 address. - pub fn from_v4(addr: Ipv4Addr) -> Self { - Self::new(u128::from(u32::from(addr)) << 96) - } - - /// Creates a new address value for an IPv6 address. - pub fn from_v6(addr: Ipv6Addr) -> Self { - Self::new(u128::from(addr)) - } - - /// Returns the raw bits of the underlying integer. - pub fn into_int(self) -> u128 { - self.0 - } - - /// Converts the address value into an IPv4 address. - /// - /// The methods disregards the lower twelve bytes of the value. - pub fn into_v4(self) -> Ipv4Addr { - ((self.0 >> 96) as u32).into() - } - - /// Converts the address value into an IPv6 address. - pub fn into_v6(self) -> Ipv6Addr { - self.0.into() - } - - /// Checks whether the host portion of the bits used in a prefix is zero. - fn is_host_zero(self, len: u8) -> bool { - self.0.trailing_zeros() >= 128u32.saturating_sub(len.into()) - } - - /// Clears the bits in the host portion of a prefix. - fn clear_host(self, len: u8) -> Self { - if len == 0 { - Bits(0) - } - else { - Bits(self.0 & (u128::MAX << (128u8.saturating_sub(len)))) - } - } - - /// Returns a value with all but the first `prefix_len` bits set. - /// - /// The first `prefix_len` bits are retained. Thus, the returned address - /// is the largest address in a prefix of this length. - fn into_max(self, prefix_len: u8) -> Self { - if prefix_len >= 128 { - self - } - else { - Self(self.0 | (u128::MAX >> prefix_len as usize)) - } - } -} - - -//--- From - -impl From for Bits { - fn from(addr: u128) -> Self { - Self::new(addr) - } -} - -impl From for Bits { - fn from(addr: Ipv4Addr) -> Self { - Self::from_v4(addr) - } -} - -impl From for Bits { - fn from(addr: Ipv6Addr) -> Self { - Self::from_v6(addr) - } -} - -impl From for Bits { - fn from(addr: IpAddr) -> Self { - match addr { - IpAddr::V4(addr) => Self::from(addr), - IpAddr::V6(addr) => Self::from(addr) - } - } -} - -impl From for u128 { - fn from(addr: Bits) -> u128 { - addr.into_int() - } -} - -impl From for Ipv4Addr { - fn from(addr: Bits) -> Ipv4Addr { - addr.into_v4() - } -} - -impl From for Ipv6Addr { - fn from(addr: Bits) -> Ipv6Addr { - addr.into_v6() - } -} - - -//--- Debug - -impl fmt::Debug for Bits { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Bits") - .field(&format_args!("{}", self.into_v6())) - .finish() - } -} - - -//------------ FamilyAndLen -------------------------------------------------- - -/// The address family and prefix length stored in a single byte. -/// -/// This private types wraps a `u8` and uses it to store both the address -/// family – i.e., whether this is an IPv4 or IPv6 prefix –, and the prefix -/// length. -/// -/// The encoding is as follows: Values up to 32 represent IPv4 prefixes with -/// the value as their prefix length. If the left-most bit is set, the value -/// is a IPv6 prefix with the length encoded by flipping all the bits. The -/// value of 64 stands in for an IPv6 prefix with length 128. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -struct FamilyAndLen(u8); - -impl FamilyAndLen { - /// Creates a value for an IPv4 prefix. - pub fn new_v4(len: u8) -> Result { - if len > 32 { - Err(PrefixError::LenOverflow) - } - else { - Ok(Self(len)) - } - } - - /// Creates a value for an IPv6 prefix. - pub fn new_v6(len: u8) -> Result { - match len.cmp(&128) { - Ordering::Greater => Err(PrefixError::LenOverflow), - Ordering::Equal => Ok(Self(0x40)), - Ordering::Less => Ok(Self(len ^ 0xFF)) - } - } - - /// Returns whether this a IPv4 prefix. - pub fn is_v4(self) -> bool { - self.0 & 0xc0 == 0 - } - - /// Returns whether this a IPv6 prefix. - pub fn is_v6(self) -> bool { - self.0 & 0xc0 != 0 - } - - /// Returns the prefix length. - #[allow(clippy::len_without_is_empty)] - pub fn len(self) -> u8 { - match self.0 & 0xc0 { - 0x00 => self.0, - 0x40 => 128, - _ => self.0 ^ 0xFF - } - } -} - -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for FamilyAndLen { - fn arbitrary( - u: &mut arbitrary::Unstructured<'a> - ) -> arbitrary::Result { - if bool::arbitrary(u)? { - Ok(Self(u8::arbitrary(u)? % 33)) - } - else { - match u8::arbitrary(u)? % 129 { - 128 => Ok(Self(0x40)), - val => Ok(Self(val ^ 0xFF)) - } - } - } -} - - -//------------ Prefix -------------------------------------------------------- - -/// An IP address prefix: an IP address and a prefix length. -/// -/// # Ordering -/// -/// Prefixes are ordered in the following way: -/// - IPv4 comes before IPv6 -/// - More-specifics come before less-specifics -/// - Other than that, prefixes are sorted numerically from low to high -/// -/// The rationale behind this ordering is that in most use cases processing a -/// more-specific before any less-specific is more efficient (i.e. longest -/// prefix matching in routing/forwarding)) or preventing unwanted -/// intermediate stage (i.e. ROAs/VRPs for less-specifics making -/// not-yet-processed more-specifics Invalid). -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct Prefix { - /// The address family and prefix length all in one. - family_and_len: FamilyAndLen, - - /// The actual bits of the prefix. - bits: Bits, -} - -impl Prefix { - /// Creates a new prefix from an address and a length. - /// - /// The function returns an error if `len` is too large for the address - /// family of `addr`. - /// - /// Use `saturating_new` if you want the prefix length to be capped - /// instead. - pub fn new(addr: IpAddr, len: u8) -> Result { - match addr { - IpAddr::V4(addr) => Self::new_v4(addr, len), - IpAddr::V6(addr) => Self::new_v6(addr, len), - } - } - - /// Creates a new prefix from an IPv4 address and a prefix length. - /// - /// The function returns an error if `len` is greater than 32. - /// - /// Use `saturating_new_v4` if you want the prefix length to be capped - /// instead. - pub fn new_v4(addr: Ipv4Addr, len: u8) -> Result { - let family_and_len = FamilyAndLen::new_v4(len)?; - - // Check that host bits are zero. - let bits = Bits::from_v4(addr); - if !bits.is_host_zero(len) { - return Err(PrefixError::NonZeroHost) - } - - Ok(Prefix { family_and_len, bits }) - } - - /// Creates a new prefix from an IPv6 adddress and a prefix length. - /// - /// The function returns an error if `len` is greater than 128. - /// - /// Use `saturating_new_v6` if you want the prefix length to be capped - /// instead. - pub fn new_v6(addr: Ipv6Addr, len: u8) -> Result { - let family_and_len = FamilyAndLen::new_v6(len)?; - - // Check that host bits are zero. - let bits = Bits::from_v6(addr); - if !bits.is_host_zero(len) { - return Err(PrefixError::NonZeroHost) - } - - Ok(Prefix { family_and_len, bits }) - } - - /// Creates a new prefix zeroing out host bits. - pub fn new_relaxed(addr: IpAddr, len: u8) -> Result { - match addr { - IpAddr::V4(addr) => Self::new_v4_relaxed(addr, len), - IpAddr::V6(addr) => Self::new_v6_relaxed(addr, len), - } - } - - /// Creates a new prefix zeroing out host bits. - pub fn new_v4_relaxed( - addr: Ipv4Addr, len: u8 - ) -> Result { - let family_and_len = FamilyAndLen::new_v4(len)?; - Ok(Prefix { - bits: Bits::from_v4(addr).clear_host(len), - family_and_len - }) - } - - /// Creates a new prefix zeroing out host bits. - pub fn new_v6_relaxed( - addr: Ipv6Addr, len: u8 - ) -> Result { - let family_and_len = FamilyAndLen::new_v6(len)?; - Ok(Prefix { - bits: Bits::from_v6(addr).clear_host(len), - family_and_len - }) - } - - /// Returns whether the prefix is for an IPv4 address. - pub fn is_v4(self) -> bool { - self.family_and_len.is_v4() - } - - /// Returns whether the prefix is for an IPv6 address. - pub fn is_v6(self) -> bool { - self.family_and_len.is_v6() - } - - /// Returns the IP address part of a prefix. - pub fn addr(self) -> IpAddr { - if self.is_v4() { - self.bits.into_v4().into() - } - else { - self.bits.into_v6().into() - } - } - - /// Returns the length part of a prefix. - #[allow(clippy::len_without_is_empty)] - pub fn len(self) -> u8 { - self.family_and_len.len() - } - - /// Returns the prefix as a pair of the address and length. - pub fn addr_and_len(self) -> (IpAddr, u8) { - (self.addr(), self.len()) - } - - /// Returns the smallest address of the prefix. - /// - /// This is the same as [`addr`][Self::addr]. - pub fn min_addr(self) -> IpAddr { - self.addr() - } - - /// Returns the largest address of the prefix. - pub fn max_addr(self) -> IpAddr { - let bits = self.bits.into_max(self.len()); - if self.is_v4() { - bits.into_v4().into() - } - else { - bits.into_v6().into() - } - } - - /// Returns whether the prefix `self` covers the prefix `other`. - pub fn covers(self, other: Self) -> bool { - // Differing families? Not covering. - if self.is_v4() != other.is_v4() { - return false - } - - // If self is more specific than other, it can’t cover it. - if self.len() > other.len() { - return false - } - - // If we have two host prefixes, they need to be identical. - // (This needs to be extra because the bit shifting below doesn’t - // work at least in the v6 case.) - if self.is_v4() { - if self.len() == 32 && other.len() == 32 { - return self == other - } - } - else if self.len() == 128 && other.len() == 128 { - return self == other - } - - // other now needs to start with the same bits as self. - 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 - -/// See [Ordering](Prefix#ordering) in the type documentation. -impl PartialOrd for Prefix { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// See [Ordering](Prefix#ordering) in the type documentation. -impl Ord for Prefix { - fn cmp(&self, other: &Self) -> Ordering { - - match (self.is_v4(), other.is_v4()) { - (true, false) => Ordering::Less, // v4 v6 - (false, true) => Ordering::Greater, //v6 v4 - // v4 v4 or v6 v6 - (_, _) => { - if self.len() == other.len() { - self.bits.0.cmp(&other.bits.0) - } - else { - let minlen = std::cmp::min(self.len(), other.len()); - let mask = !(u128::MAX >> minlen); - if self.bits.0 & mask == other.bits.0 & mask { - // more-specific before less-specific - other.len().cmp(&self.len()) - } else { - self.bits.0.cmp(&other.bits.0) - } - } - } - } - } -} - -//--- From - -#[cfg(feature = "repository")] -impl From for Prefix { - fn from(addr: crate::repository::roa::FriendlyRoaIpAddress) -> Self { - Prefix::new( - addr.address(), addr.address_length() - ).expect("ROA IP address with illegal prefix length") - } -} - -#[cfg(feature = "repository")] -impl From for crate::repository::resources::IpBlock { - fn from(src: Prefix) -> Self { - crate::repository::resources::Prefix::new( - src.addr(), src.len() - ).into() - } -} - - -//--- Deserialize and Serialize - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for Prefix { - fn deserialize>( - deserializer: D - ) -> Result { - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = Prefix; - - fn expecting( - &self, formatter: &mut fmt::Formatter - ) -> fmt::Result { - write!(formatter, "a string with an IPv4 or IPv6 prefix") - } - - fn visit_str( - self, v: &str - ) -> Result { - Prefix::from_str(v).map_err(E::custom) - } - } - - deserializer.deserialize_str(Visitor) - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for Prefix { - fn serialize( - &self, serializer: S - ) -> Result { - serializer.collect_str(self) - } -} - - -//--- FromStr and Display - -impl FromStr for Prefix { - type Err = ParsePrefixError; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Err(ParsePrefixError::Empty) - } - let slash = s.find('/').ok_or(ParsePrefixError::MissingLen)?; - let addr = IpAddr::from_str(&s[..slash]).map_err( - ParsePrefixError::InvalidAddr - )?; - let len = u8::from_str(&s[slash + 1..]).map_err( - ParsePrefixError::InvalidLen - )?; - Prefix::new(addr, len).map_err(ParsePrefixError::InvalidPrefix) - } -} - -impl fmt::Display for Prefix { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}/{}", self.addr(), self.len()) - } -} - - -//--- Arbitrary - -#[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for Prefix { - fn arbitrary( - u: &mut arbitrary::Unstructured<'a> - ) -> arbitrary::Result { - let fal = FamilyAndLen::arbitrary(u)?; - let mut bits = Bits::arbitrary(u)?; - if fal.is_v4() { - bits.0 <<= 96; - } - Ok(Self { - family_and_len: fal, - bits: bits.clear_host(fal.len()) - }) - } -} - - - -//------------ MaxLenPrefix -------------------------------------------------- - -/// The pair of a prefix and an optional max-len. -/// -/// # Ordering -/// The ordering of MaxLenPrefixes is similar to the [ordering of -/// Prefixes](Prefix#Ordering). The only difference is the optional MaxLen. -/// When two prefixes are equal but differ in (presence of) max_len, the order -/// is as follows: -/// - any max_len always comes before no max_len -/// - a larger (higher) max_len comes before a smaller (lower) max_len (e.g. -/// 24 comes before 20). This is analog to how more-specifics come before -/// less-specifics. -/// -/// Note that the max_len can either be equal to the prefix length (with no -/// practical difference from an omitted max_len) or larger than the prefix -/// length. The max_len can not be smaller than the prefix length. Because of -/// that, we can safely order 'any max_len' before 'no max_len' for equal -/// prefixes. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -pub struct MaxLenPrefix { - /// The prefix. - prefix: Prefix, - - /// The optional maximum prefix length. - max_len: Option, -} - -impl MaxLenPrefix { - /// Creates a new value. - /// - /// The function returns an error if `max_len` is present and smaller than - /// `prefix.len()` or larger than the maximum prefix length of the - /// prefix’s address family. - pub fn new( - prefix: Prefix, max_len: Option - ) -> Result { - if let Some(max_len) = max_len { - if - (prefix.is_v4() && max_len > 32) - || max_len > 128 - { - return Err(MaxLenError::Overflow) - } - if prefix.len() > max_len { - return Err(MaxLenError::Underflow) - } - } - Ok(MaxLenPrefix { prefix, max_len }) - } - - /// Creates a value curtailing any out-of-bounds max-len. - pub fn saturating_new(prefix: Prefix, max_len: Option) -> Self { - let max_len = max_len.map(|max_len| { - if prefix.len() > max_len { - prefix.len() - } - else if prefix.is_v4() && max_len > 32 { - 32 - } - else if max_len > 128 { - 128 - } - else { - max_len - } - }); - MaxLenPrefix { prefix, max_len } - } - - /// Returns the actual prefix. - pub fn prefix(self) -> Prefix { - self.prefix - } - - /// Returns the address of the prefix. - pub fn addr(self) -> IpAddr { - self.prefix.addr() - } - - /// Returns the prefix length. - pub fn prefix_len(self) -> u8 { - self.prefix.len() - } - - /// Returns the max-length. - pub fn max_len(self) -> Option { - self.max_len - } - - /// Returns the max-length or the prefix-length if there is no max-length. - pub fn resolved_max_len(self) -> u8 { - self.max_len.unwrap_or_else(|| self.prefix.len()) - } -} - -/// See [Ordering](MaxLenPrefix#ordering) in the type documentation. -impl PartialOrd for MaxLenPrefix { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// See [Ordering](MaxLenPrefix#ordering) in the type documentation. -impl Ord for MaxLenPrefix { - fn cmp(&self, other: &Self) -> Ordering { - match self.prefix.cmp(&other.prefix) { - Ordering::Less => Ordering::Less, - Ordering::Greater => Ordering::Greater, - Ordering::Equal => { - match (self.max_len, other.max_len) { - (None, None) => Ordering::Equal, - (Some(_), None) => Ordering::Less, - (None, Some(_)) => Ordering::Greater, - (Some(n), Some(m)) => m.cmp(&n) - } - } - } - } -} - -//--- From - -impl From for MaxLenPrefix { - fn from(prefix: Prefix) -> Self { - MaxLenPrefix { prefix, max_len: None } - } -} - - -//--- FromStr and Display - -impl FromStr for MaxLenPrefix { - type Err = ParseMaxLenPrefixError; - - fn from_str(s: &str) -> Result { - let (prefix, max_len) = match s.find('-') { - Some(dash) => { - ( - Prefix::from_str(&s[..dash]).map_err( - ParseMaxLenPrefixError::InvalidPrefix - )?, - Some(u8::from_str(&s[dash + 1..]).map_err( - ParseMaxLenPrefixError::InvalidMaxLenFormat - )?) - ) - } - None => { - let prefix = Prefix::from_str(s).map_err( - ParseMaxLenPrefixError::InvalidPrefix - )?; - (prefix, None) - } - }; - Self::new(prefix, max_len).map_err( - ParseMaxLenPrefixError::InvalidMaxLenValue - ) - } -} - -impl fmt::Display for MaxLenPrefix { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.prefix())?; - if let Some(max_len) = self.max_len { - write!(f, "-{}", max_len)?; - } - Ok(()) - } -} - - -//============ Errors ======================================================== - -//------------ PrefixError --------------------------------------------------- - -/// Creating a prefix has failed. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[non_exhaustive] -pub enum PrefixError { - /// The prefix length is longer than allowed for the address family. - LenOverflow, - - /// The host portion of the address has non-zero bits set. - NonZeroHost, -} - -impl PrefixError { - /// Returns a static error message. - pub fn static_description(self) -> &'static str { - match self { - PrefixError::LenOverflow => "prefix length too large", - PrefixError::NonZeroHost => "non-zero host portion", - } - } -} - -impl From for &'static str { - fn from(err: PrefixError) -> Self { - err.static_description() - } -} - -impl fmt::Display for PrefixError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.static_description()) - } -} - -impl error::Error for PrefixError { } - - -//------------ ParsePrefixError ---------------------------------------------- - -/// Creating an IP address prefix from a string has failed. -#[derive(Clone, Debug, Eq, PartialEq)] -#[non_exhaustive] -pub enum ParsePrefixError { - /// The value parsed was empty. - Empty, - - /// The length portion after a slash was missing. - MissingLen, - - /// The address portion is invalid. - InvalidAddr(AddrParseError), - - /// The length portion is invalid. - InvalidLen(ParseIntError), - - /// The combined prefix is invalid. - InvalidPrefix(PrefixError), -} - -impl fmt::Display for ParsePrefixError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ParsePrefixError::Empty => f.write_str("empty string"), - ParsePrefixError::MissingLen => { - f.write_str("missing length portion") - } - ParsePrefixError::InvalidAddr(err) => { - write!(f, "invalid address: {}", err) - } - ParsePrefixError::InvalidLen(err) => { - write!(f, "invalid length: {}", err) - } - ParsePrefixError::InvalidPrefix(err) => err.fmt(f), - } - } -} - -impl error::Error for ParsePrefixError { } - - -//------------ MaxLenError --------------------------------------------------- - -/// A max-len prefix was constructed from illegal components. -#[derive(Clone, Debug, Eq, PartialEq)] -#[non_exhaustive] -pub enum MaxLenError { - /// The max-len is larger than allowed for the address family. - Overflow, - - /// The max-len is smaller than the prefix length. - Underflow, -} - -impl MaxLenError { - /// Returns a static error message. - pub fn static_description(self) -> &'static str { - match self { - MaxLenError::Overflow => "max-length too large", - MaxLenError::Underflow => "max-length smaller than prefix length", - } - } -} - -impl From for &'static str { - fn from(err: MaxLenError) -> Self { - err.static_description() - } -} - -impl fmt::Display for MaxLenError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - MaxLenError::Overflow => { - f.write_str("max-length too large") - } - MaxLenError::Underflow => { - f.write_str("max-length smaller than prefix length") - } - } - } -} - -impl error::Error for MaxLenError { } - - -//------------ ParseMaxLenPrefixError ---------------------------------------- - -/// Creating an max-len prefix from a string has failed. -#[derive(Clone, Debug, Eq, PartialEq)] -#[non_exhaustive] -pub enum ParseMaxLenPrefixError { - /// Parsing the prefix portion failed. - InvalidPrefix(ParsePrefixError), - - /// The max-len portion is invalid. - InvalidMaxLenFormat(ParseIntError), - - /// The max-len value is invalid. - InvalidMaxLenValue(MaxLenError) -} - -impl fmt::Display for ParseMaxLenPrefixError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ParseMaxLenPrefixError::InvalidPrefix(err) => { - err.fmt(f) - } - ParseMaxLenPrefixError::InvalidMaxLenFormat(err) => { - write!(f, "invalid max length: {}", err) - } - ParseMaxLenPrefixError::InvalidMaxLenValue(err) => { - err.fmt(f) - } - } - } -} - -impl error::Error for ParseMaxLenPrefixError { } - - -//============ Tests ========================================================= - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn good_family_and_len() { - for i in 0..=32 { - let fal = FamilyAndLen::new_v4(i).unwrap(); - assert!(fal.is_v4()); - assert!(!fal.is_v6()); - assert_eq!(fal.len(), i) - } - for i in 0..=128 { - let fal = FamilyAndLen::new_v6(i).unwrap(); - assert!(!fal.is_v4()); - assert!(fal.is_v6()); - assert_eq!(fal.len(), i) - } - } - - #[test] - fn bad_family_and_len() { - for i in 33..=255 { - assert_eq!( - FamilyAndLen::new_v4(i), - Err(PrefixError::LenOverflow) - ); - } - for i in 129..=255 { - assert_eq!( - FamilyAndLen::new_v6(i), - Err(PrefixError::LenOverflow) - ); - } - } - - #[test] - fn from_conversions() { - assert_eq!(u128::from(Bits::from(0xabcdefu128)), 0xabcdefu128); - assert_eq!( - Ipv6Addr::from(Bits::from(0xabcdefu128)), - Ipv6Addr::from(0xabcdefu128) - ); - - assert_eq!( - Ipv4Addr::from(Bits::from( - (192u128 << 24 | (168 << 16) | (10 << 8) | 20) << 96 - )), - Ipv4Addr::new(192, 168, 10, 20), - ); - - let ip4 = Ipv4Addr::new(192, 168, 10, 20); - assert_eq!(Ipv4Addr::from(Bits::from(ip4)), ip4); - } - - #[test] - fn prefix_from_str() { - assert_eq!( - Prefix::from_str("127.0.0.0/12").unwrap().addr_and_len(), - (IpAddr::from_str("127.0.0.0").unwrap(), 12) - ); - assert_eq!( - Prefix::from_str("2001:db8:10:20::/64").unwrap().addr_and_len(), - (IpAddr::from_str("2001:db8:10:20::").unwrap(), 64) - ); - assert_eq!( - Prefix::from_str("0.0.0.0/0").unwrap().addr_and_len(), - (IpAddr::from_str("0.0.0.0").unwrap(), 0) - ); - assert_eq!( - Prefix::from_str("::/0").unwrap().addr_and_len(), - (IpAddr::from_str("::").unwrap(), 0) - ); - - assert_eq!( - Prefix::from_str("127.0.0.0"), - Err(ParsePrefixError::MissingLen) - ); - assert_eq!( - Prefix::from_str("2001:db8::"), - Err(ParsePrefixError::MissingLen) - ); - assert!( - matches!( - Prefix::from_str("127.0.0.0/"), - Err(ParsePrefixError::InvalidLen(_)) - ) - ); - assert!( - matches!( - Prefix::from_str("2001:db8::/"), - Err(ParsePrefixError::InvalidLen(_)) - ) - ); - assert!( - matches!( - Prefix::from_str(""), - Err(ParsePrefixError::Empty) - ) - ); - } - - #[test] - fn ordering() { - assert!( - Prefix::from_str("192.168.10.0/24").unwrap() - < Prefix::from_str("192.168.20.0/24").unwrap() - ); - assert!( - Prefix::from_str("192.168.10.0/24").unwrap() - < Prefix::from_str("192.168.20.0/32").unwrap() - ); - - assert!( - Prefix::from_str("192.168.10.0/24").unwrap() - > Prefix::from_str("192.168.9.0/25").unwrap() - ); - assert!( - Prefix::from_str("192.168.10.0/24").unwrap() - < Prefix::from_str("192.0.0.0/8").unwrap() - ); - - assert!( - Prefix::from_str("127.0.0.1/32").unwrap() - < Prefix::from_str("::/128").unwrap() - ); - assert!( - Prefix::from_str("127.0.0.1/32").unwrap() - < Prefix::from_str("::/0").unwrap() - ); - - assert!( - Prefix::from_str("2001:db8:10:20::/64").unwrap() - < Prefix::from_str("2001:db8::/32").unwrap() - ); - assert!( - Prefix::from_str("2001:db8:10:20::/64").unwrap() - < Prefix::from_str("2001:db8:10:30::/64").unwrap() - ); - - assert!( - Prefix::from_str("127.0.0.1/32").unwrap() - < Prefix::from_str("2001:ff00::/24").unwrap() - ); - assert!( - Prefix::from_str("2001:ff00::/24").unwrap() - > Prefix::from_str("127.0.0.1/32").unwrap() - ); - - assert!(matches!( - Prefix::from_str("0.0.0.0/0") - .unwrap() - .cmp(&Prefix::from_str("0.0.0.0/0").unwrap()), - Ordering::Equal - )); - - assert!(matches!( - Prefix::from_str("192.168.1.2/32") - .unwrap() - .cmp(&Prefix::from_str("192.168.1.2/32").unwrap()), - Ordering::Equal - )); - - assert!(matches!( - Prefix::from_str("::/0") - .unwrap() - .cmp(&Prefix::from_str("::/0").unwrap()), - Ordering::Equal - )); - - assert!(matches!( - Prefix::from_str("2001:db8:e000::/40") - .unwrap() - .cmp(&Prefix::from_str("2001:db8:e000::/40").unwrap()), - Ordering::Equal - )); - - assert!(matches!( - Prefix::from_str("2001:db8::1/128") - .unwrap() - .cmp(&Prefix::from_str("2001:db8::1/128").unwrap()), - Ordering::Equal - )); - - assert!( - Prefix::from_str("0.0.0.0/0").unwrap() - < Prefix::from_str("::/0").unwrap() - ); - assert!( - Prefix::from_str("::/0").unwrap() - > Prefix::from_str("0.0.0.0/0").unwrap() - ); - } - - #[test] - fn prefixes() { - assert!(Prefix::new_v4(Ipv4Addr::from(0xffff0000), 16).is_ok()); - assert!( - Prefix::new_v6(Ipv6Addr::from(0x2001_0db8_1234 << 80), 48).is_ok() - ); - assert!(matches!( - Prefix::new_v4(Ipv4Addr::from(0xffffcafe), 16), - Err(PrefixError::NonZeroHost) - )); - assert!(matches!( - Prefix::new_v6(Ipv6Addr::from(0x2001_0db8_1234 << 80), 32), - Err(PrefixError::NonZeroHost) - )); - } - - #[test] - fn ordering_maxlenprefixes() { - assert!( - matches!( - MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap().cmp( - &MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap()), - Ordering::Equal - ) - ); - assert!( - matches!( - MaxLenPrefix::from_str("192.168.0.0/16").unwrap().cmp( - &MaxLenPrefix::from_str("192.168.0.0/16").unwrap()), - Ordering::Equal - ) - ); - assert!( - MaxLenPrefix::from_str("192.168.0.0/16-24").unwrap() < - MaxLenPrefix::from_str("192.168.0.0/16").unwrap() - ); - assert!( - MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap() < - MaxLenPrefix::from_str("192.168.0.0/16").unwrap() - ); - assert!( - MaxLenPrefix::from_str("192.168.0.0/16").unwrap() > - MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap() - ); - - assert!( - MaxLenPrefix::from_str("10.9.0.0/16").unwrap() < - MaxLenPrefix::from_str("10.10.0.0/16-24").unwrap() - ); - assert!( - MaxLenPrefix::from_str("10.10.0.0/16").unwrap() > - MaxLenPrefix::from_str("10.9.0.0/16-24").unwrap() - ); - } - - #[test] - fn relaxed_prefixes() { - assert_eq!( - Prefix::new_relaxed( - "192.168.10.20".parse::().unwrap(), 16) - .unwrap(), - Prefix::new_v4_relaxed( - "192.168.10.20".parse::().unwrap(), 16) - .unwrap() - ); - assert_eq!( - Prefix::new_relaxed( - "192.168.10.20".parse::().unwrap(), 16).unwrap(), - Prefix::new_relaxed( - "192.168.0.0".parse::().unwrap(), 16).unwrap(), - ); - assert_eq!( - Prefix::new_relaxed( - "2001:db8::10:20:30:40".parse::().unwrap(), 64) - .unwrap(), - Prefix::new_v6_relaxed( - "2001:db8::10:20:30:40".parse::().unwrap(), 64) - .unwrap() - ); - assert_eq!( - Prefix::new_relaxed( - "2001:db8::10:20:30:40".parse::().unwrap(), 64) - .unwrap(), - Prefix::new_relaxed( - "2001:db8::".parse::().unwrap(), 64) - .unwrap() - ); - } - - #[test] - fn min_max_addr() { - assert_eq!( - Prefix::from_str("192.168.0.0/16").unwrap().min_addr(), - IpAddr::from_str("192.168.0.0").unwrap() - ); - assert_eq!( - Prefix::from_str("192.168.0.0/16").unwrap().max_addr(), - IpAddr::from_str("192.168.255.255").unwrap() - ); - assert_eq!( - Prefix::from_str("192.168.1.1/32").unwrap().min_addr(), - IpAddr::from_str("192.168.1.1").unwrap() - ); - assert_eq!( - Prefix::from_str("192.168.1.1/32").unwrap().min_addr(), - Prefix::from_str("192.168.1.1/32").unwrap().max_addr() - ); - - assert_eq!( - Prefix::from_str("2001:db8:10:20::/64").unwrap().min_addr(), - IpAddr::from_str("2001:db8:10:20::").unwrap() - ); - assert_eq!( - Prefix::from_str("2001:db8:10:20::/64").unwrap().max_addr(), - IpAddr::from_str("2001:db8:10:20:ffff:ffff:ffff:ffff").unwrap() - ); - assert_eq!( - Prefix::from_str("2001:db8:10:20::1234/128").unwrap().min_addr(), - IpAddr::from_str("2001:db8:10:20::1234").unwrap() - ); - assert_eq!( - Prefix::from_str("2001:db8:10:20::1234/128").unwrap().min_addr(), - Prefix::from_str("2001:db8:10:20::1234/128").unwrap().max_addr() - ); - } - - #[test] - fn covers() { - assert!(Prefix::from_str("0.0.0.0/0").unwrap().covers( - Prefix::from_str("192.168.10.0/24").unwrap()) - ); - assert!(Prefix::from_str("::/0").unwrap().covers( - Prefix::from_str("2001:db8:10::/48").unwrap()) - ); - - assert!(Prefix::from_str("192.168.0.0/16").unwrap().covers( - Prefix::from_str("192.168.10.0/24").unwrap()) - ); - assert!(!Prefix::from_str("192.168.10.0/24").unwrap().covers( - Prefix::from_str("192.168.0.0/16").unwrap()) - ); - assert!(Prefix::from_str("2001:db8:10::/48").unwrap().covers( - Prefix::from_str("2001:db8:10:20::/64").unwrap()) - ); - assert!(!Prefix::from_str("2001:db8:10:20::/64").unwrap().covers( - Prefix::from_str("2001:db8:10::/48").unwrap()) - ); - - assert!(Prefix::from_str("192.168.10.1/32").unwrap().covers( - Prefix::from_str("192.168.10.1/32").unwrap()) - ); - assert!(!Prefix::from_str("192.168.10.1/32").unwrap().covers( - Prefix::from_str("192.168.10.2/32").unwrap()) - ); - assert!(Prefix::from_str("2001:db8:10::1234/128").unwrap().covers( - Prefix::from_str("2001:db8:10::1234/128").unwrap()) - ); - assert!(!Prefix::from_str("2001:db8:10::abcd/128").unwrap().covers( - Prefix::from_str("2001:db8:10::1234/128").unwrap()) - ); - - - assert!(!Prefix::from_str("192.168.10.0/24").unwrap().covers( - Prefix::from_str("2001:db8::1/128").unwrap()) - ); - assert!(!Prefix::from_str("2001:db8::1/128").unwrap().covers( - Prefix::from_str("192.168.10.0/24").unwrap()) - ); - } - - #[test] - fn max_len_prefix() { - let pfx4 = Prefix::from_str("192.168.0.0/16").unwrap(); - let pfx6 = Prefix::from_str("2001:db8:10::/48").unwrap(); - - assert!(MaxLenPrefix::new(pfx4, Some(24)).is_ok()); - assert!(MaxLenPrefix::new(pfx4, Some(32)).is_ok()); - assert!(MaxLenPrefix::new(pfx6, Some(64)).is_ok()); - assert!(MaxLenPrefix::new(pfx6, Some(128)).is_ok()); - - assert_eq!(MaxLenPrefix::from(pfx4).prefix_len(), 16); - assert_eq!(MaxLenPrefix::from(pfx4).prefix(), pfx4); - assert_eq!(MaxLenPrefix::from(pfx4).max_len(), None); - assert_eq!(MaxLenPrefix::from(pfx4).resolved_max_len(), 16); - - assert_eq!(MaxLenPrefix::from(pfx6).prefix_len(), 48); - assert_eq!(MaxLenPrefix::from(pfx6).prefix(), pfx6); - assert_eq!(MaxLenPrefix::from(pfx6).max_len(), None); - assert_eq!(MaxLenPrefix::from(pfx6).resolved_max_len(), 48); - - assert!(matches!( - MaxLenPrefix::new(pfx4, Some(12)), - Err(MaxLenError::Underflow) - )); - assert!(matches!( - MaxLenPrefix::new(pfx4, Some(33)), - Err(MaxLenError::Overflow) - )); - assert!(matches!( - MaxLenPrefix::new(pfx6, Some(32)), - Err(MaxLenError::Underflow) - )); - assert!(matches!( - MaxLenPrefix::new(pfx6, Some(130)), - Err(MaxLenError::Overflow) - )); - - - for i in 0..16 { - assert_eq!( - MaxLenPrefix::saturating_new(pfx4, Some(i)), - MaxLenPrefix::new(pfx4, Some(16)).unwrap() - ); - } - for i in 16..=32 { - assert_eq!( - MaxLenPrefix::saturating_new(pfx4, Some(i)), - MaxLenPrefix::new(pfx4, Some(i)).unwrap() - ); - } - for i in 33..=255 { - assert_eq!( - MaxLenPrefix::saturating_new(pfx4, Some(i)), - MaxLenPrefix::new(pfx4, Some(32)).unwrap() - ); - } - for i in 0..48 { - assert_eq!( - MaxLenPrefix::saturating_new(pfx6, Some(i)), - MaxLenPrefix::new(pfx6, Some(48)).unwrap() - ); - } - for i in 48..=128 { - assert_eq!( - MaxLenPrefix::saturating_new(pfx6, Some(i)), - MaxLenPrefix::new(pfx6, Some(i)).unwrap() - ); - } - for i in 129..=255 { - assert_eq!( - MaxLenPrefix::saturating_new(pfx6, Some(i)), - MaxLenPrefix::new(pfx6, Some(128)).unwrap() - ); - } - - assert_eq!( - MaxLenPrefix::new(pfx6, Some(56)).unwrap().resolved_max_len(), - 56 - ); - assert_eq!( - MaxLenPrefix::new(pfx6, None).unwrap().resolved_max_len(), - 48 - ); - - assert_eq!( - MaxLenPrefix::from_str("192.168.0.0/16-24").unwrap(), - MaxLenPrefix::new(pfx4, Some(24)).unwrap() - ); - assert_eq!( - MaxLenPrefix::from_str("192.168.0.0/16").unwrap(), - MaxLenPrefix::new(pfx4, None).unwrap() - ); - assert!( - matches!( - MaxLenPrefix::from_str("192.168.0.0/16-"), - Err(ParseMaxLenPrefixError::InvalidMaxLenFormat(_)) - ) - ); - assert!( - matches!( - MaxLenPrefix::from_str("192.168.0.0/16-0"), - Err(ParseMaxLenPrefixError::InvalidMaxLenValue(_)) - ) - ); - assert!( - matches!( - MaxLenPrefix::from_str("192.168.0.0/16-33"), - Err(ParseMaxLenPrefixError::InvalidMaxLenValue(_)) - ) - ); - } - - #[test] - fn max_len_prefix_display() { - assert_eq!( - format!( - "{}", MaxLenPrefix::from_str("192.168.0.0/16-32").unwrap() - ).as_str(), - "192.168.0.0/16-32" - ); - assert_eq!( - format!( - "{}", MaxLenPrefix::from_str("192.168.0.0/16").unwrap() - ).as_str(), - "192.168.0.0/16" - ); - } - - #[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/asn.rs b/src/asn.rs deleted file mode 100644 index 5c9cf97b..00000000 --- a/src/asn.rs +++ /dev/null @@ -1,767 +0,0 @@ -//! Types for Autonomous Systems Numbers (ASN) and ASN collections - -use std::{error, fmt, iter, ops, slice}; -use std::cmp::Ordering; -use std::convert::TryInto; -use std::str::FromStr; -use std::iter::Peekable; - -#[cfg(feature = "octseq")] -use octseq::builder::OctetsBuilder; - -//------------ Asn ----------------------------------------------------------- - -/// An AS number (ASN). -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Asn(u32); - -impl Asn { - pub const MIN: Asn = Asn(std::u32::MIN); - pub const MAX: Asn = Asn(std::u32::MAX); - - /// Creates an AS number from a `u32`. - pub fn from_u32(value: u32) -> Self { - Asn(value) - } - - /// Converts an AS number into a `u32`. - pub fn into_u32(self) -> u32 { - self.0 - } - - /// Try to convert a 4-octet AS number into a `u16`. - pub fn try_into_u16(self) -> Result { - self.0.try_into().map_err(|_| LargeAsnError) - } - - /// Try to convert a 4-octet AS number into a 2-octet `Asn16`. - pub fn try_into_asn16(self) -> Result { - Ok(Asn16(self.try_into_u16()?)) - } - - /// Converts an AS number into a network-order byte array. - pub fn to_raw(self) -> [u8; 4] { - self.0.to_be_bytes() - } - - #[cfg(feature = "octseq")] - pub fn compose( - self, target: &mut Target - ) -> Result<(), Target::AppendError> { - target.append_slice(&self.to_raw()) - } - - // XXX or do we want this? - // possibly returning LargeAsnError in addition to AppendError? - //pub fn compose_16( - // self, target: &mut Target - //) -> Result<(), Target::AppendError> { - // todo!() - //} -} - -//--- From - -impl From for Asn { - fn from(id: u32) -> Self { - Asn(id) - } -} - -impl From for u32 { - fn from(id: Asn) -> Self { - id.0 - } -} - -//--- FromStr - -impl FromStr for Asn { - type Err = ParseAsnError; - - fn from_str(s: &str) -> Result { - let s = if s.len() > 2 && s[..2].eq_ignore_ascii_case("as") { - &s[2..] - } else { - s - }; - - u32::from_str(s).map(Asn).map_err(|_| ParseAsnError) - } -} - - -//--- Serialize and Deserialize - -/// # Serialization -/// -/// With the `"serde"` feature enabled, `Asn` implements the `Serialize` and -/// `Deserialize` traits via _serde-derive_ as a newtype wrapping a `u32`. -/// -/// However, ASNs are often serialized as a string prefix with `AS`. In order -/// to allow this, a number of methods are provided that can be used with -/// Serde’s field attributes to choose how to serialize an ASN as part of a -/// struct. -#[cfg(feature = "serde")] -impl Asn { - /// Serializes an AS number as a simple `u32`. - /// - /// Normally, you wouldn’t need to use this method, as the default - /// implementation serializes the ASN as a newtype struct with a `u32` - /// inside which most serialization formats will turn into a sole `u32`. - /// However, in case your format doesn’t, you can use this method. - pub fn serialize_as_u32( - &self, serializer: S - ) -> Result { - serializer.serialize_u32(self.0) - } - - /// Serializes an AS number as a string without prefix. - pub fn serialize_as_bare_str( - &self, serializer: S - ) -> Result { - serializer.collect_str(&format_args!("{}", self.0)) - } - - /// Seriaizes an AS number as a string with a `AS` prefix. - pub fn serialize_as_str( - &self, serializer: S - ) -> Result { - serializer.collect_str(&format_args!("AS{}", self.0)) - } - - /// Deserializes an AS number from a simple `u32`. - /// - /// Normally, you wouldn’t need to use this method, as the default - /// implementation deserializes the ASN from a newtype struct with a - /// `u32` inside for which most serialization formats will use a sole - /// `u32`. However, in case your format doesn’t, you can use this method. - pub fn deserialize_from_u32<'de, D: serde::Deserializer<'de>>( - deserializer: D - ) -> Result { - ::deserialize(deserializer).map(Into::into) - } - - /// Deserializes an AS number from a string. - /// - /// The string may or may not have a case-insensitive `"AS"` prefix. - pub fn deserialize_from_str<'de, D: serde::de::Deserializer<'de>>( - deserializer: D - ) -> Result { - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = Asn; - - fn expecting( - &self, formatter: &mut fmt::Formatter - ) -> fmt::Result { - write!(formatter, "an AS number") - } - - fn visit_str( - self, v: &str - ) -> Result { - Asn::from_str(v).map_err(E::custom) - } - } - deserializer.deserialize_str(Visitor) - } - - /// Deserializes an AS number as either a string or `u32`. - /// - /// This function can only be used with self-describing serialization - /// formats as it uses `Deserializer::deserialize_any`. It accepts an - /// AS number as any kind of integer as well as a string with or without - /// a case-insensitive `"AS"` prefix. - pub fn deserialize_from_any<'de, D: serde::de::Deserializer<'de>>( - deserializer: D - ) -> Result { - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = Asn; - - fn expecting( - &self, formatter: &mut fmt::Formatter - ) -> fmt::Result { - write!(formatter, "an AS number") - } - - fn visit_u8( - self, v: u8 - ) -> Result { - Ok(Asn(v.into())) - } - - fn visit_u16( - self, v: u16 - ) -> Result { - Ok(Asn(v.into())) - } - - fn visit_u32( - self, v: u32 - ) -> Result { - Ok(Asn(v)) - } - - fn visit_u64( - self, v: u64 - ) -> Result { - Ok(Asn(v.try_into().map_err(E::custom)?)) - } - - fn visit_i8( - self, v: i8 - ) -> Result { - Ok(Asn(v.try_into().map_err(E::custom)?)) - } - - fn visit_i16( - self, v: i16 - ) -> Result { - Ok(Asn(v.try_into().map_err(E::custom)?)) - } - - fn visit_i32( - self, v: i32 - ) -> Result { - Ok(Asn(v.try_into().map_err(E::custom)?)) - } - - fn visit_i64( - self, v: i64 - ) -> Result { - Ok(Asn(v.try_into().map_err(E::custom)?)) - } - - fn visit_str( - self, v: &str - ) -> Result { - Asn::from_str(v).map_err(E::custom) - } - } - deserializer.deserialize_any(Visitor) - } -} - -//--- Add - -impl ops::Add for Asn { - type Output = Self; - - fn add(self, rhs: u32) -> Self { - Asn(self.0.checked_add(rhs).unwrap()) - } -} - -//--- Display - -impl fmt::Display for Asn { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "AS{}", self.0) - } -} - - - -//------------ Asn16 --------------------------------------------------------- - -/// A 2-octet ASN. -/// -/// This is only here to facilitate 'legacy' BGP, i.e., BGP messages in a BGP -/// session for which the FourOctet Capability has not been exchanged. -#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Asn16(u16); - -impl Asn16 { - pub fn from_u16(u: u16) -> Self { - Self(u) - } - pub fn to_u16(self) -> u16 { - self.0 - } - pub fn into_asn32(self) -> Asn { - Asn::from_u32(self.0 as u32) - } - pub fn to_raw(self) -> [u8; 2] { - self.0.to_be_bytes() - } -} - -//--- Display - -impl fmt::Display for Asn16 { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "AS{}", self.0) - } -} - - -//--- From / FromStr - -impl From for Asn16 { - fn from(n: u16) -> Self { - Self(n) - } -} - -fn strip_as(s: &str) -> &str { - s.strip_prefix("AS") - .or_else(|| s.strip_prefix("as")) - .or_else(|| s.strip_prefix("As")) - .or_else(|| s.strip_prefix("aS")) - .unwrap_or(s) -} - -impl FromStr for Asn16 { - type Err = ParseAsnError; - - fn from_str(s: &str) -> Result { - u16::from_str(strip_as(s)).map_err(|_| ParseAsnError) - .map(Asn16::from_u16) - - // more strict version: - /* - s.strip_prefix("AS").ok_or_else(|| "missing AS".into()) - .and_then(|e| u16::from_str(e) - .map_err(|_e| "u16 parsing failed".into()) - ) - .map(Asn16::from_u16) - */ - } -} - - - - -//------------ SmallAsnSet -------------------------------------------------- - -/// A relatively small set of ASNs. -/// -/// This type is only efficient if the amount of ASNs in it is relatively -/// small as it is represented internally by an ordered vec of ASNs to avoid -/// memory overhead. -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -pub struct SmallAsnSet(Vec); - -impl SmallAsnSet { - pub fn iter(&self) -> SmallSetIter { - self.0.iter().cloned() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn difference<'a>( - &'a self, other: &'a Self - ) -> SmallSetDifference<'a> { - SmallSetDifference { - left: self.iter().peekable(), - right: other.iter().peekable(), - } - } - - pub fn symmetric_difference<'a>( - &'a self, other: &'a Self - ) -> SmallSetSymmetricDifference<'a> { - SmallSetSymmetricDifference { - left: self.iter().peekable(), - right: other.iter().peekable(), - } - } - - pub fn intersection<'a>( - &'a self, other: &'a Self - ) -> SmallSetIntersection<'a> { - SmallSetIntersection { - left: self.iter().peekable(), - right: other.iter().peekable(), - } - } - - pub fn union<'a>(&'a self, other: &'a Self) -> SmallSetUnion<'a> { - SmallSetUnion { - left: self.iter().peekable(), - right: other.iter().peekable(), - } - } - - pub fn contains(&self, asn: Asn) -> bool { - self.0.binary_search(&asn).is_ok() - } - - // Missing: is_disjoint, is_subset, is_superset, insert, remove, -} - - -impl iter::FromIterator for SmallAsnSet { - fn from_iter>(iter: T) -> Self { - let mut res = Self(iter.into_iter().collect()); - res.0.sort(); - res - } -} - -impl<'a> IntoIterator for &'a SmallAsnSet { - type Item = Asn; - type IntoIter = SmallSetIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter().cloned() - } -} - - -//------------ SmallSetIter -------------------------------------------------- - -pub type SmallSetIter<'a> = iter::Cloned>; - - -//------------ SmallSetDifference -------------------------------------------- - -pub struct SmallSetDifference<'a> { - left: Peekable>, - right: Peekable>, -} - -impl<'a> Iterator for SmallSetDifference<'a> { - type Item = Asn; - - fn next(&mut self) -> Option { - loop { - match (self.left.peek(), self.right.peek()) { - (None, _) => return None, - (Some(_), None) => return self.left.next(), - (Some(left), Some(right)) => { - match left.cmp(right) { - Ordering::Less => return self.left.next(), - Ordering::Equal => { - let _ = self.left.next(); - let _ = self.right.next(); - } - Ordering::Greater => { - let _ = self.right.next(); - } - } - } - } - } - } -} - - -//------------ SmallSetSymmetricDifference ----------------------------------- - -pub struct SmallSetSymmetricDifference<'a> { - left: Peekable>, - right: Peekable>, -} - -impl<'a> Iterator for SmallSetSymmetricDifference<'a> { - type Item = Asn; - - fn next(&mut self) -> Option { - loop { - match (self.left.peek(),self. right.peek()) { - (None, None) => return None, - (Some(_), None) => return self.left.next(), - (None, Some(_)) => return self.right.next(), - (Some(left), Some(right)) => { - match left.cmp(right) { - Ordering::Equal => { - let _ = self.left.next(); - let _ = self.right.next(); - } - Ordering::Less => return self.left.next(), - Ordering::Greater => return self.right.next(), - } - } - } - } - } -} - - -//------------ SmallSetIntersection ------------------------------------------ - -pub struct SmallSetIntersection<'a> { - left: Peekable>, - right: Peekable>, -} - -impl<'a> Iterator for SmallSetIntersection<'a> { - type Item = Asn; - - fn next(&mut self) -> Option { - loop { - match (self.left.peek(),self. right.peek()) { - (None, _) | (_, None) => return None, - (Some(left), Some(right)) => { - match left.cmp(right) { - Ordering::Equal => { - let _ = self.left.next(); - return self.right.next() - } - Ordering::Less => { - let _ = self.left.next(); - } - Ordering::Greater => { - let _ = self.right.next(); - } - } - } - } - } - } -} - - -//------------ SmallSetUnion ------------------------------------------------- - -pub struct SmallSetUnion<'a> { - left: Peekable>, - right: Peekable>, -} - -impl<'a> Iterator for SmallSetUnion<'a> { - type Item = Asn; - - fn next(&mut self) -> Option { - match (self.left.peek(),self. right.peek()) { - (None, None) => None, - (Some(_), None) => self.left.next(), - (None, Some(_)) => self.right.next(), - (Some(left), Some(right)) => { - match left.cmp(right) { - Ordering::Less => self.left.next(), - Ordering::Equal => { - let _ = self.left.next(); - self.right.next() - } - Ordering::Greater => { - self.right.next() - } - } - } - } - } -} - - -//============ Error Types =================================================== - -//------------ ParseAsnError ------------------------------------------------ - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ParseAsnError; - -impl fmt::Display for ParseAsnError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("invalid AS number") - } -} - -impl error::Error for ParseAsnError {} - -//------------ LargeAsnError ------------------------------------------------ - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct LargeAsnError; - -impl fmt::Display for LargeAsnError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("ASN too large") - } -} - -impl error::Error for LargeAsnError {} - -//------------ LongSegmentError ---------------------------------------------- - -#[derive(Clone, Copy, Debug)] -pub struct LongSegmentError; - -impl fmt::Display for LongSegmentError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("path segment too long") - } -} - -impl error::Error for LongSegmentError { } - - -//------------ InvalidSegmentTypeError --------------------------------------- - -#[derive(Clone, Copy, Debug)] -pub struct InvalidSegmentTypeError; - -impl fmt::Display for InvalidSegmentTypeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("invalid segment type") - } -} - -impl error::Error for InvalidSegmentTypeError { } - - -//============ Tests ========================================================= - -#[cfg(all(test, feature = "serde"))] -mod test_serde { - use super::*; - use serde_test::{Token, assert_de_tokens, assert_tokens}; - - #[test] - fn asn() { - #[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)] - struct AsnTest( - Asn, - - #[serde( - deserialize_with = "Asn::deserialize_from_u32", - serialize_with = "Asn::serialize_as_u32", - )] - Asn, - - #[serde( - deserialize_with = "Asn::deserialize_from_str", - serialize_with = "Asn::serialize_as_str", - )] - Asn, - ); - - assert_tokens( - &AsnTest ( Asn(0), Asn(0), Asn(0) ), - &[ - Token::TupleStruct { name: "AsnTest", len: 3 }, - Token::NewtypeStruct { name: "Asn" }, Token::U32(0), - Token::U32(0), - Token::Str("AS0"), - Token::TupleStructEnd, - ] - ); - } - - #[test] - fn asn_any() { - #[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)] - struct AsnTest( - #[serde(deserialize_with = "Asn::deserialize_from_any")] - Asn, - #[serde(deserialize_with = "Asn::deserialize_from_any")] - Asn, - #[serde(deserialize_with = "Asn::deserialize_from_any")] - Asn, - #[serde(deserialize_with = "Asn::deserialize_from_any")] - Asn, - #[serde(deserialize_with = "Asn::deserialize_from_any")] - Asn, - #[serde(deserialize_with = "Asn::deserialize_from_any")] - Asn, - ); - - assert_de_tokens( - &AsnTest(Asn(0), Asn(0), Asn(0), Asn(0), Asn(0), Asn(0)), - &[ - Token::TupleStruct { name: "AsnTest", len: 5 }, - Token::U32(0), - Token::U64(0), - Token::I64(0), - Token::Str("0"), - Token::Str("AS0"), - Token::Str("As0"), - Token::TupleStructEnd, - ] - ); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashSet; - - #[test] - fn asn() { - assert_eq!(Asn::from_u32(1234), Asn(1234)); - assert_eq!(Asn(1234).into_u32(), 1234); - - assert_eq!(Asn::from(1234_u32), Asn(1234)); - assert_eq!(u32::from(Asn(1234)), 1234_u32); - - assert_eq!(format!("{}", Asn(1234)).as_str(), "AS1234"); - - assert_eq!("0".parse::(), Ok(Asn(0))); - assert_eq!("AS1234".parse::(), Ok(Asn(1234))); - assert_eq!("as1234".parse::(), Ok(Asn(1234))); - assert_eq!("As1234".parse::(), Ok(Asn(1234))); - assert_eq!("aS1234".parse::(), Ok(Asn(1234))); - assert_eq!("1234".parse::(), Ok(Asn(1234))); - - assert_eq!("".parse::(), Err(ParseAsnError)); - assert_eq!("-1234".parse::(), Err(ParseAsnError)); - assert_eq!("4294967296".parse::(), Err(ParseAsnError)); - } - - //--- SmallAsnSet - - // Checks that our set operation does the same as the same on - // HashSet. - macro_rules! check_set_fn { - ( $fn:ident, $left:expr, $right:expr $(,)? ) => {{ - let left = Vec::from_iter($left.into_iter().map(Asn::from_u32)); - let right = Vec::from_iter($right.into_iter().map(Asn::from_u32)); - - let set_fn = { - let left = SmallAsnSet::from_iter( - left.clone().into_iter() - ); - let right = SmallAsnSet::from_iter( - right.clone().into_iter() - ); - left.$fn(&right).collect::>() - }; - let hash_fn: HashSet = { - let left: HashSet = HashSet::from_iter( - left.clone().into_iter() - ); - let right: HashSet = HashSet::from_iter( - right.clone().into_iter() - ); - left.$fn(&right).cloned().collect() - }; - assert_eq!(set_fn, hash_fn); - }} - } - - macro_rules! check_all_set_fns { - ( $left:expr, $right:expr $(,)? ) => {{ - check_set_fn!(difference, $left, $right); - check_set_fn!(symmetric_difference, $left, $right); - check_set_fn!(intersection, $left, $right); - check_set_fn!(union, $left, $right); - }} - } - - #[test] - fn small_set_operations() { - check_all_set_fns!([0, 1, 2, 3], [0, 1, 2, 3]); - check_all_set_fns!([0, 1, 2], [0, 1, 2, 3]); - check_all_set_fns!([0, 1, 2, 3], [0, 1, 2]); - check_all_set_fns!([0, 1, 2, 3], [0, 1, 2]); - check_all_set_fns!([], []); - check_all_set_fns!([1, 2, 3], []); - check_all_set_fns!([], [1, 2, 3]); - } -} diff --git a/src/lib.rs b/src/lib.rs index fd01ccc5..0f46336b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,5 @@ //! A library for IP routing primitives. -//pub mod addr; -//pub mod asn; #[cfg(feature = "bgp")] pub mod bgp; pub mod bgpsec; From 0e37d92d157c032a69a546dcb34ce5df19f496dd Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:36:04 +0100 Subject: [PATCH 81/96] Conditionally derive De/Serialize for all NLRI related types --- src/bgp/nlri/afisafi.rs | 15 +++++++++++++++ src/bgp/nlri/common.rs | 2 +- src/bgp/nlri/evpn.rs | 2 +- src/bgp/nlri/flowspec.rs | 2 +- src/bgp/nlri/mpls.rs | 4 ++-- src/bgp/nlri/mpls_vpn.rs | 2 +- src/bgp/nlri/routetarget.rs | 1 + src/bgp/nlri/vpls.rs | 2 +- 8 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 56f94ab3..fb86df7e 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -36,6 +36,7 @@ macro_rules! addpath { ($nlri:ident $(<$gen:ident>)? ) => paste! { #[allow(clippy::derived_hash_with_manual_eq)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Hash)] pub struct [<$nlri AddpathNlri>]$(<$gen>)?(PathId, [<$nlri Nlri>]$(<$gen>)?); impl$(<$gen: Clone + Debug + Hash>)? AfiSafiNlri for [<$nlri AddpathNlri>]$(<$gen>)? { @@ -214,6 +215,7 @@ paste! { // this enforces these derives on all *Nlri structs. #[derive(Clone, Debug, Hash)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Nlri { $($( [<$afi_name $safi_name>]([<$afi_name $safi_name Nlri>]$(<$gen>)?), @@ -543,6 +545,7 @@ afisafi! { // --- Ipv4Unicast #[derive(Clone, Debug, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv4UnicastNlri(Prefix); impl AfiSafiNlri for Ipv4UnicastNlri { @@ -634,6 +637,7 @@ impl fmt::Display for Ipv4UnicastNlri { //--- Ipv4Multicast #[derive(Clone, Debug, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv4MulticastNlri(Prefix); impl AfiSafiNlri for Ipv4MulticastNlri { @@ -705,6 +709,7 @@ impl fmt::Display for Ipv4MulticastNlri { //--- Ipv4MplsUnicast #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv4MplsUnicastNlri(MplsNlri); impl AfiSafiNlri for Ipv4MplsUnicastNlri { @@ -759,6 +764,7 @@ impl fmt::Display for Ipv4MplsUnicastNlri { //--- Ipv4MplsVpnUnicastNlri #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv4MplsVpnUnicastNlri(MplsVpnNlri); impl AfiSafiNlri for Ipv4MplsVpnUnicastNlri { @@ -795,6 +801,7 @@ impl fmt::Display for Ipv4MplsVpnUnicastNlri { #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv4RouteTargetNlri(RouteTargetNlri); impl AfiSafiNlri for Ipv4RouteTargetNlri { @@ -828,6 +835,7 @@ impl fmt::Display for Ipv4RouteTargetNlri { //--- Ipv4FlowSpec #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv4FlowSpecNlri(FlowSpecNlri); impl AfiSafiNlri for Ipv4FlowSpecNlri { @@ -889,6 +897,7 @@ impl fmt::Display for Ipv4FlowSpecNlri { //--- Ipv6Unicast #[derive(Clone, Debug, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv6UnicastNlri(Prefix); impl AfiSafiNlri for Ipv6UnicastNlri { type Nlri = Prefix; @@ -958,6 +967,7 @@ impl fmt::Display for Ipv6UnicastNlri { //--- Ipv6Multicast #[derive(Clone, Debug, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv6MulticastNlri(Prefix); impl AfiSafiNlri for Ipv6MulticastNlri { @@ -990,6 +1000,7 @@ impl fmt::Display for Ipv6MulticastNlri { //--- Ipv6MplsUnicast #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv6MplsUnicastNlri(MplsNlri); impl AfiSafiNlri for Ipv6MplsUnicastNlri { @@ -1035,6 +1046,7 @@ impl fmt::Display for Ipv6MplsUnicastNlri { //--- Ipv6MplsVpnUnicastNlri #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv6MplsVpnUnicastNlri(MplsVpnNlri); impl AfiSafiNlri for Ipv6MplsVpnUnicastNlri { @@ -1071,6 +1083,7 @@ impl fmt::Display for Ipv6MplsVpnUnicastNlri { //--- Ipv6FlowSpec #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ipv6FlowSpecNlri(FlowSpecNlri); impl AfiSafiNlri for Ipv6FlowSpecNlri { @@ -1127,6 +1140,7 @@ impl Ipv4MplsUnicastNlri { //--- L2VpnVpls #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct L2VpnVplsNlri(VplsNlri); impl AfiSafiNlri for L2VpnVplsNlri { @@ -1160,6 +1174,7 @@ impl fmt::Display for L2VpnVplsNlri { //--- Evpn #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct L2VpnEvpnNlri(EvpnNlri); impl AfiSafiNlri for L2VpnEvpnNlri { diff --git a/src/bgp/nlri/common.rs b/src/bgp/nlri/common.rs index b728dfcf..f33c5877 100644 --- a/src/bgp/nlri/common.rs +++ b/src/bgp/nlri/common.rs @@ -12,7 +12,7 @@ use std::fmt; /// /// Used in all AddpathNlri variants. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PathId(pub u32); impl fmt::Display for PathId { diff --git a/src/bgp/nlri/evpn.rs b/src/bgp/nlri/evpn.rs index be4370d8..0ee7cdd7 100644 --- a/src/bgp/nlri/evpn.rs +++ b/src/bgp/nlri/evpn.rs @@ -22,7 +22,7 @@ typeenum!( /// /// **TODO**: implement accessor methods for the contents of this NLRI. #[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EvpnNlri { #[allow(dead_code)] route_type: EvpnRouteType, diff --git a/src/bgp/nlri/flowspec.rs b/src/bgp/nlri/flowspec.rs index 53aaf1d5..35b48b66 100644 --- a/src/bgp/nlri/flowspec.rs +++ b/src/bgp/nlri/flowspec.rs @@ -12,7 +12,7 @@ use super::afisafi::Afi; /// /// Also see [`crate::flowspec`]. #[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FlowSpecNlri { #[allow(dead_code)] afi: Afi, diff --git a/src/bgp/nlri/mpls.rs b/src/bgp/nlri/mpls.rs index 69841d48..c02f6882 100644 --- a/src/bgp/nlri/mpls.rs +++ b/src/bgp/nlri/mpls.rs @@ -8,7 +8,7 @@ use super::afisafi::Afi; /// NLRI comprised of a [`Prefix`] and MPLS `Labels`. #[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MplsNlri { prefix: Prefix, labels: Labels, @@ -81,7 +81,7 @@ impl fmt::Display for MplsNlri { /// MPLS labels, part of [`MplsNlri`] and [`MplsVpnNlri`]. #[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Labels { octets: Octs } diff --git a/src/bgp/nlri/mpls_vpn.rs b/src/bgp/nlri/mpls_vpn.rs index c9a338f6..dd1452ca 100644 --- a/src/bgp/nlri/mpls_vpn.rs +++ b/src/bgp/nlri/mpls_vpn.rs @@ -13,7 +13,7 @@ use super::mpls::Labels; /// NLRI comprised of a [`BasicNlri`], MPLS `Labels` and a VPN /// `RouteDistinguisher`. #[derive(Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MplsVpnNlri { prefix: Prefix, labels: Labels, diff --git a/src/bgp/nlri/routetarget.rs b/src/bgp/nlri/routetarget.rs index 9faeef4d..e77e2a24 100644 --- a/src/bgp/nlri/routetarget.rs +++ b/src/bgp/nlri/routetarget.rs @@ -9,6 +9,7 @@ use super::common::prefix_bits_to_bytes; /// /// **TODO**: implement accessor methods for the contents of this NLRI. #[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct RouteTargetNlri { raw: Octs } diff --git a/src/bgp/nlri/vpls.rs b/src/bgp/nlri/vpls.rs index 6ddd1b4b..215a071f 100644 --- a/src/bgp/nlri/vpls.rs +++ b/src/bgp/nlri/vpls.rs @@ -7,7 +7,7 @@ use super::mpls_vpn::RouteDistinguisher; /// VPLS Information as defined in RFC4761. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct VplsNlri { rd: RouteDistinguisher, ve_id: u16, From 66063410c9c8ace601941a1e7b26a7ede4639f80 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 16:46:55 +0100 Subject: [PATCH 82/96] Add From impls for FlowSpecNlri --- src/bgp/nlri/afisafi.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index fb86df7e..158a0d3c 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -886,6 +886,13 @@ impl NlriCompose for Ipv4FlowSpecNlri } } +impl From> for FlowSpecNlri { + fn from(value: Ipv4FlowSpecNlri) -> Self { + value.0 + } +} + + impl fmt::Display for Ipv4FlowSpecNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1108,6 +1115,12 @@ where } } +impl From> for FlowSpecNlri { + fn from(value: Ipv6FlowSpecNlri) -> Self { + value.0 + } +} + impl fmt::Display for Ipv6FlowSpecNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) From c8b541c692efd547fbfcb37a733db359e7d3bdb6 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Fri, 22 Mar 2024 17:43:46 +0100 Subject: [PATCH 83/96] Add fn {announcement,withdrawal}_fams() on UpdateMessage --- src/bgp/message/update.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index e995e0dc..9bd5c4ba 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -198,6 +198,16 @@ impl UpdateMessage { ) } + pub fn announcement_fams(&self) -> impl Iterator { + let afi_safis = self.afi_safis(); + [afi_safis.1, afi_safis.3].into_iter().flatten() + } + + pub fn withdrawal_fams(&self) -> impl Iterator { + let afi_safis = self.afi_safis(); + [afi_safis.0, afi_safis.2].into_iter().flatten() + } + /// Returns an iterator over the conventional withdrawals. /// /// The withdrawals are always IPv4 Unicast, but can contain Path IDs. From 42e90d2f489d5e11e8cb19e66c341ef465308be2 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 25 Mar 2024 09:22:03 +0100 Subject: [PATCH 84/96] Fix docstring/doctests wrt change to inetnum --- src/bgp/communities.rs | 2 +- src/bgp/message/open.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bgp/communities.rs b/src/bgp/communities.rs index 17a13799..8a206c03 100644 --- a/src/bgp/communities.rs +++ b/src/bgp/communities.rs @@ -26,7 +26,7 @@ //! and [`to_wellknown()`](`Community::to_wellknown()`). //! //! ``` -//! use routecore::asn::Asn; +//! use inetnum::asn::Asn; //! use routecore::bgp::communities::Community; //! use std::str::FromStr; //! diff --git a/src/bgp/message/open.rs b/src/bgp/message/open.rs index 00ccadac..1e6e5ab4 100644 --- a/src/bgp/message/open.rs +++ b/src/bgp/message/open.rs @@ -30,7 +30,7 @@ pub struct OpenMessage { /// /// * [`my_asn()`][`OpenMessage::my_asn`]: returns the 32bit ASN if present, /// otherwise falls back to the conventional 16bit ASN (though represented as -/// the 32bit [`routecore::asn::Asn`][`Asn`]); +/// the 32bit [`inetnum::asn::Asn`][`Asn`]); /// * [`multiprotocol_ids()`][`OpenMessage::multiprotocol_ids`]: returns an /// iterator over all the AFI/SAFI combinations listed as Capability in the /// Optional Parameters. If this yields an empty iterator, one can assume the From 349923e3cc3d79629764841e51f069d96b80090a Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 25 Mar 2024 14:12:48 +0100 Subject: [PATCH 85/96] Make some NLRI related pub --- src/bgp/nlri/flowspec.rs | 2 +- src/bgp/nlri/mod.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bgp/nlri/flowspec.rs b/src/bgp/nlri/flowspec.rs index 35b48b66..391eff82 100644 --- a/src/bgp/nlri/flowspec.rs +++ b/src/bgp/nlri/flowspec.rs @@ -20,7 +20,7 @@ pub struct FlowSpecNlri { } impl FlowSpecNlri { - pub(crate) fn raw(&self) -> &Octs { + pub fn raw(&self) -> &Octs { &self.raw } } diff --git a/src/bgp/nlri/mod.rs b/src/bgp/nlri/mod.rs index df32a9e2..2c10e70f 100644 --- a/src/bgp/nlri/mod.rs +++ b/src/bgp/nlri/mod.rs @@ -2,9 +2,9 @@ pub mod afisafi; pub mod nexthop; pub mod common; -pub(crate) mod evpn; -pub(crate) mod flowspec; -pub(crate) mod mpls; -pub(crate) mod mpls_vpn; -pub(crate) mod routetarget; -pub(crate) mod vpls; +pub mod evpn; +pub mod flowspec; +pub mod mpls; +pub mod mpls_vpn; +pub mod routetarget; +pub mod vpls; From 42d269b1ff02a34db9cc4578a45469e137782ccd Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 25 Mar 2024 14:14:15 +0100 Subject: [PATCH 86/96] Mark Nlri as Eq --- src/bgp/nlri/afisafi.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 158a0d3c..4fdfc7a6 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -348,6 +348,8 @@ $($( //--- Trait implementations for macro generated types +impl> Eq for Nlri {} + impl PartialEq> for Nlri where Octs: AsRef<[u8]>, Other: AsRef<[u8]> From 98121c49c00471976a9cae7ec3f22f16e2ea9fd7 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 25 Mar 2024 14:16:04 +0100 Subject: [PATCH 87/96] Remove pub fn iter_for_afi_safi --- src/bgp/nlri/afisafi.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 4fdfc7a6..335ca768 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -1283,17 +1283,6 @@ where } -pub fn iter_for_afi_safi<'a, O, P, ASP>( - parser: Parser<'a, P>, -) -> NlriIter<'a, O, P, ASP> -where - O: Octets, - P: Octets = O>, - ASP: AfiSafiParse<'a, O, P> -{ - NlriIter::<'a, O, P, ASP>::new(parser) -} - impl<'a, O, P, ASP: AfiSafiParse<'a, O, P>> Iterator for NlriIter<'a, O, P, ASP> where P: Octets = O> From af12707f3ab451b26538d31c4c4a8401c5240714 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 25 Mar 2024 14:45:02 +0100 Subject: [PATCH 88/96] Introduce enum NlriType --- src/bgp/message/update.rs | 10 ++- src/bgp/nlri/afisafi.rs | 132 +++++++++++++++++++++++++++++--------- 2 files changed, 108 insertions(+), 34 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index 9bd5c4ba..d8d89ec1 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -248,7 +248,10 @@ impl UpdateMessage { let afi = parser.parse_u16_be()?; let safi = parser.parse_u8()?; let afi_safi = AfiSafi::from((afi, safi)); - Ok(Some(NlriEnumIter::new(parser, afi_safi))) + Ok(Some(NlriEnumIter::new( + parser, + (afi_safi, self.pdu_parse_info.mp_unreach_addpath()).into() + ))) } else { Ok(None) } @@ -374,7 +377,10 @@ impl UpdateMessage { NextHop::skip(&mut parser)?; parser.advance(1)?; // 1 reserved byte - Ok(Some(NlriEnumIter::new(parser, afi_safi))) + Ok(Some(NlriEnumIter::new( + parser, + (afi_safi, self.pdu_parse_info.mp_reach_addpath()).into() + ))) } else { Ok(None) } diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 335ca768..9f256a05 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -66,6 +66,12 @@ paste! { } } + impl$(<$gen>)? IsNlri for [<$nlri AddpathNlri>]$(<$gen>)? { + fn nlri_type() -> NlriType { + NlriType::[<$nlri AddpathNlri>] + } + } + impl$(<$gen: Clone + Debug + Hash>)? Addpath for [<$nlri AddpathNlri>]$(<$gen>)? { fn path_id(&self) -> PathId { self.0 @@ -247,6 +253,42 @@ paste! { } } + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] + pub enum NlriType { + $($( + [<$afi_name $safi_name Nlri>], + [<$afi_name $safi_name AddpathNlri>], + )+)+ + Unsupported(u16, u8), + } + + impl NlriType { + pub fn afi_safi(&self) -> AfiSafiType { + match self { + $($( + Self::[<$afi_name $safi_name Nlri>] => AfiSafiType::[<$afi_name $safi_name >], + Self::[<$afi_name $safi_name AddpathNlri>] => AfiSafiType::[<$afi_name $safi_name >], + )+)+ + Self::Unsupported(a, s) => AfiSafiType::Unsupported(*a, *s) + } + } + } + + impl From<(AfiSafiType, bool)> for NlriType { + fn from(t: (AfiSafiType, bool)) -> Self { + match (t.0, t.1) { + $($( + (AfiSafiType::[<$afi_name $safi_name>], false) => NlriType::[<$afi_name $safi_name Nlri>], + (AfiSafiType::[<$afi_name $safi_name>], true) => NlriType::[<$afi_name $safi_name AddpathNlri>], + )+)+ + (AfiSafiType::Unsupported(a, s), _) => NlriType::Unsupported(a, s), + } + } + } + + $($( // Instead of doing: //pub struct [<$afi_name $safi_name Nlri>]; @@ -261,6 +303,12 @@ $($( } } + impl$(<$gen>)? IsNlri for [<$afi_name $safi_name Nlri>]$(<$gen>)? { + fn nlri_type() -> NlriType { + NlriType::[<$afi_name $safi_name Nlri>] + } + } + impl From<[<$afi_name $safi_name Nlri>]$(<$gen>)?> for Nlri { fn from(n: [<$afi_name $safi_name Nlri>]$(<$gen>)?) -> Self { Nlri::[<$afi_name $safi_name>](n) @@ -431,17 +479,17 @@ pub trait AfiSafi { fn afi_safi() -> AfiSafiType; } +pub trait IsNlri { + fn nlri_type() -> NlriType; +} + /// A type representing an NLRI for a certain AFI+SAFI. -pub trait AfiSafiNlri: AfiSafi + Clone + Hash + Debug { +pub trait AfiSafiNlri: AfiSafi + IsNlri + Clone + Hash + Debug { type Nlri; fn nlri(&self) -> Self::Nlri; - - // TODO - // can/should we merge in AfiSafiParse here? - } -pub trait AfiSafiParse<'a, O, P>: Sized +pub trait AfiSafiParse<'a, O, P>: Sized + IsNlri where P: 'a + Octets = O> { type Output: AfiSafi; // XXX do we actually still need this? @@ -503,11 +551,6 @@ pub trait Addpath: AfiSafiNlri { fn path_id(&self) -> PathId; } -pub trait IsAddpathPrefix { - fn prefix(&self) -> Prefix; - fn path_id(&self) -> PathId; -} - //------------ Implementations ----------------------------------------------- @@ -1268,7 +1311,10 @@ where ASP::Output::afi_safi() } - // + pub fn nlri_type(&self) -> NlriType { + ASP::nlri_type() + } + // Validate the entire parser so we can safely return items from this // iterator, instead of returning Option, ParseError> // @@ -1301,15 +1347,20 @@ where /// structs. pub struct NlriEnumIter<'a, P> { parser: Parser<'a, P>, - afisafi: AfiSafiType, + ty: NlriType, } + impl<'a, P> NlriEnumIter<'a, P> { - pub fn new(parser: Parser<'a, P>, afisafi: AfiSafiType) -> Self { - Self { parser, afisafi } + pub fn new(parser: Parser<'a, P>, ty: NlriType) -> Self { + Self { parser, ty } + } + + pub fn nlri_type(&self) -> NlriType { + self.ty } pub fn afi_safi(&self) -> AfiSafiType { - self.afisafi + self.ty.afi_safi() } } @@ -1325,22 +1376,36 @@ where return None } - let res = match self.afisafi { - AfiSafiType::Ipv4Unicast => Ipv4UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4Unicast), - AfiSafiType::Ipv4Multicast => Ipv4MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv4Multicast), - AfiSafiType::Ipv4MplsUnicast => Ipv4MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicast), - AfiSafiType::Ipv4MplsVpnUnicast => Ipv4MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicast), - AfiSafiType::Ipv4RouteTarget => Ipv4RouteTargetNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTarget), - AfiSafiType::Ipv4FlowSpec => Ipv4FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpec), - AfiSafiType::Ipv6Unicast => Ipv6UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6Unicast), - AfiSafiType::Ipv6Multicast => Ipv6MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv6Multicast), - AfiSafiType::Ipv6MplsUnicast => Ipv6MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicast), - AfiSafiType::Ipv6MplsVpnUnicast => Ipv6MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicast), - AfiSafiType::Ipv6FlowSpec => Ipv6FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpec), - AfiSafiType::L2VpnVpls => L2VpnVplsNlri::parse(&mut self.parser).map(Nlri::L2VpnVpls), - AfiSafiType::L2VpnEvpn => L2VpnEvpnNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpn), - AfiSafiType::Unsupported(_, _) => { return None } + let res = match self.ty { + NlriType::Ipv4UnicastNlri => Ipv4UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4Unicast), + NlriType::Ipv4UnicastAddpathNlri => Ipv4UnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4UnicastAddpath), + NlriType::Ipv4MulticastNlri => Ipv4MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv4Multicast), + NlriType::Ipv4MulticastAddpathNlri => Ipv4MulticastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MulticastAddpath), + NlriType::Ipv4MplsUnicastNlri => Ipv4MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicast), + NlriType::Ipv4MplsUnicastAddpathNlri => Ipv4MplsUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicastAddpath), + NlriType::Ipv4MplsVpnUnicastNlri => Ipv4MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicast), + NlriType::Ipv4MplsVpnUnicastAddpathNlri => Ipv4MplsVpnUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicastAddpath), + NlriType::Ipv4RouteTargetNlri => Ipv4RouteTargetNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTarget), + NlriType::Ipv4RouteTargetAddpathNlri => Ipv4RouteTargetAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTargetAddpath), + NlriType::Ipv4FlowSpecNlri => Ipv4FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpec), + NlriType::Ipv4FlowSpecAddpathNlri => Ipv4FlowSpecAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpecAddpath), + NlriType::Ipv6UnicastNlri => Ipv6UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6Unicast), + NlriType::Ipv6UnicastAddpathNlri => Ipv6UnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6UnicastAddpath), + NlriType::Ipv6MulticastNlri => Ipv6MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv6Multicast), + NlriType::Ipv6MulticastAddpathNlri => Ipv6MulticastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MulticastAddpath), + NlriType::Ipv6MplsUnicastNlri => Ipv6MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicast), + NlriType::Ipv6MplsUnicastAddpathNlri => Ipv6MplsUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicastAddpath), + NlriType::Ipv6MplsVpnUnicastNlri => Ipv6MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicast), + NlriType::Ipv6MplsVpnUnicastAddpathNlri => Ipv6MplsVpnUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicastAddpath), + NlriType::Ipv6FlowSpecNlri => Ipv6FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpec), + NlriType::Ipv6FlowSpecAddpathNlri => Ipv6FlowSpecAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpecAddpath), + NlriType::L2VpnVplsNlri => L2VpnVplsNlri::parse(&mut self.parser).map(Nlri::L2VpnVpls), + NlriType::L2VpnVplsAddpathNlri => L2VpnVplsAddpathNlri::parse(&mut self.parser).map(Nlri::L2VpnVplsAddpath), + NlriType::L2VpnEvpnNlri => L2VpnEvpnNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpn), + NlriType::L2VpnEvpnAddpathNlri => L2VpnEvpnAddpathNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpnAddpath), + NlriType::Unsupported(..) => { return None; } }; + Some(res) } } @@ -1352,7 +1417,10 @@ where P: Octets = O>, { fn from(iter: NlriIter<'a, O, P, ASP>) -> Self { - Self { parser: iter.parser, afisafi: iter.afi_safi() } + Self { + parser: iter.parser, + ty: ASP::nlri_type() + } } } From 99f4667ca049bdb0f4a9561feee0b1e52e5d6522 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Mon, 25 Mar 2024 16:30:08 +0100 Subject: [PATCH 89/96] Use NlriType instead of AfiSafiType in Update(Builder) where applicable --- src/bgp/message/update.rs | 52 ++++++++++++---------- src/bgp/message/update_builder.rs | 46 ++++++++----------- src/bgp/nlri/afisafi.rs | 74 +++++++++++++++++-------------- 3 files changed, 88 insertions(+), 84 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index d8d89ec1..f40495e0 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -25,7 +25,7 @@ use crate::bgp::types::{ }; use crate::bgp::nlri::afisafi::{ - AfiSafiNlri, AfiSafiParse, NlriIter, NlriEnumIter, Nlri as NlriEnum + AfiSafiNlri, AfiSafiParse, NlriIter, NlriEnumIter, Nlri, NlriType }; use crate::util::parser::ParseError; @@ -185,25 +185,31 @@ impl UpdateMessage { /// Once we switch over to the new AfiSafiType enum, we can signal PathId /// presence/absence. pub fn afi_safis(&self) -> ( - Option, - Option, - Option, - Option, + Option, + Option, + Option, + Option, ) { ( - (!self.withdrawals.is_empty()).then_some(AfiSafi::Ipv4Unicast), - (!self.announcements.is_empty()).then_some(AfiSafi::Ipv4Unicast), - self.mp_withdrawals().ok().flatten().map(|a| a.afi_safi()), - self.mp_announcements().ok().flatten().map(|a| a.afi_safi()), + (!self.withdrawals.is_empty()).then_some(( + AfiSafi::Ipv4Unicast, + self.pdu_parse_info.conventional_addpath()).into() + ), + (!self.announcements.is_empty()).then_some(( + AfiSafi::Ipv4Unicast, + self.pdu_parse_info.conventional_addpath()).into() + ), + self.mp_withdrawals().ok().flatten().map(|w| w.nlri_type()), + self.mp_announcements().ok().flatten().map(|a| a.nlri_type()), ) } - pub fn announcement_fams(&self) -> impl Iterator { + pub fn announcement_fams(&self) -> impl Iterator { let afi_safis = self.afi_safis(); [afi_safis.1, afi_safis.3].into_iter().flatten() } - pub fn withdrawal_fams(&self) -> impl Iterator { + pub fn withdrawal_fams(&self) -> impl Iterator { let afi_safis = self.afi_safis(); [afi_safis.0, afi_safis.2].into_iter().flatten() } @@ -211,10 +217,10 @@ impl UpdateMessage { /// Returns an iterator over the conventional withdrawals. /// /// The withdrawals are always IPv4 Unicast, but can contain Path IDs. - /// Therefore, iterator yields variants of `NlriEnum`. + /// Therefore, iterator yields variants of `Nlri`. pub fn conventional_withdrawals(&self) -> Result< - impl Iterator>, ParseError>> + '_, + impl Iterator>, ParseError>> + '_, ParseError > { @@ -226,10 +232,10 @@ impl UpdateMessage { }; Ok(normal_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::from)) + .map(|n| n.map(Nlri::from)) .chain( addpath_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::from)) + .map(|n| n.map(Nlri::from)) ) ) } @@ -260,10 +266,10 @@ impl UpdateMessage { /// Returns a combined iterator of conventional and MP_UNREACH_NLRI. /// /// Note that this iterator might contain NLRI of different AFI/SAFI - /// types and yields variants of `NlriEnum`. + /// types and yields variants of `Nlri`. pub fn withdrawals(&self) -> Result< - impl Iterator>, ParseError>>, + impl Iterator>, ParseError>>, ParseError > { @@ -312,7 +318,7 @@ impl UpdateMessage { /// For more fine-grained control, consider using the /// `unicast_withdrawals` method. pub fn withdrawals_vec(&self) - -> Result>>, ParseError> + -> Result>>, ParseError> { let conv = self.conventional_withdrawals()?; let mp = self.mp_withdrawals()?; @@ -343,7 +349,7 @@ impl UpdateMessage { /// Returns the conventional announcements. pub fn conventional_announcements(&self) -> Result< - impl Iterator>, ParseError>>, + impl Iterator>, ParseError>>, ParseError> { let pp = Parser::with_range(self.octets(), self.announcements.clone()); @@ -356,10 +362,10 @@ impl UpdateMessage { ; Ok(normal_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::from)) + .map(|n| n.map(Nlri::from)) .chain( addpath_iter.into_iter().flatten() - .map(|n| n.map(NlriEnum::from)) + .map(|n| n.map(Nlri::from)) )) } @@ -411,7 +417,7 @@ impl UpdateMessage { /// types. pub fn announcements(&self) -> Result< - impl Iterator>, ParseError>>, + impl Iterator>, ParseError>>, ParseError > { @@ -470,7 +476,7 @@ impl UpdateMessage { /// For more fine-grained control, consider using the /// `unicast_announcements` method. pub fn announcements_vec(&self) - -> Result>>, ParseError> + -> Result>>, ParseError> { let conv = self.conventional_announcements()?; let mp = self.mp_announcements()?; diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index d5618637..da9206ac 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -1352,6 +1352,7 @@ mod tests { Ipv4MulticastNlri, Ipv6UnicastNlri, Ipv4FlowSpecNlri, + NlriType, }; use crate::bgp::types::{AfiSafi, OriginType, PathId}; @@ -2118,56 +2119,47 @@ mod tests { }; let target = BytesMut::new(); - let (_unreach_afisafi, reach_afisafi) = match original.afi_safis() { - (_, _, Some(mp_u), Some(mp_r)) => (mp_u, mp_r), - (_, _, None, Some(mp_r)) => (mp_r, mp_r), - (_, _, Some(mp_u), None) => (mp_u, mp_u), - (Some(c_w), Some(c_a), _, _) => (c_w, c_a), - (Some(c_w), None, _, _) => (c_w, c_w), - (None, Some(c_a), _, _) => (c_a, c_a), - (None, None, None, None) => - { - // conventional IPv4 End-of-RIB - (AfiSafi::Ipv4Unicast, AfiSafi::Ipv4Unicast) - } - }; + let reach_afisafi = original.announcement_fams().last() + .unwrap_or(NlriType::Ipv4Unicast); + let composed = match reach_afisafi { - AfiSafi::Ipv4Unicast => { + NlriType::Ipv4Unicast => { let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::from_update_message(&original, &sc, target).unwrap(); builder.add_announcements_from_pdu(&original, &sc); builder.add_withdrawals_from_pdu(&original, &sc); builder.into_message(&sc) } - AfiSafi::Ipv4Multicast => { + NlriType::Ipv4Multicast => { let mut builder = UpdateBuilder::<_, Ipv4MulticastNlri>::from_update_message(&original, &sc, target).unwrap(); builder.add_announcements_from_pdu(&original, &sc); builder.add_withdrawals_from_pdu(&original, &sc); builder.into_message(&sc) } - AfiSafi::Ipv4MplsUnicast => todo!(), - AfiSafi::Ipv4MplsVpnUnicast => todo!(), - AfiSafi::Ipv4RouteTarget => todo!(), - AfiSafi::Ipv4FlowSpec => { + NlriType::Ipv4MplsUnicast => todo!(), + NlriType::Ipv4MplsVpnUnicast => todo!(), + NlriType::Ipv4RouteTarget => todo!(), + NlriType::Ipv4FlowSpec => { let mut builder = UpdateBuilder::<_, Ipv4FlowSpecNlri<_>>::from_update_message(&original, &sc, target).unwrap(); builder.add_announcements_from_pdu(&original, &sc); builder.add_withdrawals_from_pdu(&original, &sc); builder.into_message(&sc) } - AfiSafi::Ipv6Unicast => { + NlriType::Ipv6Unicast => { let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::from_update_message(&original, &sc, target).unwrap(); builder.add_announcements_from_pdu(&original, &sc); builder.add_withdrawals_from_pdu(&original, &sc); builder.into_message(&sc) } - AfiSafi::Ipv6Multicast => todo!(), - AfiSafi::Ipv6MplsUnicast => todo!(), - AfiSafi::Ipv6MplsVpnUnicast => todo!(), - AfiSafi::Ipv6FlowSpec => todo!(), - AfiSafi::L2VpnVpls => todo!(), - AfiSafi::L2VpnEvpn => todo!(), - AfiSafi::Unsupported(_, _) => todo!(), + NlriType::Ipv6Multicast => todo!(), + NlriType::Ipv6MplsUnicast => todo!(), + NlriType::Ipv6MplsVpnUnicast => todo!(), + NlriType::Ipv6FlowSpec => todo!(), + NlriType::L2VpnVpls => todo!(), + NlriType::L2VpnEvpn => todo!(), + NlriType::Unsupported(_, _) => todo!(), + _ => todo!(), // ADD-PATH catch-all }; //let mut builder = UpdateBuilder::<_, reach_afisafi>::from_update_message( // &original, &sc, target diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 9f256a05..2a0f617a 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -68,7 +68,7 @@ paste! { impl$(<$gen>)? IsNlri for [<$nlri AddpathNlri>]$(<$gen>)? { fn nlri_type() -> NlriType { - NlriType::[<$nlri AddpathNlri>] + NlriType::[<$nlri Addpath>] } } @@ -258,8 +258,8 @@ paste! { #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub enum NlriType { $($( - [<$afi_name $safi_name Nlri>], - [<$afi_name $safi_name AddpathNlri>], + [<$afi_name $safi_name>], + [<$afi_name $safi_name Addpath>], )+)+ Unsupported(u16, u8), } @@ -268,8 +268,8 @@ paste! { pub fn afi_safi(&self) -> AfiSafiType { match self { $($( - Self::[<$afi_name $safi_name Nlri>] => AfiSafiType::[<$afi_name $safi_name >], - Self::[<$afi_name $safi_name AddpathNlri>] => AfiSafiType::[<$afi_name $safi_name >], + Self::[<$afi_name $safi_name>] => AfiSafiType::[<$afi_name $safi_name >], + Self::[<$afi_name $safi_name Addpath>] => AfiSafiType::[<$afi_name $safi_name >], )+)+ Self::Unsupported(a, s) => AfiSafiType::Unsupported(*a, *s) } @@ -280,14 +280,20 @@ paste! { fn from(t: (AfiSafiType, bool)) -> Self { match (t.0, t.1) { $($( - (AfiSafiType::[<$afi_name $safi_name>], false) => NlriType::[<$afi_name $safi_name Nlri>], - (AfiSafiType::[<$afi_name $safi_name>], true) => NlriType::[<$afi_name $safi_name AddpathNlri>], + (AfiSafiType::[<$afi_name $safi_name>], false) => NlriType::[<$afi_name $safi_name >], + (AfiSafiType::[<$afi_name $safi_name>], true) => NlriType::[<$afi_name $safi_name Addpath>], )+)+ (AfiSafiType::Unsupported(a, s), _) => NlriType::Unsupported(a, s), } } } + impl From for AfiSafiType { + fn from(n: NlriType) -> Self { + n.afi_safi() + } + } + $($( // Instead of doing: @@ -305,7 +311,7 @@ $($( impl$(<$gen>)? IsNlri for [<$afi_name $safi_name Nlri>]$(<$gen>)? { fn nlri_type() -> NlriType { - NlriType::[<$afi_name $safi_name Nlri>] + NlriType::[<$afi_name $safi_name>] } } @@ -1377,32 +1383,32 @@ where } let res = match self.ty { - NlriType::Ipv4UnicastNlri => Ipv4UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4Unicast), - NlriType::Ipv4UnicastAddpathNlri => Ipv4UnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4UnicastAddpath), - NlriType::Ipv4MulticastNlri => Ipv4MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv4Multicast), - NlriType::Ipv4MulticastAddpathNlri => Ipv4MulticastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MulticastAddpath), - NlriType::Ipv4MplsUnicastNlri => Ipv4MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicast), - NlriType::Ipv4MplsUnicastAddpathNlri => Ipv4MplsUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicastAddpath), - NlriType::Ipv4MplsVpnUnicastNlri => Ipv4MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicast), - NlriType::Ipv4MplsVpnUnicastAddpathNlri => Ipv4MplsVpnUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicastAddpath), - NlriType::Ipv4RouteTargetNlri => Ipv4RouteTargetNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTarget), - NlriType::Ipv4RouteTargetAddpathNlri => Ipv4RouteTargetAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTargetAddpath), - NlriType::Ipv4FlowSpecNlri => Ipv4FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpec), - NlriType::Ipv4FlowSpecAddpathNlri => Ipv4FlowSpecAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpecAddpath), - NlriType::Ipv6UnicastNlri => Ipv6UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6Unicast), - NlriType::Ipv6UnicastAddpathNlri => Ipv6UnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6UnicastAddpath), - NlriType::Ipv6MulticastNlri => Ipv6MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv6Multicast), - NlriType::Ipv6MulticastAddpathNlri => Ipv6MulticastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MulticastAddpath), - NlriType::Ipv6MplsUnicastNlri => Ipv6MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicast), - NlriType::Ipv6MplsUnicastAddpathNlri => Ipv6MplsUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicastAddpath), - NlriType::Ipv6MplsVpnUnicastNlri => Ipv6MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicast), - NlriType::Ipv6MplsVpnUnicastAddpathNlri => Ipv6MplsVpnUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicastAddpath), - NlriType::Ipv6FlowSpecNlri => Ipv6FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpec), - NlriType::Ipv6FlowSpecAddpathNlri => Ipv6FlowSpecAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpecAddpath), - NlriType::L2VpnVplsNlri => L2VpnVplsNlri::parse(&mut self.parser).map(Nlri::L2VpnVpls), - NlriType::L2VpnVplsAddpathNlri => L2VpnVplsAddpathNlri::parse(&mut self.parser).map(Nlri::L2VpnVplsAddpath), - NlriType::L2VpnEvpnNlri => L2VpnEvpnNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpn), - NlriType::L2VpnEvpnAddpathNlri => L2VpnEvpnAddpathNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpnAddpath), + NlriType::Ipv4Unicast => Ipv4UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4Unicast), + NlriType::Ipv4UnicastAddpath => Ipv4UnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4UnicastAddpath), + NlriType::Ipv4Multicast => Ipv4MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv4Multicast), + NlriType::Ipv4MulticastAddpath => Ipv4MulticastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MulticastAddpath), + NlriType::Ipv4MplsUnicast => Ipv4MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicast), + NlriType::Ipv4MplsUnicastAddpath => Ipv4MplsUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsUnicastAddpath), + NlriType::Ipv4MplsVpnUnicast => Ipv4MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicast), + NlriType::Ipv4MplsVpnUnicastAddpath => Ipv4MplsVpnUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4MplsVpnUnicastAddpath), + NlriType::Ipv4RouteTarget => Ipv4RouteTargetNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTarget), + NlriType::Ipv4RouteTargetAddpath => Ipv4RouteTargetAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4RouteTargetAddpath), + NlriType::Ipv4FlowSpec => Ipv4FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpec), + NlriType::Ipv4FlowSpecAddpath => Ipv4FlowSpecAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv4FlowSpecAddpath), + NlriType::Ipv6Unicast => Ipv6UnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6Unicast), + NlriType::Ipv6UnicastAddpath => Ipv6UnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6UnicastAddpath), + NlriType::Ipv6Multicast => Ipv6MulticastNlri::parse(&mut self.parser).map(Nlri::Ipv6Multicast), + NlriType::Ipv6MulticastAddpath => Ipv6MulticastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MulticastAddpath), + NlriType::Ipv6MplsUnicast => Ipv6MplsUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicast), + NlriType::Ipv6MplsUnicastAddpath => Ipv6MplsUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsUnicastAddpath), + NlriType::Ipv6MplsVpnUnicast => Ipv6MplsVpnUnicastNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicast), + NlriType::Ipv6MplsVpnUnicastAddpath => Ipv6MplsVpnUnicastAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6MplsVpnUnicastAddpath), + NlriType::Ipv6FlowSpec => Ipv6FlowSpecNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpec), + NlriType::Ipv6FlowSpecAddpath => Ipv6FlowSpecAddpathNlri::parse(&mut self.parser).map(Nlri::Ipv6FlowSpecAddpath), + NlriType::L2VpnVpls => L2VpnVplsNlri::parse(&mut self.parser).map(Nlri::L2VpnVpls), + NlriType::L2VpnVplsAddpath => L2VpnVplsAddpathNlri::parse(&mut self.parser).map(Nlri::L2VpnVplsAddpath), + NlriType::L2VpnEvpn => L2VpnEvpnNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpn), + NlriType::L2VpnEvpnAddpath => L2VpnEvpnAddpathNlri::parse(&mut self.parser).map(Nlri::L2VpnEvpnAddpath), NlriType::Unsupported(..) => { return None; } }; From ca54e23e6888404b103c367a72a76d1dcfe92d2d Mon Sep 17 00:00:00 2001 From: Jasper den Hertog Date: Tue, 26 Mar 2024 09:24:17 +0100 Subject: [PATCH 90/96] this can go now --- src/bgp/workshop/route.rs | 324 +++++++++++++++++++------------------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 9a37efcc..0228b31d 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -313,81 +313,81 @@ impl WorkshopAttribute for crate::bgp::types::NextHop { //----------------------------------------------------------------------------- -use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiParse}; - -pub fn pdu_into_rws<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> Vec -where - Octs: 'a + Octets = R>, - R: Hash + Clone + Debug, - Vec: From>, - T: From>>, - //Nlri//: AfiSafiNlri // + Hash + Debug -{ - - let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - - let mut res = Vec::new(); - for a in pdu.announcements().unwrap() { - res.push( - T::from( - RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) - ) - ); - } - - res -} - -pub fn pdu_into_typed_rws<'a, Octs, T, R, AFN>(pdu: &'a UpdateMessage) -> Vec -where - Octs: 'a + Octets = R>, - - //R: Hash + Clone + Debug + Octets, - T: From>, - AFN: AfiSafiNlri + AfiSafiParse<'a, R, Octs, Output = AFN>, - - R: Octets, - Vec: OctetsFrom, -{ - - let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - - let mut res = Vec::new(); - if let Ok(Some(iter)) = pdu.typed_announcements::<_, AFN>() { - for a in iter { - res.push( - T::from( - RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) - ) - ); - } - } else { - eprintln!("empty or invalid NLRI iter"); - } - - res -} - - - -pub fn pdu_into_rws_iter<'a, Octs, T, R>(pdu: &'a UpdateMessage) --> impl Iterator + '_ -where - Octs: 'a + Octets = R>, - R: Hash + Clone + Debug, - Vec: From>, - T: From>>, - //Nlri//: AfiSafiNlri // + Hash + Debug -{ - - let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - - pdu.announcements().unwrap().map(move |a| - T::from( - RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) - ) - ) -} +// use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiParse}; + +// fn pdu_into_rws<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> Vec +// where +// Octs: 'a + Octets = R>, +// R: Hash + Clone + Debug, +// Vec: From>, +// T: From>>, +// //Nlri//: AfiSafiNlri // + Hash + Debug +// { + +// let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + +// let mut res = Vec::new(); +// for a in pdu.announcements().unwrap() { +// res.push( +// T::from( +// RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) +// ) +// ); +// } + +// res +// } + +// fn pdu_into_typed_rws<'a, Octs, T, R, AFN>(pdu: &'a UpdateMessage) -> Vec +// where +// Octs: 'a + Octets = R>, + +// //R: Hash + Clone + Debug + Octets, +// T: From>, +// AFN: AfiSafiNlri + AfiSafiParse<'a, R, Octs, Output = AFN>, + +// R: Octets, +// Vec: OctetsFrom, +// { + +// let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + +// let mut res = Vec::new(); +// if let Ok(Some(iter)) = pdu.typed_announcements::<_, AFN>() { +// for a in iter { +// res.push( +// T::from( +// RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) +// ) +// ); +// } +// } else { +// eprintln!("empty or invalid NLRI iter"); +// } + +// res +// } + + + +// fn pdu_into_rws_iter<'a, Octs, T, R>(pdu: &'a UpdateMessage) +// -> impl Iterator + '_ +// where +// Octs: 'a + Octets = R>, +// R: Hash + Clone + Debug, +// Vec: From>, +// T: From>>, +// //Nlri//: AfiSafiNlri // + Hash + Debug +// { + +// let pa_map = PaMap::from_update_pdu(pdu).unwrap(); + +// pdu.announcements().unwrap().map(move |a| +// T::from( +// RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) +// ) +// ) +// } // XXX to be moved to roto @@ -490,92 +490,92 @@ impl TryFrom> for BasicNlri { */ -#[allow(unused_imports)] -#[cfg(test)] -mod tests { - use super::*; - use crate::bgp::message::update::SessionConfig; - - use crate::bgp::nlri::afisafi::{ - Ipv4UnicastNlri, - Ipv6UnicastNlri, - Ipv6UnicastAddpathNlri, - Ipv4FlowSpecNlri, - }; - - - #[test] - fn pdu_into_rws_vec() { - - // UPDATE with 5 ipv6 nlri - let raw = vec![ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - //0x00, 0x88, - 0x00, 0x88 + 6, - 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, - 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, - 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, - 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, - 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, - 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, - 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, - 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, - 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, - 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, - 16, 1, 2, - 16, 10, 20 - - ]; - let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) - .unwrap(); - - //let res: Vec> = pdu_into_rws(&pdu); - let res: Vec> = pdu_into_rws(&pdu); - assert_eq!(res.len(), 7); - for rws in res { - println!("{}", rws.nlri()); - } - } - - #[test] - fn pdu_into_rws_iter_test() { - - // UPDATE with 5 ipv6 nlri + 2 conventional - let raw = vec![ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - //0x00, 0x88, - 0x00, 0x88 + 6, - 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, - 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, - 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, - 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, - 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, - 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, - 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, - 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, - 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, - 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, - 16, 1, 2, - 16, 10, 20 - - ]; - let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) - .unwrap(); - - assert_eq!(pdu_into_rws_iter::<_, RouteWorkshop<_>, _>(&pdu).count(), 7); - } +// #[allow(unused_imports)] +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::bgp::message::update::SessionConfig; + +// use crate::bgp::nlri::afisafi::{ +// Ipv4UnicastNlri, +// Ipv6UnicastNlri, +// Ipv6UnicastAddpathNlri, +// Ipv4FlowSpecNlri, +// }; + + +// #[test] +// fn pdu_into_rws_vec() { + +// // UPDATE with 5 ipv6 nlri +// let raw = vec![ +// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +// //0x00, 0x88, +// 0x00, 0x88 + 6, +// 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, +// 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, +// 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, +// 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +// 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, +// 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, +// 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, +// 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, +// 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, +// 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, +// 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, +// 16, 1, 2, +// 16, 10, 20 + +// ]; +// let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) +// .unwrap(); + +// //let res: Vec> = pdu_into_rws(&pdu); +// let res: Vec> = pdu_into_rws(&pdu); +// assert_eq!(res.len(), 7); +// for rws in res { +// println!("{}", rws.nlri()); +// } +// } + +// #[test] +// fn pdu_into_rws_iter_test() { + +// // UPDATE with 5 ipv6 nlri + 2 conventional +// let raw = vec![ +// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +// //0x00, 0x88, +// 0x00, 0x88 + 6, +// 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, +// 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, +// 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, +// 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +// 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, +// 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, +// 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, +// 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, +// 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, +// 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, +// 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, +// 16, 1, 2, +// 16, 10, 20 + +// ]; +// let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) +// .unwrap(); + +// assert_eq!(pdu_into_rws_iter::<_, RouteWorkshop<_>, _>(&pdu).count(), 7); +// } //XXX to be moved to roto /* @@ -672,4 +672,4 @@ mod tests { */ -} +// } From a996da40a9fc9ffc81d35263d4107a66fa739457 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 26 Mar 2024 09:43:20 +0100 Subject: [PATCH 91/96] Make NextHop live on RouteWorkshop --- src/bgp/message/update.rs | 4 ++ src/bgp/nlri/afisafi.rs | 3 + src/bgp/workshop/route.rs | 114 ++++++++++++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 16 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index f40495e0..c71eeb59 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -49,6 +49,10 @@ impl UpdateMessage { &self.octets } + pub fn pdu_parse_info(&self) -> PduParseInfo { + self.pdu_parse_info + } + ///// Returns the [`Header`] for this message. //pub fn header(&self) -> Header<&Octs> { // Header::for_slice(&self.octets) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 2a0f617a..5e48bc92 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -493,6 +493,9 @@ pub trait IsNlri { pub trait AfiSafiNlri: AfiSafi + IsNlri + Clone + Hash + Debug { type Nlri; fn nlri(&self) -> Self::Nlri; + fn allowed_next_hops(&self) -> impl Iterator { + [Self::afi_safi()].into_iter() + } } pub trait AfiSafiParse<'a, O, P>: Sized + IsNlri diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index 9a37efcc..ead71dc0 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -19,8 +19,9 @@ use crate::bgp::{ }, }; -use crate::bgp::nlri::afisafi::Nlri; - +use crate::bgp::nlri::afisafi::{AfiSafiType, Nlri}; +use crate::bgp::nlri::nexthop::NextHop; +use crate::bgp::types::ConventionalNextHop; //------------ TypedRoute ---------------------------------------------------- @@ -83,17 +84,40 @@ impl From>> for PathAttribute { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct RouteWorkshop(N, PaMap); +pub struct RouteWorkshop(N, Option, PaMap); -impl RouteWorkshop { - pub fn new(nlri: N) -> Self { - Self(nlri, PaMap::empty()) - } +impl RouteWorkshop { + /// Creates an empty RouteWorkshop. + /// + /// The resulting RouteWorkshop has its NextHop set to `None` and an empty + /// [`PaMap`]. + pub fn new(nlri: N) -> Self { + Self(nlri, None, PaMap::empty()) + } + + /// Creates a RouteWorkshop from one NLRI and a [`PaMap`]. + /// + /// No logic is applied to the contents of the attributes in the PaMap. + /// When creating a RouteWorkshop via this constructor, the caller needs + /// to ensure the contents of the PaMap are sensible for the provided + /// NLRI. + /// + /// Consider using [`RouteWorkshop::from_update_pdu`] when the original + /// PDU is available. + // XXX do we actually need and want this? pub fn from_pa_map(nlri: N, pa_map: PaMap) -> Self { - Self(nlri, pa_map) + Self(nlri, None, pa_map) } + + /// Creates a RouteWorkshop from one NLRI and its BGP [`UpdateMessage`]. + /// + /// Based on the type of NLRI, i.e. conventional or Multi Protocol, the + /// Next Hop from the NEXT_HOP path attribute or the field in the + /// MP_REACH_NLRI attribute is set in the resulting RouteWorkshop. + /// In both cases, the NEXT_HOP path attribute is omitted from the + /// attached [`PaMap`]. pub fn from_update_pdu( nlri: N, pdu: &UpdateMessage, @@ -101,41 +125,99 @@ impl RouteWorkshop { where for<'a> Vec: OctetsFrom>, { - PaMap::from_update_pdu(pdu) - .map(|r| Self(nlri, r)) + let mut res = Self::new(nlri); + + if N::afi_safi() == AfiSafiType::Ipv4Unicast && + pdu.has_conventional_nlri() + { + if let Ok(Some(nh)) = pdu.conventional_next_hop() { + res.set_nexthop(nh); + let mut pamap = PaMap::from_update_pdu(pdu)?; + let _ = pamap.remove::(); + res.set_attributes(pamap); + return Ok(res); + } else { + return Err(ComposeError::InvalidAttribute); + } + } + + if let Ok(Some(nh)) = pdu.mp_next_hop() { + res.set_nexthop(nh); + let mut pamap = PaMap::from_update_pdu(pdu)?; + let _ = pamap.remove::(); + res.set_attributes(pamap); + Ok(res) + } else { + Err(ComposeError::InvalidAttribute) + } + } + + + /// Validates the contents of this RouteWorkshop. + /// + /// If the combination of the various pieces of content in this + /// RouteWorkshop could produce a valid BGP UPDATE PDU, this method + /// returns `Ok(())`. The following checks are performed: + /// + /// * The NextHop is set, and is compatible with the NLRI type. + pub fn validate(&self) -> Result<(), ComposeError> { + match self.1 { + None => { return Err(ComposeError::InvalidAttribute); } + Some(nh) => { + // TODO + /* + if !self.0.allowed_next_hops().any(|a| a == nh.afi_safi()) { + return Err(ComposeError::IllegalCombination); + } + */ + } + } + Ok(()) } pub fn nlri(&self) -> &N { &self.0 } + pub fn nexthop(&self) -> &Option { + &self.1 + } + + pub fn set_nexthop(&mut self, nh: NextHop) -> Option { + self.1.replace(nh) + } + pub fn set_attr>( &mut self, value: WA, ) -> Result<(), ComposeError> { - WA::store(value, &mut self.1) + WA::store(value, &mut self.2) } pub fn get_attr>( &self, ) -> Option { - self.1.get::().or_else(|| A::retrieve(&self.1)) + self.2.get::().or_else(|| A::retrieve(&self.2)) } pub fn clone_into_route(&self) -> Route { - Route::(self.0.clone(), self.1.clone()) + Route::(self.0.clone(), self.2.clone()) } pub fn into_route(self) -> Route { - Route::(self.0, self.1) + Route::(self.0, self.2) } pub fn attributes(&self) -> &PaMap { - &self.1 + &self.2 + } + + pub fn set_attributes(&mut self, pa_map: PaMap) { + self.2 = pa_map; } pub fn attributes_mut(&mut self) -> &mut PaMap { - &mut self.1 + &mut self.2 } } From 0b0bbf226780d6bbf510ff2932c1cd1b0d611fc6 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 26 Mar 2024 10:02:16 +0100 Subject: [PATCH 92/96] Cleanup after merge and add basic tests --- src/bgp/workshop/route.rs | 401 +++++--------------------------------- 1 file changed, 44 insertions(+), 357 deletions(-) diff --git a/src/bgp/workshop/route.rs b/src/bgp/workshop/route.rs index ef6aedd5..d60b89e6 100644 --- a/src/bgp/workshop/route.rs +++ b/src/bgp/workshop/route.rs @@ -19,10 +19,11 @@ use crate::bgp::{ }, }; -use crate::bgp::nlri::afisafi::{AfiSafiType, Nlri}; +use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiType, Nlri}; use crate::bgp::nlri::nexthop::NextHop; use crate::bgp::types::ConventionalNextHop; + //------------ TypedRoute ---------------------------------------------------- #[derive(Debug)] @@ -163,7 +164,7 @@ impl RouteWorkshop { pub fn validate(&self) -> Result<(), ComposeError> { match self.1 { None => { return Err(ComposeError::InvalidAttribute); } - Some(nh) => { + Some(_nh) => { // TODO /* if !self.0.allowed_next_hops().any(|a| a == nh.afi_safi()) { @@ -358,313 +359,25 @@ impl WorkshopAttribute for Nlri> { } */ -//------------ NextHopWorkshop ----------------------------------------------- - -impl FromAttribute for crate::bgp::types::NextHop { } - -/* -impl WorkshopAttribute for crate::bgp::types::NextHop { - fn retrieve(attrs: &PaMap) -> Option { - if let Some(next_hop) = - attrs.get::() - { - Some(crate::bgp::types::NextHop::Unicast(next_hop.0.into())) - } else if let Some(nlri) = attrs.get::() { - Some(*nlri.get_nexthop()) - } else { - Some(crate::bgp::types::NextHop::Empty) - } - } - - fn store( - local_attr: Self, - attrs: &mut PaMap, - ) -> Result<(), ComposeError> { - if let Some(mut nlri) = attrs.get::() { - nlri.set_nexthop(local_attr)?; - attrs.set(nlri).ok_or(ComposeError::InvalidAttribute)?; - Ok(()) - } else { - Err(ComposeError::InvalidAttribute) - } - } -} -*/ - - - -//----------------------------------------------------------------------------- - -// use crate::bgp::nlri::afisafi::{AfiSafiNlri, AfiSafiParse}; - -// fn pdu_into_rws<'a, Octs, T, R>(pdu: &'a UpdateMessage) -> Vec -// where -// Octs: 'a + Octets = R>, -// R: Hash + Clone + Debug, -// Vec: From>, -// T: From>>, -// //Nlri//: AfiSafiNlri // + Hash + Debug -// { - -// let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - -// let mut res = Vec::new(); -// for a in pdu.announcements().unwrap() { -// res.push( -// T::from( -// RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) -// ) -// ); -// } - -// res -// } +#[allow(unused_imports)] +#[cfg(test)] +mod tests { + use super::*; + use crate::bgp::message::update::SessionConfig; -// fn pdu_into_typed_rws<'a, Octs, T, R, AFN>(pdu: &'a UpdateMessage) -> Vec -// where -// Octs: 'a + Octets = R>, + use crate::bgp::nlri::afisafi::{ + Ipv4UnicastNlri, + Ipv6UnicastNlri, + Ipv6UnicastAddpathNlri, + Ipv4FlowSpecNlri, + }; -// //R: Hash + Clone + Debug + Octets, -// T: From>, -// AFN: AfiSafiNlri + AfiSafiParse<'a, R, Octs, Output = AFN>, -// R: Octets, -// Vec: OctetsFrom, -// { - -// let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - -// let mut res = Vec::new(); -// if let Ok(Some(iter)) = pdu.typed_announcements::<_, AFN>() { -// for a in iter { -// res.push( -// T::from( -// RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) -// ) -// ); -// } -// } else { -// eprintln!("empty or invalid NLRI iter"); -// } - -// res -// } - - - -// fn pdu_into_rws_iter<'a, Octs, T, R>(pdu: &'a UpdateMessage) -// -> impl Iterator + '_ -// where -// Octs: 'a + Octets = R>, -// R: Hash + Clone + Debug, -// Vec: From>, -// T: From>>, -// //Nlri//: AfiSafiNlri // + Hash + Debug -// { - -// let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - -// pdu.announcements().unwrap().map(move |a| -// T::from( -// RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) -// ) -// ) -// } - - -// XXX to be moved to roto -/* -pub fn pdu_into_rws_basic_iter<'a, Octs, R>(pdu: &'a UpdateMessage) --> impl Iterator> + '_ -where - Octs: 'a + Octets = R>, - R: Hash + Clone + Debug, - Vec: From>, -{ - - let pa_map = PaMap::from_update_pdu(pdu).unwrap(); - - pdu.announcements().unwrap().filter_map(move |a| - a.ok().map(|n| BasicNlri::try_from(n).ok()).map(|a| - RouteWorkshop::from_pa_map(a.unwrap(), pa_map.clone()) - )) -} -*/ - - - -// XXX to be moved to roto -/* -//------------ BasicNlri again ------------------------------------------------ - -use inetnum::addr::Prefix; -use std::fmt; -use crate::bgp::nlri::afisafi::{AfiSafiType, IsPrefix}; -use crate::bgp::types::PathId; - - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct BasicNlri { - ty: AfiSafiType, - prefix: Prefix, - path_id: Option -} - -impl BasicNlri { - pub fn prefix(&self) -> Prefix { - self.prefix - } - - pub fn path_id(&self) -> Option { - self.path_id - } - - pub fn get_type(&self) -> AfiSafiType { - self.ty - } -} - -impl fmt::Display for BasicNlri { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.prefix) - } -} - -impl From for BasicNlri { - fn from(n: N) -> BasicNlri { - BasicNlri { - ty: N::afi_safi(), - prefix: n.prefix(), - path_id: n.path_id(), - } - } -} - - -impl From> for RouteWorkshop -where N: IsPrefix -{ - fn from(value: RouteWorkshop) -> Self { - RouteWorkshop(value.0.into(), value.1) - } -} - - -impl TryFrom> for BasicNlri { - type Error = &'static str; - - fn try_from(n: Nlri) -> Result { - match n { - Nlri::Ipv4Unicast(n) => Ok(n.into()), - Nlri::Ipv4UnicastAddpath(n) => Ok(n.into()), - Nlri::Ipv4Multicast(n) => Ok(n.into()), - Nlri::Ipv4MulticastAddpath(n) => Ok(n.into()), - Nlri::Ipv6Unicast(n) => Ok(n.into()), - Nlri::Ipv6UnicastAddpath(n) => Ok(n.into()), - Nlri::Ipv6Multicast(n) => Ok(n.into()), - Nlri::Ipv6MulticastAddpath(n) => Ok(n.into()), - _ => Err("NLRI not basic"), - } - } -} - -*/ - - -// #[allow(unused_imports)] -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::bgp::message::update::SessionConfig; - -// use crate::bgp::nlri::afisafi::{ -// Ipv4UnicastNlri, -// Ipv6UnicastNlri, -// Ipv6UnicastAddpathNlri, -// Ipv4FlowSpecNlri, -// }; - - -// #[test] -// fn pdu_into_rws_vec() { - -// // UPDATE with 5 ipv6 nlri -// let raw = vec![ -// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -// //0x00, 0x88, -// 0x00, 0x88 + 6, -// 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, -// 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, -// 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, -// 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -// 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, -// 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, -// 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, -// 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, -// 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, -// 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, -// 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, -// 16, 1, 2, -// 16, 10, 20 - -// ]; -// let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) -// .unwrap(); - -// //let res: Vec> = pdu_into_rws(&pdu); -// let res: Vec> = pdu_into_rws(&pdu); -// assert_eq!(res.len(), 7); -// for rws in res { -// println!("{}", rws.nlri()); -// } -// } - -// #[test] -// fn pdu_into_rws_iter_test() { - -// // UPDATE with 5 ipv6 nlri + 2 conventional -// let raw = vec![ -// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -// //0x00, 0x88, -// 0x00, 0x88 + 6, -// 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, -// 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, -// 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, -// 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -// 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, -// 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, -// 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, -// 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, -// 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, -// 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, -// 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, -// 16, 1, 2, -// 16, 10, 20 - -// ]; -// let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) -// .unwrap(); - -// assert_eq!(pdu_into_rws_iter::<_, RouteWorkshop<_>, _>(&pdu).count(), 7); -// } - - //XXX to be moved to roto - /* #[test] - fn pdu_into_rws_typed() { + fn rws_from_pdu() { - // UPDATE with 5 ipv6 nlri + 2 conventional + // UPDATE with 5 ipv6 nlri, 2 conventional, but NO conventional + // NEXT_HOP attribute. let raw = vec![ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -689,69 +402,43 @@ impl TryFrom> for BasicNlri { 16, 10, 20 ]; - let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) + let pdu = UpdateMessage::from_octets(raw, &SessionConfig::modern()) .unwrap(); - let res = pdu_into_typed_rws::<_, RouteWorkshop, _, Ipv6UnicastNlri>(&pdu); - for rws in &res { - println!("{}", rws.nlri()); - } - assert_eq!(res.len(), 5); + let mp_nlri = pdu.typed_announcements::<_, Ipv6UnicastNlri>() + .unwrap().unwrap().next().unwrap().unwrap(); + let mp_rws = RouteWorkshop::from_update_pdu(mp_nlri, &pdu).unwrap(); - let res = pdu_into_typed_rws::<_, RouteWorkshop, _, Ipv4UnicastNlri>(&pdu); - for rws in &res { - println!("{}", rws.nlri()); - } - assert_eq!(res.len(), 2); + mp_rws.validate().unwrap(); - let res = pdu_into_typed_rws::<_, RouteWorkshop<_>, _, Ipv4FlowSpecNlri<_>>(&pdu); - for rws in &res { - println!("{}", rws.nlri()); - } - assert_eq!(res.len(), 0); + let conv_nlri = pdu.typed_announcements::<_, Ipv4UnicastNlri>() + .unwrap().unwrap().next().unwrap().unwrap(); + assert!(RouteWorkshop::from_update_pdu(conv_nlri, &pdu).is_err()); } - #[test] - fn pdu_into_basic() { - // UPDATE with 5 ipv6 nlri + 2 conventional + #[test] + fn rws_from_pdu_valid_conv() { let raw = vec![ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - //0x00, 0x88, - 0x00, 0x88 + 6, - 0x02, 0x00, 0x00, 0x00, 0x71, 0x80, - 0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00, - 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, - 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00, - 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, - 0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, - 0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8, - 0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00, - 0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, - 0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, - 16, 1, 2, - 16, 10, 20 - + // BGP UPDATE, single conventional announcement, MultiExitDisc + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x37, 0x02, + 0x00, 0x00, 0x00, 0x1b, 0x40, 0x01, 0x01, 0x00, 0x40, 0x02, + 0x06, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x40, 0x03, 0x04, + 0x0a, 0xff, 0x00, 0x65, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x0a, 0x0a, 0x0a, 0x02 ]; - let pdu = UpdateMessage::from_octets(&raw, &SessionConfig::modern()) + + let pdu = UpdateMessage::from_octets(raw, &SessionConfig::modern()) .unwrap(); - let res = pdu_into_rws_basic_iter(&pdu); - for rws in res { - println!("{}", rws.nlri()); - } - let res = pdu_into_rws_basic_iter(&pdu); - assert_eq!(res.count(), 7); + let conv_nlri = pdu.typed_announcements::<_, Ipv4UnicastNlri>() + .unwrap().unwrap().next().unwrap().unwrap(); + let conv_rws =RouteWorkshop::from_update_pdu(conv_nlri, &pdu).unwrap(); + assert_eq!( + conv_rws.1, + Some(NextHop::Unicast("10.255.0.101".parse().unwrap())) + ); } - -*/ - - -// } +} From 878696548ac236de87d2fa3d241abf26db6f47a0 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 26 Mar 2024 10:10:23 +0100 Subject: [PATCH 93/96] Leave the nexthop compability check on AfiSafiNlri for now --- src/bgp/nlri/afisafi.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 5e48bc92..269a5ae9 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -493,9 +493,9 @@ pub trait IsNlri { pub trait AfiSafiNlri: AfiSafi + IsNlri + Clone + Hash + Debug { type Nlri; fn nlri(&self) -> Self::Nlri; - fn allowed_next_hops(&self) -> impl Iterator { - [Self::afi_safi()].into_iter() - } + + // TODO + //fn nexthop_compatible(&self, nh: &super::nexthop::NextHop) -> bool; } pub trait AfiSafiParse<'a, O, P>: Sized + IsNlri From d10f30df0c7d270ed920bd14729603cbfada3a25 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 26 Mar 2024 10:39:26 +0100 Subject: [PATCH 94/96] Make Update::typed_withdrawals consistent with ~announcements, fix bulk test in UpdateBuilder --- src/bgp/message/update.rs | 8 ++++++-- src/bgp/message/update_builder.rs | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bgp/message/update.rs b/src/bgp/message/update.rs index c71eeb59..228d947e 100644 --- a/src/bgp/message/update.rs +++ b/src/bgp/message/update.rs @@ -302,8 +302,12 @@ impl UpdateMessage { pa.type_code() == 15 ) { let mut parser = pa.value_into_parser(); - let _afi = parser.parse_u16_be()?; - let _safi = parser.parse_u8()?; + let afi = parser.parse_u16_be()?; + let safi = parser.parse_u8()?; + if AfiSafi::from((afi, safi)) != ASP::afi_safi() { + return Ok(None); + //return Err(ParseError::form_error("different AFI+SAFI than requested")); + } Ok(Some(NlriIter::<_, _, ASP>::new(parser))) } else { diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index da9206ac..ecc5b9c4 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -2120,6 +2120,9 @@ mod tests { let target = BytesMut::new(); let reach_afisafi = original.announcement_fams().last() + .or( + original.withdrawal_fams().last() + ) .unwrap_or(NlriType::Ipv4Unicast); From de0f76e402b04ad62fe31e677ee3527d0c7eca52 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 26 Mar 2024 13:29:52 +0100 Subject: [PATCH 95/96] Add PartialEq impls for all concrete NLRI and the Nlri enum --- src/bgp/nlri/afisafi.rs | 211 +++++++++++++++++++++++++++++------- src/bgp/nlri/evpn.rs | 10 ++ src/bgp/nlri/flowspec.rs | 11 ++ src/bgp/nlri/routetarget.rs | 9 ++ 4 files changed, 199 insertions(+), 42 deletions(-) diff --git a/src/bgp/nlri/afisafi.rs b/src/bgp/nlri/afisafi.rs index 269a5ae9..64851933 100644 --- a/src/bgp/nlri/afisafi.rs +++ b/src/bgp/nlri/afisafi.rs @@ -414,50 +414,35 @@ where Octs: AsRef<[u8]>, (Ipv4Unicast(p1), Ipv4Unicast(p2)) => p1 == p2, (Ipv4UnicastAddpath(p1), Ipv4UnicastAddpath(p2)) => p1 == p2, (Ipv4Multicast(p1), Ipv4Multicast(p2)) => p1 == p2, - (Ipv4MulticastAddpath(_), Ipv4MulticastAddpath(_)) => todo!(), - (Ipv4MplsUnicast(_), Ipv4MplsUnicast(_)) => todo!(), - (Ipv4MplsUnicastAddpath(_), Ipv4MplsUnicastAddpath(_)) => todo!(), - (Ipv4MplsVpnUnicast(_), Ipv4MplsVpnUnicast(_)) => todo!(), - (Ipv4MplsVpnUnicastAddpath(_), Ipv4MplsVpnUnicastAddpath(_)) => todo!(), - (Ipv4RouteTarget(_), Ipv4RouteTarget(_)) => todo!(), - (Ipv4RouteTargetAddpath(_), Ipv4RouteTargetAddpath(_)) => todo!(), - (Ipv4FlowSpec(_), Ipv4FlowSpec(_)) => todo!(), - (Ipv4FlowSpecAddpath(_), Ipv4FlowSpecAddpath(_)) => todo!(), + (Ipv4MulticastAddpath(p1), Ipv4MulticastAddpath(p2)) => p1 == p2, + (Ipv4MplsUnicast(p1), Ipv4MplsUnicast(p2)) => p1 == p2, + (Ipv4MplsUnicastAddpath(p1), Ipv4MplsUnicastAddpath(p2)) => p1 == p2, + (Ipv4MplsVpnUnicast(p1), Ipv4MplsVpnUnicast(p2)) => p1 == p2, + (Ipv4MplsVpnUnicastAddpath(p1), Ipv4MplsVpnUnicastAddpath(p2)) => p1 == p2, + (Ipv4RouteTarget(p1), Ipv4RouteTarget(p2)) => p1 == p2, + (Ipv4RouteTargetAddpath(p1), Ipv4RouteTargetAddpath(p2)) => p1 == p2, + (Ipv4FlowSpec(p1), Ipv4FlowSpec(p2)) => p1 == p2, + (Ipv4FlowSpecAddpath(p1), Ipv4FlowSpecAddpath(p2)) => p1 == p2, (Ipv6Unicast(p1), Ipv6Unicast(p2)) => p1 == p2, - (Ipv6UnicastAddpath(_), Ipv6UnicastAddpath(_)) => todo!(), - (Ipv6Multicast(_), Ipv6Multicast(_)) => todo!(), - (Ipv6MulticastAddpath(_), Ipv6MulticastAddpath(_)) => todo!(), - (Ipv6MplsUnicast(_), Ipv6MplsUnicast(_)) => todo!(), - (Ipv6MplsUnicastAddpath(_), Ipv6MplsUnicastAddpath(_)) => todo!(), - (Ipv6MplsVpnUnicast(_), Ipv6MplsVpnUnicast(_)) => todo!(), - (Ipv6MplsVpnUnicastAddpath(_), Ipv6MplsVpnUnicastAddpath(_)) => todo!(), - (Ipv6FlowSpec(_), Ipv6FlowSpec(_)) => todo!(), - (Ipv6FlowSpecAddpath(_), Ipv6FlowSpecAddpath(_)) => todo!(), - (L2VpnVpls(_), L2VpnVpls(_)) => todo!(), - (L2VpnVplsAddpath(_), L2VpnVplsAddpath(_)) => todo!(), - (L2VpnEvpn(_), L2VpnEvpn(_)) => todo!(), - (L2VpnEvpnAddpath(_), L2VpnEvpnAddpath(_)) => todo!(), + (Ipv6UnicastAddpath(p1), Ipv6UnicastAddpath(p2)) => p1 == p2, + (Ipv6Multicast(p1), Ipv6Multicast(p2)) => p1 == p2, + (Ipv6MulticastAddpath(p1), Ipv6MulticastAddpath(p2)) => p1 == p2, + (Ipv6MplsUnicast(p1), Ipv6MplsUnicast(p2)) => p1 == p2, + (Ipv6MplsUnicastAddpath(p1), Ipv6MplsUnicastAddpath(p2)) => p1 == p2, + (Ipv6MplsVpnUnicast(p1), Ipv6MplsVpnUnicast(p2)) => p1 == p2, + (Ipv6MplsVpnUnicastAddpath(p1), Ipv6MplsVpnUnicastAddpath(p2)) => p1 == p2, + (Ipv6FlowSpec(p1), Ipv6FlowSpec(p2)) => p1 == p2, + (Ipv6FlowSpecAddpath(p1), Ipv6FlowSpecAddpath(p2)) => p1 == p2, + (L2VpnVpls(p1), L2VpnVpls(p2)) => p1 == p2, + (L2VpnVplsAddpath(p1), L2VpnVplsAddpath(p2)) => p1 == p2, + (L2VpnEvpn(p1), L2VpnEvpn(p2)) => p1 == p2, + (L2VpnEvpnAddpath(p1), L2VpnEvpnAddpath(p2)) => p1 == p2, _ => false } - - /* - match (self, other) { - (Ipv4Unicast(s), Ipv4Unicast(o)) | - //(Multicast(s), Nlri::Multicast(o)) => s == o, - //(Mpls(s), Nlri::Mpls(o)) => s == o, - //(MplsVpn(s), Nlri::MplsVpn(o)) => s == o, - //(Vpls(s), Nlri::Vpls(o)) => s == o, - //(FlowSpec(s), Nlri::FlowSpec(o)) => s == o, - //(RouteTarget(s), Nlri::RouteTarget(o)) => s == o, - - _ => todo!(), // TMP while refactoring - //_ => false - } - */ } } -// XXX While Nlri<()> might make more sense, it clashes with trait bounds +// While Nlri<()> might make more sense, it clashes with trait bounds // like Vec: OctetsFrom elsewhere, as, From<()> is not implemented for // Vec. Similarly, () is not AsRef<[u8]>. impl Nlri<&[u8]> { @@ -501,7 +486,7 @@ pub trait AfiSafiNlri: AfiSafi + IsNlri + Clone + Hash + Debug { pub trait AfiSafiParse<'a, O, P>: Sized + IsNlri where P: 'a + Octets = O> { - type Output: AfiSafi; // XXX do we actually still need this? + type Output: AfiSafi; fn parse(parser: &mut Parser<'a, P>) -> Result; } @@ -520,8 +505,6 @@ pub trait IsPrefix: AfiSafiNlri { fn path_id(&self) -> Option { None } - // TODO - // fn into_routeworkshop() -> RouteWorkshop<_>; } // with this blanket impl we can't distinguish addpath from non-addpath @@ -754,6 +737,12 @@ impl NlriCompose for Ipv4MulticastNlri { } } +impl PartialEq for Ipv4MulticastAddpathNlri { + fn eq(&self, other: &Ipv4MulticastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv4MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -845,6 +834,24 @@ where } } +impl PartialEq> for Ipv4MplsVpnUnicastNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4MplsVpnUnicastNlri) -> bool { + self.0 == other.0 + } +} + +impl PartialEq> for Ipv4MplsVpnUnicastAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4MplsVpnUnicastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv4MplsVpnUnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -880,6 +887,24 @@ where } } +impl PartialEq> for Ipv4RouteTargetNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4RouteTargetNlri) -> bool { + self.0 == other.0 + } +} + +impl PartialEq> for Ipv4RouteTargetAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4RouteTargetAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv4RouteTargetNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -946,6 +971,24 @@ impl From> for FlowSpecNlri { } } +impl PartialEq> for Ipv4FlowSpecNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4FlowSpecNlri) -> bool { + self.0 == other.0 + } +} + +impl PartialEq> for Ipv4FlowSpecAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv4FlowSpecAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv4FlowSpecNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1019,6 +1062,13 @@ impl NlriCompose for Ipv6UnicastNlri { } } + +impl PartialEq for Ipv6UnicastAddpathNlri { + fn eq(&self, other: &Ipv6UnicastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv6UnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1051,6 +1101,12 @@ where } } +impl PartialEq for Ipv6MulticastAddpathNlri { + fn eq(&self, other: &Ipv6MulticastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv6MulticastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1098,6 +1154,15 @@ where Octs: AsRef<[u8]>, } } +impl PartialEq> for Ipv6MplsUnicastAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv6MplsUnicastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv6MplsUnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1134,6 +1199,24 @@ where } } +impl PartialEq> for Ipv6MplsVpnUnicastNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv6MplsVpnUnicastNlri) -> bool { + self.0 == other.0 + } +} + +impl PartialEq> for Ipv6MplsVpnUnicastAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv6MplsVpnUnicastAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv6MplsVpnUnicastNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1175,6 +1258,24 @@ impl From> for FlowSpecNlri { } } +impl PartialEq> for Ipv6FlowSpecNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv6FlowSpecNlri) -> bool { + self.0 == other.0 + } +} + +impl PartialEq> for Ipv6FlowSpecAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &Ipv6FlowSpecAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for Ipv6FlowSpecNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1182,6 +1283,7 @@ impl fmt::Display for Ipv6FlowSpecNlri { } +/* impl Ipv6UnicastAddpathNlri { pub fn iter<'a, O, P>(parser: Parser<'a, P>) -> NlriIter<'a, O, P, Self> where @@ -1201,12 +1303,13 @@ impl Ipv4MplsUnicastNlri { NlriIter::ipv4_mplsunicast(parser) } } +*/ //------------ L2Vpn ---------------------------------------------------------- //--- L2VpnVpls -#[derive(Clone, Debug, Hash)] +#[derive(Clone, Debug, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct L2VpnVplsNlri(VplsNlri); @@ -1232,6 +1335,12 @@ where } } +impl PartialEq for L2VpnVplsAddpathNlri { + fn eq(&self, other: &L2VpnVplsAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for L2VpnVplsNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -1266,6 +1375,24 @@ where } } +impl PartialEq> for L2VpnEvpnNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &L2VpnEvpnNlri) -> bool { + self.0 == other.0 + } +} + +impl PartialEq> for L2VpnEvpnAddpathNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &L2VpnEvpnAddpathNlri) -> bool { + self.0 == other.0 + } +} + impl fmt::Display for L2VpnEvpnNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) diff --git a/src/bgp/nlri/evpn.rs b/src/bgp/nlri/evpn.rs index 0ee7cdd7..5e4e0f56 100644 --- a/src/bgp/nlri/evpn.rs +++ b/src/bgp/nlri/evpn.rs @@ -54,6 +54,16 @@ impl EvpnNlri { } } +impl PartialEq> for EvpnNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &EvpnNlri) -> bool { + self.route_type == other.route_type && + self.raw.as_ref() == other.raw.as_ref() + } +} + impl fmt::Display for EvpnNlri { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "EVPN-{}", self.route_type) diff --git a/src/bgp/nlri/flowspec.rs b/src/bgp/nlri/flowspec.rs index 391eff82..9927c3dd 100644 --- a/src/bgp/nlri/flowspec.rs +++ b/src/bgp/nlri/flowspec.rs @@ -74,6 +74,17 @@ impl FlowSpecNlri { } } +impl PartialEq> for FlowSpecNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &FlowSpecNlri) -> bool { + self.afi == other.afi && + self.raw.as_ref() == other.raw.as_ref() + } +} + + impl fmt::Display for FlowSpecNlri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "FLOWSPEC-NLRI") diff --git a/src/bgp/nlri/routetarget.rs b/src/bgp/nlri/routetarget.rs index e77e2a24..a89b7e96 100644 --- a/src/bgp/nlri/routetarget.rs +++ b/src/bgp/nlri/routetarget.rs @@ -28,6 +28,15 @@ impl RouteTargetNlri { } } +impl PartialEq> for RouteTargetNlri +where Octs: AsRef<[u8]>, + Other: AsRef<[u8]> +{ + fn eq(&self, other: &RouteTargetNlri) -> bool { + self.raw.as_ref() == other.raw.as_ref() + } +} + impl fmt::Display for RouteTargetNlri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ROUTE-TARGET-NLRI") From a6b0399be24b92fb31ffb57d05662fb724068a90 Mon Sep 17 00:00:00 2001 From: Luuk Hendriks Date: Tue, 26 Mar 2024 13:32:23 +0100 Subject: [PATCH 96/96] Fix test --- src/bgp/message/update_builder.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bgp/message/update_builder.rs b/src/bgp/message/update_builder.rs index ecc5b9c4..5a5345ee 100644 --- a/src/bgp/message/update_builder.rs +++ b/src/bgp/message/update_builder.rs @@ -2588,8 +2588,12 @@ mod tests { Vec::new() ).unwrap(); - for w in original.typed_withdrawals::<_, Ipv4UnicastNlri>().unwrap().unwrap() { - builder.add_withdrawal(w.unwrap()).unwrap(); + if let Ok(Some(iter)) = + original.typed_withdrawals::<_, Ipv4UnicastNlri>() + { + for a in iter { + builder.add_withdrawal(a.unwrap()).unwrap(); + } } if let Ok(Some(iter)) =