bgpkit_parser/parser/bgp/attributes/
mod.rsmod attr_01_origin;
mod attr_02_17_as_path;
mod attr_03_next_hop;
mod attr_04_med;
mod attr_05_local_pref;
mod attr_07_18_aggregator;
mod attr_08_communities;
mod attr_09_originator;
mod attr_10_13_cluster;
mod attr_14_15_nlri;
mod attr_16_25_extended_communities;
mod attr_32_large_communities;
mod attr_35_otc;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use log::{debug, warn};
use std::net::IpAddr;
use crate::models::*;
use crate::error::ParserError;
use crate::parser::bgp::attributes::attr_01_origin::{encode_origin, parse_origin};
use crate::parser::bgp::attributes::attr_02_17_as_path::{encode_as_path, parse_as_path};
use crate::parser::bgp::attributes::attr_03_next_hop::{encode_next_hop, parse_next_hop};
use crate::parser::bgp::attributes::attr_04_med::{encode_med, parse_med};
use crate::parser::bgp::attributes::attr_05_local_pref::{encode_local_pref, parse_local_pref};
use crate::parser::bgp::attributes::attr_07_18_aggregator::{encode_aggregator, parse_aggregator};
use crate::parser::bgp::attributes::attr_08_communities::{
encode_regular_communities, parse_regular_communities,
};
use crate::parser::bgp::attributes::attr_09_originator::{
encode_originator_id, parse_originator_id,
};
use crate::parser::bgp::attributes::attr_10_13_cluster::{encode_clusters, parse_clusters};
use crate::parser::bgp::attributes::attr_14_15_nlri::{encode_nlri, parse_nlri};
use crate::parser::bgp::attributes::attr_16_25_extended_communities::{
encode_extended_communities, encode_ipv6_extended_communities, parse_extended_community,
parse_ipv6_extended_community,
};
use crate::parser::bgp::attributes::attr_32_large_communities::{
encode_large_communities, parse_large_communities,
};
use crate::parser::bgp::attributes::attr_35_otc::{
encode_only_to_customer, parse_only_to_customer,
};
use crate::parser::ReadUtils;
pub fn parse_attributes(
mut data: Bytes,
asn_len: &AsnLength,
add_path: bool,
afi: Option<Afi>,
safi: Option<Safi>,
prefixes: Option<&[NetworkPrefix]>,
) -> Result<Attributes, ParserError> {
let mut attributes: Vec<Attribute> = Vec::with_capacity(20);
while data.remaining() >= 3 {
let flag = AttrFlags::from_bits_retain(data.get_u8());
let attr_type = data.get_u8();
let attr_length = match flag.contains(AttrFlags::EXTENDED) {
false => data.get_u8() as usize,
true => data.get_u16() as usize,
};
let mut partial = false;
if flag.contains(AttrFlags::PARTIAL) {
partial = true;
}
debug!(
"reading attribute: type -- {:?}, length -- {}",
&attr_type, attr_length
);
let attr_type = match AttrType::from(attr_type) {
attr_type @ AttrType::Unknown(unknown_type) => {
let bytes = data.read_n_bytes(attr_length)?;
let attr_value = match get_deprecated_attr_type(unknown_type) {
Some(t) => {
debug!("deprecated attribute type: {} - {}", unknown_type, t);
AttributeValue::Deprecated(AttrRaw { attr_type, bytes })
}
None => {
debug!("unknown attribute type: {}", unknown_type);
AttributeValue::Unknown(AttrRaw { attr_type, bytes })
}
};
assert_eq!(attr_type, attr_value.attr_type());
attributes.push(Attribute {
value: attr_value,
flag,
});
continue;
}
t => t,
};
let bytes_left = data.remaining();
if data.remaining() < attr_length {
warn!(
"not enough bytes: input bytes left - {}, want to read - {}; skipping",
bytes_left, attr_length
);
break;
}
data.has_n_remaining(attr_length)?;
let mut attr_data = data.split_to(attr_length);
let attr = match attr_type {
AttrType::ORIGIN => parse_origin(attr_data),
AttrType::AS_PATH => {
parse_as_path(attr_data, asn_len).map(|path| AttributeValue::AsPath {
path,
is_as4: false,
})
}
AttrType::NEXT_HOP => parse_next_hop(attr_data, &afi),
AttrType::MULTI_EXIT_DISCRIMINATOR => parse_med(attr_data),
AttrType::LOCAL_PREFERENCE => parse_local_pref(attr_data),
AttrType::ATOMIC_AGGREGATE => Ok(AttributeValue::AtomicAggregate),
AttrType::AGGREGATOR => {
parse_aggregator(attr_data, asn_len).map(|(asn, id)| AttributeValue::Aggregator {
asn,
id,
is_as4: false,
})
}
AttrType::ORIGINATOR_ID => parse_originator_id(attr_data),
AttrType::CLUSTER_LIST => parse_clusters(attr_data),
AttrType::MP_REACHABLE_NLRI => {
parse_nlri(attr_data, &afi, &safi, &prefixes, true, add_path)
}
AttrType::MP_UNREACHABLE_NLRI => {
parse_nlri(attr_data, &afi, &safi, &prefixes, false, add_path)
}
AttrType::AS4_PATH => parse_as_path(attr_data, &AsnLength::Bits32)
.map(|path| AttributeValue::AsPath { path, is_as4: true }),
AttrType::AS4_AGGREGATOR => {
parse_aggregator(attr_data, &AsnLength::Bits32).map(|(asn, id)| {
AttributeValue::Aggregator {
asn,
id,
is_as4: true,
}
})
}
AttrType::COMMUNITIES => parse_regular_communities(attr_data),
AttrType::LARGE_COMMUNITIES => parse_large_communities(attr_data),
AttrType::EXTENDED_COMMUNITIES => parse_extended_community(attr_data),
AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES => {
parse_ipv6_extended_community(attr_data)
}
AttrType::DEVELOPMENT => {
let mut value = vec![];
for _i in 0..attr_length {
value.push(attr_data.get_u8());
}
Ok(AttributeValue::Development(value))
}
AttrType::ONLY_TO_CUSTOMER => parse_only_to_customer(attr_data),
_ => Err(ParserError::Unsupported(format!(
"unsupported attribute type: {:?}",
attr_type
))),
};
match attr {
Ok(value) => {
assert_eq!(attr_type, value.attr_type());
attributes.push(Attribute { value, flag });
}
Err(e) => {
if partial {
debug!("PARTIAL: {}", e.to_string());
} else {
debug!("{}", e.to_string());
}
continue;
}
};
}
Ok(Attributes::from(attributes))
}
impl Attribute {
pub fn encode(&self, add_path: bool, asn_len: AsnLength) -> Bytes {
let mut bytes = BytesMut::new();
let flag = self.flag.bits();
let type_code = self.value.attr_type().into();
bytes.put_u8(flag);
bytes.put_u8(type_code);
let value_bytes = match &self.value {
AttributeValue::Origin(v) => encode_origin(v),
AttributeValue::AsPath { path, is_as4 } => {
let four_byte = match is_as4 {
true => AsnLength::Bits32,
false => match asn_len.is_four_byte() {
true => AsnLength::Bits32,
false => AsnLength::Bits16,
},
};
encode_as_path(path, four_byte)
}
AttributeValue::NextHop(v) => encode_next_hop(v),
AttributeValue::MultiExitDiscriminator(v) => encode_med(*v),
AttributeValue::LocalPreference(v) => encode_local_pref(*v),
AttributeValue::OnlyToCustomer(v) => encode_only_to_customer(v.into()),
AttributeValue::AtomicAggregate => Bytes::default(),
AttributeValue::Aggregator { asn, id, is_as4: _ } => {
encode_aggregator(asn, &IpAddr::from(*id))
}
AttributeValue::Communities(v) => encode_regular_communities(v),
AttributeValue::ExtendedCommunities(v) => encode_extended_communities(v),
AttributeValue::LargeCommunities(v) => encode_large_communities(v),
AttributeValue::Ipv6AddressSpecificExtendedCommunities(v) => {
encode_ipv6_extended_communities(v)
}
AttributeValue::OriginatorId(v) => encode_originator_id(&IpAddr::from(*v)),
AttributeValue::Clusters(v) => encode_clusters(v),
AttributeValue::MpReachNlri(v) => encode_nlri(v, true, add_path),
AttributeValue::MpUnreachNlri(v) => encode_nlri(v, false, add_path),
AttributeValue::Development(v) => Bytes::from(v.to_owned()),
AttributeValue::Deprecated(v) => Bytes::from(v.bytes.to_owned()),
AttributeValue::Unknown(v) => Bytes::from(v.bytes.to_owned()),
};
match self.is_extended() {
false => {
bytes.put_u8(value_bytes.len() as u8);
}
true => {
bytes.put_u16(value_bytes.len() as u16);
}
}
bytes.extend(value_bytes);
bytes.freeze()
}
}
impl Attributes {
pub fn encode(&self, add_path: bool, asn_len: AsnLength) -> Bytes {
let mut bytes = BytesMut::new();
for attr in &self.inner {
bytes.extend(attr.encode(add_path, asn_len));
}
bytes.freeze()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unknwon_attribute_type() {
let data = Bytes::from(vec![0x40, 0xFE, 0x00]);
let asn_len = AsnLength::Bits16;
let add_path = false;
let afi = None;
let safi = None;
let prefixes = None;
let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes);
assert!(attributes.is_ok());
let attributes = attributes.unwrap();
assert_eq!(attributes.inner.len(), 1);
assert_eq!(
attributes.inner[0].value.attr_type(),
AttrType::Unknown(254)
);
}
}