Skip to main content

bgp_rs/update/
attributes.rs

1use crate::Capabilities;
2
3use byteorder::{BigEndian, ReadBytesExt};
4
5use std::fmt::{Display, Formatter};
6use std::io::{Cursor, Error, ErrorKind, Read};
7use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
8
9use crate::*;
10
11#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
12#[allow(non_camel_case_types)]
13#[allow(missing_docs)]
14pub enum Identifier {
15    ORIGIN = 1,
16    AS_PATH = 2,
17    NEXT_HOP = 3,
18    MULTI_EXIT_DISC = 4,
19    LOCAL_PREF = 5,
20    ATOMIC_AGGREGATOR = 6,
21    AGGREGATOR = 7,
22    COMMUNITY = 8,
23    ORIGINATOR_ID = 9,
24    CLUSTER_LIST = 10,
25    DPA = 11,
26    ADVERTISER = 12,
27    CLUSTER_ID = 13,
28    MP_REACH_NLRI = 14,
29    MP_UNREACH_NLRI = 15,
30    EXTENDED_COMMUNITIES = 16,
31    AS4_PATH = 17,
32    AS4_AGGREGATOR = 18,
33    SSA = 19,
34    CONNECTOR = 20,
35    AS_PATHLIMIT = 21,
36    PMSI_TUNNEL = 22,
37    TUNNEL_ENCAPSULATION = 23,
38    TRAFFIC_ENGINEERING = 24,
39    IPV6_SPECIFIC_EXTENDED_COMMUNITY = 25,
40    AIGP = 26,
41    PE_DISTINGUISHER_LABELS = 27,
42    ENTROPY_LABEL_CAPABILITY = 28,
43    BGP_LS = 29,
44    LARGE_COMMUNITY = 32,
45    BGPSEC_PATH = 33,
46    BGP_PREFIX_SID = 34,
47    ATTR_SET = 128,
48}
49
50/// Represents a path attribute that described meta data of a specific route.
51#[derive(Debug, Clone)]
52#[allow(non_camel_case_types)]
53pub enum PathAttribute {
54    /// Indicates how an UPDATE message has been generated. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
55    ORIGIN(Origin),
56
57    /// Represents the path through which an UPDATE message traveled. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
58    AS_PATH(ASPath),
59
60    /// Indicates IP address that is to be used as a next hop. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
61    NEXT_HOP(IpAddr),
62
63    /// Used to discriminate between multiple exit or entry points. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
64    MULTI_EXIT_DISC(u32),
65
66    /// Represents the degree of preference for internal routes. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
67    LOCAL_PREF(u32),
68
69    /// May be used when a route has been aggregated. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
70    ATOMIC_AGGREGATOR,
71
72    /// May be used to add information on who aggregated this route. Defined in [RFC4271](http://www.iana.org/go/rfc4271).
73    AGGREGATOR((u32, Ipv4Addr)),
74
75    /// Enables users to add extra information. Defined in [RFC1997](http://www.iana.org/go/rfc1997).
76    COMMUNITY(Vec<u32>),
77
78    /// Defined in [RFC4456](http://www.iana.org/go/rfc4456).
79    ORIGINATOR_ID(u32),
80
81    /// Defined in [RFC4456](http://www.iana.org/go/rfc4456).
82    /// Holds a list of CLUSTER_IDs.
83    CLUSTER_LIST(Vec<u32>),
84
85    /// Defined in [RFC6938](http://www.iana.org/go/rfc6938). **(deprecated)**
86    /// Tuple represents the (ASN specifying the preference, DPA value).
87    DPA((u16, u32)),
88
89    /// Defined in [RFC6938](http://www.iana.org/go/rfc6938). **(deprecated)**
90    ADVERTISER,
91
92    /// Defined in [RFC6938](http://www.iana.org/go/rfc6938). **(deprecated)**
93    CLUSTER_ID,
94
95    /// Multi-protocol extensions. Defined in [RFC4760](http://www.iana.org/go/rfc4760).
96    MP_REACH_NLRI(MPReachNLRI),
97
98    /// Multi-protocol extensions. Defined in [RFC4760](http://www.iana.org/go/rfc4760).
99    MP_UNREACH_NLRI(MPUnreachNLRI),
100
101    /// Defined in [RFC4360](http://www.iana.org/go/rfc4360).
102    EXTENDED_COMMUNITIES(Vec<u64>),
103
104    /// AS_PATH using 32-bit ASN. Defined in [RFC6793](http://www.iana.org/go/rfc6793).
105    AS4_PATH(ASPath),
106
107    /// AGGREGATOR using 32-bit ASN. Defined in [RFC6793](http://www.iana.org/go/rfc6793).
108    AS4_AGGREGATOR((u32, Ipv4Addr)),
109
110    /// SAFI Specific Attribute  **(deprecated)**.
111    SSA,
112
113    /// Defined in [RFC6037](http://www.iana.org/go/rfc6037).  **(deprecated)**
114    CONNECTOR(Ipv4Addr),
115
116    /// Defined [here](http://www.iana.org/go/draft-ietf-idr-as-pathlimit).  **(deprecated)**
117    AS_PATHLIMIT((u8, u32)),
118
119    /// Defined in [RFC6514](http://www.iana.org/go/rfc6514).
120    /// Specifies the (Flags, Tunnel Type + MPLS Label, Tunnel Identifier) fields.
121    PMSI_TUNNEL((u8, u32, Vec<u8>)),
122
123    /// Defined in [RFC5512](http://www.iana.org/go/rfc5512).
124    /// Specifies the (Tunnel Type, Value) fields.
125    TUNNEL_ENCAPSULATION((u16, Vec<u8>)),
126
127    /// Defined in [RFC5543](http://www.iana.org/go/rfc5543).
128    TRAFFIC_ENGINEERING,
129
130    /// Defined in [RFC5701](http://www.iana.org/go/rfc5701).
131    /// Specifies the (Transitive, Sub-type, Global Administrator, Local Administrator) fields.
132    IPV6_SPECIFIC_EXTENDED_COMMUNITY((u8, u8, Ipv6Addr, u16)),
133
134    /// Defined in [RFC7311](http://www.iana.org/go/rfc7311).
135    /// Specifies the (Type, Value) fields.
136    AIGP((u8, Vec<u8>)),
137
138    /// Defined in [RFC6514](http://www.iana.org/go/rfc6514).
139    PE_DISTINGUISHER_LABELS,
140
141    /// Defined in [RFC6790](http://www.iana.org/go/rfc6790).
142    ENTROPY_LABEL_CAPABILITY,
143
144    /// Defined in [RFC7752](http://www.iana.org/go/rfc7752).  **(deprecated)**
145    BGP_LS,
146
147    /// Defined in [RFC8092](http://www.iana.org/go/rfc8092).
148    LARGE_COMMUNITY(Vec<(u32, u32, u32)>),
149
150    /// Defined in [RFC8205](http://www.iana.org/go/rfc8205).
151    BGPSEC_PATH,
152
153    /// Defined [here](http://www.iana.org/go/draft-ietf-idr-bgp-prefix-sid-27).
154    BGP_PREFIX_SID,
155
156    /// Defined in [RFC6368](http://www.iana.org/go/rfc6368).
157    ATTR_SET((u32, Vec<PathAttribute>)),
158}
159
160struct ReadCountingStream<'a, R: Read> {
161    stream: &'a mut R,
162    remaining: usize,
163}
164
165impl<'a, R: Read> Read for ReadCountingStream<'a, R> {
166    fn read(&mut self, buff: &mut [u8]) -> Result<usize, Error> {
167        if buff.len() > self.remaining {
168            return Err(Error::new(
169                ErrorKind::Other,
170                "Attribute decode tried to read more than its length",
171            ));
172        }
173        let res = self.stream.read(buff)?;
174        self.remaining -= res;
175        Ok(res)
176    }
177}
178
179impl PathAttribute {
180    ///
181    /// Reads a Path Attribute from an object that implements Read.
182    ///
183    /// # Panics
184    /// This function does not panic.
185    ///
186    /// # Errors
187    /// Any IO error will be returned while reading from the stream.
188    /// Behavior is undefined when an ill-formatted stream is provided.
189    ///
190    /// # Safety
191    /// This function does not make use of unsafe code.
192    ///
193    pub fn parse(
194        stream: &mut impl Read,
195        capabilities: &Capabilities,
196    ) -> Result<PathAttribute, Error> {
197        let flags = stream.read_u8()?;
198        let code = stream.read_u8()?;
199
200        // Check if the Extended Length bit is set.
201        let length: u16 = if flags & (1 << 4) == 0 {
202            u16::from(stream.read_u8()?)
203        } else {
204            stream.read_u16::<BigEndian>()?
205        };
206
207        let mut count_stream = ReadCountingStream {
208            stream,
209            remaining: length as usize,
210        };
211
212        let res =
213            PathAttribute::parse_limited(&mut count_stream, capabilities, flags, code, length);
214
215        // Some routes include bogus attributes, which we attempt to parse, but if they're supposed
216        // to be longer than we parsed, just ignore the remaining bytes.
217        if count_stream.remaining != 0 {
218            let mut dummy_buff = vec![0; count_stream.remaining];
219            stream.read_exact(&mut dummy_buff)?;
220        }
221        res
222    }
223
224    fn parse_limited(
225        stream: &mut impl Read,
226        capabilities: &Capabilities,
227        _flags: u8,
228        code: u8,
229        length: u16,
230    ) -> Result<PathAttribute, Error> {
231        match code {
232            1 => Ok(PathAttribute::ORIGIN(Origin::parse(stream)?)),
233            2 => Ok(PathAttribute::AS_PATH(ASPath::parse(
234                stream,
235                length,
236                capabilities,
237            )?)),
238            3 => {
239                let ip: IpAddr = if length == 4 {
240                    IpAddr::V4(Ipv4Addr::from(stream.read_u32::<BigEndian>()?))
241                } else {
242                    IpAddr::V6(Ipv6Addr::from(stream.read_u128::<BigEndian>()?))
243                };
244
245                Ok(PathAttribute::NEXT_HOP(ip))
246            }
247            4 => Ok(PathAttribute::MULTI_EXIT_DISC(
248                stream.read_u32::<BigEndian>()?,
249            )),
250            5 => Ok(PathAttribute::LOCAL_PREF(stream.read_u32::<BigEndian>()?)),
251            6 => Ok(PathAttribute::ATOMIC_AGGREGATOR),
252            7 => {
253                let asn = if length == 6 {
254                    u32::from(stream.read_u16::<BigEndian>()?)
255                } else {
256                    stream.read_u32::<BigEndian>()?
257                };
258
259                let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?);
260                Ok(PathAttribute::AGGREGATOR((asn, ip)))
261            }
262            8 => {
263                let mut communities = Vec::with_capacity(usize::from(length / 4));
264                for _ in 0..(length / 4) {
265                    communities.push(stream.read_u32::<BigEndian>()?)
266                }
267
268                Ok(PathAttribute::COMMUNITY(communities))
269            }
270            9 => Ok(PathAttribute::ORIGINATOR_ID(
271                stream.read_u32::<BigEndian>()?,
272            )),
273            10 => {
274                let mut ids = Vec::with_capacity(usize::from(length / 4));
275                for _ in 0..(length / 4) {
276                    ids.push(stream.read_u32::<BigEndian>()?)
277                }
278
279                Ok(PathAttribute::CLUSTER_LIST(ids))
280            }
281            11 => Ok(PathAttribute::DPA((
282                stream.read_u16::<BigEndian>()?,
283                stream.read_u32::<BigEndian>()?,
284            ))),
285            14 => Ok(PathAttribute::MP_REACH_NLRI(MPReachNLRI::parse(
286                stream,
287                length,
288                capabilities,
289            )?)),
290            15 => Ok(PathAttribute::MP_UNREACH_NLRI(MPUnreachNLRI::parse(
291                stream,
292                length,
293                capabilities,
294            )?)),
295            16 => {
296                let mut communities = Vec::with_capacity(usize::from(length / 8));
297                for _ in 0..(length / 8) {
298                    communities.push(stream.read_u64::<BigEndian>()?)
299                }
300
301                Ok(PathAttribute::EXTENDED_COMMUNITIES(communities))
302            }
303            17 => Ok(PathAttribute::AS4_PATH(ASPath::parse(
304                stream,
305                length,
306                capabilities,
307            )?)),
308            18 => {
309                let asn = stream.read_u32::<BigEndian>()?;
310                let ip = Ipv4Addr::from(stream.read_u32::<BigEndian>()?);
311                Ok(PathAttribute::AS4_AGGREGATOR((asn, ip)))
312            }
313            20 => {
314                let mut buf = vec![0u8; length as usize];
315                stream.read_exact(&mut buf)?;
316
317                let mut cur = Cursor::new(buf);
318                let _ = cur.read_u16::<BigEndian>()?;
319                // I have no idea what this is.. both Junos and IOS-XR send this but it's
320                // not covered in the RFC at all
321                let _ = cur.read_u64::<BigEndian>()?;
322                let ip = Ipv4Addr::from(cur.read_u32::<BigEndian>()?);
323
324                Ok(PathAttribute::CONNECTOR(ip))
325            }
326            21 => {
327                let limit = stream.read_u8()?;
328                let asn = stream.read_u32::<BigEndian>()?;
329
330                Ok(PathAttribute::AS_PATHLIMIT((limit, asn)))
331            }
332            22 => {
333                let flags = stream.read_u8()?;
334                let label = stream.read_u32::<BigEndian>()?;
335                let mut identifier = vec![0; usize::from(length - 4)];
336                stream.read_exact(&mut identifier)?;
337
338                Ok(PathAttribute::PMSI_TUNNEL((flags, label, identifier)))
339            }
340            23 => {
341                let tunnel_type = stream.read_u16::<BigEndian>()?;
342                let length = stream.read_u16::<BigEndian>()?;
343                let mut value = vec![0; usize::from(length)];
344                stream.read_exact(&mut value)?;
345
346                Ok(PathAttribute::TUNNEL_ENCAPSULATION((tunnel_type, value)))
347            }
348            25 => {
349                let transitive = stream.read_u8()?;
350                let subtype = stream.read_u8()?;
351                let global_admin = Ipv6Addr::from(stream.read_u128::<BigEndian>()?);
352                let local_admin = stream.read_u16::<BigEndian>()?;
353
354                Ok(PathAttribute::IPV6_SPECIFIC_EXTENDED_COMMUNITY((
355                    transitive,
356                    subtype,
357                    global_admin,
358                    local_admin,
359                )))
360            }
361            26 => {
362                let aigp_type = stream.read_u8()?;
363                let length = stream.read_u16::<BigEndian>()?;
364                if length < 3 {
365                    Err(Error::new(
366                        ErrorKind::Other,
367                        format!("Bogus AIGP length: {} < 3", length),
368                    ))
369                } else {
370                    let mut value = vec![0; usize::from(length - 3)];
371                    stream.read_exact(&mut value)?;
372
373                    Ok(PathAttribute::AIGP((aigp_type, value)))
374                }
375            }
376            28 => {
377                stream.read_exact(&mut vec![0u8; length as usize])?;
378
379                Ok(PathAttribute::ENTROPY_LABEL_CAPABILITY)
380            }
381            32 => {
382                let mut communities: Vec<(u32, u32, u32)> =
383                    Vec::with_capacity(usize::from(length / 12));
384                for _ in 0..(length / 12) {
385                    let admin = stream.read_u32::<BigEndian>()?;
386                    let part1 = stream.read_u32::<BigEndian>()?;
387                    let part2 = stream.read_u32::<BigEndian>()?;
388                    communities.push((admin, part1, part2))
389                }
390
391                Ok(PathAttribute::LARGE_COMMUNITY(communities))
392            }
393            128 => {
394                let asn = stream.read_u32::<BigEndian>()?;
395
396                let mut buffer = vec![0; length as usize - 4];
397                stream.read_exact(&mut buffer)?;
398
399                let mut cursor = Cursor::new(buffer);
400
401                let mut attributes = Vec::with_capacity(5);
402                while cursor.position() < (length - 4).into() {
403                    let result = PathAttribute::parse(&mut cursor, capabilities);
404                    match result {
405                        Err(x) => println!("Error: {}", x),
406                        Ok(x) => attributes.push(x),
407                    }
408                }
409
410                Ok(PathAttribute::ATTR_SET((asn, attributes)))
411            }
412            x => {
413                let mut buffer = vec![0; usize::from(length)];
414                stream.read_exact(&mut buffer)?;
415
416                Err(Error::new(
417                    ErrorKind::Other,
418                    format!("Unknown path attribute type found: {}", x),
419                ))
420            }
421        }
422    }
423
424    /// Retrieve the identifier belonging to this PathAttribute
425    pub fn id(&self) -> Identifier {
426        match self {
427            PathAttribute::ORIGIN(_) => Identifier::ORIGIN,
428            PathAttribute::AS_PATH(_) => Identifier::AS_PATH,
429            PathAttribute::NEXT_HOP(_) => Identifier::NEXT_HOP,
430            PathAttribute::MULTI_EXIT_DISC(_) => Identifier::MULTI_EXIT_DISC,
431            PathAttribute::LOCAL_PREF(_) => Identifier::LOCAL_PREF,
432            PathAttribute::ATOMIC_AGGREGATOR => Identifier::ATOMIC_AGGREGATOR,
433            PathAttribute::AGGREGATOR(_) => Identifier::AGGREGATOR,
434            PathAttribute::COMMUNITY(_) => Identifier::COMMUNITY,
435            PathAttribute::ORIGINATOR_ID(_) => Identifier::ORIGINATOR_ID,
436            PathAttribute::CLUSTER_LIST(_) => Identifier::CLUSTER_LIST,
437            PathAttribute::DPA(_) => Identifier::DPA,
438            PathAttribute::ADVERTISER => Identifier::ADVERTISER,
439            PathAttribute::CLUSTER_ID => Identifier::CLUSTER_ID,
440            PathAttribute::MP_REACH_NLRI(_) => Identifier::MP_REACH_NLRI,
441            PathAttribute::MP_UNREACH_NLRI(_) => Identifier::MP_UNREACH_NLRI,
442            PathAttribute::EXTENDED_COMMUNITIES(_) => Identifier::EXTENDED_COMMUNITIES,
443            PathAttribute::AS4_PATH(_) => Identifier::AS4_PATH,
444            PathAttribute::AS4_AGGREGATOR(_) => Identifier::AS4_AGGREGATOR,
445            PathAttribute::SSA => Identifier::SSA,
446            PathAttribute::CONNECTOR(_) => Identifier::CONNECTOR,
447            PathAttribute::AS_PATHLIMIT(_) => Identifier::AS_PATHLIMIT,
448            PathAttribute::PMSI_TUNNEL(_) => Identifier::PMSI_TUNNEL,
449            PathAttribute::TUNNEL_ENCAPSULATION(_) => Identifier::TUNNEL_ENCAPSULATION,
450            PathAttribute::TRAFFIC_ENGINEERING => Identifier::TRAFFIC_ENGINEERING,
451            PathAttribute::IPV6_SPECIFIC_EXTENDED_COMMUNITY(_) => {
452                Identifier::IPV6_SPECIFIC_EXTENDED_COMMUNITY
453            }
454            PathAttribute::AIGP(_) => Identifier::AIGP,
455            PathAttribute::PE_DISTINGUISHER_LABELS => Identifier::PE_DISTINGUISHER_LABELS,
456            PathAttribute::ENTROPY_LABEL_CAPABILITY => Identifier::ENTROPY_LABEL_CAPABILITY,
457            PathAttribute::BGP_LS => Identifier::BGP_LS,
458            PathAttribute::LARGE_COMMUNITY(_) => Identifier::LARGE_COMMUNITY,
459            PathAttribute::BGPSEC_PATH => Identifier::BGPSEC_PATH,
460            PathAttribute::BGP_PREFIX_SID => Identifier::BGP_PREFIX_SID,
461            PathAttribute::ATTR_SET(_) => Identifier::ATTR_SET,
462        }
463    }
464
465    /// Encode path attribute to bytes
466    pub fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
467        use PathAttribute::*;
468        let mut bytes = Vec::with_capacity(8);
469        let (mut flags, identifier) = match self {
470            ORIGIN(origin) => {
471                let value: u8 = match origin {
472                    Origin::IGP => 0,
473                    Origin::EGP => 1,
474                    Origin::INCOMPLETE => 2,
475                };
476                bytes.write_u8(value)?;
477                (0x40, Identifier::ORIGIN)
478            }
479            AS_PATH(as_path) => {
480                as_path.encode(&mut bytes)?;
481                (0x40, Identifier::AS_PATH)
482            }
483            COMMUNITY(communities) => {
484                for comm in communities {
485                    bytes.write_u32::<BigEndian>(*comm)?;
486                }
487                (0xc0, Identifier::COMMUNITY)
488            }
489            NEXT_HOP(next_hop) => {
490                match next_hop {
491                    IpAddr::V4(addr) => bytes.write_all(&addr.octets())?,
492                    IpAddr::V6(addr) => bytes.write_all(&addr.octets())?,
493                }
494                (0x40, Identifier::NEXT_HOP)
495            }
496            MULTI_EXIT_DISC(med) => {
497                bytes.write_u32::<BigEndian>(*med)?;
498                (0x80, Identifier::MULTI_EXIT_DISC)
499            }
500            LOCAL_PREF(pref) => {
501                bytes.write_u32::<BigEndian>(*pref)?;
502                (0x40, Identifier::LOCAL_PREF)
503            }
504            MP_REACH_NLRI(mp_reach) => {
505                mp_reach.encode(&mut bytes)?;
506                (0x80, Identifier::MP_REACH_NLRI)
507            }
508            MP_UNREACH_NLRI(mp_unreach) => {
509                mp_unreach.encode(&mut bytes)?;
510                (0x80, Identifier::MP_UNREACH_NLRI)
511            }
512            EXTENDED_COMMUNITIES(ext_communities) => {
513                for comm in ext_communities {
514                    bytes.write_u64::<BigEndian>(*comm)?;
515                }
516                (0xc0, Identifier::EXTENDED_COMMUNITIES)
517            }
518            CLUSTER_LIST(clusters) => {
519                for cluster in clusters {
520                    bytes.write_u32::<BigEndian>(*cluster)?;
521                }
522                (0x80, Identifier::CLUSTER_LIST)
523            }
524            ORIGINATOR_ID(origin_id) => {
525                bytes.write_u32::<BigEndian>(*origin_id)?;
526                (0x80, Identifier::ORIGINATOR_ID)
527            }
528            AS4_PATH(as_path) => {
529                as_path.encode(&mut bytes)?;
530                (0xc0, Identifier::AS4_PATH)
531            }
532            AGGREGATOR((asn, ip)) => {
533                bytes.write_u16::<BigEndian>(*asn as u16)?;
534                bytes.write_u32::<BigEndian>((*ip).into())?;
535                (0xc0, Identifier::AGGREGATOR)
536            }
537            _ => {
538                unimplemented!("{:?}", self);
539            }
540        };
541        // Use extended length if the attribute bytes are greater than 255
542        // Or if a PathAttribute has explicitly set the ext-length bit (0x10)
543        let is_extended_length = bytes.len() > std::u8::MAX as usize || (flags & 0x10) == 0x10;
544        if is_extended_length {
545            flags |= 0x10; // Set extended length bit
546        }
547        buf.write_u8(flags)?;
548        buf.write_u8(identifier as u8)?;
549        if is_extended_length {
550            buf.write_u16::<BigEndian>(bytes.len() as u16)?;
551        } else {
552            buf.write_u8(bytes.len() as u8)?;
553        }
554        buf.write_all(&bytes)
555    }
556}
557
558/// Indicated how an announcement has been generated.
559#[derive(Debug, Clone)]
560pub enum Origin {
561    /// Generated by an Interior Gateway Protocol
562    IGP,
563
564    /// Generated by an Exterior Gateway Protocol
565    EGP,
566
567    /// Unknown how this route has been generated.
568    INCOMPLETE,
569}
570
571impl Origin {
572    fn parse(stream: &mut impl Read) -> Result<Origin, Error> {
573        match stream.read_u8()? {
574            0 => Ok(Origin::IGP),
575            1 => Ok(Origin::EGP),
576            2 => Ok(Origin::INCOMPLETE),
577            _ => Err(Error::new(ErrorKind::Other, "Unknown origin type found.")),
578        }
579    }
580}
581
582impl Display for Origin {
583    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
584        match self {
585            Origin::IGP => write!(f, "IGP"),
586            Origin::EGP => write!(f, "EGP"),
587            Origin::INCOMPLETE => write!(f, "Incomplete"),
588        }
589    }
590}
591
592/// Represents the path that an announcement has traveled.
593#[derive(Debug, Clone)]
594pub struct ASPath {
595    /// A collection of segments that together form the path that a message has traveled.
596    pub segments: Vec<Segment>,
597}
598
599impl ASPath {
600    fn parse(stream: &mut impl Read, length: u16, _: &Capabilities) -> Result<ASPath, Error> {
601        let segments = Segment::parse_unknown_segments(stream, length)?;
602        Ok(ASPath { segments })
603    }
604
605    /// Retrieves the AS that originated the announcement.
606    /// Returns None if it is originated by as an AS_SET.
607    pub fn origin(&self) -> Option<u32> {
608        let segment = self.segments.last()?;
609        if let Segment::AS_SEQUENCE(x) = segment {
610            return Some(*x.last()?);
611        }
612        None
613    }
614
615    /// Does this AsPath contain 4-byte ASNs
616    pub fn has_4_byte_asns(&self) -> bool {
617        self.segments.iter().any(|s| s.has_4_byte_asns())
618    }
619
620    /// Returns the AS_PATH as a singular sequence of ASN.
621    /// Returns None if there are any AS_SET segments.
622    pub fn sequence(&self) -> Option<Vec<u32>> {
623        let mut sequence = Vec::with_capacity(8);
624        for segment in &self.segments {
625            match segment {
626                Segment::AS_SEQUENCE(x) => sequence.extend(x),
627                Segment::AS_SET(_) => return None,
628            }
629        }
630
631        Some(sequence)
632    }
633
634    /// Encode AS Path to bytes
635    pub fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
636        for segment in &self.segments {
637            let (path_type, seq) = match segment {
638                Segment::AS_SET(set) => (1u8, set),
639                Segment::AS_SEQUENCE(seq) => (2u8, seq),
640            };
641            buf.write_u8(path_type)?;
642            buf.write_u8(seq.len() as u8)?;
643            let is_4_byte_aspath = self.has_4_byte_asns();
644            for asn in seq.iter() {
645                if is_4_byte_aspath {
646                    buf.write_u32::<BigEndian>(*asn)?;
647                } else {
648                    buf.write_u16::<BigEndian>(*asn as u16)?;
649                }
650            }
651        }
652        Ok(())
653    }
654}
655
656/// Represents the segment type of an AS_PATH. Can be either AS_SEQUENCE or AS_SET.
657#[derive(Debug, Clone)]
658#[allow(non_camel_case_types)]
659pub enum Segment {
660    /// Represents a sequence of ASN that an announcement traveled through.
661    AS_SEQUENCE(Vec<u32>),
662
663    /// Represents a set of ASN through which a BGP message traveled.
664    AS_SET(Vec<u32>),
665}
666
667impl Segment {
668    /// Are there any 4-byte ASNs in the Segment
669    pub fn has_4_byte_asns(&self) -> bool {
670        let asns = match &self {
671            Segment::AS_SEQUENCE(asns) => asns,
672            Segment::AS_SET(asns) => asns,
673        };
674        asns.iter().any(|a| a > &(std::u16::MAX as u32))
675    }
676
677    fn parse_unknown_segments(stream: &mut impl Read, length: u16) -> Result<Vec<Segment>, Error> {
678        // Read in everything so we can touch the buffer multiple times in order to
679        // work out what we have
680        let mut buf = vec![0u8; length as usize];
681        stream.read_exact(&mut buf)?;
682        let size = buf.len();
683        let mut cur = Cursor::new(buf);
684
685        'as_len: for i in 1..=2u64 {
686            cur.set_position(0);
687
688            // Now attempt to work out whether the first segment is 2 byte or 4 byte
689            let assumed_as_len = i * 2;
690            let mut total_segments = 0u64;
691
692            while cur.position() < size as u64 {
693                let segment_type = cur.read_u8()?;
694                let segment_len = cur.read_u8()?;
695
696                // If the second segment type isn't valid, pretty sure this isn't 2 byte
697                if (assumed_as_len == 2 && total_segments >= 1)
698                    && (segment_type < 1 || segment_type > 2)
699                {
700                    continue 'as_len;
701                }
702
703                cur.set_position(cur.position() + (u64::from(segment_len) * assumed_as_len));
704                total_segments += 1;
705            }
706
707            if cur.position() == u64::from(length) {
708                cur.set_position(0);
709
710                match i {
711                    1 => {
712                        return Segment::parse_u16_segments(&mut cur, length);
713                    }
714                    2 => {
715                        return Segment::parse_u32_segments(&mut cur, length);
716                    }
717                    _ => {}
718                };
719            }
720        }
721
722        Err(Error::new(
723            ErrorKind::Other,
724            "Invalid AS_PATH length detected",
725        ))
726    }
727
728    fn parse_u16_segments(stream: &mut impl Read, length: u16) -> Result<Vec<Segment>, Error> {
729        let mut segments: Vec<Segment> = Vec::with_capacity(1);
730
731        // While there are multiple AS_PATH segments, parse the segments.
732        let mut size = length;
733        while size != 0 {
734            // The type of a segment, either AS_SET or AS_SEQUENCE.
735            let segment_type = stream.read_u8()?;
736
737            // The amount of ASN inside a segment.
738            let segment_length = stream.read_u8()?;
739
740            // Construct a Vec<u32> such that one interface be used when handling AS_PATHs.
741            let mut elements: Vec<u32> = Vec::with_capacity(usize::from(segment_length));
742
743            // Parse the ASN as 16-bit ASN.
744            for _ in 0..segment_length {
745                elements.push(u32::from(stream.read_u16::<BigEndian>()?));
746            }
747
748            match segment_type {
749                1 => segments.push(Segment::AS_SET(elements)),
750                2 => segments.push(Segment::AS_SEQUENCE(elements)),
751                x => {
752                    return Err(Error::new(
753                        ErrorKind::Other,
754                        format!("Unknown AS_PATH (2 byte) segment type found: {}", x),
755                    ));
756                }
757            }
758
759            size -= 2 + (u16::from(segment_length) * 2);
760        }
761
762        Ok(segments)
763    }
764
765    fn parse_u32_segments(stream: &mut impl Read, length: u16) -> Result<Vec<Segment>, Error> {
766        let mut segments: Vec<Segment> = Vec::with_capacity(1);
767
768        // While there are multiple AS_PATH segments, parse the segments.
769        let mut size: i32 = i32::from(length);
770
771        while size != 0 {
772            // The type of a segment, either AS_SET or AS_SEQUENCE.
773            let segment_type = stream.read_u8()?;
774
775            // The amount of ASN inside a segment.
776            let segment_length = stream.read_u8()?;
777
778            // Construct a Vec<u32> such that one interface be used when handling AS_PATHs.
779            let mut elements: Vec<u32> = Vec::with_capacity(usize::from(segment_length));
780
781            // Parse the ASN as 32-bit ASN.
782            for _ in 0..segment_length {
783                elements.push(stream.read_u32::<BigEndian>()?);
784            }
785
786            match segment_type {
787                1 => segments.push(Segment::AS_SET(elements)),
788                2 => segments.push(Segment::AS_SEQUENCE(elements)),
789                x => {
790                    return Err(Error::new(
791                        ErrorKind::Other,
792                        format!("Unknown AS_PATH (4 byte) segment type found: {}", x),
793                    ));
794                }
795            }
796
797            size -= 2 + i32::from(u16::from(segment_length) * 4);
798        }
799
800        Ok(segments)
801    }
802}