// Copyright 2021 Rayhaan Jaufeerally. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::bgp_packet::constants::AddressFamilyIdentifier; use crate::bgp_packet::constants::SubsequentAddressFamilyIdentifier; use crate::bgp_packet::nlri::NLRI; use crate::bgp_packet::traits::BGPParserError; use crate::bgp_packet::traits::ParserContext; use crate::bgp_packet::traits::ReadablePacket; use crate::bgp_packet::traits::WritablePacket; use byteorder::ByteOrder; use byteorder::NetworkEndian; use nom::number::complete::{be_u16, be_u32, be_u8}; use nom::Err::Failure; use nom::IResult; use serde::Serialize; use std::convert::TryInto; use std::fmt; use std::net::Ipv4Addr; use std::net::Ipv6Addr; /// PathAttribute represents path attributes in a BGP Update message. #[derive(Debug, PartialEq, Clone, Serialize)] pub enum PathAttribute { OriginPathAttribute(OriginPathAttribute), ASPathAttribute(ASPathAttribute), NextHopPathAttribute(NextHopPathAttribute), MultiExitDiscPathAttribute(MultiExitDiscPathAttribute), LocalPrefPathAttribute(LocalPrefPathAttribute), AtomicAggregatePathAttribute(AtomicAggregatePathAttribute), AggregatorPathAttribute(AggregatorPathAttribute), CommunitiesPathAttribute(CommunitiesPathAttribute), ExtendedCommunitiesPathAttribute(ExtendedCommunitiesPathAttribute), LargeCommunitiesPathAttribute(LargeCommunitiesPathAttribute), MPReachNLRIPathAttribute(MPReachNLRIPathAttribute), MPUnreachNLRIPathAttribute(MPUnreachNLRIPathAttribute), UnknownPathAttribute(Vec), } const PATH_ATTRIBUTE_FLAG_OPTONAL: u8 = 0x80; // when set to 1: optional, well-known: 0. const PATH_ATTRIBUTE_FLAG_TRANSITIVE: u8 = 0x40; // when set to 1: transitive, non-transitive: 0. const _PATH_ATTRIBUTE_FLAG_PARTIAL: u8 = 0x20; // when set to 1: partial, complete: 0. const PATH_ATTRIBUTE_EXTENDED_LENGTH: u8 = 0x10; // when set to 1: length is u16, otherwise when 0 length is u8. // For well known attributes the transitive bit MUST be set to 1. // Write the type, length and call the child serializer impl WritablePacket for PathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { Ok(match self { PathAttribute::OriginPathAttribute(a) => { let typ: u8 = 1; let flag: u8 = PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::ASPathAttribute(a) => { let typ: u8 = 2; let flag: u8 = PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::NextHopPathAttribute(a) => { let typ: u8 = 3; let flag: u8 = PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::MultiExitDiscPathAttribute(a) => { let typ: u8 = 4; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::LocalPrefPathAttribute(a) => { let typ: u8 = 5; let flag: u8 = 0; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::AtomicAggregatePathAttribute(a) => { let typ: u8 = 6; let flag: u8 = 0; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::AggregatorPathAttribute(a) => { let typ: u8 = 7; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL | PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::CommunitiesPathAttribute(a) => { let typ: u8 = 8; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL | PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::MPReachNLRIPathAttribute(a) => { let typ: u8 = 14; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::MPUnreachNLRIPathAttribute(a) => { let typ: u8 = 15; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::ExtendedCommunitiesPathAttribute(a) => { let typ: u8 = 16; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL | PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::LargeCommunitiesPathAttribute(a) => { let typ: u8 = 32; let flag: u8 = PATH_ATTRIBUTE_FLAG_OPTONAL | PATH_ATTRIBUTE_FLAG_TRANSITIVE; let len: u8 = a.wire_len(ctx)? as u8; [vec![flag, typ, len], a.to_wire(ctx)?].concat() } PathAttribute::UnknownPathAttribute(u) => u.to_vec(), }) } fn wire_len(&self, ctx: &ParserContext) -> Result { Ok(match self { PathAttribute::OriginPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::ASPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::NextHopPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::MultiExitDiscPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::LocalPrefPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::AtomicAggregatePathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::AggregatorPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::CommunitiesPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::MPReachNLRIPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::MPUnreachNLRIPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::ExtendedCommunitiesPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::LargeCommunitiesPathAttribute(a) => 3 + a.wire_len(ctx)?, PathAttribute::UnknownPathAttribute(u) => u.len() as u16, }) } } // Read the type, length and dispatch accordingly. impl ReadablePacket for PathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, flag) = be_u8(buf)?; let (buf, typ) = be_u8(buf)?; let mut len8 = 0; // to preserve the 1 octet length for the unknown option. let (buf, len) = match flag & PATH_ATTRIBUTE_EXTENDED_LENGTH { PATH_ATTRIBUTE_EXTENDED_LENGTH => be_u16(buf)?, _ => { let (b, t) = be_u8(buf)?; len8 = t; (b, t as u16) } }; // Explicitly read the attribute here and pass the attribute only buffer to the child parser. let (buf, pa_buf) = nom::bytes::complete::take(len)(buf)?; let (_, res): (_, PathAttribute) = match typ { 1 => { let (b, r) = OriginPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::OriginPathAttribute(r)) } 2 => { let (b, r) = ASPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::ASPathAttribute(r)) } 3 => { let (b, r) = NextHopPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::NextHopPathAttribute(r)) } 4 => { let (b, r) = MultiExitDiscPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::MultiExitDiscPathAttribute(r)) } 5 => { let (b, r) = LocalPrefPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::LocalPrefPathAttribute(r)) } 6 => { let (b, r) = AtomicAggregatePathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::AtomicAggregatePathAttribute(r)) } 7 => { let (b, r) = AggregatorPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::AggregatorPathAttribute(r)) } 8 => { let (b, r) = CommunitiesPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::CommunitiesPathAttribute(r)) } 14 => { let (b, r) = MPReachNLRIPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::MPReachNLRIPathAttribute(r)) } 15 => { let (b, r) = MPUnreachNLRIPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::MPUnreachNLRIPathAttribute(r)) } 16 => { let (b, r) = ExtendedCommunitiesPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::ExtendedCommunitiesPathAttribute(r)) } 32 => { let (b, r) = LargeCommunitiesPathAttribute::from_wire(ctx, pa_buf)?; (b, PathAttribute::LargeCommunitiesPathAttribute(r)) } _ => { let mut tmp = vec![flag, typ]; if len8 != 0 { tmp.push(len8); } else { let mut t = [0u8; 2]; byteorder::NetworkEndian::write_u16(&mut t, len); tmp.append(&mut t.to_vec()); } tmp.extend(pa_buf.to_vec()); (&[], PathAttribute::UnknownPathAttribute(tmp)) } }; Ok((buf, res)) } } impl fmt::Display for PathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PathAttribute::OriginPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::ASPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::NextHopPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::MultiExitDiscPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::LocalPrefPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::AtomicAggregatePathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::AggregatorPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::CommunitiesPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::MPReachNLRIPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::MPUnreachNLRIPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::ExtendedCommunitiesPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::LargeCommunitiesPathAttribute(a) => std::fmt::Display::fmt(&a, f), PathAttribute::UnknownPathAttribute(a) => { write!(f, "unknown PathAttribute, bytes: {:?}", a) } } } } // Path attribute implementations. /// Origin path attribute is a mandatory attribute defined in RFC4271. #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct OriginPathAttribute(pub u8); pub mod origin_path_attribute_values { use super::OriginPathAttribute; pub const IGP: OriginPathAttribute = OriginPathAttribute(0); pub const EGP: OriginPathAttribute = OriginPathAttribute(1); pub const UNKNOWN: OriginPathAttribute = OriginPathAttribute(2); } impl ReadablePacket for OriginPathAttribute { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, opa) = be_u8(buf)?; Ok((buf, OriginPathAttribute(opa))) } } impl WritablePacket for OriginPathAttribute { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { Ok(vec![self.0]) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(1) } } impl fmt::Display for OriginPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use origin_path_attribute_values::*; match self { &IGP => write!(f, "Origin: IGP"), &EGP => write!(f, "Origin: EGP"), &UNKNOWN => write!(f, "Origin: Unknown"), _ => write!(f, "Origin: invalid value"), } } } /// ASPathAttribute is a well-known mandatory attribute that contains a list of TLV encoded path /// segments. Type is either 1 for AS_SET or 2 for AS_SEQUENCE, length is a 1 octet field /// containing the number of ASNS and the value contains the ASNs. This is defined in Section 4.3 /// of RFC4271. #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct ASPathAttribute { pub segments: Vec, } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct ASPathSegment { /// ordered is true when representing an AS_SEQUENCE, andd false when /// representing an AS_SET. pub ordered: bool, /// Path is the list of ASNs. pub path: Vec, } impl ASPathAttribute { pub fn from_asns(asns: Vec) -> PathAttribute { let segment = ASPathSegment { ordered: true, path: asns, }; PathAttribute::ASPathAttribute(ASPathAttribute { segments: vec![segment], }) } } impl ReadablePacket for ASPathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let parse_segment = |ctx: &ParserContext, buf: &'a [u8]| -> IResult<&'a [u8], ASPathSegment, BGPParserError<&'a [u8]>> { let (buf, typ) = be_u8(buf)?; let (buf, len) = be_u8(buf)?; let (buf, asns): (_, Vec) = match ctx.four_octet_asn { Some(true) => nom::multi::many_m_n(len as usize, len as usize, be_u32)(buf)?, Some(false) => { let (buf, asn_u16) = nom::multi::many_m_n(len as usize, len as usize, be_u16)(buf)?; let mut asn_u32: Vec = Vec::new(); for asn in asn_u16 { asn_u32.push(asn as u32); } (buf, asn_u32) } None => { return Err(Failure(BGPParserError::CustomText( "Can't parse ASPath without four_octet_asn being set".to_owned(), ))); } }; Ok(( buf, ASPathSegment { ordered: (typ == 2), path: asns, }, )) }; let (buf, segments): (_, Vec) = nom::multi::many0(|buf: &'a [u8]| parse_segment(ctx, buf))(buf)?; Ok((buf, ASPathAttribute { segments })) } } impl WritablePacket for ASPathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { if !ctx.four_octet_asn.unwrap_or(false) { return Err( "Can't use ASPathAttribute to communicate with legacy peer, use AS4PathAttribute", ); } let mut wire: Vec = Vec::new(); for segment in &self.segments { wire.push(if segment.ordered { 2 } else { 1 }); wire.push( segment .path .len() .try_into() .map_err(|_| "ASPath segment too long")?, ); for asn in &segment.path { let mut tmp: Vec = vec![0u8; 4]; NetworkEndian::write_u32(&mut tmp, *asn); wire.append(&mut tmp); } } Ok(wire) } fn wire_len(&self, _: &ParserContext) -> Result { let mut ctr: u16 = 0; for segment in &self.segments { ctr += 2; ctr += (4 * segment.path.len()) as u16; } Ok(ctr) } } impl fmt::Display for ASPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AS Path: {{ ")?; for segment in &self.segments { write!(f, "Segment [ ")?; if segment.ordered { write!(f, "Type: AS_SEGMENT ")? } else { write!(f, "Type: AS_SET ")? }; for asn in &segment.path { write!(f, "{} ", asn)?; } write!(f, " ]")?; } write!(f, "] }}") } } // TODO: AS4 path attribute // Per RFC 6793 the AS4 path attribute is for legacy BGP speakers to propagate // 4 octet ASNs in update messages. #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct AS4PathAttribute { pub ordered: bool, pub path: Vec, } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct NextHopPathAttribute(pub Ipv4Addr); impl ReadablePacket for NextHopPathAttribute { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (_, ip_u32) = be_u32(buf)?; let nexthop = Ipv4Addr::from(ip_u32); Ok((buf, NextHopPathAttribute(nexthop))) } } impl WritablePacket for NextHopPathAttribute { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { return Ok(self.0.octets().to_vec()); } fn wire_len(&self, _: &ParserContext) -> Result { return Ok(4); } } impl fmt::Display for NextHopPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NextHop: {}", self.0) } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct MultiExitDiscPathAttribute(pub u32); impl ReadablePacket for MultiExitDiscPathAttribute { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, val) = be_u32(buf)?; Ok((buf, MultiExitDiscPathAttribute(val))) } } impl WritablePacket for MultiExitDiscPathAttribute { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { let mut buf: Vec = vec![0u8; 4]; byteorder::NetworkEndian::write_u32(&mut buf, self.0); Ok(buf) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(4) } } impl fmt::Display for MultiExitDiscPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "MultiExitDisc: {}", self.0) } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct LocalPrefPathAttribute(pub u32); impl ReadablePacket for LocalPrefPathAttribute { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, val) = be_u32(buf)?; Ok((buf, LocalPrefPathAttribute(val))) } } impl WritablePacket for LocalPrefPathAttribute { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { let mut buf: Vec = vec![0u8; 4]; byteorder::NetworkEndian::write_u32(&mut buf, self.0); Ok(buf) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(4) } } impl fmt::Display for LocalPrefPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "LocalPref: {}", self.0) } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct AtomicAggregatePathAttribute {} impl ReadablePacket for AtomicAggregatePathAttribute { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { Ok((buf, AtomicAggregatePathAttribute {})) } } impl WritablePacket for AtomicAggregatePathAttribute { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { Ok(vec![]) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(0) } } impl fmt::Display for AtomicAggregatePathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomicAggregate: present") } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct AggregatorPathAttribute { pub asn: u32, pub ip: Ipv4Addr, } // TODO: Support non AS4 peers. impl ReadablePacket for AggregatorPathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { if !ctx.four_octet_asn.is_some() { return Err(Failure(BGPParserError::CustomText( "Non four byte ASN not supported (AggregatorPathAttribute from_wire)".to_string(), ))); } let (buf, asn) = be_u32(buf)?; let (buf, ip) = nom::bytes::complete::take(4u8)(buf)?; let correct: [u8; 4] = ip.try_into().expect("wrong slice len"); Ok(( buf, AggregatorPathAttribute { asn, ip: Ipv4Addr::from(correct), }, )) } } impl WritablePacket for AggregatorPathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { if !ctx.four_octet_asn.is_some() { panic!("Non four byte ASN not supported (AggregatorPathAttribute from_wire)"); } let mut buf: Vec = vec![0u8; 4]; byteorder::NetworkEndian::write_u32(&mut buf, self.asn); buf.extend(self.ip.octets().to_vec()); Ok(buf) } fn wire_len(&self, ctx: &ParserContext) -> Result { if !ctx.four_octet_asn.is_some() { panic!("Non four byte ASN not supported (AggregatorPathAttribute from_wire)"); } Ok(8) } } impl fmt::Display for AggregatorPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Aggregator: asn: {}, ip: {}", self.asn, self.ip) } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct CommunitiesPathAttribute { pub values: Vec, } impl ReadablePacket for CommunitiesPathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, values): (_, Vec) = nom::multi::many0(|i| CommunitiesPayload::from_wire(ctx, i))(buf)?; Ok((buf, CommunitiesPathAttribute { values })) } } impl WritablePacket for CommunitiesPathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { let mut buf = vec![]; for val in &self.values { buf.extend(val.to_wire(ctx)?); } Ok(buf) } fn wire_len(&self, ctx: &ParserContext) -> Result { let mut ttl: u16 = 0; for val in &self.values { ttl += val.wire_len(ctx)?; } Ok(ttl) } } impl fmt::Display for CommunitiesPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Communities: [ ")?; for c in &self.values { write!(f, " {}, ", c)?; } write!(f, " ] ") } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct CommunitiesPayload { pub asn: u16, pub payload: u16, } impl ReadablePacket for CommunitiesPayload { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, asn): (_, u16) = be_u16(buf)?; let (buf, payload): (_, u16) = be_u16(buf)?; Ok((buf, CommunitiesPayload { asn, payload })) } } impl WritablePacket for CommunitiesPayload { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { let mut buf = vec![0u8; 4]; byteorder::NetworkEndian::write_u16(&mut buf[0..2], self.asn); byteorder::NetworkEndian::write_u16(&mut buf[2..4], self.payload); Ok(buf) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(4) } } impl fmt::Display for CommunitiesPayload { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.asn, self.payload) } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct ExtendedCommunitiesPathAttribute { pub t_high: u8, // TODO: Handle t_low and subtypes of the Extended Communities attribute as defined in rfc4360. pub value: Vec, } impl ReadablePacket for ExtendedCommunitiesPathAttribute { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, t_high) = be_u8(buf)?; let (buf, value) = nom::bytes::complete::take(7u8)(buf)?; Ok(( buf, ExtendedCommunitiesPathAttribute { t_high, value: value.to_vec(), }, )) } } impl WritablePacket for ExtendedCommunitiesPathAttribute { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { if !self.value.len() == 7 { return Err("ExtendedCommunitiesPathAttribute value length != 7"); } Ok(vec![vec![self.t_high], self.value.to_owned()].concat()) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(8) } } impl fmt::Display for ExtendedCommunitiesPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ExtendedCommunities: {} {:?}", self.t_high, self.value) } } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct LargeCommunitiesPathAttribute { pub values: Vec, } #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct LargeCommunitiesPayload { pub global_admin: u32, pub ld1: u32, pub ld2: u32, } impl ReadablePacket for LargeCommunitiesPayload { fn from_wire<'a>( _: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, global_admin) = be_u32(buf)?; let (buf, ld1) = be_u32(buf)?; let (buf, ld2) = be_u32(buf)?; Ok(( buf, LargeCommunitiesPayload { global_admin, ld1, ld2, }, )) } } impl WritablePacket for LargeCommunitiesPayload { fn to_wire(&self, _: &ParserContext) -> Result, &'static str> { let mut buf = vec![0u8; 12]; byteorder::NetworkEndian::write_u32(&mut buf[0..4], self.global_admin); byteorder::NetworkEndian::write_u32(&mut buf[4..8], self.ld1); byteorder::NetworkEndian::write_u32(&mut buf[8..12], self.ld2); Ok(buf) } fn wire_len(&self, _: &ParserContext) -> Result { Ok(12) } } impl fmt::Display for LargeCommunitiesPayload { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}:{}", self.global_admin, self.ld1, self.ld2) } } impl ReadablePacket for LargeCommunitiesPathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, values): (_, Vec) = nom::multi::many0(|i| LargeCommunitiesPayload::from_wire(ctx, i))(buf)?; Ok((buf, LargeCommunitiesPathAttribute { values })) } } impl WritablePacket for LargeCommunitiesPathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { let mut buf = vec![]; for val in &self.values { buf.extend(val.to_wire(ctx)?); } Ok(buf) } fn wire_len(&self, ctx: &ParserContext) -> Result { let mut ttl: u16 = 0; for val in &self.values { ttl += val.wire_len(ctx)?; } Ok(ttl) } } impl fmt::Display for LargeCommunitiesPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "LargeCommunities: [")?; for c in &self.values { write!(f, " {}, ", c)?; } write!(f, "]") } } /// MPReachPathAattribute implements the MultiProtocol extensions to BGP (RFC4760) #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct MPReachNLRIPathAttribute { pub afi: AddressFamilyIdentifier, pub safi: SubsequentAddressFamilyIdentifier, pub nexthop: Vec, pub nlris: Vec, } impl MPReachNLRIPathAttribute { // https://datatracker.ietf.org/doc/html/rfc2545 describes what the nexthop // field can contain. Returns a tuple of (global_nh, linklocal_nh) pub fn nexthop_to_v6(self) -> Option<(Ipv6Addr, Option)> { return match self.nexthop.len() { 16 => { let nh_bytes: [u8; 16] = self.nexthop.try_into().unwrap(); Some((Ipv6Addr::from(nh_bytes), None)) } 32 => { let global_nh_bytes: [u8; 16] = self.nexthop[0..16].try_into().unwrap(); let llnh_bytes: [u8; 16] = self.nexthop[16..32].try_into().unwrap(); Some(( Ipv6Addr::from(global_nh_bytes), Some(Ipv6Addr::from(llnh_bytes)), )) } _ => None, }; } } impl ReadablePacket for MPReachNLRIPathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, afi) = AddressFamilyIdentifier::from_wire(ctx, buf)?; let (buf, safi) = SubsequentAddressFamilyIdentifier::from_wire(ctx, buf)?; let (buf, nexthop): (_, Vec) = nom::multi::length_value(be_u8, nom::multi::many0(be_u8))(buf)?; // Reserved field set to 0. let (buf, _) = be_u8(buf)?; let (buf, nlris): (_, Vec) = nom::multi::many0(|i| NLRI::from_wire(ctx, i))(buf)?; Ok(( buf, MPReachNLRIPathAttribute { afi, safi, nexthop, nlris, }, )) } } impl WritablePacket for MPReachNLRIPathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { let mut buf = vec![0u8; 4]; byteorder::NetworkEndian::write_u16(&mut buf[0..2], self.afi.into()); buf[2] = self.safi.into(); buf[3] = self.nexthop.len() as u8; buf.extend(&self.nexthop); // Reserved field set to 0. buf.push(0); for nlri in &self.nlris { buf.extend(nlri.to_wire(ctx)?); } Ok(buf) } fn wire_len(&self, ctx: &ParserContext) -> Result { let mut ctr: u16 = 0; ctr += 4; // afi + safi + the (len of nexthop) octet ctr += self.nexthop.len() as u16; ctr += 1; // Reserved octet. for nlri in &self.nlris { ctr += nlri.wire_len(ctx)?; } Ok(ctr) } } impl fmt::Display for MPReachNLRIPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "MPReachNLRI: afi: {} safi: {}, nexthop: {:?} nlris: [", self.afi, self.safi, self.nexthop )?; for nlri in &self.nlris { std::fmt::Display::fmt(nlri, f)?; } write!(f, "]") } } /// MPUnreachNLRIPathAttribute represents a MultiProtocol prefix withdrawal. #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct MPUnreachNLRIPathAttribute { pub afi: AddressFamilyIdentifier, pub safi: SubsequentAddressFamilyIdentifier, pub nlris: Vec, } impl ReadablePacket for MPUnreachNLRIPathAttribute { fn from_wire<'a>( ctx: &ParserContext, buf: &'a [u8], ) -> IResult<&'a [u8], Self, BGPParserError<&'a [u8]>> { let (buf, afi) = AddressFamilyIdentifier::from_wire(ctx, buf)?; let (buf, safi) = SubsequentAddressFamilyIdentifier::from_wire(ctx, buf)?; let (buf, nlris): (_, Vec) = nom::multi::many0(|i| NLRI::from_wire(ctx, i))(buf)?; Ok((buf, MPUnreachNLRIPathAttribute { afi, safi, nlris })) } } impl WritablePacket for MPUnreachNLRIPathAttribute { fn to_wire(&self, ctx: &ParserContext) -> Result, &'static str> { let mut buf = vec![0u8; 3]; NetworkEndian::write_u16(&mut buf[0..2], self.afi.into()); buf[2] = self.safi.into(); for nlri in &self.nlris { buf.extend(nlri.to_wire(ctx)?); } Ok(buf) } fn wire_len(&self, ctx: &ParserContext) -> Result { let mut ctr: u16 = 0; ctr += 3; for nlri in &self.nlris { ctr += nlri.wire_len(ctx)?; } Ok(ctr) } } impl fmt::Display for MPUnreachNLRIPathAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "MPUnreachNLRI: afi: {} safi: {}, nlris: [", self.afi, self.safi )?; for nlri in &self.nlris { std::fmt::Display::fmt(nlri, f)?; } write!(f, "]") } } #[cfg(test)] mod tests { use std::net::Ipv4Addr; use crate::bgp_packet::constants::AddressFamilyIdentifier::Ipv6; use crate::bgp_packet::constants::SubsequentAddressFamilyIdentifier::Unicast; use crate::bgp_packet::traits::ParserContext; use crate::bgp_packet::traits::ReadablePacket; use crate::bgp_packet::traits::WritablePacket; use super::ASPathAttribute; use super::CommunitiesPathAttribute; use super::LargeCommunitiesPathAttribute; use super::LocalPrefPathAttribute; use super::MPReachNLRIPathAttribute; use super::MPUnreachNLRIPathAttribute; use super::MultiExitDiscPathAttribute; use super::NextHopPathAttribute; use super::PathAttribute; #[test] fn test_as_path_segment() { let as_path_bytes = &[ 0x02, 0x04, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x20, 0x1a, 0x00, 0x00, 0x78, 0xfc, ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = &ASPathAttribute::from_wire(ctx, as_path_bytes).unwrap(); let expected_aspath: Vec = vec![39540, 57118, 8218, 30972]; assert_eq!(result.1.segments.len(), 1); assert!(result.1.segments[0].ordered); assert_eq!(result.1.segments[0].path, expected_aspath); let wire = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, as_path_bytes); } #[test] fn test_as_path_multi_segment() { let as_path_bytes = &[ 0x02, 0x04, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x20, 0x1a, 0x00, 0x00, 0x78, 0xfc, 0x01, 0x02, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0xdf, 0x1e, ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = &ASPathAttribute::from_wire(ctx, as_path_bytes).unwrap(); let expected_aspath: Vec = vec![39540, 57118, 8218, 30972]; let expected_asset: Vec = vec![39540, 57118]; assert_eq!(result.1.segments.len(), 2); assert!(result.1.segments[0].ordered); assert_eq!(result.1.segments[0].path, expected_aspath); assert!(!result.1.segments[1].ordered); assert_eq!(result.1.segments[1].path, expected_asset); let wire = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, as_path_bytes); } #[test] fn test_next_hop_path_attribute() { let nh_bytes: &[u8] = &[192, 168, 1, 1]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = NextHopPathAttribute::from_wire(ctx, nh_bytes).unwrap(); assert_eq!(result.1 .0, "192.168.1.1".parse::().unwrap()); let wire = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, nh_bytes); assert_eq!(result.1.wire_len(ctx).unwrap(), wire.len() as u16); } #[test] fn test_multi_exit_discriminator_path_attribute() { let med_bytes: &[u8] = &[0xca, 0x00, 0x00, 0xbe]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = MultiExitDiscPathAttribute::from_wire(ctx, med_bytes).unwrap(); assert_eq!(result.1 .0, 3388997822); let wire = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, med_bytes); assert_eq!(result.1.wire_len(ctx).unwrap(), wire.len() as u16); } #[test] fn test_local_pref_path_attribute() { let local_pref_bytes: &[u8] = &[0xca, 0x00, 0x00, 0xbe]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = LocalPrefPathAttribute::from_wire(ctx, local_pref_bytes).unwrap(); assert_eq!(result.1 .0, 3388997822); let wire = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, local_pref_bytes); assert_eq!(result.1.wire_len(ctx).unwrap(), wire.len() as u16); } #[test] fn test_communities_path_attribute() { let communities_bytes: &[u8] = &[ 0x00, 0x00, 0x32, 0xbd, 0x00, 0x00, 0x41, 0x5f, 0x32, 0xe6, 0x00, 0x01, 0x32, 0xe6, 0x10, 0x73, 0x32, 0xe6, 0xca, 0xbd, 0x57, 0x54, 0x0b, 0xb8, 0x57, 0x54, 0x0b, 0xb9, 0x57, 0x54, 0x2b, 0x5c, 0x57, 0x54, 0xff, 0xe6, 0x57, 0x54, 0xff, 0xf1, 0x6f, 0xf7, 0xff, 0xf1, 0x73, 0xfb, 0x0f, 0xa0, 0x73, 0xfb, 0x0f, 0xc8, 0x9a, 0x74, 0x0f, 0xa0, 0x9a, 0x74, 0x0f, 0xb4, 0xdf, 0x1e, 0x07, 0xd0, 0xdf, 0x1e, 0x07, 0xe4, ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = CommunitiesPathAttribute::from_wire(ctx, communities_bytes).unwrap(); let expected_communities: Vec<(u16, u16)> = vec![ (0, 0x32bd), (0, 0x415f), (13030, 1), (13030, 4211), (13030, 51901), (22356, 3000), (22356, 3001), (22356, 11100), (22356, 65510), (22356, 65521), (28663, 65521), (29691, 4000), (29691, 4040), (39540, 4000), (39540, 4020), (57118, 2000), (57118, 2020), ]; assert_eq!(result.1.values.len(), expected_communities.len()); for (i, community) in result.1.values.iter().enumerate() { assert_eq!(community.asn, expected_communities[i].0); assert_eq!(community.payload, expected_communities[i].1); } let wire: Vec = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, communities_bytes); assert_eq!(wire.len() as u16, result.1.wire_len(ctx).unwrap()); } #[test] fn test_large_communities_path_attribute() { let large_community_bytes: &[u8] = &[ 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0x1e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result = LargeCommunitiesPathAttribute::from_wire(ctx, large_community_bytes).unwrap(); assert_eq!(result.1.values.len(), 2); assert_eq!(result.1.values[0].global_admin, 57118); assert_eq!(result.1.values[0].ld1, 20); assert_eq!(result.1.values[0].ld2, 0); assert_eq!(result.1.values[1].global_admin, 57118); assert_eq!(result.1.values[1].ld1, 20); assert_eq!(result.1.values[1].ld2, 20); let wire: Vec = result.1.to_wire(ctx).unwrap(); assert_eq!(wire, large_community_bytes); assert_eq!(wire.len() as u16, result.1.wire_len(ctx).unwrap()); } #[test] fn test_mp_reach_nlri_path_attribute() { let mp_reach_bytes: &[u8] = &[ 0x00, 0x02, // IPv6 0x01, // Unicast 0x10, // Length of IPv6 nexthop 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, // nh addr part one 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, // nh addr part two 0x00, // Reserved 0x20, 0x20, 0x01, 0x0d, 0xb8, // NLRI 1 0x10, 0xfe, 0x80, // NLRI 2 ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result: (&[u8], MPReachNLRIPathAttribute) = MPReachNLRIPathAttribute::from_wire(ctx, mp_reach_bytes).unwrap(); assert_eq!(result.1.afi, Ipv6); assert_eq!(result.1.safi, Unicast); assert_eq!( result.1.nexthop, vec![ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, // nh addr part one 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, // nh addr part two ] ); assert_eq!(result.1.nlris.len(), 2); assert_eq!(format!("{}", result.1.nlris[0]), "2001:db8::/32"); assert_eq!(format!("{}", result.1.nlris[1]), "fe80::/16"); assert_eq!(result.0.len(), 0); let wire: Vec = result.1.to_wire(ctx).unwrap(); assert_eq!(wire.as_slice(), mp_reach_bytes); assert_eq!(result.1.wire_len(ctx).unwrap() as usize, wire.len()); } #[test] fn test_mp_unreach_nlri_path_attribute() { let mp_unreach_bytes: &[u8] = &[ 0x00, 0x02, // IPv6 0x01, // Unicast 0x20, 0x20, 0x01, 0x0d, 0xb8, // NLRI 1 0x10, 0xfe, 0x80, // NLRI 2 ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let result: (&[u8], MPUnreachNLRIPathAttribute) = MPUnreachNLRIPathAttribute::from_wire(ctx, mp_unreach_bytes).unwrap(); assert_eq!(result.1.afi, Ipv6); assert_eq!(result.1.safi, Unicast); assert_eq!(result.1.nlris.len(), 2); assert_eq!(format!("{}", result.1.nlris[0]), "2001:db8::/32"); assert_eq!(format!("{}", result.1.nlris[1]), "fe80::/16"); assert_eq!(result.0.len(), 0); let wire: Vec = result.1.to_wire(ctx).unwrap(); assert_eq!(wire.as_slice(), mp_unreach_bytes); assert_eq!(result.1.wire_len(ctx).unwrap() as usize, wire.len()); } // Tests the high level dispatching of the path attribute parser #[test] fn test_path_attribute_parsing<'a>() { let path_attr_bytes: &[u8] = &[ 0x40, 0x01, 0x01, 0x00, 0x50, 0x02, 0x00, 0x1a, 0x02, 0x06, 0x00, 0x00, 0x9a, 0x74, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x0b, 0x62, 0x00, 0x00, 0x19, 0x35, 0x00, 0x00, 0x20, 0x9a, 0x00, 0x00, 0x34, 0x17, 0x40, 0x03, 0x04, 0xb9, 0x5f, 0xdb, 0x24, 0xc0, 0x08, 0x2c, 0x0b, 0x62, 0x01, 0xa4, 0x0b, 0x62, 0x04, 0xbf, 0x0b, 0x62, 0x08, 0xa6, 0x0b, 0x62, 0x0c, 0x80, 0x19, 0x35, 0x07, 0xd0, 0x19, 0x35, 0x09, 0xc4, 0x19, 0x35, 0x09, 0xcf, 0x62, 0x03, 0x0b, 0x62, 0x62, 0x03, 0x2f, 0x69, 0x9a, 0x74, 0x0f, 0xa0, 0x9a, 0x74, 0x0f, 0xbe, ]; let ctx = &ParserContext::new().four_octet_asn(true).nlri_mode(Ipv6); let (buf, res): (_, Vec) = nom::multi::many0(|buf: &'a [u8]| PathAttribute::from_wire(ctx, buf))(path_attr_bytes) .unwrap(); assert_eq!(buf.len(), 0); let expected_str = "[OriginPathAttribute(OriginPathAttribute(0)), \ ASPathAttribute(ASPathAttribute { segments: \ [ASPathSegment { ordered: true, path: [39540, 25091, 2914, 6453, 8346, 13335] }] }), \ NextHopPathAttribute(NextHopPathAttribute(185.95.219.36)), \ CommunitiesPathAttribute(CommunitiesPathAttribute { values: \ [CommunitiesPayload { asn: 2914, payload: 420 }, \ CommunitiesPayload { asn: 2914, payload: 1215 }, \ CommunitiesPayload { asn: 2914, payload: 2214 }, \ CommunitiesPayload { asn: 2914, payload: 3200 }, \ CommunitiesPayload { asn: 6453, payload: 2000 }, \ CommunitiesPayload { asn: 6453, payload: 2500 }, \ CommunitiesPayload { asn: 6453, payload: 2511 }, \ CommunitiesPayload { asn: 25091, payload: 2914 }, \ CommunitiesPayload { asn: 25091, payload: 12137 }, \ CommunitiesPayload { asn: 39540, payload: 4000 }, \ CommunitiesPayload { asn: 39540, payload: 4030 }] })]"; assert_eq!(format!("{:?}", res), expected_str); } }