Skip to main content

bgp_packet/
message.rs

1use std::net::{Ipv4Addr, Ipv6Addr};
2use std::ops::Deref;
3
4use bitfield::bitfield;
5use bytes::BufMut;
6use bytes::BytesMut;
7use eyre::Context;
8use eyre::Result;
9use eyre::bail;
10use eyre::eyre;
11use nom::Err::Failure;
12use nom::IResult;
13use nom::Parser;
14use nom::number::complete::be_u8;
15use nom::number::complete::be_u16;
16use nom::number::complete::be_u32;
17use serde::{Deserialize, Serialize};
18use serde_repr::{Deserialize_repr, Serialize_repr};
19use strum::EnumDiscriminants;
20
21use crate::constants::AddressFamilyId;
22use crate::constants::SubsequentAfi;
23use crate::ip_prefix::IpPrefix;
24use crate::open::OpenMessage;
25use crate::parser::BgpParserError;
26use crate::parser::ParserContext;
27
28/// A sentinel AS number used to denote that the actual AS number is provided as
29/// a 4 byte value in the capabilities instead.
30pub const AS_TRANS: u16 = 23456;
31
32/// Version number used in the BGP4 protocol.
33pub const BGP4_VERSION: u8 = 4;
34
35/// Message represents the top-level messages in the BGP protocol.
36#[derive(Debug, Serialize, Deserialize, EnumDiscriminants)]
37#[repr(u8)]
38pub enum Message {
39    Open(OpenMessage) = 1,
40    Update(UpdateMessage) = 2,
41    Notification(NotificationMessage) = 3,
42    KeepAlive = 4,
43    /// BGP Route Refresh message (RFC 2918).
44    RouteRefresh = 5,
45}
46
47impl Message {}
48
49/// Represents a BGP Update message.
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct UpdateMessage {
52    pub withdrawn_routes: Vec<IpPrefix>,
53    pub path_attributes: Vec<PathAttribute>,
54    pub announced_routes: Vec<IpPrefix>,
55}
56
57impl UpdateMessage {
58    pub fn from_wire<'a>(
59        _ctx: &ParserContext,
60        _buf: &'a [u8],
61    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
62        todo!();
63    }
64
65    pub fn to_wire(&self, _ctx: &ParserContext, _out: &mut BytesMut) -> Result<()> {
66        todo!()
67    }
68
69    pub fn wire_len(&self, _ctx: &ParserContext) -> Result<u16> {
70        todo!()
71    }
72}
73
74bitfield! {
75    #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
76    pub struct PathAttributeFlags(u8);
77    impl new;
78    u8;
79    optional, set_optional: 7;
80    transitive, set_transitive: 6;
81    partial, set_partial: 5;
82    extended_length, set_extended_length: 4;
83}
84
85impl Deref for PathAttributeFlags {
86    type Target = u8;
87
88    fn deref(&self) -> &Self::Target {
89        &self.0
90    }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, EnumDiscriminants)]
94#[repr(u8)]
95pub enum PathAttribute {
96    Origin(OriginPathAttribute) = 1,
97    AsPath(AsPathAttribute) = 2,
98    NextHop(NextHopPathAttribute) = 3,
99    MultiExitDisc(MultiExitDiscPathAttribute) = 4,
100    LocalPref(LocalPrefPathAttribute) = 5,
101    AtomicAggregate(AtomicAggregatePathAttribute) = 6,
102    Aggregator(AggregatorPathAttribute) = 7,
103    Communitites(CommunitiesPathAttribute) = 8,
104    MpReachNlri(MpReachNlriPathAttribute) = 14,
105    MpUnreachNlri(MpUnreachNlriPathAttribute) = 15,
106    ExtendedCommunities(ExtendedCommunitiesPathAttribute) = 16,
107    LargeCommunities(LargeCommunitiesPathAttribute) = 32,
108    UnknownPathAttribute {
109        flags: PathAttributeFlags,
110        type_code: u8,
111        payload: Vec<u8>,
112    },
113}
114
115impl From<u8> for PathAttributeDiscriminants {
116    fn from(value: u8) -> Self {
117        match value {
118            v if v == Self::Origin as u8 => Self::Origin,
119            v if v == Self::AsPath as u8 => Self::AsPath,
120            v if v == Self::NextHop as u8 => Self::NextHop,
121            v if v == Self::MultiExitDisc as u8 => Self::MultiExitDisc,
122            v if v == Self::LocalPref as u8 => Self::LocalPref,
123            v if v == Self::AtomicAggregate as u8 => Self::AtomicAggregate,
124            v if v == Self::Aggregator as u8 => Self::Aggregator,
125            v if v == Self::Communitites as u8 => Self::Communitites,
126            v if v == Self::MpReachNlri as u8 => Self::MpReachNlri,
127            v if v == Self::MpUnreachNlri as u8 => Self::MpUnreachNlri,
128            v if v == Self::ExtendedCommunities as u8 => Self::ExtendedCommunities,
129            v if v == Self::LargeCommunities as u8 => Self::LargeCommunities,
130            _ => Self::UnknownPathAttribute,
131        }
132    }
133}
134
135impl PathAttribute {
136    fn discriminant(&self) -> u8 {
137        match self {
138            PathAttribute::Origin(_) => PathAttributeDiscriminants::Origin as u8,
139            PathAttribute::AsPath(_) => PathAttributeDiscriminants::AsPath as u8,
140            PathAttribute::NextHop(_) => PathAttributeDiscriminants::NextHop as u8,
141            PathAttribute::MultiExitDisc(_) => PathAttributeDiscriminants::MultiExitDisc as u8,
142            PathAttribute::LocalPref(_) => PathAttributeDiscriminants::LocalPref as u8,
143            PathAttribute::AtomicAggregate(_) => PathAttributeDiscriminants::AtomicAggregate as u8,
144            PathAttribute::Aggregator(_) => PathAttributeDiscriminants::Aggregator as u8,
145            PathAttribute::Communitites(_) => PathAttributeDiscriminants::Communitites as u8,
146            PathAttribute::MpReachNlri(_) => PathAttributeDiscriminants::MpReachNlri as u8,
147            PathAttribute::MpUnreachNlri(_) => PathAttributeDiscriminants::MpUnreachNlri as u8,
148            PathAttribute::ExtendedCommunities(_) => {
149                PathAttributeDiscriminants::ExtendedCommunities as u8
150            }
151            PathAttribute::LargeCommunities(_) => {
152                PathAttributeDiscriminants::LargeCommunities as u8
153            }
154            PathAttribute::UnknownPathAttribute { type_code, .. } => *type_code,
155        }
156    }
157
158    fn optional(&self) -> bool {
159        match self {
160            PathAttribute::Origin(_) => false,
161            PathAttribute::AsPath(_) => false,
162            PathAttribute::NextHop(_) => false,
163            PathAttribute::MultiExitDisc(_) => true,
164            PathAttribute::LocalPref(_) => false,
165            PathAttribute::AtomicAggregate(_) => true,
166            PathAttribute::Aggregator(_) => true,
167            PathAttribute::Communitites(_) => true,
168            PathAttribute::MpReachNlri(_) => true,
169            PathAttribute::MpUnreachNlri(_) => true,
170            PathAttribute::ExtendedCommunities(_) => true,
171            PathAttribute::LargeCommunities(_) => true,
172            PathAttribute::UnknownPathAttribute { flags, .. } => flags.optional(),
173        }
174    }
175
176    fn transitive(&self) -> bool {
177        match self {
178            PathAttribute::Origin(_) => true,
179            PathAttribute::AsPath(_) => true,
180            PathAttribute::NextHop(_) => true,
181            PathAttribute::MultiExitDisc(_) => false,
182            PathAttribute::LocalPref(_) => true,
183            PathAttribute::AtomicAggregate(_) => true,
184            PathAttribute::Aggregator(_) => false,
185            PathAttribute::Communitites(_) => true,
186            PathAttribute::MpReachNlri(_) => false,
187            PathAttribute::MpUnreachNlri(_) => false,
188            PathAttribute::ExtendedCommunities(_) => true,
189            PathAttribute::LargeCommunities(_) => true,
190            PathAttribute::UnknownPathAttribute { flags, .. } => flags.transitive(),
191        }
192    }
193
194    /// The from_wire parser for `PathAttribute` consumes type and length which it uses to
195    /// determine how many bytes to take and pass down to the corresponding sub-parser.
196    pub fn from_wire<'a>(
197        ctx: &ParserContext,
198        buf: &'a [u8],
199    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
200        let (buf, attr_flags) = be_u8(buf).map(|(buf, b)| (buf, PathAttributeFlags(b)))?;
201        let (buf, type_code) = be_u8(buf)?;
202
203        let (buf, length): (_, u16) = if attr_flags.extended_length() {
204            be_u16(buf)?
205        } else {
206            be_u8(buf).map(|(buf, b)| (buf, b as u16))?
207        };
208
209        let discriminant = PathAttributeDiscriminants::from(type_code);
210        Self::parse_known_path_attribute(ctx, buf, discriminant, length)
211    }
212
213    fn parse_known_path_attribute<'a>(
214        ctx: &ParserContext,
215        buf: &'a [u8],
216        discriminant: PathAttributeDiscriminants,
217        length: u16,
218    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
219        let (buf, pa_buf) = nom::bytes::take(length).parse(buf)?;
220
221        let attr: PathAttribute = match discriminant {
222            PathAttributeDiscriminants::Origin => {
223                PathAttribute::Origin(OriginPathAttribute::from_wire(ctx, pa_buf)?.1)
224            }
225            PathAttributeDiscriminants::AsPath => {
226                PathAttribute::AsPath(AsPathAttribute::from_wire(ctx, pa_buf)?.1)
227            }
228            PathAttributeDiscriminants::NextHop => {
229                PathAttribute::NextHop(NextHopPathAttribute::from_wire(ctx, pa_buf)?.1)
230            }
231            PathAttributeDiscriminants::MultiExitDisc => {
232                PathAttribute::MultiExitDisc(MultiExitDiscPathAttribute::from_wire(ctx, pa_buf)?.1)
233            }
234            PathAttributeDiscriminants::LocalPref => {
235                PathAttribute::LocalPref(LocalPrefPathAttribute::from_wire(ctx, pa_buf)?.1)
236            }
237            PathAttributeDiscriminants::AtomicAggregate => PathAttribute::AtomicAggregate(
238                AtomicAggregatePathAttribute::from_wire(ctx, pa_buf)?.1,
239            ),
240            PathAttributeDiscriminants::Aggregator => {
241                PathAttribute::Aggregator(AggregatorPathAttribute::from_wire(ctx, pa_buf)?.1)
242            }
243            PathAttributeDiscriminants::Communitites => {
244                PathAttribute::Communitites(CommunitiesPathAttribute::from_wire(ctx, pa_buf)?.1)
245            }
246            PathAttributeDiscriminants::MpReachNlri => {
247                PathAttribute::MpReachNlri(MpReachNlriPathAttribute::from_wire(ctx, pa_buf)?.1)
248            }
249            PathAttributeDiscriminants::MpUnreachNlri => {
250                PathAttribute::MpUnreachNlri(MpUnreachNlriPathAttribute::from_wire(ctx, pa_buf)?.1)
251            }
252            PathAttributeDiscriminants::ExtendedCommunities => PathAttribute::ExtendedCommunities(
253                ExtendedCommunitiesPathAttribute::from_wire(ctx, pa_buf)?.1,
254            ),
255            PathAttributeDiscriminants::LargeCommunities => PathAttribute::LargeCommunities(
256                LargeCommunitiesPathAttribute::from_wire(ctx, pa_buf)?.1,
257            ),
258            PathAttributeDiscriminants::UnknownPathAttribute => unreachable!(
259                "parse_known_path_attribute must never be called with an unknown attribute"
260            ),
261        };
262        Ok((buf, attr))
263    }
264
265    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
266        macro_rules! write_path_attribute {
267            ($out: expr, $attribute: expr) => {
268                let wire_len = $attribute.wire_len(ctx)?;
269                let attr_flags = PathAttributeFlags::new(
270                    self.optional(),
271                    self.transitive(),
272                    /*partial=*/ false,
273                    wire_len > 255,
274                );
275                out.put_u8(*attr_flags);
276                out.put_u8(self.discriminant() as u8);
277                write_wire_len!(out, wire_len);
278                $attribute.to_wire(ctx, out)?;
279            };
280        }
281
282        macro_rules! write_wire_len {
283            ($out: expr, $wire_len: expr) => {
284                if $wire_len > 255 {
285                    $out.put_u16($wire_len as u16);
286                } else {
287                    $out.put_u8($wire_len as u8);
288                }
289            };
290        }
291
292        match self {
293            PathAttribute::Origin(origin_path_attribute) => {
294                write_path_attribute!(out, origin_path_attribute);
295            }
296            PathAttribute::AsPath(as_path_attribute) => {
297                write_path_attribute!(out, as_path_attribute);
298            }
299            PathAttribute::NextHop(next_hop_path_attribute) => {
300                write_path_attribute!(out, next_hop_path_attribute);
301            }
302            PathAttribute::MultiExitDisc(multi_exit_disc_path_attribute) => {
303                write_path_attribute!(out, multi_exit_disc_path_attribute);
304            }
305            PathAttribute::LocalPref(local_pref_path_attribute) => {
306                write_path_attribute!(out, local_pref_path_attribute);
307            }
308            PathAttribute::AtomicAggregate(atomic_aggregate_path_attribute) => {
309                write_path_attribute!(out, atomic_aggregate_path_attribute);
310            }
311            PathAttribute::Aggregator(aggregator_path_attribute) => {
312                write_path_attribute!(out, aggregator_path_attribute);
313            }
314            PathAttribute::Communitites(communities_path_attribute) => {
315                write_path_attribute!(out, communities_path_attribute);
316            }
317            PathAttribute::MpReachNlri(mp_reach_nlri_path_attribute) => {
318                write_path_attribute!(out, mp_reach_nlri_path_attribute);
319            }
320            PathAttribute::MpUnreachNlri(mp_unreach_nlri_path_attribute) => {
321                write_path_attribute!(out, mp_unreach_nlri_path_attribute);
322            }
323            PathAttribute::ExtendedCommunities(extended_communities_path_attribute) => {
324                write_path_attribute!(out, extended_communities_path_attribute);
325            }
326            PathAttribute::LargeCommunities(large_communities_path_attribute) => {
327                write_path_attribute!(out, large_communities_path_attribute);
328            }
329            PathAttribute::UnknownPathAttribute {
330                flags,
331                type_code,
332                payload,
333            } => {
334                out.put_u8(flags.0);
335                if flags.extended_length() {
336                    out.put_u16(payload.len() as u16);
337                } else {
338                    out.put_u8(payload.len() as u8);
339                }
340                out.put_u8(*type_code);
341                out.put(payload.as_slice());
342            }
343        }
344
345        Ok(())
346    }
347}
348
349/// Origin path attribute is a mandatory attribute defined in RFC4271.
350#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize_repr, Deserialize_repr)]
351#[repr(u8)]
352pub enum OriginPathAttribute {
353    IGP = 0,
354    EGP = 1,
355    INCOMPLETE = 2,
356}
357
358impl OriginPathAttribute {
359    pub fn from_wire<'a>(
360        _: &ParserContext,
361        buf: &'a [u8],
362    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
363        let (buf, byte) = be_u8(buf)?;
364        let attr = match byte {
365            b if b == OriginPathAttribute::IGP as u8 => OriginPathAttribute::IGP,
366            b if b == OriginPathAttribute::EGP as u8 => OriginPathAttribute::EGP,
367            b if b == OriginPathAttribute::INCOMPLETE as u8 => OriginPathAttribute::INCOMPLETE,
368            _ => {
369                return IResult::Err(nom::Err::Failure(BgpParserError::Eyre(eyre!(
370                    "Unknown Origin type {}",
371                    byte
372                ))));
373            }
374        };
375        Ok((buf, attr))
376    }
377
378    pub fn wire_len(&self, _ctx: &ParserContext) -> Result<u16> {
379        Ok(1)
380    }
381
382    pub fn to_wire(&self, _ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
383        out.put_u8(*self as u8);
384        Ok(())
385    }
386}
387
388impl TryFrom<u8> for OriginPathAttribute {
389    type Error = eyre::Error;
390
391    fn try_from(value: u8) -> Result<Self> {
392        match value {
393            0 => Ok(Self::IGP),
394            1 => Ok(Self::EGP),
395            2 => Ok(Self::INCOMPLETE),
396            other => bail!("Unexpected origin code {}", other),
397        }
398    }
399}
400
401/// ASPathAttribute is a well-known mandatory attribute that contains a list of TLV encoded path
402/// segments. Type is either 1 for AS_SET or 2 for AS_SEQUENCE, length is a 1 octet field
403/// containing the number of ASNS and the value contains the ASNs. This is defined in Section 4.3
404/// of RFC4271.
405#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
406pub struct AsPathAttribute {
407    pub segments: Vec<AsPathSegment>,
408}
409
410#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
411pub struct AsPathSegment {
412    /// ordered is true when representing an AS_SEQUENCE, andd false when
413    /// representing an AS_SET.
414    pub ordered: bool,
415    /// Path is the list of ASNs.
416    pub path: Vec<u32>,
417}
418
419impl AsPathAttribute {
420    pub fn from_asns(asns: Vec<u32>) -> PathAttribute {
421        let segment = AsPathSegment {
422            ordered: true,
423            path: asns,
424        };
425        PathAttribute::AsPath(AsPathAttribute {
426            segments: vec![segment],
427        })
428    }
429
430    pub fn from_wire<'a>(
431        ctx: &ParserContext,
432        buf: &'a [u8],
433    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
434        let parse_segment = |ctx: &ParserContext,
435                             buf: &'a [u8]|
436         -> IResult<&'a [u8], AsPathSegment, BgpParserError<&'a [u8]>> {
437            let (buf, typ) = be_u8(buf)?;
438            let (buf, len) = be_u8(buf)?;
439            let (buf, asns): (_, Vec<u32>) = match ctx.four_octet_asn {
440                Some(true) => {
441                    nom::multi::many_m_n(len as usize, len as usize, be_u32).parse(buf)?
442                }
443                Some(false) => nom::multi::many_m_n(len as usize, len as usize, be_u16)
444                    .parse(buf)
445                    .map(|(buf, asns)| (buf, asns.iter().map(|asn| *asn as u32).collect()))?,
446                None => {
447                    return Err(nom::Err::Failure(BgpParserError::CustomText(
448                        "Context must set four_octet_asn before being used",
449                    )));
450                }
451            };
452
453            Ok((
454                buf,
455                AsPathSegment {
456                    ordered: typ == 2,
457                    path: asns,
458                },
459            ))
460        };
461
462        let (buf, segments) =
463            nom::multi::many0(|buf: &'a [u8]| parse_segment(ctx, buf)).parse(buf)?;
464
465        Ok((buf, Self { segments }))
466    }
467
468    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
469        if ctx.four_octet_asn.is_none_or(|val| !val) {
470            bail!("AsPathAttribute can only be sent for four_octet_asn enabled peers");
471        }
472
473        for segment in &self.segments {
474            // Segment type.
475            out.put_u8(if segment.ordered { 2 } else { 1 });
476            // Segment AS length.
477            out.put_u8(
478                segment
479                    .path
480                    .len()
481                    .try_into()
482                    .wrap_err("AS Path length too long")?,
483            );
484            // AS numbers.
485            match ctx
486                .four_octet_asn
487                .ok_or(eyre!("ctx.four_octet_asn must be set"))?
488            {
489                true => {
490                    for asn in &segment.path {
491                        out.put_u32(*asn);
492                    }
493                }
494                false => {
495                    for asn in &segment.path {
496                        out.put_u16(
497                            u16::try_from(*asn)
498                                .map_err(|e| eyre!("AS number did not fit into u16: {}", e))?,
499                        );
500                    }
501                }
502            }
503        }
504
505        Ok(())
506    }
507
508    pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
509        let mut counter = 0;
510        for segment in &self.segments {
511            counter += match ctx.four_octet_asn {
512                Some(true) => 2 + (4 * segment.path.len()),
513                Some(false) => 2 + (2 * segment.path.len()),
514                None => bail!("ParserContext needs four_octet_asn set"),
515            };
516        }
517        Ok(counter as u16)
518    }
519}
520
521#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
522pub struct NextHopPathAttribute(pub Ipv4Addr);
523
524impl NextHopPathAttribute {
525    pub fn from_wire<'a>(
526        _: &ParserContext,
527        buf: &'a [u8],
528    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
529        let (buf, ip_u32) = be_u32(buf)?;
530        Ok((buf, Self(Ipv4Addr::from(ip_u32))))
531    }
532
533    pub fn to_wire(&self, _ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
534        out.put_u32(self.0.into());
535        Ok(())
536    }
537
538    pub fn wire_len(&self, _: &ParserContext) -> Result<u32> {
539        Ok(4)
540    }
541}
542
543#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
544pub struct MultiExitDiscPathAttribute(pub u32);
545
546impl MultiExitDiscPathAttribute {
547    pub fn from_wire<'a>(
548        _: &ParserContext,
549        buf: &'a [u8],
550    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
551        let (buf, val) = be_u32(buf)?;
552        Ok((buf, Self(val)))
553    }
554
555    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
556        out.put_u32(self.0);
557        Ok(())
558    }
559
560    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
561        Ok(4)
562    }
563}
564
565#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
566pub struct LocalPrefPathAttribute(pub u32);
567
568impl LocalPrefPathAttribute {
569    pub fn from_wire<'a>(
570        _: &ParserContext,
571        buf: &'a [u8],
572    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
573        let (buf, val) = be_u32(buf)?;
574        Ok((buf, Self(val)))
575    }
576
577    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
578        out.put_u32(self.0);
579        Ok(())
580    }
581
582    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
583        Ok(4)
584    }
585}
586
587#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
588pub struct AtomicAggregatePathAttribute {}
589
590impl AtomicAggregatePathAttribute {
591    pub fn from_wire<'a>(
592        _: &ParserContext,
593        buf: &'a [u8],
594    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
595        Ok((buf, Self {}))
596    }
597
598    pub fn to_wire(&self, _: &ParserContext, _: &mut BytesMut) -> Result<()> {
599        Ok(())
600    }
601
602    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
603        Ok(0)
604    }
605}
606
607#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
608pub struct AggregatorPathAttribute {
609    pub asn: u32,
610    pub ip: Ipv4Addr,
611}
612
613impl AggregatorPathAttribute {
614    pub fn from_wire<'a>(
615        ctx: &ParserContext,
616        buf: &'a [u8],
617    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
618        if ctx.four_octet_asn.is_none_or(|val| !val) {
619            return Err(nom::Err::Failure(BgpParserError::CustomText(
620                "AggregatorPathAttribute can only be parsed for four_octet_asn enabled peers",
621            )));
622        }
623        let (buf, asn) = be_u32(buf)?;
624        let (buf, ip) = be_u32(buf)?;
625        Ok((
626            buf,
627            Self {
628                asn,
629                ip: Ipv4Addr::from(ip),
630            },
631        ))
632    }
633
634    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
635        if ctx.four_octet_asn.is_none_or(|val| !val) {
636            bail!("AggregatorPathAttribute can only be sent for four_octet_asn enabled peers");
637        }
638        out.put_u32(self.asn);
639        out.put_u32(self.ip.into());
640        Ok(())
641    }
642
643    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
644        Ok(8)
645    }
646}
647
648#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
649pub struct CommunitiesPathAttribute(Vec<(u16, u16)>);
650
651impl CommunitiesPathAttribute {
652    pub fn from_wire<'a>(
653        _: &ParserContext,
654        buf: &'a [u8],
655    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
656        let (buf, values) = nom::multi::many0(|i| (be_u16, be_u16).parse(i)).parse(buf)?;
657        Ok((buf, CommunitiesPathAttribute(values)))
658    }
659
660    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
661        for value in &self.0 {
662            out.put_u16(value.0);
663            out.put_u16(value.1);
664        }
665        Ok(())
666    }
667
668    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
669        Ok((self.0.len() * 4) as u16)
670    }
671}
672
673/// Extended Communities as defined in https://www.rfc-editor.org/rfc/rfc4360.html.
674#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
675pub struct ExtendedCommunitiesPathAttribute {
676    pub extended_communities: Vec<ExtendedCommunity>,
677}
678
679impl ExtendedCommunitiesPathAttribute {
680    pub fn from_wire<'a>(
681        ctx: &ParserContext,
682        buf: &'a [u8],
683    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
684        let (buf, extended_communities) =
685            nom::multi::many1(|buf| ExtendedCommunity::from_wire(ctx, buf)).parse(buf)?;
686        Ok((
687            buf,
688            Self {
689                extended_communities,
690            },
691        ))
692    }
693
694    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
695        for ec in &self.extended_communities {
696            ec.to_wire(ctx, out)?;
697        }
698        Ok(())
699    }
700
701    pub fn wire_len(&self, ctx: &ParserContext) -> Result<u16> {
702        Ok(self
703            .extended_communities
704            .iter()
705            .map(|ec| ec.wire_len(ctx))
706            .sum::<Result<u16>>()?)
707    }
708}
709
710#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
711pub enum ExtendedCommunity {
712    /// AS Specific Extended Community as specified in Section 3.1 of RFC4360.
713    AsSpecific {
714        typ: u8,
715        sub_typ: u8,
716        global_admin: u16,
717        local_admin: u32,
718    },
719    /// Ipv4 Address Specific Extended Community as specified in Section 3.2 of RFC4360.
720    Ipv4AddrSpecific {
721        typ: u8,
722        sub_typ: u8,
723        global_admin: u32,
724        local_admin: u16,
725    },
726    /// Opaque Extended Community as specified in Section 3.3 of RFC4360.
727    Opaque {
728        typ: u8,
729        sub_typ: u8,
730        value: [u8; 5],
731    },
732    /// Route Target Community as specified in Section 4 of RFC4360.
733    RouteTarget {
734        typ: u8,
735        sub_typ: u8,
736        global_admin: u32,
737        local_admin: u16,
738    },
739    /// Route Origin Community as specified in Section 5 of RFC4360.
740    RouteOrigin {
741        typ: u8,
742        sub_typ: u8,
743        global_admin: u16,
744        local_admin: u32,
745    },
746}
747
748impl ExtendedCommunity {
749    pub fn from_wire<'a>(
750        _: &ParserContext,
751        buf: &'a [u8],
752    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
753        let (buf, typ) = be_u8(buf)?;
754        let (buf, sub_typ) = be_u8(buf)?;
755        let (buf, parsed) = match (typ, sub_typ) {
756            // Route Target Extended Community.
757            (0x00 | 0x01 | 0x02, 0x02) => {
758                let (buf, global_admin) = be_u32(buf)?;
759                let (buf, local_admin) = be_u16(buf)?;
760                (
761                    buf,
762                    Self::RouteTarget {
763                        typ,
764                        sub_typ,
765                        global_admin,
766                        local_admin,
767                    },
768                )
769            }
770            // Route Origin Extended Community.
771            (0x00 | 0x01 | 0x02, 0x03) => {
772                let (buf, global_admin) = be_u16(buf)?;
773                let (buf, local_admin) = be_u32(buf)?;
774                (
775                    buf,
776                    Self::RouteOrigin {
777                        typ,
778                        sub_typ,
779                        global_admin,
780                        local_admin,
781                    },
782                )
783            }
784            // AS specific Extended Community.
785            (0x00 | 0x40, _) => {
786                let (buf, global_admin) = be_u16(buf)?;
787                let (buf, local_admin) = be_u32(buf)?;
788                (
789                    buf,
790                    Self::AsSpecific {
791                        typ,
792                        sub_typ,
793                        global_admin,
794                        local_admin,
795                    },
796                )
797            }
798            // IPv4 Address Specific Extended Community.
799            (0x01 | 0x41, _) => {
800                let (buf, global_admin) = be_u32(buf)?;
801                let (buf, local_admin) = be_u16(buf)?;
802                (
803                    buf,
804                    Self::Ipv4AddrSpecific {
805                        typ,
806                        sub_typ,
807                        global_admin,
808                        local_admin,
809                    },
810                )
811            }
812            _ => {
813                let (buf, payload) = nom::bytes::take(5_usize).parse(buf)?;
814                let value: [u8; 5] = payload.try_into().map_err(|_| {
815                    Failure(BgpParserError::CustomText(
816                        "Expected exactly 5 bytes from the parser",
817                    ))
818                })?;
819                (
820                    buf,
821                    Self::Opaque {
822                        typ,
823                        sub_typ,
824                        value,
825                    },
826                )
827            }
828        };
829
830        return Ok((buf, parsed));
831    }
832
833    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
834        match self {
835            ExtendedCommunity::AsSpecific {
836                typ,
837                sub_typ,
838                global_admin,
839                local_admin,
840            } => {
841                out.put_u8(*typ);
842                out.put_u8(*sub_typ);
843                out.put_u16(*global_admin);
844                out.put_u32(*local_admin);
845            }
846            ExtendedCommunity::Ipv4AddrSpecific {
847                typ,
848                sub_typ,
849                global_admin,
850                local_admin,
851            } => {
852                out.put_u8(*typ);
853                out.put_u8(*sub_typ);
854                out.put_u32(*global_admin);
855                out.put_u16(*local_admin);
856            }
857            ExtendedCommunity::Opaque {
858                typ,
859                sub_typ,
860                value,
861            } => {
862                out.put_u8(*typ);
863                out.put_u8(*sub_typ);
864                out.put(&value[..]);
865            }
866            ExtendedCommunity::RouteTarget {
867                typ,
868                sub_typ,
869                global_admin,
870                local_admin,
871            } => {
872                out.put_u8(*typ);
873                out.put_u8(*sub_typ);
874                out.put_u32(*global_admin);
875                out.put_u16(*local_admin);
876            }
877            ExtendedCommunity::RouteOrigin {
878                typ,
879                sub_typ,
880                global_admin,
881                local_admin,
882            } => {
883                out.put_u8(*typ);
884                out.put_u8(*sub_typ);
885                out.put_u16(*global_admin);
886                out.put_u32(*local_admin);
887            }
888        }
889        Ok(())
890    }
891
892    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
893        Ok(8)
894    }
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
898pub struct LargeCommunitiesPathAttribute {
899    pub communities: Vec<LargeCommunity>,
900}
901
902impl LargeCommunitiesPathAttribute {
903    pub fn from_wire<'a>(
904        ctx: &ParserContext,
905        buf: &'a [u8],
906    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
907        let (buf, communities) =
908            nom::multi::many1(|buf| LargeCommunity::from_wire(ctx, buf)).parse(buf)?;
909        Ok((buf, Self { communities }))
910    }
911
912    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
913        for community in &self.communities {
914            community.to_wire(ctx, out)?;
915        }
916        Ok(())
917    }
918
919    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
920        Ok(12_u16 * self.communities.len() as u16)
921    }
922}
923
924#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
925pub struct LargeCommunity {
926    pub global_admin: u32,
927    pub data_1: u32,
928    pub data_2: u32,
929}
930
931impl LargeCommunity {
932    pub fn from_wire<'a>(
933        _: &ParserContext,
934        buf: &'a [u8],
935    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
936        let (buf, global_admin) = be_u32(buf)?;
937        let (buf, data_1) = be_u32(buf)?;
938        let (buf, data_2) = be_u32(buf)?;
939        Ok((
940            buf,
941            Self {
942                global_admin,
943                data_1,
944                data_2,
945            },
946        ))
947    }
948
949    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
950        out.put_u32(self.global_admin);
951        out.put_u32(self.data_1);
952        out.put_u32(self.data_2);
953        Ok(())
954    }
955
956    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
957        Ok(12)
958    }
959}
960
961#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
962pub enum NlriNextHop {
963    /// Represents an IPv4 address nexthop.
964    Ipv4(Ipv4Addr),
965    /// Represents a global IPv6 nexthop.
966    Ipv6(Ipv6Addr),
967    /// Represents a IPv6 Link Local and global address pair nexthop.
968    Ipv6WithLl {
969        global: Ipv6Addr,
970        link_local: Ipv6Addr,
971    },
972}
973
974impl NlriNextHop {
975    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
976        match self {
977            NlriNextHop::Ipv4(ipv4_addr) => out.put(&ipv4_addr.octets()[..]),
978            NlriNextHop::Ipv6(ipv6_addr) => out.put(&ipv6_addr.octets()[..]),
979            NlriNextHop::Ipv6WithLl { global, link_local } => {
980                out.put(&global.octets()[..]);
981                out.put(&link_local.octets()[..])
982            }
983        }
984
985        Ok(())
986    }
987
988    pub fn wire_len(&self) -> u8 {
989        match self {
990            NlriNextHop::Ipv4(_) => 4,
991            NlriNextHop::Ipv6(_) => 16,
992            NlriNextHop::Ipv6WithLl { .. } => 32,
993        }
994    }
995}
996
997// parse_prefix is a helper function that implements an NLRI parser for the given AFI.
998fn parse_prefix<'a>(
999    afi: AddressFamilyId,
1000    buf: &'a [u8],
1001) -> IResult<&'a [u8], IpPrefix, BgpParserError<&'a [u8]>> {
1002    let (buf, prefix_len) = be_u8(buf)?;
1003    let byte_len = (prefix_len + 7) / 8;
1004    let (buf, prefix_bytes) = nom::bytes::take(byte_len as usize).parse(buf)?;
1005    let prefix = IpPrefix::new(afi, prefix_bytes.to_vec(), prefix_len)
1006        .map_err(|e| Failure(BgpParserError::Eyre(e)))?;
1007    Ok((buf, prefix))
1008}
1009
1010#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1011pub struct MpReachNlriPathAttribute {
1012    pub afi: AddressFamilyId,
1013    pub safi: SubsequentAfi,
1014    /// Next hop address (either IPv4 or IPv6 for now).
1015    pub next_hop: NlriNextHop,
1016    pub prefixes: Vec<IpPrefix>,
1017}
1018
1019impl MpReachNlriPathAttribute {
1020    pub fn from_wire<'a>(
1021        _: &ParserContext,
1022        buf: &'a [u8],
1023    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
1024        let (buf, raw_afi) = be_u16(buf)?;
1025        let afi =
1026            AddressFamilyId::try_from(raw_afi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
1027
1028        let (buf, raw_safi) = be_u8(buf)?;
1029        let safi =
1030            SubsequentAfi::try_from(raw_safi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
1031
1032        let (buf, nh_len) = be_u8(buf)?;
1033
1034        let (buf, next_hop, prefixes) = match afi {
1035            AddressFamilyId::Ipv4 => {
1036                // Read the length of the nexthop which should equal 4.
1037                if nh_len != 4 {
1038                    return Err(Failure(BgpParserError::Eyre(eyre!(
1039                        "Got nexthop address length {} when expected 4 for IPv4 AFI",
1040                        nh_len
1041                    ))));
1042                }
1043                // Read the nexthop address which should now be an IPv4 address.
1044                let (buf, nh_bytes) = be_u32(buf)?;
1045                let next_hop = NlriNextHop::Ipv4(Ipv4Addr::from(nh_bytes));
1046
1047                let (buf, prefixes) =
1048                    nom::multi::many0(|buf| parse_prefix(AddressFamilyId::Ipv4, buf)).parse(buf)?;
1049                (buf, next_hop, prefixes)
1050            }
1051            AddressFamilyId::Ipv6 => {
1052                // https://datatracker.ietf.org/doc/html/rfc2545 defines that the nexthop address may be 16 or 32 bytes long.
1053                let (buf, nh_bytes) = nom::bytes::take(nh_len as usize).parse(buf)?;
1054                let nexthop = match nh_bytes.len() {
1055                    16 => {
1056                        // unwrap should never fire since we have explicitly checked the length.
1057                        let slice: [u8; 16] = nh_bytes.try_into().unwrap();
1058                        NlriNextHop::Ipv6(Ipv6Addr::from(slice))
1059                    }
1060                    32 => {
1061                        // unwrap should never fire since we have explicitly checked the length.
1062                        let slice: [u8; 32] = nh_bytes.try_into().unwrap();
1063                        let global_bytes: [u8; 16] = slice[0..16].try_into().unwrap();
1064                        let global = Ipv6Addr::from(global_bytes);
1065                        let link_local_bytes: [u8; 16] = slice[16..32].try_into().unwrap();
1066                        let link_local = Ipv6Addr::from(link_local_bytes);
1067                        NlriNextHop::Ipv6WithLl { global, link_local }
1068                    }
1069                    _ => {
1070                        return Err(Failure(BgpParserError::Eyre(eyre!(
1071                            "Mismatched IPv6 nexthop length, got {}, want 16 or 32",
1072                            nh_bytes.len()
1073                        ))));
1074                    }
1075                };
1076                // Reserved 0 byte (formerly SNPA).
1077                let (buf, _) = be_u8(buf)?;
1078                let (buf, prefixes) =
1079                    nom::multi::many0(|buf| parse_prefix(AddressFamilyId::Ipv6, buf)).parse(buf)?;
1080                (buf, nexthop, prefixes)
1081            }
1082        };
1083
1084        Ok((
1085            buf,
1086            Self {
1087                afi,
1088                safi,
1089                next_hop,
1090                prefixes,
1091            },
1092        ))
1093    }
1094
1095    pub fn to_wire(&self, ctx: &ParserContext, out: &mut BytesMut) -> Result<()> {
1096        out.put_u16(self.afi as u16);
1097        out.put_u8(self.safi as u8);
1098        out.put_u8(self.next_hop.wire_len());
1099        self.next_hop.to_wire(ctx, out)?;
1100        out.put_u8(0);
1101        for prefix in &self.prefixes {
1102            out.put_u8(prefix.length);
1103            out.put(&prefix.prefix[..]);
1104        }
1105        Ok(())
1106    }
1107
1108    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
1109        Ok(4_u16
1110            + self.next_hop.wire_len() as u16
1111            + 1 // Reserved byte (SNPA).
1112            + self
1113                .prefixes
1114                .iter()
1115                .map(|p| 1 + p.prefix.len() as u16)
1116                .sum::<u16>())
1117    }
1118}
1119
1120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1121pub struct MpUnreachNlriPathAttribute {
1122    pub afi: AddressFamilyId,
1123    pub safi: SubsequentAfi,
1124    pub prefixes: Vec<IpPrefix>,
1125}
1126
1127impl MpUnreachNlriPathAttribute {
1128    pub fn from_wire<'a>(
1129        _: &ParserContext,
1130        buf: &'a [u8],
1131    ) -> IResult<&'a [u8], Self, BgpParserError<&'a [u8]>> {
1132        let (buf, raw_afi) = be_u16(buf)?;
1133        let afi =
1134            AddressFamilyId::try_from(raw_afi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
1135
1136        let (buf, raw_safi) = be_u8(buf)?;
1137        let safi =
1138            SubsequentAfi::try_from(raw_safi).map_err(|e| Failure(BgpParserError::Eyre(e)))?;
1139
1140        let (buf, prefixes) = nom::multi::many0(|buf| parse_prefix(afi, buf)).parse(buf)?;
1141
1142        Ok((
1143            buf,
1144            MpUnreachNlriPathAttribute {
1145                afi,
1146                safi,
1147                prefixes,
1148            },
1149        ))
1150    }
1151
1152    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<()> {
1153        out.put_u16(self.afi as u16);
1154        out.put_u8(self.safi as u8);
1155        for prefix in &self.prefixes {
1156            out.put_u8(prefix.length);
1157            out.put(&prefix.prefix[..]);
1158        }
1159        Ok(())
1160    }
1161
1162    pub fn wire_len(&self, _: &ParserContext) -> Result<u16> {
1163        Ok(3 + self
1164            .prefixes
1165            .iter()
1166            .map(|p| 1 + p.prefix.len() as u16)
1167            .sum::<u16>())
1168    }
1169}
1170
1171#[derive(Debug, Clone, Serialize, Deserialize)]
1172pub struct NotificationMessage {}
1173
1174#[cfg(test)]
1175mod tests {
1176    use std::{
1177        net::{Ipv4Addr, Ipv6Addr},
1178        str::FromStr,
1179    };
1180
1181    use bytes::BytesMut;
1182    use eyre::{Result, eyre};
1183
1184    use crate::{
1185        constants::{AddressFamilyId, SubsequentAfi},
1186        ip_prefix::IpPrefix,
1187        message::{
1188            AggregatorPathAttribute, AsPathAttribute, AsPathSegment, AtomicAggregatePathAttribute,
1189            CommunitiesPathAttribute, ExtendedCommunitiesPathAttribute, ExtendedCommunity,
1190            LargeCommunitiesPathAttribute, LargeCommunity, LocalPrefPathAttribute,
1191            MpReachNlriPathAttribute, MpUnreachNlriPathAttribute, MultiExitDiscPathAttribute,
1192            NextHopPathAttribute, NlriNextHop, OriginPathAttribute, PathAttribute,
1193        },
1194        parser::ParserContext,
1195    };
1196
1197    macro_rules! test_path_attribute_roundtrip {
1198        ($name:ident, $input_bytes: expr, $expected: expr) => {
1199            #[test]
1200            fn $name() -> Result<()> {
1201                let ctx = &default_v6_context();
1202                let (buf, parsed) = PathAttribute::from_wire(ctx, $input_bytes)?;
1203                assert_eq!(parsed, $expected);
1204                assert!(buf.is_empty());
1205                let mut out = BytesMut::with_capacity(u16::MAX as usize);
1206                parsed.to_wire(ctx, &mut out)?;
1207                assert_eq!(out.to_vec(), $input_bytes);
1208                Ok(())
1209            }
1210        };
1211    }
1212
1213    macro_rules! ipv6 {
1214        ($addr_str: expr) => {
1215            Ipv6Addr::from_str($addr_str)
1216                .map_err(|e| eyre!("Failed to parse IPv6 address: {}", e))?
1217        };
1218    }
1219
1220    /// Creates a default context for evaluating the test cases below.
1221    /// This uses `four_octet_asn` set to true and address_family to IPv6.
1222    fn default_v6_context() -> ParserContext {
1223        ParserContext {
1224            four_octet_asn: Some(true),
1225            address_family: Some(AddressFamilyId::Ipv6),
1226        }
1227    }
1228
1229    // Origin Path Attribute.
1230    test_path_attribute_roundtrip!(
1231        test_origin_igp_roundtrip,
1232        &[0x40, 0x01, 0x01, 0x00],
1233        PathAttribute::Origin(OriginPathAttribute::IGP)
1234    );
1235
1236    test_path_attribute_roundtrip!(
1237        test_origin_egp_roundtrip,
1238        &[0x40, 0x01, 0x01, 0x01],
1239        PathAttribute::Origin(OriginPathAttribute::EGP)
1240    );
1241
1242    test_path_attribute_roundtrip!(
1243        test_origin_incomplete_roundtrip,
1244        &[0x40, 0x01, 0x01, 0x02],
1245        PathAttribute::Origin(OriginPathAttribute::INCOMPLETE)
1246    );
1247
1248    // AS Path Attribute.
1249    test_path_attribute_roundtrip!(
1250        test_as_path_segment,
1251        &[
1252            0x40, 0x02, 0x12, 0x02, 0x04, 0x00, 0x00, 0xfd, 0xe8, 0x00, 0x00, 0xfd, 0xe9, 0x00,
1253            0x00, 0xfd, 0xea, 0x00, 0x00, 0xfd, 0xeb
1254        ],
1255        PathAttribute::AsPath(AsPathAttribute {
1256            segments: vec![AsPathSegment {
1257                ordered: true,
1258                path: vec![65000, 65001, 65002, 65003],
1259            }]
1260        })
1261    );
1262
1263    test_path_attribute_roundtrip!(
1264        test_as_path_mixed_segments,
1265        &[
1266            0x40, 0x02, 0x1c, 0x02, 0x04, 0x00, 0x00, 0xfd, 0xe8, 0x00, 0x00, 0xfd, 0xe9, 0x00,
1267            0x00, 0xfd, 0xea, 0x00, 0x00, 0xfd, 0xeb, 0x01, 0x02, 0x00, 0x00, 0xfd, 0xea, 0x00,
1268            0x00, 0xfd, 0xeb,
1269        ],
1270        PathAttribute::AsPath(AsPathAttribute {
1271            segments: vec![
1272                AsPathSegment {
1273                    ordered: true,
1274                    path: vec![65000, 65001, 65002, 65003],
1275                },
1276                AsPathSegment {
1277                    ordered: false,
1278                    path: vec![65002, 65003],
1279                }
1280            ]
1281        })
1282    );
1283
1284    // Next Hop Path Attribute.
1285    test_path_attribute_roundtrip!(
1286        test_next_hop,
1287        &[0x40, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01],
1288        PathAttribute::NextHop(NextHopPathAttribute(Ipv4Addr::new(192, 168, 1, 1)))
1289    );
1290
1291    // Multi Exit Discriminator Path Attribute.
1292    test_path_attribute_roundtrip!(
1293        test_multi_exit_discriminator,
1294        &[0x80, 0x04, 0x04, 0xca, 0xfe, 0xba, 0xbe],
1295        PathAttribute::MultiExitDisc(MultiExitDiscPathAttribute(0xcafebabe))
1296    );
1297
1298    // Local Pref Path Attribute.
1299    test_path_attribute_roundtrip!(
1300        test_local_pref,
1301        &[0x40, 0x05, 0x04, 0xca, 0xfe, 0xd0, 0x0d],
1302        PathAttribute::LocalPref(LocalPrefPathAttribute(0xcafed00d))
1303    );
1304
1305    // Atomic Aggregate Path Attribute.
1306    test_path_attribute_roundtrip!(
1307        test_atomic_aggregate,
1308        &[0xc0, 0x06, 0x00],
1309        PathAttribute::AtomicAggregate(AtomicAggregatePathAttribute {})
1310    );
1311
1312    // Aggregator Path Attribute.
1313    test_path_attribute_roundtrip!(
1314        test_aggregator,
1315        &[
1316            0x80, 0x07, 0x08, 0x00, 0x00, 0xfd, 0xe8, 0xc0, 0xa8, 0x01, 0x01
1317        ],
1318        PathAttribute::Aggregator(AggregatorPathAttribute {
1319            asn: 65000,
1320            ip: Ipv4Addr::new(192, 168, 1, 1)
1321        })
1322    );
1323
1324    // Communities Path Attribute.
1325    test_path_attribute_roundtrip!(
1326        test_communities,
1327        &[0xc0, 0x08, 0x04, 0xca, 0xfe, 0xba, 0xbe],
1328        PathAttribute::Communitites(CommunitiesPathAttribute(vec![(0xcafe, 0xbabe)]))
1329    );
1330
1331    // MP Reach NLRI Path Attribute.
1332    test_path_attribute_roundtrip!(
1333        test_mp_reach_nlri,
1334        &[
1335            0x80, 0x0e, 0x2a, 0x00, 0x02, 0x01, 0x20, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00,
1336            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00,
1337            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x20, 0x20,
1338            0x01, 0x0d, 0xb8
1339        ],
1340        PathAttribute::MpReachNlri(MpReachNlriPathAttribute {
1341            afi: AddressFamilyId::Ipv6,
1342            safi: SubsequentAfi::Unicast,
1343            next_hop: NlriNextHop::Ipv6WithLl {
1344                global: ipv6!("2001:db8::1"),
1345                link_local: ipv6!("fe80::12"),
1346            },
1347            prefixes: vec![IpPrefix {
1348                address_family: AddressFamilyId::Ipv6,
1349                prefix: vec![0x20, 0x01, 0x0d, 0xb8],
1350                length: 32,
1351            }]
1352        })
1353    );
1354
1355    // MP Unreach NLRI Path Attribute.
1356    test_path_attribute_roundtrip!(
1357        mp_unreach_nlri,
1358        &[
1359            0x80, 0x0f, 0x08, 0x00, 0x02, 0x01, 0x20, 0x20, 0x01, 0x0d, 0xb8
1360        ],
1361        PathAttribute::MpUnreachNlri(MpUnreachNlriPathAttribute {
1362            afi: AddressFamilyId::Ipv6,
1363            safi: SubsequentAfi::Unicast,
1364            prefixes: vec![IpPrefix {
1365                address_family: AddressFamilyId::Ipv6,
1366                prefix: vec![0x20, 0x01, 0x0d, 0xb8],
1367                length: 32,
1368            }],
1369        })
1370    );
1371
1372    // Extended Communities Path Attribute.
1373    test_path_attribute_roundtrip!(
1374        test_extended_communities,
1375        &[
1376            0xc0, 0x10, 0x18, 0x00, 0x03, 0xfd, 0xe8, 0x00, 0x00, 0x05, 0x39, 0x00, 0x03, 0xfd,
1377            0xe9, 0x00, 0x00, 0x05, 0x39, 0x02, 0x03, 0xfd, 0xe8, 0x00, 0x00, 0x05, 0x39
1378        ],
1379        PathAttribute::ExtendedCommunities(ExtendedCommunitiesPathAttribute {
1380            extended_communities: vec![
1381                ExtendedCommunity::RouteOrigin {
1382                    typ: 0,
1383                    sub_typ: 3,
1384                    global_admin: 65000,
1385                    local_admin: 1337
1386                },
1387                ExtendedCommunity::RouteOrigin {
1388                    typ: 0,
1389                    sub_typ: 3,
1390                    global_admin: 65001,
1391                    local_admin: 1337
1392                },
1393                ExtendedCommunity::RouteOrigin {
1394                    typ: 2,
1395                    sub_typ: 3,
1396                    global_admin: 65000,
1397                    local_admin: 1337
1398                },
1399            ]
1400        })
1401    );
1402
1403    // Large Communities Path Attribute.
1404    test_path_attribute_roundtrip!(
1405        test_large_communities,
1406        &[
1407            0xc0, 0x20, 0x0c, 0x00, 0x00, 0xfd, 0xe8, 0x00, 0x00, 0xca, 0xfe, 0x00, 0x00, 0xf0,
1408            0x0d
1409        ],
1410        PathAttribute::LargeCommunities(LargeCommunitiesPathAttribute {
1411            communities: vec![LargeCommunity {
1412                global_admin: 65000,
1413                data_1: 0xcafe,
1414                data_2: 0xf00d,
1415            }]
1416        })
1417    );
1418}