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
28pub const AS_TRANS: u16 = 23456;
31
32pub const BGP4_VERSION: u8 = 4;
34
35#[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 RouteRefresh = 5,
45}
46
47impl Message {}
48
49#[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 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 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#[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#[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 pub ordered: bool,
415 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 out.put_u8(if segment.ordered { 2 } else { 1 });
476 out.put_u8(
478 segment
479 .path
480 .len()
481 .try_into()
482 .wrap_err("AS Path length too long")?,
483 );
484 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#[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 AsSpecific {
714 typ: u8,
715 sub_typ: u8,
716 global_admin: u16,
717 local_admin: u32,
718 },
719 Ipv4AddrSpecific {
721 typ: u8,
722 sub_typ: u8,
723 global_admin: u32,
724 local_admin: u16,
725 },
726 Opaque {
728 typ: u8,
729 sub_typ: u8,
730 value: [u8; 5],
731 },
732 RouteTarget {
734 typ: u8,
735 sub_typ: u8,
736 global_admin: u32,
737 local_admin: u16,
738 },
739 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 (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 (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 (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 (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 Ipv4(Ipv4Addr),
965 Ipv6(Ipv6Addr),
967 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
997fn 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 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 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 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 let (buf, nh_bytes) = nom::bytes::take(nh_len as usize).parse(buf)?;
1054 let nexthop = match nh_bytes.len() {
1055 16 => {
1056 let slice: [u8; 16] = nh_bytes.try_into().unwrap();
1058 NlriNextHop::Ipv6(Ipv6Addr::from(slice))
1059 }
1060 32 => {
1061 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 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 + 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 fn default_v6_context() -> ParserContext {
1223 ParserContext {
1224 four_octet_asn: Some(true),
1225 address_family: Some(AddressFamilyId::Ipv6),
1226 }
1227 }
1228
1229 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 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 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 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 test_path_attribute_roundtrip!(
1300 test_local_pref,
1301 &[0x40, 0x05, 0x04, 0xca, 0xfe, 0xd0, 0x0d],
1302 PathAttribute::LocalPref(LocalPrefPathAttribute(0xcafed00d))
1303 );
1304
1305 test_path_attribute_roundtrip!(
1307 test_atomic_aggregate,
1308 &[0xc0, 0x06, 0x00],
1309 PathAttribute::AtomicAggregate(AtomicAggregatePathAttribute {})
1310 );
1311
1312 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 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 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 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 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 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}