1use std::collections::{HashMap, HashSet};
8use std::convert::TryFrom;
9use std::io::{Error, ErrorKind, Read, Write};
10
11use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
12
13use crate::*;
14
15#[derive(Clone, Debug)]
17pub struct Open {
18 pub version: u8,
20
21 pub peer_asn: u16,
23
24 pub hold_timer: u16,
26
27 pub identifier: u32,
29
30 pub parameters: Vec<OpenParameter>,
32}
33
34impl Open {
35 pub fn parse(stream: &mut impl Read) -> Result<Open, Error> {
37 let version = stream.read_u8()?;
38 let peer_asn = stream.read_u16::<BigEndian>()?;
39 let hold_timer = stream.read_u16::<BigEndian>()?;
40 let identifier = stream.read_u32::<BigEndian>()?;
41 let mut length = stream.read_u8()? as i32;
42
43 let mut parameters: Vec<OpenParameter> = Vec::with_capacity(length as usize);
44
45 while length > 0 {
46 let (bytes_read, parameter) = OpenParameter::parse(stream)?;
47 parameters.push(parameter);
48 length -= bytes_read as i32;
49 }
50 if length != 0 {
51 return Err(Error::new(
52 ErrorKind::InvalidData,
53 "Open length does not match options length",
54 ));
55 }
56
57 Ok(Open {
58 version,
59 peer_asn,
60 hold_timer,
61 identifier,
62 parameters,
63 })
64 }
65
66 pub fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
68 buf.write_u8(self.version)?;
69 buf.write_u16::<BigEndian>(self.peer_asn)?;
70 buf.write_u16::<BigEndian>(self.hold_timer)?;
71 buf.write_u32::<BigEndian>(self.identifier)?;
72
73 let mut parameter_buf: Vec<u8> = Vec::with_capacity(4);
74 for p in self.parameters.iter() {
75 p.encode(&mut parameter_buf)?;
76 }
77 if parameter_buf.len() > std::u8::MAX as usize {
78 return Err(Error::new(
79 ErrorKind::Other,
80 format!(
81 "Cannot encode parameters with length {}",
82 parameter_buf.len()
83 ),
84 ));
85 }
86 buf.write_u8(parameter_buf.len() as u8)?;
87 buf.write_all(¶meter_buf)
88 }
89}
90
91#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
93#[repr(u8)]
94pub enum AddPathDirection {
95 ReceivePaths = 1,
97
98 SendPaths = 2,
100
101 SendReceivePaths = 3,
103}
104
105impl TryFrom<u8> for AddPathDirection {
106 type Error = Error;
107
108 fn try_from(value: u8) -> Result<Self, Self::Error> {
109 match value {
110 1 => Ok(AddPathDirection::ReceivePaths),
111 2 => Ok(AddPathDirection::SendPaths),
112 3 => Ok(AddPathDirection::SendReceivePaths),
113 _ => {
114 let msg = format!(
115 "Number {} does not represent a valid ADD-PATH direction.",
116 value
117 );
118 Err(std::io::Error::new(std::io::ErrorKind::Other, msg))
119 }
120 }
121 }
122}
123
124#[derive(Clone, Debug)]
126pub enum OpenCapability {
127 MultiProtocol((AFI, SAFI)),
129 RouteRefresh,
131 OutboundRouteFiltering(HashSet<(AFI, SAFI, u8, AddPathDirection)>),
133 FourByteASN(u32),
135 AddPath(Vec<(AFI, SAFI, AddPathDirection)>),
137 Unknown {
139 cap_code: u8,
141
142 cap_length: u8,
144
145 value: Vec<u8>,
147 },
148}
149
150impl OpenCapability {
151 fn parse(stream: &mut impl Read) -> Result<(u16, OpenCapability), Error> {
152 let cap_code = stream.read_u8()?;
153 let cap_length = stream.read_u8()?;
154
155 Ok((
156 2 + (cap_length as u16),
157 match cap_code {
158 1 => {
160 if cap_length != 4 {
161 return Err(Error::new(
162 ErrorKind::InvalidData,
163 "Multi-Protocol capability must be 4 bytes in length",
164 ));
165 }
166 let afi = AFI::try_from(stream.read_u16::<BigEndian>()?)?;
167 let _ = stream.read_u8()?;
168 let safi = SAFI::try_from(stream.read_u8()?)?;
169 OpenCapability::MultiProtocol((afi, safi))
170 }
171 2 => {
173 if cap_length != 0 {
174 return Err(Error::new(
175 ErrorKind::InvalidData,
176 "Route-Refresh capability must be 0 bytes in length",
177 ));
178 }
179 OpenCapability::RouteRefresh
180 }
181 3 => {
183 if cap_length < 5 || (cap_length - 5) % 2 != 0 {
184 return Err(Error::new(
185 ErrorKind::InvalidData,
186 "Outbound Route Filtering capability has an invalid length",
187 ));
188 }
189 let afi = AFI::try_from(stream.read_u16::<BigEndian>()?)?;
190 let _ = stream.read_u8()?;
191 let safi = SAFI::try_from(stream.read_u8()?)?;
192 let count = stream.read_u8()?;
193 let mut types: HashSet<(AFI, SAFI, u8, AddPathDirection)> = HashSet::new();
194 for _ in 0..count {
195 types.insert((
196 afi,
197 safi,
198 stream.read_u8()?,
199 AddPathDirection::try_from(stream.read_u8()?)?,
200 ));
201 }
202 OpenCapability::OutboundRouteFiltering(types)
203 }
204 65 => {
206 if cap_length != 4 {
207 return Err(Error::new(
208 ErrorKind::InvalidData,
209 "4-byte ASN capability must be 4 bytes in length",
210 ));
211 }
212 OpenCapability::FourByteASN(stream.read_u32::<BigEndian>()?)
213 }
214 69 => {
215 if cap_length % 4 != 0 {
216 return Err(Error::new(
217 ErrorKind::InvalidData,
218 "ADD-PATH capability length must be divisble by 4",
219 ));
220 }
221 let mut add_paths = Vec::with_capacity(cap_length as usize / 4);
222 for _ in 0..(cap_length / 4) {
223 add_paths.push((
224 AFI::try_from(stream.read_u16::<BigEndian>()?)?,
225 SAFI::try_from(stream.read_u8()?)?,
226 AddPathDirection::try_from(stream.read_u8()?)?,
227 ));
228 }
229 OpenCapability::AddPath(add_paths)
230 }
231 _ => {
232 let mut value = vec![0; cap_length as usize];
233 stream.read_exact(&mut value)?;
234 OpenCapability::Unknown {
235 cap_code,
236 cap_length,
237 value,
238 }
239 }
240 },
241 ))
242 }
243
244 fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
245 let mut cap_buf: Vec<u8> = Vec::with_capacity(20);
246 match self {
247 OpenCapability::MultiProtocol((afi, safi)) => {
248 cap_buf.write_u8(1)?; cap_buf.write_u8(4)?; cap_buf.write_u16::<BigEndian>(*afi as u16)?;
251 cap_buf.write_u8(0)?; cap_buf.write_u8(*safi as u8)?;
253 }
254 OpenCapability::RouteRefresh => {
255 cap_buf.write_u8(2)?; cap_buf.write_u8(0)?; }
258 OpenCapability::OutboundRouteFiltering(orfs) => {
259 let length = orfs.len();
260 for (i, orf) in orfs.iter().enumerate() {
261 let (afi, safi, orf_type, orf_direction) = orf;
262 if i == 0 {
263 cap_buf.write_u16::<BigEndian>(*afi as u16)?;
264 cap_buf.write_u8(0)?; cap_buf.write_u8(*safi as u8)?;
266 cap_buf.write_u8(length as u8)?;
267 }
268 cap_buf.write_u8(*orf_type)?;
269 cap_buf.write_u8(*orf_direction as u8)?;
270 }
271 }
272 OpenCapability::FourByteASN(asn) => {
273 cap_buf.write_u8(65)?; cap_buf.write_u8(4)?; cap_buf.write_u32::<BigEndian>(*asn)?;
276 }
277 OpenCapability::AddPath(add_paths) => {
278 cap_buf.write_u8(69)?; if add_paths.len() * 4 > std::u8::MAX as usize {
280 return Err(Error::new(
281 ErrorKind::Other,
282 format!(
283 "Cannot encode ADD-PATH with too many AFIs {}",
284 add_paths.len()
285 ),
286 ));
287 }
288 cap_buf.write_u8(add_paths.len() as u8 * 4)?; for p in add_paths.iter() {
290 cap_buf.write_u16::<BigEndian>(p.0 as u16)?;
291 cap_buf.write_u8(p.1 as u8)?;
292 cap_buf.write_u8(p.2 as u8)?;
293 }
294 }
295 OpenCapability::Unknown {
296 cap_code,
297 cap_length,
298 value,
299 } => {
300 cap_buf.write_u8(*cap_code)?;
301 cap_buf.write_u8(*cap_length)?;
302 cap_buf.write_all(&value)?;
303 }
304 }
305 buf.write_u8(2)?; buf.write_u8(cap_buf.len() as u8)?;
307 buf.write_all(&cap_buf)
308 }
309}
310
311#[derive(Clone, Debug)]
313pub enum OpenParameter {
314 Capabilities(Vec<OpenCapability>),
316
317 Unknown {
319 param_type: u8,
321
322 param_length: u8,
324
325 value: Vec<u8>,
327 },
328}
329
330impl OpenParameter {
331 fn parse(stream: &mut impl Read) -> Result<(u16, OpenParameter), Error> {
332 let param_type = stream.read_u8()?;
333 let param_length = stream.read_u8()?;
334
335 Ok((
336 2 + (param_length as u16),
337 if param_type == 2 {
338 let mut bytes_read: i32 = 0;
339 let mut capabilities = Vec::with_capacity(param_length as usize / 2);
340 while bytes_read < param_length as i32 {
341 let (cap_length, cap) = OpenCapability::parse(stream)?;
342 capabilities.push(cap);
343 bytes_read += cap_length as i32;
344 }
345 if bytes_read != param_length as i32 {
346 return Err(Error::new(
347 ErrorKind::InvalidData,
348 format!(
349 "Capability length {} does not match parameter length {}",
350 bytes_read, param_length
351 ),
352 ));
353 } else {
354 OpenParameter::Capabilities(capabilities)
355 }
356 } else {
357 let mut value = vec![0; param_length as usize];
358 stream.read_exact(&mut value)?;
359 OpenParameter::Unknown {
360 param_type,
361 param_length,
362 value,
363 }
364 },
365 ))
366 }
367
368 fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
369 match self {
370 OpenParameter::Capabilities(caps) => {
371 let mut cap_buf: Vec<u8> = Vec::with_capacity(20);
372 for c in caps.iter() {
373 c.encode(&mut cap_buf)?;
374 }
375 if cap_buf.len() > std::u8::MAX as usize {
376 return Err(Error::new(
377 ErrorKind::Other,
378 format!("Cannot encode capabilities with length {}", cap_buf.len()),
379 ));
380 }
381 buf.write_all(&cap_buf)
382 }
383 OpenParameter::Unknown {
384 param_type,
385 param_length,
386 value,
387 } => {
388 buf.write_u8(*param_type)?;
389 buf.write_u8(*param_length)?;
390 buf.write_all(&value)
391 }
392 }
393 }
394}
395
396#[allow(non_snake_case)]
398#[derive(Clone, Debug, Default)]
399pub struct Capabilities {
400 pub MP_BGP_SUPPORT: HashSet<(AFI, SAFI)>,
403 pub ROUTE_REFRESH_SUPPORT: bool,
405 pub OUTBOUND_ROUTE_FILTERING_SUPPORT: HashSet<(AFI, SAFI, u8, AddPathDirection)>,
407 pub EXTENDED_NEXT_HOP_ENCODING: HashMap<(AFI, SAFI), AFI>,
409 pub BGPSEC_SUPPORT: bool,
411 pub MULTIPLE_LABELS_SUPPORT: HashMap<(AFI, SAFI), u8>,
413 pub GRACEFUL_RESTART_SUPPORT: HashSet<(AFI, SAFI)>,
415 pub FOUR_OCTET_ASN_SUPPORT: bool,
417 pub ADD_PATH_SUPPORT: HashMap<(AFI, SAFI), AddPathDirection>,
419 pub EXTENDED_PATH_NLRI_SUPPORT: bool,
421 pub ENHANCED_ROUTE_REFRESH_SUPPORT: bool,
423 pub LONG_LIVED_GRACEFUL_RESTART: bool,
425}
426
427impl Capabilities {
428 pub fn from_parameters(parameters: Vec<OpenParameter>) -> Self {
430 let mut capabilities = Capabilities::default();
431
432 for parameter in parameters {
433 if let OpenParameter::Capabilities(caps) = parameter {
434 for capability in caps {
435 match capability {
436 OpenCapability::MultiProtocol(family) => {
437 capabilities.MP_BGP_SUPPORT.insert(family);
438 }
439 OpenCapability::RouteRefresh => {
440 capabilities.ROUTE_REFRESH_SUPPORT = true;
441 }
442 OpenCapability::OutboundRouteFiltering(families) => {
443 capabilities.OUTBOUND_ROUTE_FILTERING_SUPPORT = families;
444 }
445 OpenCapability::FourByteASN(_) => {
446 capabilities.FOUR_OCTET_ASN_SUPPORT = true;
447 }
448 OpenCapability::AddPath(paths) => {
449 capabilities.EXTENDED_PATH_NLRI_SUPPORT = true;
450 for path in paths {
451 capabilities
452 .ADD_PATH_SUPPORT
453 .insert((path.0, path.1), path.2);
454 }
455 }
456 _ => (),
458 }
459 }
460 }
461 }
462
463 capabilities
464 }
465}