|
| 1 | +use crate::{Prefix, AFI}; |
| 2 | +use byteorder::{BigEndian, ReadBytesExt}; |
| 3 | +use std::io::{Cursor, Error, ErrorKind, Read}; |
| 4 | +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
| 5 | + |
| 6 | +#[derive(Debug)] |
| 7 | +#[allow(non_camel_case_types)] |
| 8 | +pub enum Attribute { |
| 9 | + ORIGIN(Origin), |
| 10 | + AS_PATH(ASPath), |
| 11 | + NEXT_HOP(IpAddr), |
| 12 | + MULTI_EXIT_DISC(u32), |
| 13 | + LOCAL_PREF(u32), |
| 14 | + ATOMIC_AGGREGATOR, |
| 15 | + AGGREGATOR((u32, Ipv4Addr)), |
| 16 | + COMMUNITY(Vec<u32>), |
| 17 | + ORIGINATOR_ID(u32), |
| 18 | + CLUSTER_LIST, |
| 19 | + DPA, |
| 20 | + ADVERTISER, |
| 21 | + CLUSTER_ID, |
| 22 | + MP_REACH_NLRI(MPReachNLRI), |
| 23 | + MP_UNREACH_NLRI(MPUnreachNLRI), |
| 24 | + EXTENDED_COMMUNITIES(Vec<u64>), |
| 25 | + AS4_PATH, |
| 26 | + AS4_AGGREGATOR, |
| 27 | + SSA, |
| 28 | + CONNECTOR, |
| 29 | + AS_PATHLIMIT, |
| 30 | + PMSI_TUNNEL, |
| 31 | + TUNNEL_ENCAPSULATION, |
| 32 | + TRAFFIC_ENGINEERING, |
| 33 | + IPV6_SPECIFIC_EXTENDED_COMMUNITY, |
| 34 | + AIGP, |
| 35 | + PE_DISTINGUISHER_LABELS, |
| 36 | + BGP_LS, |
| 37 | + LARGE_COMMUNITY(Vec<(u32, u32, u32)>), |
| 38 | + BGPSEC_PATH, |
| 39 | + BGP_PREFIX_SID, |
| 40 | + ATTR_SET, |
| 41 | +} |
| 42 | + |
| 43 | +impl Attribute { |
| 44 | + pub fn parse(stream: &mut Read) -> Result<Attribute, Error> { |
| 45 | + let flags = stream.read_u8()?; |
| 46 | + let code = stream.read_u8()?; |
| 47 | + |
| 48 | + // Check if the Extended Length bit is set. |
| 49 | + let length: u16 = if flags & (1 << 4) == 0 { |
| 50 | + stream.read_u8()? as u16 |
| 51 | + } else { |
| 52 | + stream.read_u16::<BigEndian>()? |
| 53 | + }; |
| 54 | + |
| 55 | + match code { |
| 56 | + 1 => Ok(Attribute::ORIGIN(Origin::parse(stream)?)), |
| 57 | + 2 => Ok(Attribute::AS_PATH(ASPath::parse(stream, length)?)), |
| 58 | + 3 => { |
| 59 | + let ip: IpAddr = if length == 4 { |
| 60 | + IpAddr::V4(Ipv4Addr::from(stream.read_u32::<BigEndian>()?)) |
| 61 | + } else { |
| 62 | + IpAddr::V6(Ipv6Addr::from(stream.read_u128::<BigEndian>()?)) |
| 63 | + }; |
| 64 | + |
| 65 | + Ok(Attribute::NEXT_HOP(ip)) |
| 66 | + } |
| 67 | + 4 => Ok(Attribute::MULTI_EXIT_DISC(stream.read_u32::<BigEndian>()?)), |
| 68 | + 5 => Ok(Attribute::LOCAL_PREF(stream.read_u32::<BigEndian>()?)), |
| 69 | + 6 => Ok(Attribute::ATOMIC_AGGREGATOR), |
| 70 | + 7 => { |
| 71 | + let asn = if length == 6 { |
| 72 | + stream.read_u16::<BigEndian>()? as u32 |
| 73 | + } else { |
| 74 | + stream.read_u32::<BigEndian>()? |
| 75 | + }; |
| 76 | + |
| 77 | + let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?); |
| 78 | + Ok(Attribute::AGGREGATOR((asn, ip))) |
| 79 | + } |
| 80 | + 8 => { |
| 81 | + let mut communities = Vec::with_capacity((length / 4) as usize); |
| 82 | + for _ in 0..(length / 4) { |
| 83 | + communities.push(stream.read_u32::<BigEndian>()?) |
| 84 | + } |
| 85 | + |
| 86 | + Ok(Attribute::COMMUNITY(communities)) |
| 87 | + } |
| 88 | + 9 => Ok(Attribute::ORIGINATOR_ID(stream.read_u32::<BigEndian>()?)), |
| 89 | + 14 => Ok(Attribute::MP_REACH_NLRI(MPReachNLRI::parse( |
| 90 | + stream, length, |
| 91 | + )?)), |
| 92 | + 15 => Ok(Attribute::MP_UNREACH_NLRI(MPUnreachNLRI::parse( |
| 93 | + stream, length, |
| 94 | + )?)), |
| 95 | + 16 => { |
| 96 | + let mut communities = Vec::with_capacity((length / 8) as usize); |
| 97 | + for _ in 0..(length / 8) { |
| 98 | + communities.push(stream.read_u64::<BigEndian>()?) |
| 99 | + } |
| 100 | + |
| 101 | + Ok(Attribute::EXTENDED_COMMUNITIES(communities)) |
| 102 | + } |
| 103 | + 32 => { |
| 104 | + let mut communities: Vec<(u32, u32, u32)> = |
| 105 | + Vec::with_capacity((length / 12) as usize); |
| 106 | + for _ in 0..(length / 12) { |
| 107 | + let admin = stream.read_u32::<BigEndian>()?; |
| 108 | + let part1 = stream.read_u32::<BigEndian>()?; |
| 109 | + let part2 = stream.read_u32::<BigEndian>()?; |
| 110 | + communities.push((admin, part1, part2)) |
| 111 | + } |
| 112 | + |
| 113 | + Ok(Attribute::LARGE_COMMUNITY(communities)) |
| 114 | + } |
| 115 | + x => { |
| 116 | + let mut buffer = vec![0; length as usize]; |
| 117 | + stream.read_exact(&mut buffer); |
| 118 | + |
| 119 | + Err(Error::new( |
| 120 | + ErrorKind::Other, |
| 121 | + format!("Unknown path attribute type found: {}", x), |
| 122 | + )) |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +#[derive(Debug)] |
| 129 | +pub enum Origin { |
| 130 | + IGP, |
| 131 | + EGP, |
| 132 | + INCOMPLETE, |
| 133 | +} |
| 134 | + |
| 135 | +impl Origin { |
| 136 | + pub fn parse(stream: &mut Read) -> Result<Origin, Error> { |
| 137 | + match stream.read_u8()? { |
| 138 | + 0 => Ok(Origin::IGP), |
| 139 | + 1 => Ok(Origin::EGP), |
| 140 | + 2 => Ok(Origin::INCOMPLETE), |
| 141 | + _ => Err(Error::new(ErrorKind::Other, "Unknown origin type found.")), |
| 142 | + } |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +#[derive(Debug)] |
| 147 | +pub struct ASPath { |
| 148 | + pub segments: Vec<Segment>, |
| 149 | +} |
| 150 | + |
| 151 | +impl ASPath { |
| 152 | + // TODO: Give argument that determines the AS size. |
| 153 | + pub fn parse(stream: &mut Read, length: u16) -> Result<ASPath, Error> { |
| 154 | + // Create an AS_PATH struct with a capacity of 1, since AS_SETs |
| 155 | + // or multiple AS_SEQUENCES, are not seen often anymore. |
| 156 | + let mut path = ASPath { |
| 157 | + segments: Vec::with_capacity(1), |
| 158 | + }; |
| 159 | + |
| 160 | + // While there are multiple AS_PATH segments, parse the segments. |
| 161 | + let mut size = length; |
| 162 | + while size != 0 { |
| 163 | + let segment_type = stream.read_u8()?; |
| 164 | + let length = stream.read_u8()?; |
| 165 | + let mut values: Vec<u32> = Vec::with_capacity(length as usize); |
| 166 | + |
| 167 | + for _ in 0..length { |
| 168 | + values.push(stream.read_u32::<BigEndian>()?); |
| 169 | + } |
| 170 | + |
| 171 | + match segment_type { |
| 172 | + 1 => path.segments.push(Segment::AS_SEQUENCE(values)), |
| 173 | + 2 => path.segments.push(Segment::AS_SET(values)), |
| 174 | + x => { |
| 175 | + return Err(Error::new( |
| 176 | + ErrorKind::Other, |
| 177 | + format!("Unknown AS_PATH segment type found: {}", x), |
| 178 | + )); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + size -= 2 + (length as u16 * 4); |
| 183 | + } |
| 184 | + |
| 185 | + Ok(path) |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +#[derive(Debug)] |
| 190 | +#[allow(non_camel_case_types)] |
| 191 | +pub enum Segment { |
| 192 | + AS_SEQUENCE(Vec<u32>), |
| 193 | + AS_SET(Vec<u32>), |
| 194 | +} |
| 195 | + |
| 196 | +#[derive(Debug)] |
| 197 | +pub struct MPReachNLRI { |
| 198 | + pub afi: AFI, |
| 199 | + pub safi: u8, |
| 200 | + pub next_hop: Vec<u8>, |
| 201 | + pub announced_routes: Vec<Prefix>, |
| 202 | +} |
| 203 | + |
| 204 | +impl MPReachNLRI { |
| 205 | + // TODO: Give argument that determines the AS size. |
| 206 | + pub fn parse(stream: &mut Read, length: u16) -> Result<MPReachNLRI, Error> { |
| 207 | + let afi = AFI::from(stream.read_u16::<BigEndian>()?)?; |
| 208 | + let safi = stream.read_u8()?; |
| 209 | + |
| 210 | + let next_hop_length = stream.read_u8()?; |
| 211 | + let mut next_hop = vec![0; next_hop_length as usize]; |
| 212 | + stream.read_exact(&mut next_hop)?; |
| 213 | + |
| 214 | + let _reserved = stream.read_u8()?; |
| 215 | + |
| 216 | + // ---------------------------- |
| 217 | + // Read NLRI |
| 218 | + // ---------------------------- |
| 219 | + let size = length - (5 + next_hop_length) as u16; |
| 220 | + |
| 221 | + let mut buffer = vec![0; size as usize]; |
| 222 | + stream.read_exact(&mut buffer); |
| 223 | + let mut cursor = Cursor::new(buffer); |
| 224 | + let mut announced_routes: Vec<Prefix> = Vec::with_capacity(4); |
| 225 | + |
| 226 | + while cursor.position() < size as u64 { |
| 227 | + announced_routes.push(Prefix::parse(&mut cursor)?); |
| 228 | + } |
| 229 | + |
| 230 | + Ok(MPReachNLRI { |
| 231 | + afi, |
| 232 | + safi, |
| 233 | + next_hop, |
| 234 | + announced_routes, |
| 235 | + }) |
| 236 | + } |
| 237 | +} |
| 238 | + |
| 239 | +#[derive(Debug)] |
| 240 | +pub struct MPUnreachNLRI { |
| 241 | + pub afi: AFI, |
| 242 | + pub safi: u8, |
| 243 | + pub withdrawn_routes: Vec<Prefix>, |
| 244 | +} |
| 245 | + |
| 246 | +impl MPUnreachNLRI { |
| 247 | + // TODO: Give argument that determines the AS size. |
| 248 | + pub fn parse(stream: &mut Read, length: u16) -> Result<MPUnreachNLRI, Error> { |
| 249 | + let afi = AFI::from(stream.read_u16::<BigEndian>()?)?; |
| 250 | + let safi = stream.read_u8()?; |
| 251 | + |
| 252 | + // ---------------------------- |
| 253 | + // Read NLRI |
| 254 | + // ---------------------------- |
| 255 | + let size = length - 3 as u16; |
| 256 | + |
| 257 | + let mut buffer = vec![0; size as usize]; |
| 258 | + stream.read_exact(&mut buffer); |
| 259 | + let mut cursor = Cursor::new(buffer); |
| 260 | + let mut withdrawn_routes: Vec<Prefix> = Vec::with_capacity(4); |
| 261 | + |
| 262 | + while cursor.position() < size as u64 { |
| 263 | + withdrawn_routes.push(Prefix::parse(&mut cursor)?); |
| 264 | + } |
| 265 | + |
| 266 | + Ok(MPUnreachNLRI { |
| 267 | + afi, |
| 268 | + safi, |
| 269 | + withdrawn_routes, |
| 270 | + }) |
| 271 | + } |
| 272 | +} |
0 commit comments