use crate::{Prefix, AFI};
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Cursor, Error, ErrorKind, Read};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum Identifier {
ORIGIN = 1,
AS_PATH = 2,
NEXT_HOP = 3,
MULTI_EXIT_DISC = 4,
LOCAL_PREF = 5,
ATOMIC_AGGREGATOR = 6,
AGGREGATOR = 7,
COMMUNITY = 8,
ORIGINATOR_ID = 9,
CLUSTER_LIST = 10,
DPA = 11,
ADVERTISER = 12,
CLUSTER_ID = 13,
MP_REACH_NLRI = 14,
MP_UNREACH_NLRI = 15,
EXTENDED_COMMUNITIES = 16,
AS4_PATH = 17,
AS4_AGGREGATOR = 18,
SSA = 19,
CONNECTOR = 20,
AS_PATHLIMIT = 21,
PMSI_TUNNEL = 22,
TUNNEL_ENCAPSULATION = 23,
TRAFFIC_ENGINEERING = 24,
IPV6_SPECIFIC_EXTENDED_COMMUNITY = 25,
AIGP = 26,
PE_DISTINGUISHER_LABELS = 27,
BGP_LS = 29,
LARGE_COMMUNITY = 32,
BGPSEC_PATH = 33,
BGP_PREFIX_SID = 34,
ATTR_SET = 128,
}
#[derive(Debug, Clone)]
#[allow(non_camel_case_types)]
pub enum PathAttribute {
ORIGIN(Origin),
AS_PATH(ASPath),
NEXT_HOP(IpAddr),
MULTI_EXIT_DISC(u32),
LOCAL_PREF(u32),
ATOMIC_AGGREGATOR,
AGGREGATOR((u32, Ipv4Addr)),
COMMUNITY(Vec<u32>),
ORIGINATOR_ID(u32),
CLUSTER_LIST(Vec<u32>),
DPA((u16, u32)),
ADVERTISER,
CLUSTER_ID,
MP_REACH_NLRI(MPReachNLRI),
MP_UNREACH_NLRI(MPUnreachNLRI),
EXTENDED_COMMUNITIES(Vec<u64>),
AS4_PATH(ASPath),
AS4_AGGREGATOR((u32, Ipv4Addr)),
SSA,
CONNECTOR(Ipv4Addr),
AS_PATHLIMIT((u8, u32)),
PMSI_TUNNEL((u8, u32, Vec<u8>)),
TUNNEL_ENCAPSULATION((u16, Vec<u8>)),
TRAFFIC_ENGINEERING,
IPV6_SPECIFIC_EXTENDED_COMMUNITY((u8, u8, Ipv6Addr, u16)),
AIGP((u8, Vec<u8>)),
PE_DISTINGUISHER_LABELS,
BGP_LS,
LARGE_COMMUNITY(Vec<(u32, u32, u32)>),
BGPSEC_PATH,
BGP_PREFIX_SID,
ATTR_SET((u32, Vec<PathAttribute>)),
}
impl PathAttribute {
pub fn parse(stream: &mut Read) -> Result<PathAttribute, Error> {
let flags = stream.read_u8()?;
let code = stream.read_u8()?;
let length: u16 = if flags & (1 << 4) == 0 {
u16::from(stream.read_u8()?)
} else {
stream.read_u16::<BigEndian>()?
};
match code {
1 => Ok(PathAttribute::ORIGIN(Origin::parse(stream)?)),
2 => Ok(PathAttribute::AS_PATH(ASPath::parse(stream, length)?)),
3 => {
let ip: IpAddr = if length == 4 {
IpAddr::V4(Ipv4Addr::from(stream.read_u32::<BigEndian>()?))
} else {
IpAddr::V6(Ipv6Addr::from(stream.read_u128::<BigEndian>()?))
};
Ok(PathAttribute::NEXT_HOP(ip))
}
4 => Ok(PathAttribute::MULTI_EXIT_DISC(
stream.read_u32::<BigEndian>()?,
)),
5 => Ok(PathAttribute::LOCAL_PREF(stream.read_u32::<BigEndian>()?)),
6 => Ok(PathAttribute::ATOMIC_AGGREGATOR),
7 => {
let asn = if length == 6 {
u32::from(stream.read_u16::<BigEndian>()?)
} else {
stream.read_u32::<BigEndian>()?
};
let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?);
Ok(PathAttribute::AGGREGATOR((asn, ip)))
}
8 => {
let mut communities = Vec::with_capacity(usize::from(length / 4));
for _ in 0..(length / 4) {
communities.push(stream.read_u32::<BigEndian>()?)
}
Ok(PathAttribute::COMMUNITY(communities))
}
9 => Ok(PathAttribute::ORIGINATOR_ID(
stream.read_u32::<BigEndian>()?,
)),
10 => {
let mut ids = Vec::with_capacity(usize::from(length / 4));
for _ in 0..(length / 4) {
ids.push(stream.read_u32::<BigEndian>()?)
}
Ok(PathAttribute::CLUSTER_LIST(ids))
}
11 => Ok(PathAttribute::DPA((
stream.read_u16::<BigEndian>()?,
stream.read_u32::<BigEndian>()?,
))),
14 => Ok(PathAttribute::MP_REACH_NLRI(MPReachNLRI::parse(
stream, length,
)?)),
15 => Ok(PathAttribute::MP_UNREACH_NLRI(MPUnreachNLRI::parse(
stream, length,
)?)),
16 => {
let mut communities = Vec::with_capacity(usize::from(length / 8));
for _ in 0..(length / 8) {
communities.push(stream.read_u64::<BigEndian>()?)
}
Ok(PathAttribute::EXTENDED_COMMUNITIES(communities))
}
17 => Ok(PathAttribute::AS4_PATH(ASPath::parse(stream, length)?)),
18 => {
let asn = stream.read_u32::<BigEndian>()?;
let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?);
Ok(PathAttribute::AS4_AGGREGATOR((asn, ip)))
}
20 => {
stream.read_u16::<BigEndian>()?;
let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?);
Ok(PathAttribute::CONNECTOR(ip))
}
21 => {
let limit = stream.read_u8()?;
let asn = stream.read_u32::<BigEndian>()?;
Ok(PathAttribute::AS_PATHLIMIT((limit, asn)))
}
22 => {
let flags = stream.read_u8()?;
let label = stream.read_u32::<BigEndian>()?;
let mut identifier = vec![0; usize::from(length - 4)];
stream.read_exact(&mut identifier)?;
Ok(PathAttribute::PMSI_TUNNEL((flags, label, identifier)))
}
23 => {
let tunnel_type = stream.read_u16::<BigEndian>()?;
let length = stream.read_u16::<BigEndian>()?;
let mut value = vec![0; usize::from(length)];
stream.read_exact(&mut value)?;
Ok(PathAttribute::TUNNEL_ENCAPSULATION((tunnel_type, value)))
}
25 => {
let transitive = stream.read_u8()?;
let subtype = stream.read_u8()?;
let global_admin = Ipv6Addr::from(stream.read_u128::<BigEndian>()?);
let local_admin = stream.read_u16::<BigEndian>()?;
Ok(PathAttribute::IPV6_SPECIFIC_EXTENDED_COMMUNITY((
transitive,
subtype,
global_admin,
local_admin,
)))
}
26 => {
let aigp_type = stream.read_u8()?;
let length = stream.read_u16::<BigEndian>()?;
let mut value = vec![0; usize::from(length - 3)];
stream.read_exact(&mut value)?;
Ok(PathAttribute::AIGP((aigp_type, value)))
}
32 => {
let mut communities: Vec<(u32, u32, u32)> =
Vec::with_capacity(usize::from(length / 12));
for _ in 0..(length / 12) {
let admin = stream.read_u32::<BigEndian>()?;
let part1 = stream.read_u32::<BigEndian>()?;
let part2 = stream.read_u32::<BigEndian>()?;
communities.push((admin, part1, part2))
}
Ok(PathAttribute::LARGE_COMMUNITY(communities))
}
128 => {
let asn = stream.read_u32::<BigEndian>()?;
let mut buffer = vec![0; length as usize - 4];
stream.read_exact(&mut buffer)?;
let mut cursor = Cursor::new(buffer);
let mut attributes = Vec::with_capacity(5);
while cursor.position() < (length - 4).into() {
let result = PathAttribute::parse(&mut cursor);
match result {
Err(x) => println!("Error: {}", x),
Ok(x) => attributes.push(x),
}
}
Ok(PathAttribute::ATTR_SET((asn, attributes)))
}
x => {
let mut buffer = vec![0; usize::from(length)];
stream.read_exact(&mut buffer)?;
Err(Error::new(
ErrorKind::Other,
format!("Unknown path attribute type found: {}", x),
))
}
}
}
pub fn id(&self) -> Identifier {
match self {
PathAttribute::ORIGIN(_) => Identifier::ORIGIN,
PathAttribute::AS_PATH(_) => Identifier::AS_PATH,
PathAttribute::NEXT_HOP(_) => Identifier::NEXT_HOP,
PathAttribute::MULTI_EXIT_DISC(_) => Identifier::MULTI_EXIT_DISC,
PathAttribute::LOCAL_PREF(_) => Identifier::LOCAL_PREF,
PathAttribute::ATOMIC_AGGREGATOR => Identifier::ATOMIC_AGGREGATOR,
PathAttribute::AGGREGATOR(_) => Identifier::AGGREGATOR,
PathAttribute::COMMUNITY(_) => Identifier::COMMUNITY,
PathAttribute::ORIGINATOR_ID(_) => Identifier::ORIGINATOR_ID,
PathAttribute::CLUSTER_LIST(_) => Identifier::CLUSTER_LIST,
PathAttribute::DPA(_) => Identifier::DPA,
PathAttribute::ADVERTISER => Identifier::ADVERTISER,
PathAttribute::CLUSTER_ID => Identifier::CLUSTER_ID,
PathAttribute::MP_REACH_NLRI(_) => Identifier::MP_REACH_NLRI,
PathAttribute::MP_UNREACH_NLRI(_) => Identifier::MP_UNREACH_NLRI,
PathAttribute::EXTENDED_COMMUNITIES(_) => Identifier::EXTENDED_COMMUNITIES,
PathAttribute::AS4_PATH(_) => Identifier::AS4_PATH,
PathAttribute::AS4_AGGREGATOR(_) => Identifier::AS4_AGGREGATOR,
PathAttribute::SSA => Identifier::SSA,
PathAttribute::CONNECTOR(_) => Identifier::CONNECTOR,
PathAttribute::AS_PATHLIMIT(_) => Identifier::AS_PATHLIMIT,
PathAttribute::PMSI_TUNNEL(_) => Identifier::PMSI_TUNNEL,
PathAttribute::TUNNEL_ENCAPSULATION(_) => Identifier::TUNNEL_ENCAPSULATION,
PathAttribute::TRAFFIC_ENGINEERING => Identifier::TRAFFIC_ENGINEERING,
PathAttribute::IPV6_SPECIFIC_EXTENDED_COMMUNITY(_) => {
Identifier::IPV6_SPECIFIC_EXTENDED_COMMUNITY
}
PathAttribute::AIGP(_) => Identifier::AIGP,
PathAttribute::PE_DISTINGUISHER_LABELS => Identifier::PE_DISTINGUISHER_LABELS,
PathAttribute::BGP_LS => Identifier::BGP_LS,
PathAttribute::LARGE_COMMUNITY(_) => Identifier::LARGE_COMMUNITY,
PathAttribute::BGPSEC_PATH => Identifier::BGPSEC_PATH,
PathAttribute::BGP_PREFIX_SID => Identifier::BGP_PREFIX_SID,
PathAttribute::ATTR_SET(_) => Identifier::ATTR_SET,
}
}
}
#[derive(Debug, Clone)]
pub enum Origin {
IGP,
EGP,
INCOMPLETE,
}
impl Origin {
fn parse(stream: &mut Read) -> Result<Origin, Error> {
match stream.read_u8()? {
0 => Ok(Origin::IGP),
1 => Ok(Origin::EGP),
2 => Ok(Origin::INCOMPLETE),
_ => Err(Error::new(ErrorKind::Other, "Unknown origin type found.")),
}
}
}
#[derive(Debug, Clone)]
pub struct ASPath {
pub segments: Vec<Segment>,
}
impl ASPath {
fn parse(stream: &mut Read, length: u16) -> Result<ASPath, Error> {
let mut path = ASPath {
segments: Vec::with_capacity(1),
};
let mut size = length;
while size != 0 {
let segment_type = stream.read_u8()?;
let length = stream.read_u8()?;
let mut values: Vec<u32> = Vec::with_capacity(usize::from(length));
for _ in 0..length {
values.push(stream.read_u32::<BigEndian>()?);
}
match segment_type {
1 => path.segments.push(Segment::AS_SET(values)),
2 => path.segments.push(Segment::AS_SEQUENCE(values)),
x => {
return Err(Error::new(
ErrorKind::Other,
format!("Unknown AS_PATH segment type found: {}", x),
));
}
}
size -= 2 + (u16::from(length) * 4);
}
Ok(path)
}
pub fn origin(&self) -> Option<u32> {
let segment = self.segments.first()?;
if let Segment::AS_SEQUENCE(x) = segment {
return Some(*x.first()?);
}
None
}
pub fn sequence(&self) -> Option<Vec<u32>> {
let mut sequence = Vec::with_capacity(8);
for segment in &self.segments {
match segment {
Segment::AS_SEQUENCE(x) => sequence.extend(x),
Segment::AS_SET(_) => return None,
}
}
Some(sequence)
}
}
#[derive(Debug, Clone)]
#[allow(non_camel_case_types)]
pub enum Segment {
AS_SEQUENCE(Vec<u32>),
AS_SET(Vec<u32>),
}
#[derive(Debug, Clone)]
pub struct MPReachNLRI {
pub afi: AFI,
pub safi: u8,
pub next_hop: Vec<u8>,
pub announced_routes: Vec<Prefix>,
}
impl MPReachNLRI {
fn parse(stream: &mut Read, length: u16) -> Result<MPReachNLRI, Error> {
let afi = AFI::from(stream.read_u16::<BigEndian>()?)?;
let safi = stream.read_u8()?;
let next_hop_length = stream.read_u8()?;
let mut next_hop = vec![0; usize::from(next_hop_length)];
stream.read_exact(&mut next_hop)?;
let _reserved = stream.read_u8()?;
let size = length - u16::from(5 + next_hop_length);
let mut buffer = vec![0; usize::from(size)];
stream.read_exact(&mut buffer)?;
let mut cursor = Cursor::new(buffer);
let mut announced_routes: Vec<Prefix> = Vec::with_capacity(4);
while cursor.position() < u64::from(size) {
announced_routes.push(Prefix::parse(&mut cursor, afi)?);
}
Ok(MPReachNLRI {
afi,
safi,
next_hop,
announced_routes,
})
}
}
#[derive(Debug, Clone)]
pub struct MPUnreachNLRI {
pub afi: AFI,
pub safi: u8,
pub withdrawn_routes: Vec<Prefix>,
}
impl MPUnreachNLRI {
fn parse(stream: &mut Read, length: u16) -> Result<MPUnreachNLRI, Error> {
let afi = AFI::from(stream.read_u16::<BigEndian>()?)?;
let safi = stream.read_u8()?;
let size = length - 3;
let mut buffer = vec![0; usize::from(size)];
stream.read_exact(&mut buffer)?;
let mut cursor = Cursor::new(buffer);
let mut withdrawn_routes: Vec<Prefix> = Vec::with_capacity(4);
while cursor.position() < u64::from(size) {
withdrawn_routes.push(Prefix::parse(&mut cursor, afi)?);
}
Ok(MPUnreachNLRI {
afi,
safi,
withdrawn_routes,
})
}
}