Skip to main content

bgp_packet/
open.rs

1use std::net::Ipv4Addr;
2
3use bitfield::bitfield;
4use bytes::{BufMut, BytesMut};
5use eyre::{Result, bail, eyre};
6use nom::{
7    Err::Failure,
8    IResult, Parser,
9    combinator::peek,
10    number::complete::{be_u8, be_u16, be_u24, be_u32},
11};
12use serde::{Deserialize, Serialize};
13use strum::EnumDiscriminants;
14
15use crate::{
16    constants::{AddressFamilyId, SubsequentAfi},
17    parser::{BgpParserError, ParserContext, ProtocolErrorValues},
18};
19
20macro_rules! fail {
21    ( $($arg:expr),* $(,)? ) => {
22        Failure(BgpParserError::Eyre(eyre!($($arg),*)))
23    };
24}
25
26/// Contains messages related to the BGP Open message,
27/// i.e. the Open message itself and options / capabilities.
28#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
29pub struct OpenMessage {
30    /// Version of the BGP protocol in use.
31    pub version: u8,
32    /// AS Number of the BGP speaker, or AS_TRANS if using 4 byte ASN.
33    pub asn: u16,
34    /// Hold time parameter in seconds.
35    pub hold_time: u16,
36    /// Global identifier of the BGP speaker.
37    pub identifier: Ipv4Addr,
38    /// Options.
39    pub options: Vec<OpenOption>,
40}
41
42impl OpenMessage {
43    /// The from_wire implementation parses a BGP Open message from a buffer
44    /// containing the payload of the open message (i.e. without the BGP header / type / length)
45    /// and the buf passed in must contain only the open message.
46    pub fn from_wire<'a>(
47        ctx: &ParserContext,
48        buf: &'a [u8],
49    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
50        let (buf, version) = be_u8(buf)?;
51        if version != 4 {
52            return Err(Failure(BgpParserError::ProtocolError(
53                ProtocolErrorValues::UnknownOpenOption(version),
54            )));
55        }
56
57        let (buf, asn) = be_u16(buf)?;
58        let (buf, hold_time) = be_u16(buf)?;
59        let (buf, identifier) = be_u32(buf)?;
60
61        // RFC 9072: Extended Open Options length.
62        // To encode a capabilities length greater than 255 the packet structure
63        // is prefixed with the extended length option (255), a length of 255,
64        // and then a u16 containing the payload option.
65        let (buf, opt_len) = be_u8(buf)?;
66        let (_, inner) = peek(be_u8).parse(buf)?;
67
68        let (buf, opt_len) = if inner == 255 {
69            let (buf, _) = be_u8(buf)?; // Skip past the inner type.
70            be_u16(buf)?
71        } else {
72            (buf, opt_len as u16)
73        };
74
75        let (buf, opt_buf) = nom::bytes::take(opt_len).parse(buf)?;
76        let (opt_buf, options) =
77            nom::multi::many0(|buf| OpenOption::from_wire(ctx, buf)).parse(opt_buf)?;
78
79        if !opt_buf.is_empty() {
80            return Err(fail!(
81                "Leftover bytes after parsing open options: {:x?}",
82                opt_buf
83            ));
84        }
85
86        Ok((
87            buf,
88            Self {
89                version,
90                asn,
91                hold_time,
92                identifier: Ipv4Addr::from(identifier),
93                options,
94            },
95        ))
96    }
97
98    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
99        out.put_u8(self.version);
100        out.put_u16(self.asn);
101        out.put_u16(self.hold_time);
102        out.put_u32(self.identifier.into());
103
104        // RFC 9072: Depending on whether the options fit in < 255 bytes
105        // we encode the length accordingly.
106        let opt_len = self
107            .options
108            .iter()
109            .map(|o| o.wire_len(ctx))
110            .sum::<Result<u16>>()?;
111        if opt_len > u8::MAX as u16 {
112            out.put_u8(0xff); // Set length to 255 as per RFC 9072.
113            out.put_u8(0xff); // Open option type for Ext length.
114            out.put_u16(opt_len);
115        } else {
116            out.put_u8(opt_len as u8);
117        }
118
119        for option in &self.options {
120            option.to_wire(ctx, out)?;
121        }
122
123        Ok(())
124    }
125
126    pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
127        let mut total = 10;
128        let opt_len = self
129            .options
130            .iter()
131            .map(|o| o.wire_len(ctx))
132            .sum::<Result<u16>>()?;
133        if opt_len > u8::MAX as u16 {
134            total += opt_len + 3; // 1+2 bytes extra for the type + extended length.
135        } else {
136            total += opt_len;
137        }
138        Ok(total)
139    }
140}
141
142#[derive(Debug, Serialize, Deserialize, EnumDiscriminants, PartialEq, Eq)]
143#[repr(u8)]
144pub enum OpenOption {
145    Capabilities(Vec<Capability>) = 2,
146}
147
148impl OpenOption {
149    pub fn from_wire<'a>(
150        ctx: &ParserContext,
151        buf: &'a [u8],
152    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
153        let (buf, type_code) = be_u8(buf)?;
154        let (buf, length) = be_u8(buf)?;
155
156        let (buf, payload) = nom::bytes::take(length).parse(buf)?;
157
158        match type_code {
159            v if v == OpenOptionDiscriminants::Capabilities as u8 => {
160                let (payload_consumed, (capabilities, _eof)) = nom::multi::many_till(
161                    |buf| Capability::from_wire(ctx, buf),
162                    nom::combinator::eof,
163                )
164                .parse(payload)?;
165
166                if !payload_consumed.is_empty() {
167                    return Err(fail!(
168                        "Leftover bytes when parsing BGP capabilities: {:x?}",
169                        payload_consumed
170                    ));
171                }
172                Ok((buf, Self::Capabilities(capabilities)))
173            }
174            other => {
175                return Err(Failure(BgpParserError::ProtocolError(
176                    ProtocolErrorValues::UnknownOpenOption(other),
177                )));
178            }
179        }
180    }
181
182    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
183        match self {
184            OpenOption::Capabilities(items) => {
185                let cap_len = items.iter().map(|i| i.wire_len(ctx)).sum::<Result<u16>>()?;
186                if cap_len > u8::MAX as u16 {
187                    bail!("Cannot write Capabilities with more than u8::MAX length");
188                }
189                out.put_u8(OpenOptionDiscriminants::Capabilities as u8);
190                out.put_u8(cap_len as u8);
191                for item in items {
192                    item.to_wire(ctx, out)?;
193                }
194            }
195        }
196        Ok(())
197    }
198
199    pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
200        match self {
201            OpenOption::Capabilities(items) => items
202                .iter()
203                .map(|i| i.wire_len(ctx))
204                .sum::<Result<u16>>()
205                .map(|v| v + 2),
206        }
207    }
208}
209
210#[derive(Debug, Serialize, Deserialize, EnumDiscriminants, PartialEq, Eq)]
211#[repr(u8)]
212pub enum Capability {
213    /// MultiProtocol Extension (RFC 2858).
214    MultiProtocol {
215        afi: AddressFamilyId,
216        safi: SubsequentAfi,
217    } = 1,
218    /// Route Refresh capability (RFC 2918).
219    RouteRefresh = 2,
220    /// Outbound Route Filtering (RFC 5291).
221    /// https://datatracker.ietf.org/doc/html/rfc5291
222    OutboundRouteFilter {
223        afi: AddressFamilyId,
224        safi: SubsequentAfi,
225        orfs: Vec<OutboundRouteFilterEntry>,
226    } = 3,
227    /// Extended Next Hop encoding (RFC 8950).
228    ExtendedNextHop {
229        entries: Vec<ExtendedNextHop>,
230    } = 5,
231    /// Extended Message (RFC 8654).
232    ExtendedMessage = 6,
233    /// BGPSec (RFC 8205).
234    BgpSec {
235        header: BgpSecHeader,
236        afi: AddressFamilyId,
237    } = 7,
238    /// Multiple labels compatibility (RFC 8277).
239    MultiLabelCompat {} = 8,
240    BgpRole {
241        role: BgpRole,
242    } = 9,
243    /// Graceful restart capability (RFC 4724).
244    GracefulRestart {
245        /// When set to true it means that the BGP speaker has just restarted
246        /// and the peer must not wait for the End of RIB marker from the
247        /// speaker before advertising routing information to the speaker.
248        restart_state: bool,
249        /// This is the estimated time (in seconds) it will take for the
250        /// BGP session to be re-established after a restart.  This can be
251        /// used to speed up routing convergence by its peer in case that
252        /// the BGP speaker does not come back after a restart.
253        restart_time: u16,
254        /// The AFI and SAFI, taken in combination, indicate that Graceful
255        /// Restart is supported for routes that are advertised with the
256        /// same AFI and SAFI.  Routes may be explicitly associated with a
257        /// particular AFI and SAFI using the encoding of [BGP-MP] or
258        /// implicitly associated with <AFI=IPv4, SAFI=Unicast> if using
259        /// the encoding of [BGP-4].
260        entries: Vec<GracefulRestartEntry>,
261    } = 64,
262    /// Four Byte ASN (RFC 4274).
263    FourByteAsn {
264        asn: u32,
265    } = 65,
266    /// Additional Path (RFC 7911).
267    AddPath {
268        afi: AddressFamilyId,
269        safi: SubsequentAfi,
270        send_recv: AddPathSendRecv,
271    } = 69,
272    /// Enhanced Route Refresh (RFC 7313).
273    EnhancedRouteRefresh = 70,
274    /// Long Lived Graceful Restart (RFC 9494).
275    LongLivedGracefulRestart(Vec<LongLivedGracefulRestart>) = 71,
276    /// Unknown Capability encapsulates any capabilities that we don't know about.
277    UnknownCapability {
278        type_code: u8,
279        length: u8,
280        value: Vec<u8>,
281    },
282}
283
284impl Capability {
285    fn discriminant(&self) -> u8 {
286        match self {
287            Capability::MultiProtocol { .. } => CapabilityDiscriminants::MultiProtocol as u8,
288            Capability::RouteRefresh => CapabilityDiscriminants::RouteRefresh as u8,
289            Capability::OutboundRouteFilter { .. } => {
290                CapabilityDiscriminants::OutboundRouteFilter as u8
291            }
292            Capability::ExtendedNextHop { .. } => CapabilityDiscriminants::ExtendedNextHop as u8,
293            Capability::ExtendedMessage => CapabilityDiscriminants::ExtendedMessage as u8,
294            Capability::BgpSec { .. } => CapabilityDiscriminants::BgpSec as u8,
295            Capability::MultiLabelCompat {} => CapabilityDiscriminants::MultiLabelCompat as u8,
296            Capability::BgpRole { .. } => CapabilityDiscriminants::BgpRole as u8,
297            Capability::GracefulRestart { .. } => CapabilityDiscriminants::GracefulRestart as u8,
298            Capability::FourByteAsn { .. } => CapabilityDiscriminants::FourByteAsn as u8,
299            Capability::AddPath { .. } => CapabilityDiscriminants::AddPath as u8,
300            Capability::EnhancedRouteRefresh => CapabilityDiscriminants::EnhancedRouteRefresh as u8,
301            Capability::LongLivedGracefulRestart(_) => {
302                CapabilityDiscriminants::LongLivedGracefulRestart as u8
303            }
304            Capability::UnknownCapability { type_code, .. } => *type_code,
305        }
306    }
307    /// `from_wire` parses a Capability message.
308    pub fn from_wire<'a>(
309        _ctx: &ParserContext,
310        buf: &'a [u8],
311    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
312        let (buf, typ) = be_u8(buf)?;
313        let (buf, len) = be_u8(buf)?;
314        let (buf, cap_buf) = nom::bytes::take(len).parse(buf)?;
315        macro_rules! require_empty {
316            ($buf:expr, $cap_type:expr) => {
317                if !buf.is_empty() {
318                    return Err(nom::Err::Failure(BgpParserError::Eyre(eyre!(
319                        "Unexpected leftover bytes {:x?} while parsing {} capability",
320                        $buf,
321                        $cap_type
322                    ))));
323                }
324            };
325        }
326        match typ {
327            v if v == CapabilityDiscriminants::MultiProtocol as u8 => {
328                // MultiProtocol capability consists of an (AFI, SAFI).
329                let (cap_buf, afi) = be_u16(cap_buf)?;
330                let (cap_buf, _) = be_u8(cap_buf)?;
331                let (cap_buf, safi) = be_u8(cap_buf)?;
332                require_empty!(cap_buf, "MultiProtocol");
333                let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
334                let safi = SubsequentAfi::try_from(safi).map_err(|e| fail!("{}", e))?;
335                Ok((buf, Self::MultiProtocol { afi, safi }))
336            }
337            v if v == CapabilityDiscriminants::RouteRefresh as u8 => {
338                require_empty!(cap_buf, "RouteRefresh");
339                Ok((buf, Self::RouteRefresh))
340            }
341            v if v == CapabilityDiscriminants::OutboundRouteFilter as u8 => {
342                let (cap_buf, afi) = be_u16(cap_buf)?;
343                let (cap_buf, _) = be_u8(cap_buf)?; // Move past reverved byte.
344                let (cap_buf, safi) = be_u8(cap_buf)?;
345                let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
346                let safi = SubsequentAfi::try_from(safi).map_err(|e| fail!("{}", e))?;
347
348                let (cap_buf, _num_orfs) = be_u8(cap_buf)?;
349
350                let parse_orf = |buf: &'a [u8]| -> IResult<
351                    &'a [u8],
352                    OutboundRouteFilterEntry,
353                    BgpParserError<&'a [u8]>,
354                > {
355                    let (buf, orf_type) = be_u8(buf)?;
356                    let (buf, send_recv) = be_u8(buf)?;
357                    let orf_type = OrfType::from(orf_type);
358                    let send_recv = OrfSendRecv::try_from(send_recv)
359                        .map_err(|e| fail!("Failed to parse ORF send/recv: {}", e))?;
360
361                    Ok((
362                        buf,
363                        OutboundRouteFilterEntry {
364                            r#type: orf_type,
365                            send_recv,
366                        },
367                    ))
368                };
369
370                let (cap_buf, orfs) = nom::multi::many0(parse_orf).parse(cap_buf)?;
371                require_empty!(cap_buf, "OutboundRouteFilter");
372
373                Ok((buf, Self::OutboundRouteFilter { afi, safi, orfs }))
374            }
375            v if v == CapabilityDiscriminants::ExtendedNextHop as u8 => {
376                // parse_enh parses a single extended nexthop entry.
377                let parse_enh = |buf: &'a [u8]| -> IResult<
378                    &'a [u8],
379                    ExtendedNextHop,
380                    BgpParserError<&'a [u8]>,
381                > {
382                    let (buf, afi) = be_u16(buf)?;
383                    let (buf, safi) = be_u16(buf)?;
384                    let (buf, nh_afi) = be_u16(buf)?;
385                    let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
386                    let safi = SubsequentAfi::try_from(safi).map_err(|e| fail!("{}", e))?;
387                    let nh_afi = AddressFamilyId::try_from(nh_afi).map_err(|e| fail!("{}", e))?;
388                    Ok((buf, ExtendedNextHop { afi, safi, nh_afi }))
389                };
390                let (cap_buf, entries) = nom::multi::many1(parse_enh).parse(cap_buf)?;
391                require_empty!(cap_buf, "ExtendedNextHop");
392
393                Ok((cap_buf, Self::ExtendedNextHop { entries }))
394            }
395            v if v == CapabilityDiscriminants::ExtendedMessage as u8 => {
396                require_empty!(cap_buf, "ExtendedMessage");
397                Ok((cap_buf, Self::ExtendedMessage))
398            }
399            v if v == CapabilityDiscriminants::BgpSec as u8 => {
400                let (cap_buf, header) = be_u8(cap_buf)?;
401                let (cap_buf, afi) = be_u16(cap_buf)?;
402                let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
403                require_empty!(cap_buf, "BgpSec");
404                Ok((
405                    cap_buf,
406                    Self::BgpSec {
407                        header: BgpSecHeader(header),
408                        afi,
409                    },
410                ))
411            }
412            v if v == CapabilityDiscriminants::MultiLabelCompat as u8 => {
413                todo!()
414            }
415            v if v == CapabilityDiscriminants::BgpRole as u8 => {
416                let (cap_buf, role) = be_u8(cap_buf)?;
417                let role = BgpRole::try_from(role).map_err(|e| fail!("{}", e))?;
418                require_empty!(cap_buf, "BgpRole");
419                Ok((cap_buf, Self::BgpRole { role }))
420            }
421            v if v == CapabilityDiscriminants::GracefulRestart as u8 => {
422                // flags_restart_time is: (4 bits: restart flags, 12 bits: restart time (s)).
423                let (cap_buf, flags_restart_time) = be_u16(cap_buf)?;
424                let restart_state = ((1 << 15) & flags_restart_time) != 0;
425                let restart_time = flags_restart_time & 0xfff;
426                let parse_entry = |buf: &'a [u8]| -> IResult<
427                    &'a [u8],
428                    GracefulRestartEntry,
429                    BgpParserError<&'a [u8]>,
430                > {
431                    let (buf, afi) = be_u16(buf)?;
432                    let (buf, safi) = be_u8(buf)?;
433                    let (buf, flags) = be_u8(buf)?;
434                    let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
435                    let safi = SubsequentAfi::try_from(safi).map_err(|e| fail!("{}", e))?;
436                    Ok((
437                        buf,
438                        GracefulRestartEntry {
439                            afi,
440                            safi,
441                            preserve_forwarding: (flags & (1 << 7)) != 0,
442                        },
443                    ))
444                };
445                let (cap_buf, entries) = nom::multi::many0(parse_entry).parse(cap_buf)?;
446                require_empty!(cap_buf, "GracefulRestart");
447                Ok((
448                    cap_buf,
449                    Self::GracefulRestart {
450                        restart_state,
451                        restart_time,
452                        entries,
453                    },
454                ))
455            }
456            v if v == CapabilityDiscriminants::FourByteAsn as u8 => {
457                let (cap_buf, asn) = be_u32(cap_buf)?;
458                require_empty!(cap_buf, "FourByteAsn");
459                Ok((cap_buf, Self::FourByteAsn { asn }))
460            }
461            v if v == CapabilityDiscriminants::AddPath as u8 => {
462                let (cap_buf, afi) = be_u16(cap_buf)?;
463                let (cap_buf, safi) = be_u8(cap_buf)?;
464                let (cap_buf, send_recv) = be_u8(cap_buf)?;
465                let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
466                let safi = SubsequentAfi::try_from(safi).map_err(|e| fail!("{}", e))?;
467                let send_recv = AddPathSendRecv::try_from(send_recv).map_err(|e| fail!("{}", e))?;
468                require_empty!(cap_buf, "AddPath");
469                Ok((
470                    cap_buf,
471                    Self::AddPath {
472                        afi,
473                        safi,
474                        send_recv,
475                    },
476                ))
477            }
478            v if v == CapabilityDiscriminants::EnhancedRouteRefresh as u8 => {
479                require_empty!(cap_buf, "EnhancedRouteRefresh");
480                Ok((cap_buf, Self::EnhancedRouteRefresh))
481            }
482            v if v == CapabilityDiscriminants::LongLivedGracefulRestart as u8 => {
483                let parse_llgr = |buf: &'a [u8]| -> IResult<
484                    &'a [u8],
485                    LongLivedGracefulRestart,
486                    BgpParserError<&'a [u8]>,
487                > {
488                    let (buf, afi) = be_u16(buf)?;
489                    let (buf, safi) = be_u8(buf)?;
490                    let (buf, flags) = be_u8(buf)?;
491                    let (buf, stale_time) = be_u24(buf)?;
492                    let afi = AddressFamilyId::try_from(afi).map_err(|e| fail!("{}", e))?;
493                    let safi = SubsequentAfi::try_from(safi).map_err(|e| fail!("{}", e))?;
494                    Ok((
495                        buf,
496                        LongLivedGracefulRestart {
497                            afi,
498                            safi,
499                            flags,
500                            stale_time,
501                        },
502                    ))
503                };
504                let (cap_buf, entries) = nom::multi::many0(parse_llgr).parse(cap_buf)?;
505                require_empty!(cap_buf, "LongLivedGracefulRestart");
506                Ok((cap_buf, Self::LongLivedGracefulRestart(entries)))
507            }
508            type_code => {
509                // Handling an unknown capability, so just parse it out and store the value
510                // as bytes.
511                Ok((
512                    &[],
513                    Self::UnknownCapability {
514                        type_code,
515                        length: cap_buf.len() as u8,
516                        value: cap_buf.to_vec(),
517                    },
518                ))
519            }
520        }
521    }
522
523    pub fn to_wire(&self, _ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
524        out.put_u8(self.discriminant());
525        out.put_u8(self.payload_len());
526        match self {
527            Capability::MultiProtocol { afi, safi } => {
528                out.put_u16(*afi as u16);
529                out.put_u8(0); // Reserved byte.
530                out.put_u8(*safi as u8);
531            }
532            Capability::RouteRefresh => {}
533            Capability::OutboundRouteFilter { afi, safi, orfs } => {
534                out.put_u16(*afi as u16);
535                out.put_u8(0); // Reserved byte.
536                out.put_u8(*safi as u8);
537                for orf in orfs {
538                    out.put_u8(orf.r#type.clone().into());
539                    out.put_u8(orf.send_recv.clone() as u8);
540                }
541            }
542            Capability::ExtendedNextHop { entries } => {
543                for entry in entries {
544                    out.put_u16(entry.afi as u16);
545                    out.put_u16(entry.safi as u16);
546                    out.put_u16(entry.nh_afi as u16);
547                }
548            }
549            Capability::ExtendedMessage => {}
550            Capability::BgpSec { header, afi } => {
551                out.put_u8(header.0);
552                out.put_u16(*afi as u16);
553            }
554            Capability::MultiLabelCompat {} => todo!(),
555            Capability::BgpRole { role } => {
556                out.put_u8(role.into());
557            }
558            Capability::GracefulRestart {
559                restart_state,
560                restart_time,
561                entries,
562            } => {
563                let mut flags_restart_time = 0;
564                if *restart_state {
565                    flags_restart_time |= 1 << 15;
566                }
567                if *restart_time > 0xfff {
568                    bail!("Restart time too large to be encoded: {}", restart_time);
569                }
570                flags_restart_time |= restart_time;
571                out.put_u16(flags_restart_time);
572                for entry in entries {
573                    out.put_u16(entry.afi as u16);
574                    out.put_u8(entry.safi as u8);
575                    let mut flags = 0;
576                    if entry.preserve_forwarding {
577                        flags |= 1 << 7;
578                    }
579                    out.put_u8(flags);
580                }
581            }
582            Capability::FourByteAsn { asn } => {
583                out.put_u32(*asn);
584            }
585            Capability::AddPath {
586                afi,
587                safi,
588                send_recv,
589            } => {
590                out.put_u16(*afi as u16);
591                out.put_u8(*safi as u8);
592                out.put_u8(send_recv.into());
593            }
594            Capability::EnhancedRouteRefresh => {}
595            Capability::LongLivedGracefulRestart(llgrs) => {
596                for llgr in llgrs {
597                    out.put_u16(llgr.afi as u16);
598                    out.put_u8(llgr.safi as u8);
599                    out.put_u8(llgr.flags as u8);
600                    out.put(&llgr.stale_time.to_be_bytes()[1..]);
601                }
602            }
603            Capability::UnknownCapability { value, .. } => out.put(value.as_slice()),
604        }
605        Ok(())
606    }
607
608    pub fn wire_len(&self, _ctx: &ParserContext) -> Result<u16> {
609        Ok(2 + self.payload_len() as u16)
610    }
611
612    fn payload_len(&self) -> u8 {
613        match self {
614            Capability::MultiProtocol { .. } => 4,
615            Capability::RouteRefresh => 0,
616            Capability::OutboundRouteFilter { orfs, .. } => 4 + 2 * orfs.len() as u8,
617            Capability::ExtendedNextHop { entries } => 6 * entries.len() as u8,
618            Capability::ExtendedMessage => 0,
619            Capability::BgpSec { .. } => 3,
620            Capability::MultiLabelCompat {} => todo!(),
621            Capability::BgpRole { .. } => 1,
622            Capability::GracefulRestart { entries, .. } => 2 + 4 * entries.len() as u8,
623            Capability::FourByteAsn { .. } => 4,
624            Capability::AddPath { .. } => 4,
625            Capability::EnhancedRouteRefresh => 0,
626            Capability::LongLivedGracefulRestart(long_lived_graceful_restarts) => {
627                8 * long_lived_graceful_restarts.len() as u8
628            }
629            Capability::UnknownCapability { value, .. } => value.len() as u8,
630        }
631    }
632}
633
634/// Represents an entry for the OutboundRouteFilter capability.
635#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
636pub struct OutboundRouteFilterEntry {
637    pub r#type: OrfType,
638    pub send_recv: OrfSendRecv,
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize, EnumDiscriminants, PartialEq, Eq)]
642#[repr(u8)]
643pub enum OrfType {
644    Reserved = 0,
645    Unassigned(u8),
646    AddressPrefix = 64,
647    CoveringPrefix = 65,
648    VpnPrefix = 66,
649    VendorSpecific(u8),
650}
651
652impl From<u8> for OrfType {
653    fn from(value: u8) -> Self {
654        match value {
655            v if v == OrfTypeDiscriminants::Reserved as u8 => Self::Reserved,
656            v if (1..64).contains(&v) => Self::Unassigned(v),
657            v if v == OrfTypeDiscriminants::AddressPrefix as u8 => Self::AddressPrefix,
658            v if v == OrfTypeDiscriminants::CoveringPrefix as u8 => Self::CoveringPrefix,
659            v if v == OrfTypeDiscriminants::VpnPrefix as u8 => Self::VpnPrefix,
660            other => Self::VendorSpecific(other),
661        }
662    }
663}
664
665impl From<OrfType> for u8 {
666    fn from(value: OrfType) -> Self {
667        match value {
668            OrfType::Reserved => OrfTypeDiscriminants::Reserved as u8,
669            OrfType::Unassigned(inner) => inner,
670            OrfType::AddressPrefix => OrfTypeDiscriminants::AddressPrefix as u8,
671            OrfType::CoveringPrefix => OrfTypeDiscriminants::CoveringPrefix as u8,
672            OrfType::VpnPrefix => OrfTypeDiscriminants::VpnPrefix as u8,
673            OrfType::VendorSpecific(inner) => inner,
674        }
675    }
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize, EnumDiscriminants, PartialEq, Eq)]
679#[repr(u8)]
680pub enum OrfSendRecv {
681    Receive = 1,
682    Send = 2,
683    Both = 3,
684}
685
686impl TryFrom<u8> for OrfSendRecv {
687    type Error = eyre::ErrReport;
688
689    fn try_from(value: u8) -> Result<Self> {
690        Ok(match value {
691            value if value == OrfSendRecv::Receive as u8 => OrfSendRecv::Receive,
692            value if value == OrfSendRecv::Send as u8 => OrfSendRecv::Send,
693            value if value == OrfSendRecv::Both as u8 => OrfSendRecv::Both,
694            other => bail!("Invalid ORF send/recv value: {}", other),
695        })
696    }
697}
698
699#[derive(Debug, Clone, Serialize, Deserialize, EnumDiscriminants, PartialEq, Eq)]
700#[repr(u8)]
701pub enum AddPathSendRecv {
702    Receive = 1,
703    Send = 2,
704    Both = 3,
705}
706
707impl TryFrom<u8> for AddPathSendRecv {
708    type Error = eyre::ErrReport;
709
710    fn try_from(value: u8) -> Result<Self> {
711        Ok(match value {
712            value if value == AddPathSendRecv::Receive as u8 => AddPathSendRecv::Receive,
713            value if value == AddPathSendRecv::Send as u8 => AddPathSendRecv::Send,
714            value if value == AddPathSendRecv::Both as u8 => AddPathSendRecv::Both,
715            other => bail!("Invalid AddPathSendRecv send/recv value: {}", other),
716        })
717    }
718}
719
720impl From<&AddPathSendRecv> for u8 {
721    fn from(value: &AddPathSendRecv) -> Self {
722        match value {
723            AddPathSendRecv::Receive => AddPathSendRecv::Receive as u8,
724            AddPathSendRecv::Send => AddPathSendRecv::Send as u8,
725            AddPathSendRecv::Both => AddPathSendRecv::Both as u8,
726        }
727    }
728}
729
730/// `ExtendedNextHop` allows for the nexthop to be for a different address family
731/// than the one being used to advertise prefixes for. E.g. it allows for sending
732/// IPv4 prefixes with an IPv6 nexthop.
733#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
734pub struct ExtendedNextHop {
735    /// The Address Family to update the nexthop format for.
736    pub afi: AddressFamilyId,
737    /// The Subsequent Address Family to update the nexthop format for.
738    pub safi: SubsequentAfi,
739    /// The Address Family that nexthops will be encoded as.
740    pub nh_afi: AddressFamilyId,
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize, EnumDiscriminants, PartialEq, Eq)]
744#[repr(u8)]
745pub enum BgpRole {
746    Provider = 0,
747    RouteServer = 1,
748    RouteServerClient = 2,
749    Customer = 3,
750    Peer = 4,
751    Unknown(u8),
752}
753
754impl From<u8> for BgpRole {
755    fn from(value: u8) -> Self {
756        match value {
757            v if v == BgpRoleDiscriminants::Provider as u8 => Self::Provider,
758            v if v == BgpRoleDiscriminants::RouteServer as u8 => Self::RouteServer,
759            v if v == BgpRoleDiscriminants::RouteServerClient as u8 => Self::RouteServerClient,
760            v if v == BgpRoleDiscriminants::Customer as u8 => Self::Customer,
761            v if v == BgpRoleDiscriminants::Peer as u8 => Self::Peer,
762            other => Self::Unknown(other),
763        }
764    }
765}
766
767impl From<&BgpRole> for u8 {
768    fn from(value: &BgpRole) -> Self {
769        match value {
770            BgpRole::Provider => BgpRoleDiscriminants::Provider as u8,
771            BgpRole::RouteServer => BgpRoleDiscriminants::RouteServer as u8,
772            BgpRole::RouteServerClient => BgpRoleDiscriminants::RouteServerClient as u8,
773            BgpRole::Customer => BgpRoleDiscriminants::Customer as u8,
774            BgpRole::Peer => BgpRoleDiscriminants::Peer as u8,
775            BgpRole::Unknown(other) => *other,
776        }
777    }
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
781pub struct LongLivedGracefulRestart {
782    pub afi: AddressFamilyId,
783    pub safi: SubsequentAfi,
784    pub flags: u8,
785    /// stale_time is the time in seconds after which routes are considered stale.
786    /// Note that this is represented as a u24 on the wire.
787    pub stale_time: u32,
788}
789
790bitfield! {
791    #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
792    pub struct BgpSecHeader(u8);
793    impl new;
794    u8,   version, set_version   : 3, 0;   // 4 bits
795    bool, dir,     set_dir       : 4;      // single bit (hi==lo)
796    u8,   unassigned, set_unassigned : 7, 5; // 3 bits
797}
798
799/// Represents an entry in the BGP Graceful Restart capability.
800#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
801pub struct GracefulRestartEntry {
802    afi: AddressFamilyId,
803    safi: SubsequentAfi,
804    /// Forwarding State (F) bit, which can be used to indicate whether the
805    /// forwarding state for routes that were advertised with the given AFI
806    /// and SAFI has indeed been preserved during the previous BGP restart.
807    preserve_forwarding: bool,
808}
809
810#[cfg(test)]
811mod tests {
812    use std::net::Ipv4Addr;
813
814    use bytes::BytesMut;
815    use eyre::Result;
816
817    use crate::{
818        constants::{AddressFamilyId, SubsequentAfi},
819        open::{Capability, ExtendedNextHop, GracefulRestartEntry, OpenMessage, OpenOption},
820        parser::ParserContext,
821    };
822
823    macro_rules! test_capability_roundtrip {
824        ($name:ident, $input_bytes: expr, $expected: expr) => {
825            #[test]
826            fn $name() -> Result<()> {
827                let ctx = &default_v6_context();
828                let (buf, parsed) = Capability::from_wire(ctx, $input_bytes)?;
829                assert_eq!(parsed, $expected);
830                assert!(buf.is_empty());
831                let mut out = BytesMut::with_capacity(u16::MAX as usize);
832                parsed.to_wire(ctx, &mut out)?;
833                assert_eq!(out.to_vec(), $input_bytes);
834                Ok(())
835            }
836        };
837    }
838    /// Creates a default context for evaluating the test cases below.
839    /// This uses `four_octet_asn` set to true and address_family to IPv6.
840    fn default_v6_context() -> ParserContext {
841        ParserContext {
842            four_octet_asn: Some(true),
843            address_family: Some(AddressFamilyId::Ipv6),
844        }
845    }
846
847    test_capability_roundtrip!(
848        test_multiptotocol,
849        &[0x01, 0x04, 0x00, 0x01, 0x00, 0x01],
850        Capability::MultiProtocol {
851            afi: AddressFamilyId::Ipv4,
852            safi: SubsequentAfi::Unicast
853        }
854    );
855
856    test_capability_roundtrip!(test_route_refresh, &[0x02, 0x00], Capability::RouteRefresh);
857
858    test_capability_roundtrip!(
859        test_route_refresh_cisco_unsupported,
860        &[0x80, 0x00],
861        Capability::UnknownCapability {
862            type_code: 0x80,
863            length: 0x00,
864            value: vec![]
865        }
866    );
867
868    test_capability_roundtrip!(
869        test_four_octet_asn,
870        &[0x41, 0x04, 0x00, 0x00, 0xfd, 0xe8],
871        Capability::FourByteAsn { asn: 65000 }
872    );
873
874    test_capability_roundtrip!(
875        test_graceful_restart,
876        &[0x40, 0x06, 0x01, 0x2c, 0x00, 0x01, 0x01, 0x00],
877        Capability::GracefulRestart {
878            restart_state: false,
879            restart_time: 300,
880            entries: vec![GracefulRestartEntry {
881                afi: AddressFamilyId::Ipv4,
882                safi: SubsequentAfi::Unicast,
883                preserve_forwarding: false,
884            }]
885        }
886    );
887
888    test_capability_roundtrip!(
889        test_extended_nexthop,
890        &[
891            0x05, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
892            0x00, 0x01, 0x00, 0x80, 0x00, 0x02
893        ],
894        Capability::ExtendedNextHop {
895            entries: vec![
896                ExtendedNextHop {
897                    afi: AddressFamilyId::Ipv4,
898                    safi: SubsequentAfi::Unicast,
899                    nh_afi: AddressFamilyId::Ipv6,
900                },
901                ExtendedNextHop {
902                    afi: AddressFamilyId::Ipv4,
903                    safi: SubsequentAfi::Multicast,
904                    nh_afi: AddressFamilyId::Ipv6,
905                },
906                ExtendedNextHop {
907                    afi: AddressFamilyId::Ipv4,
908                    safi: SubsequentAfi::MplsLabelledVpn,
909                    nh_afi: AddressFamilyId::Ipv6
910                }
911            ]
912        }
913    );
914
915    #[test]
916    fn test_open_roundtrip() -> Result<()> {
917        let buf = &[
918            0x04, 0xfd, 0xe8, 0x00, 0xb4, 0x0a, 0x00, 0x00, 0x01, 0x38, 0x02, 0x06, 0x01, 0x04,
919            0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x80, 0x00, 0x02, 0x02, 0x02, 0x00, 0x02, 0x06,
920            0x41, 0x04, 0x00, 0x00, 0xfd, 0xe8, 0x02, 0x08, 0x40, 0x06, 0x01, 0x2c, 0x00, 0x01,
921            0x01, 0x00, 0x02, 0x14, 0x05, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01,
922            0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x80, 0x00, 0x02,
923        ];
924        let expected = OpenMessage {
925            version: 4,
926            asn: 65000,
927            hold_time: 180,
928            identifier: Ipv4Addr::new(10, 0, 0, 1),
929            options: vec![
930                OpenOption::Capabilities(vec![Capability::MultiProtocol {
931                    afi: AddressFamilyId::Ipv4,
932                    safi: SubsequentAfi::Unicast,
933                }]),
934                OpenOption::Capabilities(vec![Capability::UnknownCapability {
935                    type_code: 0x80,
936                    length: 0x00,
937                    value: vec![],
938                }]),
939                OpenOption::Capabilities(vec![Capability::RouteRefresh]),
940                OpenOption::Capabilities(vec![Capability::FourByteAsn { asn: 65000 }]),
941                OpenOption::Capabilities(vec![Capability::GracefulRestart {
942                    restart_state: false,
943                    restart_time: 300,
944                    entries: vec![GracefulRestartEntry {
945                        afi: AddressFamilyId::Ipv4,
946                        safi: SubsequentAfi::Unicast,
947                        preserve_forwarding: false,
948                    }],
949                }]),
950                OpenOption::Capabilities(vec![Capability::ExtendedNextHop {
951                    entries: vec![
952                        ExtendedNextHop {
953                            afi: AddressFamilyId::Ipv4,
954                            safi: SubsequentAfi::Unicast,
955                            nh_afi: AddressFamilyId::Ipv6,
956                        },
957                        ExtendedNextHop {
958                            afi: AddressFamilyId::Ipv4,
959                            safi: SubsequentAfi::Multicast,
960                            nh_afi: AddressFamilyId::Ipv6,
961                        },
962                        ExtendedNextHop {
963                            afi: AddressFamilyId::Ipv4,
964                            safi: SubsequentAfi::MplsLabelledVpn,
965                            nh_afi: AddressFamilyId::Ipv6,
966                        },
967                    ],
968                }]),
969            ],
970        };
971
972        let ctx = default_v6_context();
973        let (_, parsed) = OpenMessage::from_wire(&ctx, buf)?;
974        assert_eq!(parsed, expected);
975        let mut out = BytesMut::with_capacity(u16::MAX as usize);
976        parsed.to_wire(&ctx, &mut out)?;
977        assert_eq!(buf.to_vec(), out.to_vec());
978        Ok(())
979    }
980}