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())?; } _ => {}