From 5818ec4956f4f546dbeae5f53884e1e5b38d7a09 Mon Sep 17 00:00:00 2001 From: Rayhaan Jaufeerally Date: Thu, 23 Feb 2023 01:07:46 +0100 Subject: [PATCH] Add capability from rfc8950. Just the basic parser, still needs to be integrated into the rest of the code for the OPEN message parsing, and then the rest of the MP Reach/Unreach NLRI parsing needs to be added. --- bgpd/src/bgp_packet/capabilities.rs | 83 ++++++++++++++++++++++++++++- bgpd/src/bgp_packet/constants.rs | 21 ++++++++ bgpd/src/route_client/main.rs | 1 - bgpd/src/server/peer.rs | 2 + 4 files changed, 105 insertions(+), 2 deletions(-) diff --git a/bgpd/src/bgp_packet/capabilities.rs b/bgpd/src/bgp_packet/capabilities.rs index e319a36..05ab4c1 100644 --- a/bgpd/src/bgp_packet/capabilities.rs +++ b/bgpd/src/bgp_packet/capabilities.rs @@ -201,7 +201,7 @@ pub mod BGPCapabilityTypeValues { pub const ROUTE_REFRESH_BGP4: BGPCapabilityType = BGPCapabilityType(2); /// Outbound Route Filtering Capability [RFC5291] pub const OUTBOUND_ROUTE_FILTERING: BGPCapabilityType = BGPCapabilityType(3); - /// Extended Next Hop Encoding [RFC5549] + /// Extended Next Hop Encoding [RFC8950] pub const EXTENDED_NEXT_HOP: BGPCapabilityType = BGPCapabilityType(5); /// BGP Extended Message [RFC8654] pub const EXTENDED_MESSAGE: BGPCapabilityType = BGPCapabilityType(6); @@ -248,6 +248,7 @@ impl ReadablePacket for BGPCapability { })(buf)?; (buf, BGPCapabilityValue::Multiprotocol(cap)) } + // TODO: Add extended next hop. BGPCapabilityTypeValues::ROUTE_REFRESH_BGP4 => { let (buf, _) = be_u8(buf)?; let (buf, cap) = nom::multi::length_value(be_u8, |i| { @@ -616,12 +617,81 @@ impl fmt::Display for GracefulRestartCapability { } } +// RFC8950 - Advertising IPv4 NLRI with IPv6 next hop. +// GracefulRestartPayload represents the contents of the graceful restart cap. +#[derive(Clone, Debug, PartialEq)] +pub struct ExtendedNextHopEncodingCapability { + pub afi_safi_nhafi: Vec<( + AddressFamilyIdentifier, + SubsequentAddressFamilyIdentifier, + AddressFamilyIdentifier, + )>, +} + +impl WritablePacket for ExtendedNextHopEncodingCapability { + fn to_wire(&self, _ctx: &ParserContext) -> Result, &'static str> { + Ok(self + .afi_safi_nhafi + .iter() + .map(|e| { + Into::>::into(e.0) + .into_iter() + .chain(vec![0x00, Into::::into(e.1)].into_iter()) + .chain(Into::>::into(e.2).into_iter()) + .collect::>() + }) + .flatten() + .collect::>()) + } + + fn wire_len(&self, _ctx: &ParserContext) -> Result { + Ok((self.afi_safi_nhafi.len() * 6) as u16) + } +} + +impl ReadablePacket for ExtendedNextHopEncodingCapability { + fn from_wire<'a>( + ctx: &ParserContext, + buf: &'a [u8], + ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> + where + Self: Sized, + { + let (buf, tuples) = nom::combinator::complete(nom::multi::many0(nom::sequence::tuple(( + |i| AddressFamilyIdentifier::from_wire(ctx, i), + |i| { + let (buf, _) = be_u8(i)?; // Eat the 0 byte. + SubsequentAddressFamilyIdentifier::from_wire(ctx, buf) + }, + |i| AddressFamilyIdentifier::from_wire(ctx, i), + ))))(buf)?; + + IResult::Ok(( + buf, + Self { + afi_safi_nhafi: tuples, + }, + )) + } +} + +impl fmt::Display for ExtendedNextHopEncodingCapability { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ExtendednextHopEncodingCapability [")?; + for entry in &self.afi_safi_nhafi { + write!(f, "afi: {}, safi: {}, nhafi: {}", entry.0, entry.1, entry.2)?; + } + write!(f, "]") + } +} + #[cfg(test)] mod tests { use super::BGPCapability; use super::BGPCapabilityTypeValues; use super::BGPCapabilityValue; + use super::ExtendedNextHopEncodingCapability; use super::FourByteASNCapability; use super::OpenOption; use crate::bgp_packet::constants::AddressFamilyIdentifier::Ipv6; @@ -657,4 +727,15 @@ mod tests { let expected_str = "[OpenOption { option_type: BGPOpenOptionType(2), oval: Capabilities(OpenOptionCapabilities { caps: [BGPCapability { cap_type: BGPCapabilityType(1), val: Multiprotocol(MultiprotocolCapability { afi: Ipv4, safi: Unicast }) }] }) }, OpenOption { option_type: BGPOpenOptionType(2), oval: Capabilities(OpenOptionCapabilities { caps: [BGPCapability { cap_type: BGPCapabilityType(128), val: UnknownCapability(UnknownCapability { cap_code: 128, payload: [] }) }] }) }, OpenOption { option_type: BGPOpenOptionType(2), oval: Capabilities(OpenOptionCapabilities { caps: [BGPCapability { cap_type: BGPCapabilityType(2), val: RouteRefresh(RouteRefreshCapability) }] }) }, OpenOption { option_type: BGPOpenOptionType(2), oval: Capabilities(OpenOptionCapabilities { caps: [BGPCapability { cap_type: BGPCapabilityType(70), val: UnknownCapability(UnknownCapability { cap_code: 70, payload: [] }) }] }) }, OpenOption { option_type: BGPOpenOptionType(2), oval: Capabilities(OpenOptionCapabilities { caps: [BGPCapability { cap_type: BGPCapabilityType(65), val: FourByteASN(FourByteASNCapability { asn: 42 }) }] }) }]"; assert_eq!(format!("{:?}", result), expected_str); } + + #[test] + fn test_extended_next_hop_encoding_capability() { + let bytes: Vec = vec![0x00, 0x01, 0x00, 0x01, 0x00, 0x02]; + let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); + let (_, cap) = ExtendedNextHopEncodingCapability::from_wire(ctx, &bytes).unwrap(); + + let expected_str = + "ExtendednextHopEncodingCapability [afi: Ipv4, safi: Unicast, nhafi: Ipv6]"; + assert_eq!(expected_str, cap.to_string()); + } } diff --git a/bgpd/src/bgp_packet/constants.rs b/bgpd/src/bgp_packet/constants.rs index d88947e..dc1379f 100644 --- a/bgpd/src/bgp_packet/constants.rs +++ b/bgpd/src/bgp_packet/constants.rs @@ -50,6 +50,15 @@ impl TryFrom for AddressFamilyIdentifier { } } +impl Into> for AddressFamilyIdentifier { + fn into(self) -> Vec { + match self { + Self::Ipv4 => 1_u16.to_be_bytes().to_vec(), + Self::Ipv6 => 2_u16.to_be_bytes().to_vec(), + } + } +} + /// This parser for AFI makes it easier to write the other message parsers. impl ReadablePacket for AddressFamilyIdentifier { fn from_wire<'a>( @@ -80,6 +89,9 @@ impl fmt::Display for AddressFamilyIdentifier { pub enum SubsequentAddressFamilyIdentifier { Unicast, Multicast, + NlriWithMpls, + MplsLabeledVPN, + MulticastMplsVpn, } impl Into for SubsequentAddressFamilyIdentifier { @@ -87,6 +99,9 @@ impl Into for SubsequentAddressFamilyIdentifier { match self { Self::Unicast => 1, Self::Multicast => 2, + Self::NlriWithMpls => 4, + Self::MplsLabeledVPN => 128, + Self::MulticastMplsVpn => 129, } } } @@ -97,6 +112,9 @@ impl TryFrom for SubsequentAddressFamilyIdentifier { match i { 1 => Ok(Self::Unicast), 2 => Ok(Self::Multicast), + 4 => Ok(Self::NlriWithMpls), + 128 => Ok(Self::MplsLabeledVPN), + 129 => Ok(Self::MulticastMplsVpn), _ => Err(std::io::Error::new( ErrorKind::InvalidInput, format!("Unknown SAFI value: {} ", i), @@ -125,6 +143,9 @@ impl fmt::Display for SubsequentAddressFamilyIdentifier { match self { Self::Unicast => write!(f, "Unicast"), Self::Multicast => write!(f, "Multicast"), + Self::NlriWithMpls => write!(f, "NlriWithMpls"), + Self::MulticastMplsVpn => write!(f, "MulticastMplsVpn"), + Self::MplsLabeledVPN => write!(f, "MplsLabeledVpn"), } } } diff --git a/bgpd/src/route_client/main.rs b/bgpd/src/route_client/main.rs index bd561d3..808458f 100644 --- a/bgpd/src/route_client/main.rs +++ b/bgpd/src/route_client/main.rs @@ -13,7 +13,6 @@ // limitations under the License. use bgpd::route_client::netlink::NetlinkConnector; -use bgpd::route_client::southbound_interface::DummyVerifier; use bgpd::route_client::southbound_interface::SouthboundInterface; use clap::Parser; use log::trace; diff --git a/bgpd/src/server/peer.rs b/bgpd/src/server/peer.rs index 547731a..8f9db00 100644 --- a/bgpd/src/server/peer.rs +++ b/bgpd/src/server/peer.rs @@ -1276,6 +1276,7 @@ where for attr in &u.path_attributes { match attr { PathAttribute::MPReachNLRIPathAttribute(nlri) => { + // TODO: Determine which AFI/SAFI this update corresponds to. let nexthop_res = nlri.clone().nexthop_to_v6(); // TODO: How do we pick whether to use the global or LLNH? if let Some((global, _llnh_opt)) = nexthop_res { @@ -1287,6 +1288,7 @@ where } } PathAttribute::MPUnreachNLRIPathAttribute(nlri) => { + // TODO: Determine which AFI/SAFI this update corresponds to. self.process_withdrawals(nlri.nlris.clone())?; } _ => {}