1mod aspath;
3mod nlri;
4mod origin;
5
6use crate::models::network::*;
7use bitflags::bitflags;
8use bytes::Bytes;
9use num_enum::{FromPrimitive, IntoPrimitive};
10use std::cmp::Ordering;
11use std::iter::{FromIterator, Map};
12use std::net::IpAddr;
13use std::slice::Iter;
14use std::vec::IntoIter;
15
16use crate::error::BgpValidationWarning;
17use crate::models::*;
18
19pub use aspath::*;
20pub use nlri::*;
21pub use origin::*;
22
23bitflags! {
24 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
47 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48 pub struct AttrFlags: u8 {
49 const OPTIONAL = 0b10000000;
50 const TRANSITIVE = 0b01000000;
51 const PARTIAL = 0b00100000;
52 const EXTENDED = 0b00010000;
53 }
54}
55
56#[allow(non_camel_case_types)]
62#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64#[repr(u8)]
65pub enum AttrType {
66 RESERVED = 0,
67 ORIGIN = 1,
68 AS_PATH = 2,
69 NEXT_HOP = 3,
70 MULTI_EXIT_DISCRIMINATOR = 4,
71 LOCAL_PREFERENCE = 5,
72 ATOMIC_AGGREGATE = 6,
73 AGGREGATOR = 7,
74 COMMUNITIES = 8,
75 ORIGINATOR_ID = 9,
77 CLUSTER_LIST = 10,
78 MP_REACHABLE_NLRI = 14,
80 MP_UNREACHABLE_NLRI = 15,
81 EXTENDED_COMMUNITIES = 16,
83 AS4_PATH = 17,
84 AS4_AGGREGATOR = 18,
85 PMSI_TUNNEL = 22,
86 TUNNEL_ENCAPSULATION = 23,
87 TRAFFIC_ENGINEERING = 24,
88 IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES = 25,
89 AIGP = 26,
90 PE_DISTINGUISHER_LABELS = 27,
91 BGP_LS_ATTRIBUTE = 29,
92 LARGE_COMMUNITIES = 32,
93 BGPSEC_PATH = 33,
94 ONLY_TO_CUSTOMER = 35,
95 SFP_ATTRIBUTE = 37,
96 BFD_DISCRIMINATOR = 38,
97 BGP_PREFIX_SID = 40,
98 BIER = 41,
99 ATTR_SET = 128,
100 DEVELOPMENT = 255,
102
103 #[num_enum(catch_all)]
105 Unknown(u8) = 254,
108}
109
110impl PartialOrd for AttrType {
111 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
112 Some(self.cmp(other))
113 }
114}
115
116impl Ord for AttrType {
117 fn cmp(&self, other: &Self) -> Ordering {
118 u8::from(*self).cmp(&u8::from(*other))
119 }
120}
121
122pub fn get_deprecated_attr_type(attr_type: u8) -> Option<&'static str> {
123 match attr_type {
124 11 => Some("DPA"),
125 12 => Some("ADVERTISER"),
126 13 => Some("RCID_PATH"),
127 19 => Some("SAFI Specific Attribute"),
128 20 => Some("Connector Attribute"),
129 21 => Some("AS_PATHLIMIT"),
130 28 => Some("BGP Entropy Label Capability"),
131 30 | 31 | 129 | 241 | 242 | 243 => Some("RFC8093"),
132
133 _ => None,
134 }
135}
136
137#[derive(PartialEq, Clone, Default, Eq)]
139pub struct Attributes {
140 pub(crate) inner: Vec<Attribute>,
143 pub(crate) validation_warnings: Vec<BgpValidationWarning>,
145 pub(crate) attr_mask: [u64; 4],
147}
148
149impl std::fmt::Debug for Attributes {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 f.debug_struct("Attributes")
152 .field("inner", &self.inner)
153 .field("validation_warnings", &self.validation_warnings)
154 .finish()
155 }
156}
157
158impl Attributes {
159 pub fn has_attr(&self, ty: AttrType) -> bool {
160 let attr = u8::from(ty);
161 (self.attr_mask[(attr / 64) as usize] & (1u64 << (attr % 64))) != 0
162 }
163
164 pub fn get_attr(&self, ty: AttrType) -> Option<Attribute> {
165 self.inner
166 .iter()
167 .find(|x| x.value.attr_type() == ty)
168 .cloned()
169 }
170
171 pub fn add_attr(&mut self, attr: Attribute) {
172 let ty = attr.value.attr_code();
173 self.attr_mask[(ty / 64) as usize] |= 1u64 << (ty % 64);
174 self.inner.push(attr);
175 }
176
177 pub fn check_mandatory_attributes(&mut self, is_announcement: bool, has_standard_nlri: bool) {
184 if !is_announcement {
185 return;
186 }
187
188 if !self.has_attr(AttrType::ORIGIN) {
190 self.validation_warnings
191 .push(BgpValidationWarning::MissingWellKnownAttribute {
192 attr_type: AttrType::ORIGIN,
193 });
194 }
195 if !self.has_attr(AttrType::AS_PATH) {
196 self.validation_warnings
197 .push(BgpValidationWarning::MissingWellKnownAttribute {
198 attr_type: AttrType::AS_PATH,
199 });
200 }
201
202 let has_mp_reach = self.has_attr(AttrType::MP_REACHABLE_NLRI);
206 if (has_standard_nlri || !has_mp_reach) && !self.has_attr(AttrType::NEXT_HOP) {
207 self.validation_warnings
208 .push(BgpValidationWarning::MissingWellKnownAttribute {
209 attr_type: AttrType::NEXT_HOP,
210 });
211 }
212 }
213
214 pub fn add_validation_warning(&mut self, warning: BgpValidationWarning) {
216 self.validation_warnings.push(warning);
217 }
218
219 pub fn validation_warnings(&self) -> &[BgpValidationWarning] {
221 &self.validation_warnings
222 }
223
224 pub fn has_validation_warnings(&self) -> bool {
226 !self.validation_warnings.is_empty()
227 }
228
229 pub fn origin(&self) -> Origin {
232 self.inner
233 .iter()
234 .find_map(|x| match &x.value {
235 AttributeValue::Origin(x) => Some(*x),
236 _ => None,
237 })
238 .unwrap_or(Origin::INCOMPLETE)
239 }
240
241 pub fn origin_id(&self) -> Option<BgpIdentifier> {
243 self.inner.iter().find_map(|x| match &x.value {
244 AttributeValue::OriginatorId(x) => Some(*x),
245 _ => None,
246 })
247 }
248
249 pub fn next_hop(&self) -> Option<IpAddr> {
254 self.inner.iter().find_map(|x| match &x.value {
255 AttributeValue::NextHop(x) => Some(*x),
256 _ => None,
257 })
258 }
259
260 pub fn multi_exit_discriminator(&self) -> Option<u32> {
261 self.inner.iter().find_map(|x| match &x.value {
262 AttributeValue::MultiExitDiscriminator(x) => Some(*x),
263 _ => None,
264 })
265 }
266
267 pub fn local_preference(&self) -> Option<u32> {
268 self.inner.iter().find_map(|x| match &x.value {
269 AttributeValue::LocalPreference(x) => Some(*x),
270 _ => None,
271 })
272 }
273
274 pub fn only_to_customer(&self) -> Option<Asn> {
275 self.inner.iter().find_map(|x| match &x.value {
276 AttributeValue::OnlyToCustomer(x) => Some(*x),
277 _ => None,
278 })
279 }
280
281 pub fn atomic_aggregate(&self) -> bool {
282 self.inner
283 .iter()
284 .any(|x| matches!(&x.value, AttributeValue::AtomicAggregate))
285 }
286
287 pub fn aggregator(&self) -> Option<(Asn, BgpIdentifier)> {
288 self.inner.iter().rev().find_map(|x| match &x.value {
291 AttributeValue::Aggregator { asn, id, .. } => Some((*asn, *id)),
292 _ => None,
293 })
294 }
295
296 pub fn clusters(&self) -> Option<&[u32]> {
297 self.inner.iter().find_map(|x| match &x.value {
298 AttributeValue::Clusters(x) => Some(x.as_ref()),
299 _ => None,
300 })
301 }
302
303 pub fn as_path(&self) -> Option<&AsPath> {
305 self.inner.iter().rev().find_map(|x| match &x.value {
308 AttributeValue::AsPath { path, .. } => Some(path),
309 _ => None,
310 })
311 }
312
313 pub fn get_reachable_nlri(&self) -> Option<&Nlri> {
314 self.inner.iter().find_map(|x| match &x.value {
315 AttributeValue::MpReachNlri(x) => Some(x),
316 _ => None,
317 })
318 }
319
320 pub fn get_unreachable_nlri(&self) -> Option<&Nlri> {
321 self.inner.iter().find_map(|x| match &x.value {
322 AttributeValue::MpUnreachNlri(x) => Some(x),
323 _ => None,
324 })
325 }
326
327 pub fn iter_communities(&self) -> MetaCommunitiesIter<'_> {
328 MetaCommunitiesIter {
329 attributes: &self.inner,
330 index: 0,
331 }
332 }
333
334 pub fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
337 self.into_iter()
338 }
339
340 pub fn into_attributes_iter(self) -> impl Iterator<Item = Attribute> {
343 self.inner.into_iter()
344 }
345}
346
347pub struct MetaCommunitiesIter<'a> {
348 attributes: &'a [Attribute],
349 index: usize,
350}
351
352impl Iterator for MetaCommunitiesIter<'_> {
353 type Item = MetaCommunity;
354
355 fn next(&mut self) -> Option<Self::Item> {
356 loop {
357 match &self.attributes.first()?.value {
358 AttributeValue::Communities(x) if self.index < x.len() => {
359 self.index += 1;
360 return Some(MetaCommunity::Plain(x[self.index - 1]));
361 }
362 AttributeValue::ExtendedCommunities(x) if self.index < x.len() => {
363 self.index += 1;
364 return Some(MetaCommunity::Extended(x[self.index - 1]));
365 }
366 AttributeValue::LargeCommunities(x) if self.index < x.len() => {
367 self.index += 1;
368 return Some(MetaCommunity::Large(x[self.index - 1]));
369 }
370 _ => {
371 self.attributes = &self.attributes[1..];
372 self.index = 0;
373 }
374 }
375 }
376 }
377}
378
379fn compute_mask(inner: &[Attribute]) -> [u64; 4] {
380 let mut attr_mask = [0; 4];
381 for attr in inner {
382 let ty = attr.value.attr_code();
383 attr_mask[(ty / 64) as usize] |= 1u64 << (ty % 64);
384 }
385 attr_mask
386}
387
388impl FromIterator<Attribute> for Attributes {
389 fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
390 let inner: Vec<Attribute> = iter.into_iter().collect();
391 let attr_mask = compute_mask(&inner);
392 Attributes {
393 inner,
394 validation_warnings: Vec::new(),
395 attr_mask,
396 }
397 }
398}
399
400impl From<Vec<Attribute>> for Attributes {
401 fn from(value: Vec<Attribute>) -> Self {
402 let attr_mask = compute_mask(&value);
403 Attributes {
404 inner: value,
405 validation_warnings: Vec::new(),
406 attr_mask,
407 }
408 }
409}
410
411impl Extend<Attribute> for Attributes {
412 fn extend<T: IntoIterator<Item = Attribute>>(&mut self, iter: T) {
413 for attr in iter {
414 self.add_attr(attr);
415 }
416 }
417}
418
419impl Extend<AttributeValue> for Attributes {
420 fn extend<T: IntoIterator<Item = AttributeValue>>(&mut self, iter: T) {
421 self.extend(iter.into_iter().map(Attribute::from))
422 }
423}
424
425impl FromIterator<AttributeValue> for Attributes {
426 fn from_iter<T: IntoIterator<Item = AttributeValue>>(iter: T) -> Self {
427 let inner: Vec<Attribute> = iter.into_iter().map(Attribute::from).collect();
428 let attr_mask = compute_mask(&inner);
429 Attributes {
430 inner,
431 validation_warnings: Vec::new(),
432 attr_mask,
433 }
434 }
435}
436
437impl IntoIterator for Attributes {
438 type Item = AttributeValue;
439 type IntoIter = Map<IntoIter<Attribute>, fn(Attribute) -> AttributeValue>;
440
441 fn into_iter(self) -> Self::IntoIter {
442 self.inner.into_iter().map(|x| x.value)
443 }
444}
445
446impl<'a> IntoIterator for &'a Attributes {
447 type Item = &'a AttributeValue;
448 type IntoIter = Map<Iter<'a, Attribute>, fn(&Attribute) -> &AttributeValue>;
449
450 fn into_iter(self) -> Self::IntoIter {
451 self.inner.iter().map(|x| &x.value)
452 }
453}
454
455#[cfg(feature = "serde")]
456mod serde_impl {
457 use super::*;
458 use serde::{Deserialize, Deserializer, Serialize, Serializer};
459
460 impl Serialize for Attributes {
461 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
462 where
463 S: Serializer,
464 {
465 self.inner.serialize(serializer)
466 }
467 }
468
469 impl<'de> Deserialize<'de> for Attributes {
470 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
471 where
472 D: Deserializer<'de>,
473 {
474 let inner = <Vec<Attribute>>::deserialize(deserializer)?;
475 let attr_mask = compute_mask(&inner);
476 Ok(Attributes {
477 inner,
478 validation_warnings: Vec::new(),
479 attr_mask,
480 })
481 }
482 }
483}
484
485#[derive(Debug, PartialEq, Clone, Eq)]
487#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
488pub struct Attribute {
489 pub value: AttributeValue,
490 pub flag: AttrFlags,
491}
492
493impl Attribute {
494 pub const fn is_optional(&self) -> bool {
495 self.flag.contains(AttrFlags::OPTIONAL)
496 }
497
498 pub const fn is_transitive(&self) -> bool {
499 self.flag.contains(AttrFlags::TRANSITIVE)
500 }
501
502 pub const fn is_partial(&self) -> bool {
503 self.flag.contains(AttrFlags::PARTIAL)
504 }
505
506 pub const fn is_extended(&self) -> bool {
507 self.flag.contains(AttrFlags::EXTENDED)
508 }
509}
510
511impl From<AttributeValue> for Attribute {
512 fn from(value: AttributeValue) -> Self {
513 Attribute {
514 flag: value.default_flags(),
515 value,
516 }
517 }
518}
519
520#[derive(Debug, PartialEq, Clone, Eq)]
522#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
523pub struct AigpTlv {
524 pub tlv_type: u8,
525 pub length: u16,
526 pub value: Bytes,
527}
528
529#[derive(Debug, PartialEq, Clone, Eq)]
534#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
535pub struct Aigp {
536 pub tlvs: Vec<AigpTlv>,
537}
538
539impl Aigp {
540 pub fn accumulated_metric(&self) -> Option<u64> {
542 self.tlvs
543 .iter()
544 .find(|tlv| tlv.tlv_type == 1)
545 .and_then(|tlv| {
546 if tlv.value.len() >= 8 {
547 Some(u64::from_be_bytes([
548 tlv.value[0],
549 tlv.value[1],
550 tlv.value[2],
551 tlv.value[3],
552 tlv.value[4],
553 tlv.value[5],
554 tlv.value[6],
555 tlv.value[7],
556 ]))
557 } else {
558 None
559 }
560 })
561 }
562}
563
564#[derive(Debug, PartialEq, Clone, Eq)]
566#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
567pub struct RawTlv8 {
568 pub tlv_type: u8,
569 pub value: Bytes,
570}
571
572#[derive(Debug, PartialEq, Clone, Eq)]
574#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
575pub struct RawTlv8Ext {
576 pub tlv_type: u8,
577 pub value: Bytes,
578}
579
580#[derive(Debug, PartialEq, Clone, Eq)]
582#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
583pub struct RawTlv16 {
584 pub tlv_type: u16,
585 pub value: Bytes,
586}
587
588#[derive(Debug, PartialEq, Clone, Eq)]
590#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
591pub struct BfdDiscriminatorAttribute {
592 pub mode: u8,
593 pub discriminator: u32,
594 pub tlvs: Vec<RawTlv8>,
595}
596
597#[derive(Debug, PartialEq, Clone, Eq)]
599#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
600pub struct BgpPrefixSidAttribute {
601 pub tlvs: Vec<RawTlv8Ext>,
602}
603
604#[derive(Debug, PartialEq, Clone, Eq)]
606#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
607pub struct BierAttribute {
608 pub tlvs: Vec<RawTlv16>,
609}
610
611#[derive(Debug, PartialEq, Clone, Eq)]
613#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
614pub struct SfpAttribute {
615 pub tlvs: Vec<RawTlv8Ext>,
616}
617
618#[derive(Debug, PartialEq, Clone, Eq)]
626#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
627pub struct AttrSet {
628 pub origin_as: Asn,
630 pub attributes: Attributes,
632}
633
634#[derive(Debug, PartialEq, Clone, Eq)]
636#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
637pub enum AttributeValue {
638 Origin(Origin),
639 AsPath {
640 path: AsPath,
641 is_as4: bool,
642 },
643 NextHop(IpAddr),
644 MultiExitDiscriminator(u32),
645 LocalPreference(u32),
646 OnlyToCustomer(Asn),
647 AtomicAggregate,
648 Aggregator {
649 asn: Asn,
650 id: BgpIdentifier,
651 is_as4: bool,
652 },
653 Communities(Vec<Community>),
654 ExtendedCommunities(Vec<ExtendedCommunity>),
655 Ipv6AddressSpecificExtendedCommunities(Vec<Ipv6AddrExtCommunity>),
656 LargeCommunities(Vec<LargeCommunity>),
657 OriginatorId(BgpIdentifier),
658 Clusters(Vec<u32>),
659 MpReachNlri(Nlri),
660 MpUnreachNlri(Nlri),
661 LinkState(crate::models::bgp::linkstate::LinkStateAttribute),
663 TunnelEncapsulation(crate::models::bgp::tunnel_encap::TunnelEncapAttribute),
665 BfdDiscriminator(BfdDiscriminatorAttribute),
667 BgpPrefixSid(BgpPrefixSidAttribute),
669 Bier(BierAttribute),
671 Sfp(SfpAttribute),
673 Development(Vec<u8>),
674 Raw(AttrRaw),
675 Deprecated(AttrRaw),
676 Unknown(AttrRaw),
677 Aigp(Aigp),
679 AttrSet(AttrSet),
681}
682
683impl From<Origin> for AttributeValue {
684 fn from(value: Origin) -> Self {
685 AttributeValue::Origin(value)
686 }
687}
688
689impl From<AsPath> for AttributeValue {
691 fn from(path: AsPath) -> Self {
692 AttributeValue::AsPath {
693 path,
694 is_as4: false,
695 }
696 }
697}
698
699#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
703#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
704pub enum AttributeCategory {
705 WellKnownMandatory,
706 WellKnownDiscretionary,
707 OptionalTransitive,
708 OptionalNonTransitive,
709}
710
711impl AttributeValue {
712 pub fn attr_type(&self) -> AttrType {
713 match self {
714 AttributeValue::Origin(_) => AttrType::ORIGIN,
715 AttributeValue::AsPath { is_as4: false, .. } => AttrType::AS_PATH,
716 AttributeValue::AsPath { is_as4: true, .. } => AttrType::AS4_PATH,
717 AttributeValue::NextHop(_) => AttrType::NEXT_HOP,
718 AttributeValue::MultiExitDiscriminator(_) => AttrType::MULTI_EXIT_DISCRIMINATOR,
719 AttributeValue::LocalPreference(_) => AttrType::LOCAL_PREFERENCE,
720 AttributeValue::OnlyToCustomer(_) => AttrType::ONLY_TO_CUSTOMER,
721 AttributeValue::AtomicAggregate => AttrType::ATOMIC_AGGREGATE,
722 AttributeValue::Aggregator { is_as4: false, .. } => AttrType::AGGREGATOR,
723 AttributeValue::Aggregator { is_as4: true, .. } => AttrType::AS4_AGGREGATOR,
724 AttributeValue::Communities(_) => AttrType::COMMUNITIES,
725 AttributeValue::ExtendedCommunities(_) => AttrType::EXTENDED_COMMUNITIES,
726 AttributeValue::Ipv6AddressSpecificExtendedCommunities(_) => {
727 AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES
728 }
729 AttributeValue::LargeCommunities(_) => AttrType::LARGE_COMMUNITIES,
730 AttributeValue::OriginatorId(_) => AttrType::ORIGINATOR_ID,
731 AttributeValue::Clusters(_) => AttrType::CLUSTER_LIST,
732 AttributeValue::MpReachNlri(_) => AttrType::MP_REACHABLE_NLRI,
733 AttributeValue::MpUnreachNlri(_) => AttrType::MP_UNREACHABLE_NLRI,
734 AttributeValue::LinkState(_) => AttrType::BGP_LS_ATTRIBUTE,
735 AttributeValue::TunnelEncapsulation(_) => AttrType::TUNNEL_ENCAPSULATION,
736 AttributeValue::BfdDiscriminator(_) => AttrType::BFD_DISCRIMINATOR,
737 AttributeValue::BgpPrefixSid(_) => AttrType::BGP_PREFIX_SID,
738 AttributeValue::Bier(_) => AttrType::BIER,
739 AttributeValue::Sfp(_) => AttrType::SFP_ATTRIBUTE,
740 AttributeValue::Development(_) => AttrType::DEVELOPMENT,
741 AttributeValue::Raw(x) | AttributeValue::Deprecated(x) | AttributeValue::Unknown(x) => {
742 x.attr_type()
743 }
744 AttributeValue::Aigp(_) => AttrType::AIGP,
745 AttributeValue::AttrSet(_) => AttrType::ATTR_SET,
746 }
747 }
748
749 pub fn attr_code(&self) -> u8 {
750 match self {
751 AttributeValue::Raw(x) | AttributeValue::Deprecated(x) | AttributeValue::Unknown(x) => {
752 x.code
753 }
754 _ => self.attr_type().into(),
755 }
756 }
757
758 pub fn attr_category(&self) -> Option<AttributeCategory> {
759 use AttributeCategory::*;
760
761 match self {
762 AttributeValue::Origin(_) => Some(WellKnownMandatory),
763 AttributeValue::AsPath { is_as4: false, .. } => Some(WellKnownMandatory),
764 AttributeValue::AsPath { is_as4: true, .. } => Some(OptionalTransitive),
765 AttributeValue::NextHop(_) => Some(WellKnownMandatory),
766 AttributeValue::MultiExitDiscriminator(_) => Some(OptionalNonTransitive),
767 AttributeValue::LocalPreference(_) => Some(WellKnownMandatory),
769 AttributeValue::OnlyToCustomer(_) => Some(OptionalTransitive),
770 AttributeValue::AtomicAggregate => Some(WellKnownDiscretionary),
771 AttributeValue::Aggregator { .. } => Some(OptionalTransitive),
772 AttributeValue::Communities(_) => Some(OptionalTransitive),
773 AttributeValue::ExtendedCommunities(_) => Some(OptionalTransitive),
774 AttributeValue::LargeCommunities(_) => Some(OptionalTransitive),
775 AttributeValue::OriginatorId(_) => Some(OptionalNonTransitive),
776 AttributeValue::Clusters(_) => Some(OptionalNonTransitive),
777 AttributeValue::MpReachNlri(_) => Some(OptionalNonTransitive),
778 AttributeValue::MpUnreachNlri(_) => Some(OptionalNonTransitive),
779 AttributeValue::LinkState(_) => Some(OptionalNonTransitive),
780 AttributeValue::Aigp(_) => Some(OptionalNonTransitive),
781 AttributeValue::BfdDiscriminator(_) => Some(OptionalTransitive),
782 AttributeValue::BgpPrefixSid(_) => Some(OptionalTransitive),
783 AttributeValue::Bier(_) => Some(OptionalTransitive),
784 AttributeValue::Sfp(_) => Some(OptionalTransitive),
785 AttributeValue::AttrSet(_) => Some(OptionalTransitive),
786 _ => None,
787 }
788 }
789
790 pub fn default_flags(&self) -> AttrFlags {
793 match self.attr_category() {
794 None => AttrFlags::OPTIONAL | AttrFlags::PARTIAL | AttrFlags::TRANSITIVE,
795 Some(AttributeCategory::WellKnownMandatory) => AttrFlags::TRANSITIVE,
796 Some(AttributeCategory::WellKnownDiscretionary) => AttrFlags::TRANSITIVE,
797 Some(AttributeCategory::OptionalTransitive) => {
798 AttrFlags::OPTIONAL | AttrFlags::TRANSITIVE
799 }
800 Some(AttributeCategory::OptionalNonTransitive) => AttrFlags::OPTIONAL,
801 }
802 }
803}
804
805#[derive(Debug, PartialEq, Clone, Eq)]
806#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
807pub struct AttrRaw {
808 pub code: u8,
809 pub bytes: Bytes,
810}
811
812impl AttrRaw {
813 pub fn attr_type(&self) -> AttrType {
819 AttrType::from(self.code)
820 }
821}
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826 use std::net::Ipv4Addr;
827 use std::str::FromStr;
828
829 #[test]
830 fn test_attr_type() {
831 let attr_value = AttributeValue::Origin(Origin::IGP);
832 assert_eq!(attr_value.attr_type(), AttrType::ORIGIN);
833 }
834
835 #[test]
836 fn test_attr_category() {
837 let attr_value = AttributeValue::Origin(Origin::IGP);
838 let category = attr_value.attr_category().unwrap();
839 assert_eq!(category, AttributeCategory::WellKnownMandatory);
840 }
841
842 #[test]
843 fn test_default_flags() {
844 let attr_value = AttributeValue::Origin(Origin::IGP);
845 let flags = attr_value.default_flags();
846 assert_eq!(flags, AttrFlags::TRANSITIVE);
847 }
848
849 #[test]
850 fn test_from_iter_attribute_value_uses_default_flags() {
851 let attributes = Attributes::from_iter(vec![
852 AttributeValue::Origin(Origin::IGP),
853 AttributeValue::AsPath {
854 path: AsPath::new(),
855 is_as4: false,
856 },
857 ]);
858
859 assert_eq!(
860 attributes.get_attr(AttrType::ORIGIN).unwrap().flag,
861 AttrFlags::TRANSITIVE
862 );
863 assert_eq!(
864 attributes.get_attr(AttrType::AS_PATH).unwrap().flag,
865 AttrFlags::TRANSITIVE
866 );
867 }
868
869 #[test]
870 fn test_get_attr() {
871 let attribute = Attribute {
872 value: AttributeValue::Origin(Origin::IGP),
873 flag: AttrFlags::TRANSITIVE,
874 };
875
876 let mut attributes = Attributes::default();
877 attributes.add_attr(attribute.clone());
878
879 assert_eq!(attributes.get_attr(AttrType::ORIGIN), Some(attribute));
880 }
881
882 #[test]
883 fn test_has_attr() {
884 let attribute = Attribute {
885 value: AttributeValue::Origin(Origin::IGP),
886 flag: AttrFlags::TRANSITIVE,
887 };
888
889 let mut attributes = Attributes::default();
890 attributes.add_attr(attribute);
891
892 assert!(attributes.has_attr(AttrType::ORIGIN));
893 }
894
895 #[test]
896 fn test_getting_all_attributes() {
897 let mut attributes = Attributes::default();
898 attributes.add_attr(Attribute {
899 value: AttributeValue::Origin(Origin::IGP),
900 flag: AttrFlags::TRANSITIVE,
901 });
902 attributes.add_attr(Attribute {
903 value: AttributeValue::AsPath {
904 path: AsPath::new(),
905 is_as4: false,
906 },
907 flag: AttrFlags::TRANSITIVE,
908 });
909 attributes.add_attr(Attribute {
910 value: AttributeValue::NextHop(IpAddr::from_str("10.0.0.0").unwrap()),
911 flag: AttrFlags::TRANSITIVE,
912 });
913 attributes.add_attr(Attribute {
914 value: AttributeValue::MultiExitDiscriminator(1),
915 flag: AttrFlags::TRANSITIVE,
916 });
917
918 attributes.add_attr(Attribute {
919 value: AttributeValue::LocalPreference(1),
920 flag: AttrFlags::TRANSITIVE,
921 });
922 attributes.add_attr(Attribute {
923 value: AttributeValue::OnlyToCustomer(Asn::new_32bit(1)),
924 flag: AttrFlags::TRANSITIVE,
925 });
926 attributes.add_attr(Attribute {
927 value: AttributeValue::AtomicAggregate,
928 flag: AttrFlags::TRANSITIVE,
929 });
930 attributes.add_attr(Attribute {
931 value: AttributeValue::Clusters(vec![1, 2, 3]),
932 flag: AttrFlags::TRANSITIVE,
933 });
934 attributes.add_attr(Attribute {
935 value: AttributeValue::Aggregator {
936 asn: Asn::new_32bit(1),
937 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
938 is_as4: false,
939 },
940 flag: AttrFlags::TRANSITIVE,
941 });
942 attributes.add_attr(Attribute {
943 value: AttributeValue::OriginatorId(Ipv4Addr::from_str("0.0.0.0").unwrap()),
944 flag: AttrFlags::TRANSITIVE,
945 });
946
947 assert_eq!(attributes.origin(), Origin::IGP);
948 assert_eq!(attributes.as_path(), Some(&AsPath::new()));
949 assert_eq!(
950 attributes.next_hop(),
951 Some(IpAddr::from_str("10.0.0.0").unwrap())
952 );
953 assert_eq!(attributes.multi_exit_discriminator(), Some(1));
954 assert_eq!(attributes.local_preference(), Some(1));
955 assert_eq!(attributes.only_to_customer(), Some(Asn::new_32bit(1)));
956 assert!(attributes.atomic_aggregate());
957 assert_eq!(attributes.clusters(), Some(vec![1_u32, 2, 3].as_slice()));
958 assert_eq!(
959 attributes.aggregator(),
960 Some((Asn::new_32bit(1), Ipv4Addr::from_str("0.0.0.0").unwrap()))
961 );
962 assert_eq!(
963 attributes.origin_id(),
964 Some(Ipv4Addr::from_str("0.0.0.0").unwrap())
965 );
966
967 let aspath_attr = attributes.get_attr(AttrType::AS_PATH).unwrap();
968 assert!(aspath_attr.is_transitive());
969 assert!(!aspath_attr.is_extended());
970 assert!(!aspath_attr.is_partial());
971 assert!(!aspath_attr.is_optional());
972
973 for attr in attributes.iter() {
974 println!("{attr:?}");
975 }
976 }
977
978 #[test]
979 fn test_from() {
980 let origin = Origin::IGP;
981 let attr_value = AttributeValue::from(origin);
982 assert_eq!(attr_value, AttributeValue::Origin(Origin::IGP));
983
984 let aspath = AsPath::new();
985 let attr_value = AttributeValue::from(aspath);
986 assert_eq!(
987 attr_value,
988 AttributeValue::AsPath {
989 path: AsPath::new(),
990 is_as4: false
991 }
992 );
993 }
994
995 #[test]
996 fn test_well_known_mandatory_attrs() {
997 let origin_attr = AttributeValue::Origin(Origin::IGP);
998 assert_eq!(
999 origin_attr.attr_category(),
1000 Some(AttributeCategory::WellKnownMandatory)
1001 );
1002 let as_path_attr = AttributeValue::AsPath {
1003 path: AsPath::new(),
1004 is_as4: false,
1005 };
1006 assert_eq!(
1007 as_path_attr.attr_category(),
1008 Some(AttributeCategory::WellKnownMandatory)
1009 );
1010 let next_hop_attr = AttributeValue::NextHop(IpAddr::from_str("10.0.0.0").unwrap());
1011 assert_eq!(
1012 next_hop_attr.attr_category(),
1013 Some(AttributeCategory::WellKnownMandatory)
1014 );
1015 let local_preference_attr = AttributeValue::LocalPreference(1);
1016 assert_eq!(
1017 local_preference_attr.attr_category(),
1018 Some(AttributeCategory::WellKnownMandatory)
1019 );
1020 }
1021
1022 #[test]
1023 fn test_well_known_discretionary_attrs() {
1024 let atomic_aggregate_attr = AttributeValue::AtomicAggregate;
1025 assert_eq!(
1026 atomic_aggregate_attr.attr_category(),
1027 Some(AttributeCategory::WellKnownDiscretionary)
1028 );
1029 }
1030
1031 #[test]
1032 fn test_optional_transitive_attrs() {
1033 let as_path_attr = AttributeValue::AsPath {
1034 path: AsPath::new(),
1035 is_as4: true,
1036 };
1037 assert_eq!(
1038 as_path_attr.attr_category(),
1039 Some(AttributeCategory::OptionalTransitive)
1040 );
1041 let aggregator_attr = AttributeValue::Aggregator {
1042 asn: Asn::new_32bit(1),
1043 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1044 is_as4: false,
1045 };
1046 assert_eq!(
1047 aggregator_attr.attr_category(),
1048 Some(AttributeCategory::OptionalTransitive)
1049 );
1050 let only_to_customer_attr = AttributeValue::OnlyToCustomer(Asn::new_32bit(1));
1051 assert_eq!(
1052 only_to_customer_attr.attr_category(),
1053 Some(AttributeCategory::OptionalTransitive)
1054 );
1055 let communities_attr =
1056 AttributeValue::Communities(vec![Community::Custom(Asn::new_32bit(1), 1)]);
1057 assert_eq!(
1058 communities_attr.attr_category(),
1059 Some(AttributeCategory::OptionalTransitive)
1060 );
1061 let extended_communities_attr =
1062 AttributeValue::ExtendedCommunities(vec![ExtendedCommunity::Raw([0; 8])]);
1063 assert_eq!(
1064 extended_communities_attr.attr_category(),
1065 Some(AttributeCategory::OptionalTransitive)
1066 );
1067 let large_communities_attr =
1068 AttributeValue::LargeCommunities(vec![LargeCommunity::new(1, [1, 1])]);
1069 assert_eq!(
1070 large_communities_attr.attr_category(),
1071 Some(AttributeCategory::OptionalTransitive)
1072 );
1073 let aggregator_attr = AttributeValue::Aggregator {
1074 asn: Asn::new_32bit(1),
1075 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1076 is_as4: true,
1077 };
1078 assert_eq!(
1079 aggregator_attr.attr_category(),
1080 Some(AttributeCategory::OptionalTransitive)
1081 );
1082 }
1083
1084 #[test]
1085 fn test_new_attribute_attr_categories() {
1086 assert_eq!(
1088 AttributeValue::BfdDiscriminator(BfdDiscriminatorAttribute {
1089 mode: 1,
1090 discriminator: 0,
1091 tlvs: vec![],
1092 })
1093 .attr_category(),
1094 Some(AttributeCategory::OptionalTransitive)
1095 );
1096 assert_eq!(
1098 AttributeValue::BgpPrefixSid(BgpPrefixSidAttribute { tlvs: vec![] }).attr_category(),
1099 Some(AttributeCategory::OptionalTransitive)
1100 );
1101 assert_eq!(
1103 AttributeValue::Bier(BierAttribute { tlvs: vec![] }).attr_category(),
1104 Some(AttributeCategory::OptionalTransitive)
1105 );
1106 assert_eq!(
1108 AttributeValue::Sfp(SfpAttribute { tlvs: vec![] }).attr_category(),
1109 Some(AttributeCategory::OptionalTransitive)
1110 );
1111 }
1112
1113 #[test]
1114 fn test_optional_non_transitive_attrs() {
1115 let multi_exit_discriminator_attr = AttributeValue::MultiExitDiscriminator(1);
1116 assert_eq!(
1117 multi_exit_discriminator_attr.attr_category(),
1118 Some(AttributeCategory::OptionalNonTransitive)
1119 );
1120 let originator_id_attr =
1121 AttributeValue::OriginatorId(Ipv4Addr::from_str("0.0.0.0").unwrap());
1122 assert_eq!(
1123 originator_id_attr.attr_category(),
1124 Some(AttributeCategory::OptionalNonTransitive)
1125 );
1126 let clusters_attr = AttributeValue::Clusters(vec![1, 2, 3]);
1127 assert_eq!(
1128 clusters_attr.attr_category(),
1129 Some(AttributeCategory::OptionalNonTransitive)
1130 );
1131 let mp_unreach_nlri_attr = AttributeValue::MpReachNlri(Nlri::new_unreachable(
1132 NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
1133 ));
1134 assert_eq!(
1135 mp_unreach_nlri_attr.attr_category(),
1136 Some(AttributeCategory::OptionalNonTransitive)
1137 );
1138
1139 let mp_reach_nlri_attr = AttributeValue::MpUnreachNlri(Nlri::new_unreachable(
1140 NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
1141 ));
1142 assert_eq!(
1143 mp_reach_nlri_attr.attr_category(),
1144 Some(AttributeCategory::OptionalNonTransitive)
1145 );
1146 }
1147
1148 #[test]
1149 #[cfg(feature = "serde")]
1150 fn test_serde() {
1151 let attributes = Attributes::from_iter(vec![
1152 Attribute {
1153 value: AttributeValue::Origin(Origin::IGP),
1154 flag: AttrFlags::TRANSITIVE,
1155 },
1156 Attribute {
1157 value: AttributeValue::AsPath {
1158 path: AsPath::new(),
1159 is_as4: false,
1160 },
1161 flag: AttrFlags::TRANSITIVE,
1162 },
1163 ]);
1164
1165 let serialized = serde_json::to_string(&attributes).unwrap();
1166 let deserialized: Attributes = serde_json::from_str(&serialized).unwrap();
1167
1168 assert_eq!(attributes, deserialized);
1169 }
1170}