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, Clone)]
620#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
621pub struct TrafficEngineering {
622 pub switching_capability: u8,
624
625 pub encoding: u8,
627
628 pub reserved: u16,
633
634 pub max_lsp_bandwidth: [f32; 8],
636
637 pub switching_capability_specific: Bytes,
639}
640
641impl PartialEq for TrafficEngineering {
642 fn eq(&self, other: &Self) -> bool {
643 self.switching_capability == other.switching_capability
644 && self.encoding == other.encoding
645 && self.reserved == other.reserved
646 && self
647 .max_lsp_bandwidth
648 .iter()
649 .zip(other.max_lsp_bandwidth.iter())
650 .all(|(left, right)| left.to_bits() == right.to_bits())
651 && self.switching_capability_specific == other.switching_capability_specific
652 }
653}
654
655impl Eq for TrafficEngineering {}
656
657#[derive(Debug, PartialEq, Clone, Eq)]
665#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
666pub struct AttrSet {
667 pub origin_as: Asn,
669 pub attributes: Attributes,
671}
672
673#[derive(Debug, PartialEq, Clone, Eq)]
675#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
676pub enum AttributeValue {
677 Origin(Origin),
678 AsPath {
679 path: AsPath,
680 is_as4: bool,
681 },
682 NextHop(IpAddr),
683 MultiExitDiscriminator(u32),
684 LocalPreference(u32),
685 OnlyToCustomer(Asn),
686 AtomicAggregate,
687 Aggregator {
688 asn: Asn,
689 id: BgpIdentifier,
690 is_as4: bool,
691 },
692 Communities(Vec<Community>),
693 ExtendedCommunities(Vec<ExtendedCommunity>),
694 Ipv6AddressSpecificExtendedCommunities(Vec<Ipv6AddrExtCommunity>),
695 LargeCommunities(Vec<LargeCommunity>),
696 OriginatorId(BgpIdentifier),
697 Clusters(Vec<u32>),
698 MpReachNlri(Nlri),
699 MpUnreachNlri(Nlri),
700 LinkState(crate::models::bgp::linkstate::LinkStateAttribute),
702 TunnelEncapsulation(crate::models::bgp::tunnel_encap::TunnelEncapAttribute),
704 TrafficEngineering(TrafficEngineering),
706 BfdDiscriminator(BfdDiscriminatorAttribute),
708 BgpPrefixSid(BgpPrefixSidAttribute),
710 Bier(BierAttribute),
712 Sfp(SfpAttribute),
714 Development(Vec<u8>),
715 Raw(AttrRaw),
716 Deprecated(AttrRaw),
717 Unknown(AttrRaw),
718 Aigp(Aigp),
720 AttrSet(AttrSet),
722}
723
724impl From<Origin> for AttributeValue {
725 fn from(value: Origin) -> Self {
726 AttributeValue::Origin(value)
727 }
728}
729
730impl From<AsPath> for AttributeValue {
732 fn from(path: AsPath) -> Self {
733 AttributeValue::AsPath {
734 path,
735 is_as4: false,
736 }
737 }
738}
739
740#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
744#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
745pub enum AttributeCategory {
746 WellKnownMandatory,
747 WellKnownDiscretionary,
748 OptionalTransitive,
749 OptionalNonTransitive,
750}
751
752impl AttributeValue {
753 pub fn attr_type(&self) -> AttrType {
754 match self {
755 AttributeValue::Origin(_) => AttrType::ORIGIN,
756 AttributeValue::AsPath { is_as4: false, .. } => AttrType::AS_PATH,
757 AttributeValue::AsPath { is_as4: true, .. } => AttrType::AS4_PATH,
758 AttributeValue::NextHop(_) => AttrType::NEXT_HOP,
759 AttributeValue::MultiExitDiscriminator(_) => AttrType::MULTI_EXIT_DISCRIMINATOR,
760 AttributeValue::LocalPreference(_) => AttrType::LOCAL_PREFERENCE,
761 AttributeValue::OnlyToCustomer(_) => AttrType::ONLY_TO_CUSTOMER,
762 AttributeValue::AtomicAggregate => AttrType::ATOMIC_AGGREGATE,
763 AttributeValue::Aggregator { is_as4: false, .. } => AttrType::AGGREGATOR,
764 AttributeValue::Aggregator { is_as4: true, .. } => AttrType::AS4_AGGREGATOR,
765 AttributeValue::Communities(_) => AttrType::COMMUNITIES,
766 AttributeValue::ExtendedCommunities(_) => AttrType::EXTENDED_COMMUNITIES,
767 AttributeValue::Ipv6AddressSpecificExtendedCommunities(_) => {
768 AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES
769 }
770 AttributeValue::LargeCommunities(_) => AttrType::LARGE_COMMUNITIES,
771 AttributeValue::OriginatorId(_) => AttrType::ORIGINATOR_ID,
772 AttributeValue::Clusters(_) => AttrType::CLUSTER_LIST,
773 AttributeValue::MpReachNlri(_) => AttrType::MP_REACHABLE_NLRI,
774 AttributeValue::MpUnreachNlri(_) => AttrType::MP_UNREACHABLE_NLRI,
775 AttributeValue::LinkState(_) => AttrType::BGP_LS_ATTRIBUTE,
776 AttributeValue::TunnelEncapsulation(_) => AttrType::TUNNEL_ENCAPSULATION,
777 AttributeValue::TrafficEngineering(_) => AttrType::TRAFFIC_ENGINEERING,
778 AttributeValue::BfdDiscriminator(_) => AttrType::BFD_DISCRIMINATOR,
779 AttributeValue::BgpPrefixSid(_) => AttrType::BGP_PREFIX_SID,
780 AttributeValue::Bier(_) => AttrType::BIER,
781 AttributeValue::Sfp(_) => AttrType::SFP_ATTRIBUTE,
782 AttributeValue::Development(_) => AttrType::DEVELOPMENT,
783 AttributeValue::Raw(x) | AttributeValue::Deprecated(x) | AttributeValue::Unknown(x) => {
784 x.attr_type()
785 }
786 AttributeValue::Aigp(_) => AttrType::AIGP,
787 AttributeValue::AttrSet(_) => AttrType::ATTR_SET,
788 }
789 }
790
791 pub fn attr_code(&self) -> u8 {
792 match self {
793 AttributeValue::Raw(x) | AttributeValue::Deprecated(x) | AttributeValue::Unknown(x) => {
794 x.code
795 }
796 _ => self.attr_type().into(),
797 }
798 }
799
800 pub fn attr_category(&self) -> Option<AttributeCategory> {
801 use AttributeCategory::*;
802
803 match self {
804 AttributeValue::Origin(_) => Some(WellKnownMandatory),
805 AttributeValue::AsPath { is_as4: false, .. } => Some(WellKnownMandatory),
806 AttributeValue::AsPath { is_as4: true, .. } => Some(OptionalTransitive),
807 AttributeValue::NextHop(_) => Some(WellKnownMandatory),
808 AttributeValue::MultiExitDiscriminator(_) => Some(OptionalNonTransitive),
809 AttributeValue::LocalPreference(_) => Some(WellKnownMandatory),
811 AttributeValue::OnlyToCustomer(_) => Some(OptionalTransitive),
812 AttributeValue::AtomicAggregate => Some(WellKnownDiscretionary),
813 AttributeValue::Aggregator { .. } => Some(OptionalTransitive),
814 AttributeValue::Communities(_) => Some(OptionalTransitive),
815 AttributeValue::ExtendedCommunities(_) => Some(OptionalTransitive),
816 AttributeValue::LargeCommunities(_) => Some(OptionalTransitive),
817 AttributeValue::OriginatorId(_) => Some(OptionalNonTransitive),
818 AttributeValue::Clusters(_) => Some(OptionalNonTransitive),
819 AttributeValue::MpReachNlri(_) => Some(OptionalNonTransitive),
820 AttributeValue::MpUnreachNlri(_) => Some(OptionalNonTransitive),
821 AttributeValue::LinkState(_) => Some(OptionalNonTransitive),
822 AttributeValue::TrafficEngineering(_) => Some(OptionalNonTransitive),
823 AttributeValue::Aigp(_) => Some(OptionalNonTransitive),
824 AttributeValue::BfdDiscriminator(_) => Some(OptionalTransitive),
825 AttributeValue::BgpPrefixSid(_) => Some(OptionalTransitive),
826 AttributeValue::Bier(_) => Some(OptionalTransitive),
827 AttributeValue::Sfp(_) => Some(OptionalTransitive),
828 AttributeValue::AttrSet(_) => Some(OptionalTransitive),
829 _ => None,
830 }
831 }
832
833 pub fn default_flags(&self) -> AttrFlags {
836 match self.attr_category() {
837 None => AttrFlags::OPTIONAL | AttrFlags::PARTIAL | AttrFlags::TRANSITIVE,
838 Some(AttributeCategory::WellKnownMandatory) => AttrFlags::TRANSITIVE,
839 Some(AttributeCategory::WellKnownDiscretionary) => AttrFlags::TRANSITIVE,
840 Some(AttributeCategory::OptionalTransitive) => {
841 AttrFlags::OPTIONAL | AttrFlags::TRANSITIVE
842 }
843 Some(AttributeCategory::OptionalNonTransitive) => AttrFlags::OPTIONAL,
844 }
845 }
846}
847
848#[derive(Debug, PartialEq, Clone, Eq)]
849#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
850pub struct AttrRaw {
851 pub code: u8,
852 pub bytes: Bytes,
853}
854
855impl AttrRaw {
856 pub fn attr_type(&self) -> AttrType {
862 AttrType::from(self.code)
863 }
864}
865
866#[cfg(test)]
867mod tests {
868 use super::*;
869 use std::net::Ipv4Addr;
870 use std::str::FromStr;
871
872 #[test]
873 fn test_attr_type() {
874 let attr_value = AttributeValue::Origin(Origin::IGP);
875 assert_eq!(attr_value.attr_type(), AttrType::ORIGIN);
876 }
877
878 #[test]
879 fn test_attr_category() {
880 let attr_value = AttributeValue::Origin(Origin::IGP);
881 let category = attr_value.attr_category().unwrap();
882 assert_eq!(category, AttributeCategory::WellKnownMandatory);
883 }
884
885 #[test]
886 fn test_default_flags() {
887 let attr_value = AttributeValue::Origin(Origin::IGP);
888 let flags = attr_value.default_flags();
889 assert_eq!(flags, AttrFlags::TRANSITIVE);
890 }
891
892 #[test]
893 fn test_from_iter_attribute_value_uses_default_flags() {
894 let attributes = Attributes::from_iter(vec![
895 AttributeValue::Origin(Origin::IGP),
896 AttributeValue::AsPath {
897 path: AsPath::new(),
898 is_as4: false,
899 },
900 ]);
901
902 assert_eq!(
903 attributes.get_attr(AttrType::ORIGIN).unwrap().flag,
904 AttrFlags::TRANSITIVE
905 );
906 assert_eq!(
907 attributes.get_attr(AttrType::AS_PATH).unwrap().flag,
908 AttrFlags::TRANSITIVE
909 );
910 }
911
912 #[test]
913 fn test_get_attr() {
914 let attribute = Attribute {
915 value: AttributeValue::Origin(Origin::IGP),
916 flag: AttrFlags::TRANSITIVE,
917 };
918
919 let mut attributes = Attributes::default();
920 attributes.add_attr(attribute.clone());
921
922 assert_eq!(attributes.get_attr(AttrType::ORIGIN), Some(attribute));
923 }
924
925 #[test]
926 fn test_has_attr() {
927 let attribute = Attribute {
928 value: AttributeValue::Origin(Origin::IGP),
929 flag: AttrFlags::TRANSITIVE,
930 };
931
932 let mut attributes = Attributes::default();
933 attributes.add_attr(attribute);
934
935 assert!(attributes.has_attr(AttrType::ORIGIN));
936 }
937
938 #[test]
939 fn test_getting_all_attributes() {
940 let mut attributes = Attributes::default();
941 attributes.add_attr(Attribute {
942 value: AttributeValue::Origin(Origin::IGP),
943 flag: AttrFlags::TRANSITIVE,
944 });
945 attributes.add_attr(Attribute {
946 value: AttributeValue::AsPath {
947 path: AsPath::new(),
948 is_as4: false,
949 },
950 flag: AttrFlags::TRANSITIVE,
951 });
952 attributes.add_attr(Attribute {
953 value: AttributeValue::NextHop(IpAddr::from_str("10.0.0.0").unwrap()),
954 flag: AttrFlags::TRANSITIVE,
955 });
956 attributes.add_attr(Attribute {
957 value: AttributeValue::MultiExitDiscriminator(1),
958 flag: AttrFlags::TRANSITIVE,
959 });
960
961 attributes.add_attr(Attribute {
962 value: AttributeValue::LocalPreference(1),
963 flag: AttrFlags::TRANSITIVE,
964 });
965 attributes.add_attr(Attribute {
966 value: AttributeValue::OnlyToCustomer(Asn::new_32bit(1)),
967 flag: AttrFlags::TRANSITIVE,
968 });
969 attributes.add_attr(Attribute {
970 value: AttributeValue::AtomicAggregate,
971 flag: AttrFlags::TRANSITIVE,
972 });
973 attributes.add_attr(Attribute {
974 value: AttributeValue::Clusters(vec![1, 2, 3]),
975 flag: AttrFlags::TRANSITIVE,
976 });
977 attributes.add_attr(Attribute {
978 value: AttributeValue::Aggregator {
979 asn: Asn::new_32bit(1),
980 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
981 is_as4: false,
982 },
983 flag: AttrFlags::TRANSITIVE,
984 });
985 attributes.add_attr(Attribute {
986 value: AttributeValue::OriginatorId(Ipv4Addr::from_str("0.0.0.0").unwrap()),
987 flag: AttrFlags::TRANSITIVE,
988 });
989
990 assert_eq!(attributes.origin(), Origin::IGP);
991 assert_eq!(attributes.as_path(), Some(&AsPath::new()));
992 assert_eq!(
993 attributes.next_hop(),
994 Some(IpAddr::from_str("10.0.0.0").unwrap())
995 );
996 assert_eq!(attributes.multi_exit_discriminator(), Some(1));
997 assert_eq!(attributes.local_preference(), Some(1));
998 assert_eq!(attributes.only_to_customer(), Some(Asn::new_32bit(1)));
999 assert!(attributes.atomic_aggregate());
1000 assert_eq!(attributes.clusters(), Some(vec![1_u32, 2, 3].as_slice()));
1001 assert_eq!(
1002 attributes.aggregator(),
1003 Some((Asn::new_32bit(1), Ipv4Addr::from_str("0.0.0.0").unwrap()))
1004 );
1005 assert_eq!(
1006 attributes.origin_id(),
1007 Some(Ipv4Addr::from_str("0.0.0.0").unwrap())
1008 );
1009
1010 let aspath_attr = attributes.get_attr(AttrType::AS_PATH).unwrap();
1011 assert!(aspath_attr.is_transitive());
1012 assert!(!aspath_attr.is_extended());
1013 assert!(!aspath_attr.is_partial());
1014 assert!(!aspath_attr.is_optional());
1015
1016 for attr in attributes.iter() {
1017 println!("{attr:?}");
1018 }
1019 }
1020
1021 #[test]
1022 fn test_from() {
1023 let origin = Origin::IGP;
1024 let attr_value = AttributeValue::from(origin);
1025 assert_eq!(attr_value, AttributeValue::Origin(Origin::IGP));
1026
1027 let aspath = AsPath::new();
1028 let attr_value = AttributeValue::from(aspath);
1029 assert_eq!(
1030 attr_value,
1031 AttributeValue::AsPath {
1032 path: AsPath::new(),
1033 is_as4: false
1034 }
1035 );
1036 }
1037
1038 #[test]
1039 fn test_well_known_mandatory_attrs() {
1040 let origin_attr = AttributeValue::Origin(Origin::IGP);
1041 assert_eq!(
1042 origin_attr.attr_category(),
1043 Some(AttributeCategory::WellKnownMandatory)
1044 );
1045 let as_path_attr = AttributeValue::AsPath {
1046 path: AsPath::new(),
1047 is_as4: false,
1048 };
1049 assert_eq!(
1050 as_path_attr.attr_category(),
1051 Some(AttributeCategory::WellKnownMandatory)
1052 );
1053 let next_hop_attr = AttributeValue::NextHop(IpAddr::from_str("10.0.0.0").unwrap());
1054 assert_eq!(
1055 next_hop_attr.attr_category(),
1056 Some(AttributeCategory::WellKnownMandatory)
1057 );
1058 let local_preference_attr = AttributeValue::LocalPreference(1);
1059 assert_eq!(
1060 local_preference_attr.attr_category(),
1061 Some(AttributeCategory::WellKnownMandatory)
1062 );
1063 }
1064
1065 #[test]
1066 fn test_well_known_discretionary_attrs() {
1067 let atomic_aggregate_attr = AttributeValue::AtomicAggregate;
1068 assert_eq!(
1069 atomic_aggregate_attr.attr_category(),
1070 Some(AttributeCategory::WellKnownDiscretionary)
1071 );
1072 }
1073
1074 #[test]
1075 fn test_optional_transitive_attrs() {
1076 let as_path_attr = AttributeValue::AsPath {
1077 path: AsPath::new(),
1078 is_as4: true,
1079 };
1080 assert_eq!(
1081 as_path_attr.attr_category(),
1082 Some(AttributeCategory::OptionalTransitive)
1083 );
1084 let aggregator_attr = AttributeValue::Aggregator {
1085 asn: Asn::new_32bit(1),
1086 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1087 is_as4: false,
1088 };
1089 assert_eq!(
1090 aggregator_attr.attr_category(),
1091 Some(AttributeCategory::OptionalTransitive)
1092 );
1093 let only_to_customer_attr = AttributeValue::OnlyToCustomer(Asn::new_32bit(1));
1094 assert_eq!(
1095 only_to_customer_attr.attr_category(),
1096 Some(AttributeCategory::OptionalTransitive)
1097 );
1098 let communities_attr =
1099 AttributeValue::Communities(vec![Community::Custom(Asn::new_32bit(1), 1)]);
1100 assert_eq!(
1101 communities_attr.attr_category(),
1102 Some(AttributeCategory::OptionalTransitive)
1103 );
1104 let extended_communities_attr =
1105 AttributeValue::ExtendedCommunities(vec![ExtendedCommunity::Raw([0; 8])]);
1106 assert_eq!(
1107 extended_communities_attr.attr_category(),
1108 Some(AttributeCategory::OptionalTransitive)
1109 );
1110 let large_communities_attr =
1111 AttributeValue::LargeCommunities(vec![LargeCommunity::new(1, [1, 1])]);
1112 assert_eq!(
1113 large_communities_attr.attr_category(),
1114 Some(AttributeCategory::OptionalTransitive)
1115 );
1116 let aggregator_attr = AttributeValue::Aggregator {
1117 asn: Asn::new_32bit(1),
1118 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1119 is_as4: true,
1120 };
1121 assert_eq!(
1122 aggregator_attr.attr_category(),
1123 Some(AttributeCategory::OptionalTransitive)
1124 );
1125 }
1126
1127 #[test]
1128 fn test_new_attribute_attr_categories() {
1129 assert_eq!(
1131 AttributeValue::BfdDiscriminator(BfdDiscriminatorAttribute {
1132 mode: 1,
1133 discriminator: 0,
1134 tlvs: vec![],
1135 })
1136 .attr_category(),
1137 Some(AttributeCategory::OptionalTransitive)
1138 );
1139 assert_eq!(
1141 AttributeValue::BgpPrefixSid(BgpPrefixSidAttribute { tlvs: vec![] }).attr_category(),
1142 Some(AttributeCategory::OptionalTransitive)
1143 );
1144 assert_eq!(
1146 AttributeValue::Bier(BierAttribute { tlvs: vec![] }).attr_category(),
1147 Some(AttributeCategory::OptionalTransitive)
1148 );
1149 assert_eq!(
1151 AttributeValue::Sfp(SfpAttribute { tlvs: vec![] }).attr_category(),
1152 Some(AttributeCategory::OptionalTransitive)
1153 );
1154 }
1155
1156 #[test]
1157 fn test_optional_non_transitive_attrs() {
1158 let multi_exit_discriminator_attr = AttributeValue::MultiExitDiscriminator(1);
1159 assert_eq!(
1160 multi_exit_discriminator_attr.attr_category(),
1161 Some(AttributeCategory::OptionalNonTransitive)
1162 );
1163 let originator_id_attr =
1164 AttributeValue::OriginatorId(Ipv4Addr::from_str("0.0.0.0").unwrap());
1165 assert_eq!(
1166 originator_id_attr.attr_category(),
1167 Some(AttributeCategory::OptionalNonTransitive)
1168 );
1169 let clusters_attr = AttributeValue::Clusters(vec![1, 2, 3]);
1170 assert_eq!(
1171 clusters_attr.attr_category(),
1172 Some(AttributeCategory::OptionalNonTransitive)
1173 );
1174 let mp_unreach_nlri_attr = AttributeValue::MpReachNlri(Nlri::new_unreachable(
1175 NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
1176 ));
1177 assert_eq!(
1178 mp_unreach_nlri_attr.attr_category(),
1179 Some(AttributeCategory::OptionalNonTransitive)
1180 );
1181
1182 let mp_reach_nlri_attr = AttributeValue::MpUnreachNlri(Nlri::new_unreachable(
1183 NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
1184 ));
1185 assert_eq!(
1186 mp_reach_nlri_attr.attr_category(),
1187 Some(AttributeCategory::OptionalNonTransitive)
1188 );
1189
1190 let traffic_engineering_attr = AttributeValue::TrafficEngineering(TrafficEngineering {
1191 switching_capability: 1,
1192 encoding: 1,
1193 reserved: 0,
1194 max_lsp_bandwidth: [0.0; 8],
1195 switching_capability_specific: Bytes::new(),
1196 });
1197
1198 assert_eq!(
1199 traffic_engineering_attr.attr_category(),
1200 Some(AttributeCategory::OptionalNonTransitive)
1201 );
1202
1203 assert_eq!(
1204 traffic_engineering_attr.default_flags(),
1205 AttrFlags::OPTIONAL
1206 );
1207 }
1208
1209 #[test]
1210 #[cfg(feature = "serde")]
1211 fn test_serde() {
1212 let attributes = Attributes::from_iter(vec![
1213 Attribute {
1214 value: AttributeValue::Origin(Origin::IGP),
1215 flag: AttrFlags::TRANSITIVE,
1216 },
1217 Attribute {
1218 value: AttributeValue::AsPath {
1219 path: AsPath::new(),
1220 is_as4: false,
1221 },
1222 flag: AttrFlags::TRANSITIVE,
1223 },
1224 ]);
1225
1226 let serialized = serde_json::to_string(&attributes).unwrap();
1227 let deserialized: Attributes = serde_json::from_str(&serialized).unwrap();
1228
1229 assert_eq!(attributes, deserialized);
1230 }
1231}