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#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
65#[repr(u8)]
66pub enum AttrType {
67 RESERVED = 0,
68 ORIGIN = 1,
69 AS_PATH = 2,
70 NEXT_HOP = 3,
71 MULTI_EXIT_DISCRIMINATOR = 4,
72 LOCAL_PREFERENCE = 5,
73 ATOMIC_AGGREGATE = 6,
74 AGGREGATOR = 7,
75 COMMUNITIES = 8,
76 ORIGINATOR_ID = 9,
78 CLUSTER_LIST = 10,
79 MP_REACHABLE_NLRI = 14,
81 MP_UNREACHABLE_NLRI = 15,
82 EXTENDED_COMMUNITIES = 16,
84 AS4_PATH = 17,
85 AS4_AGGREGATOR = 18,
86 PMSI_TUNNEL = 22,
87 TUNNEL_ENCAPSULATION = 23,
88 TRAFFIC_ENGINEERING = 24,
89 IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES = 25,
90 AIGP = 26,
91 PE_DISTINGUISHER_LABELS = 27,
92 BGP_LS_ATTRIBUTE = 29,
93 LARGE_COMMUNITIES = 32,
94 BGPSEC_PATH = 33,
95 ONLY_TO_CUSTOMER = 35,
96 SFP_ATTRIBUTE = 37,
97 BFD_DISCRIMINATOR = 38,
98 BGP_PREFIX_SID = 40,
99 BIER = 41,
100 ATTR_SET = 128,
101 DEVELOPMENT = 255,
103
104 #[num_enum(catch_all)]
106 Unknown(u8) = 254,
109}
110
111impl PartialOrd for AttrType {
112 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
113 Some(self.cmp(other))
114 }
115}
116
117impl Ord for AttrType {
118 fn cmp(&self, other: &Self) -> Ordering {
119 u8::from(*self).cmp(&u8::from(*other))
120 }
121}
122
123pub fn get_deprecated_attr_type(attr_type: u8) -> Option<&'static str> {
124 match attr_type {
125 11 => Some("DPA"),
126 12 => Some("ADVERTISER"),
127 13 => Some("RCID_PATH"),
128 19 => Some("SAFI Specific Attribute"),
129 20 => Some("Connector Attribute"),
130 21 => Some("AS_PATHLIMIT"),
131 28 => Some("BGP Entropy Label Capability"),
132 30 | 31 | 129 | 241 | 242 | 243 => Some("RFC8093"),
133
134 _ => None,
135 }
136}
137
138#[derive(PartialEq, Clone, Default, Eq)]
140pub struct Attributes {
141 pub(crate) inner: Vec<Attribute>,
144 pub(crate) validation_warnings: Vec<BgpValidationWarning>,
146 pub(crate) attr_mask: [u64; 4],
148}
149
150impl std::fmt::Debug for Attributes {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 f.debug_struct("Attributes")
153 .field("inner", &self.inner)
154 .field("validation_warnings", &self.validation_warnings)
155 .finish()
156 }
157}
158
159impl Attributes {
160 pub fn has_attr(&self, ty: AttrType) -> bool {
161 let attr = u8::from(ty);
162 (self.attr_mask[(attr / 64) as usize] & (1u64 << (attr % 64))) != 0
163 }
164
165 pub fn get_attr(&self, ty: AttrType) -> Option<Attribute> {
166 self.inner
167 .iter()
168 .find(|x| x.value.attr_type() == ty)
169 .cloned()
170 }
171
172 pub fn add_attr(&mut self, attr: Attribute) {
173 let ty = attr.value.attr_code();
174 self.attr_mask[(ty / 64) as usize] |= 1u64 << (ty % 64);
175 self.inner.push(attr);
176 }
177
178 pub fn check_mandatory_attributes(&mut self, is_announcement: bool, has_standard_nlri: bool) {
185 if !is_announcement {
186 return;
187 }
188
189 if !self.has_attr(AttrType::ORIGIN) {
191 self.validation_warnings
192 .push(BgpValidationWarning::MissingWellKnownAttribute {
193 attr_type: AttrType::ORIGIN,
194 });
195 }
196 if !self.has_attr(AttrType::AS_PATH) {
197 self.validation_warnings
198 .push(BgpValidationWarning::MissingWellKnownAttribute {
199 attr_type: AttrType::AS_PATH,
200 });
201 }
202
203 let has_mp_reach = self.has_attr(AttrType::MP_REACHABLE_NLRI);
207 if (has_standard_nlri || !has_mp_reach) && !self.has_attr(AttrType::NEXT_HOP) {
208 self.validation_warnings
209 .push(BgpValidationWarning::MissingWellKnownAttribute {
210 attr_type: AttrType::NEXT_HOP,
211 });
212 }
213 }
214
215 pub fn add_validation_warning(&mut self, warning: BgpValidationWarning) {
217 self.validation_warnings.push(warning);
218 }
219
220 pub fn validation_warnings(&self) -> &[BgpValidationWarning] {
222 &self.validation_warnings
223 }
224
225 pub fn has_validation_warnings(&self) -> bool {
227 !self.validation_warnings.is_empty()
228 }
229
230 pub fn origin(&self) -> Origin {
233 self.inner
234 .iter()
235 .find_map(|x| match &x.value {
236 AttributeValue::Origin(x) => Some(*x),
237 _ => None,
238 })
239 .unwrap_or(Origin::INCOMPLETE)
240 }
241
242 pub fn origin_id(&self) -> Option<BgpIdentifier> {
244 self.inner.iter().find_map(|x| match &x.value {
245 AttributeValue::OriginatorId(x) => Some(*x),
246 _ => None,
247 })
248 }
249
250 pub fn next_hop(&self) -> Option<IpAddr> {
255 self.inner.iter().find_map(|x| match &x.value {
256 AttributeValue::NextHop(x) => Some(*x),
257 _ => None,
258 })
259 }
260
261 pub fn multi_exit_discriminator(&self) -> Option<u32> {
262 self.inner.iter().find_map(|x| match &x.value {
263 AttributeValue::MultiExitDiscriminator(x) => Some(*x),
264 _ => None,
265 })
266 }
267
268 pub fn local_preference(&self) -> Option<u32> {
269 self.inner.iter().find_map(|x| match &x.value {
270 AttributeValue::LocalPreference(x) => Some(*x),
271 _ => None,
272 })
273 }
274
275 pub fn only_to_customer(&self) -> Option<Asn> {
276 self.inner.iter().find_map(|x| match &x.value {
277 AttributeValue::OnlyToCustomer(x) => Some(*x),
278 _ => None,
279 })
280 }
281
282 pub fn atomic_aggregate(&self) -> bool {
283 self.inner
284 .iter()
285 .any(|x| matches!(&x.value, AttributeValue::AtomicAggregate))
286 }
287
288 pub fn aggregator(&self) -> Option<(Asn, BgpIdentifier)> {
289 self.inner.iter().rev().find_map(|x| match &x.value {
292 AttributeValue::Aggregator { asn, id } | AttributeValue::As4Aggregator { asn, id } => {
293 Some((*asn, *id))
294 }
295 _ => None,
296 })
297 }
298
299 pub fn clusters(&self) -> Option<&[u32]> {
300 self.inner.iter().find_map(|x| match &x.value {
301 AttributeValue::Clusters(x) => Some(x.as_ref()),
302 _ => None,
303 })
304 }
305
306 pub fn as_path(&self) -> Option<&AsPath> {
312 self.inner.iter().find_map(|x| match &x.value {
313 AttributeValue::AsPath(path) => Some(path),
314 _ => None,
315 })
316 }
317
318 pub fn as4_path(&self) -> Option<&AsPath> {
320 self.inner.iter().find_map(|x| match &x.value {
321 AttributeValue::As4Path(path) => Some(path),
322 _ => None,
323 })
324 }
325
326 pub fn effective_as_path(&self) -> Option<AsPath> {
333 match (self.as_path(), self.as4_path()) {
334 (None, None) => None,
335 (Some(path), None) | (None, Some(path)) => Some(path.clone()),
336 (Some(path), Some(as4_path)) => Some(AsPath::merge_aspath_as4path(path, as4_path)),
337 }
338 }
339
340 pub fn get_reachable_nlri(&self) -> Option<&Nlri> {
341 self.inner.iter().find_map(|x| match &x.value {
342 AttributeValue::MpReachNlri(x) => Some(x),
343 _ => None,
344 })
345 }
346
347 pub fn get_unreachable_nlri(&self) -> Option<&Nlri> {
348 self.inner.iter().find_map(|x| match &x.value {
349 AttributeValue::MpUnreachNlri(x) => Some(x),
350 _ => None,
351 })
352 }
353
354 pub fn iter_communities(&self) -> MetaCommunitiesIter<'_> {
355 MetaCommunitiesIter {
356 attributes: &self.inner,
357 index: 0,
358 }
359 }
360
361 pub fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
364 self.into_iter()
365 }
366
367 pub fn into_attributes_iter(self) -> impl Iterator<Item = Attribute> {
370 self.inner.into_iter()
371 }
372}
373
374pub struct MetaCommunitiesIter<'a> {
375 attributes: &'a [Attribute],
376 index: usize,
377}
378
379impl Iterator for MetaCommunitiesIter<'_> {
380 type Item = MetaCommunity;
381
382 fn next(&mut self) -> Option<Self::Item> {
383 loop {
384 match &self.attributes.first()?.value {
385 AttributeValue::Communities(x) if self.index < x.len() => {
386 self.index += 1;
387 return Some(MetaCommunity::Plain(x[self.index - 1]));
388 }
389 AttributeValue::ExtendedCommunities(x) if self.index < x.len() => {
390 self.index += 1;
391 return Some(MetaCommunity::Extended(x[self.index - 1]));
392 }
393 AttributeValue::LargeCommunities(x) if self.index < x.len() => {
394 self.index += 1;
395 return Some(MetaCommunity::Large(x[self.index - 1]));
396 }
397 _ => {
398 self.attributes = &self.attributes[1..];
399 self.index = 0;
400 }
401 }
402 }
403 }
404}
405
406fn compute_mask(inner: &[Attribute]) -> [u64; 4] {
407 let mut attr_mask = [0; 4];
408 for attr in inner {
409 let ty = attr.value.attr_code();
410 attr_mask[(ty / 64) as usize] |= 1u64 << (ty % 64);
411 }
412 attr_mask
413}
414
415impl FromIterator<Attribute> for Attributes {
416 fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
417 let inner: Vec<Attribute> = iter.into_iter().collect();
418 let attr_mask = compute_mask(&inner);
419 Attributes {
420 inner,
421 validation_warnings: Vec::new(),
422 attr_mask,
423 }
424 }
425}
426
427impl From<Vec<Attribute>> for Attributes {
428 fn from(value: Vec<Attribute>) -> Self {
429 let attr_mask = compute_mask(&value);
430 Attributes {
431 inner: value,
432 validation_warnings: Vec::new(),
433 attr_mask,
434 }
435 }
436}
437
438impl Extend<Attribute> for Attributes {
439 fn extend<T: IntoIterator<Item = Attribute>>(&mut self, iter: T) {
440 for attr in iter {
441 self.add_attr(attr);
442 }
443 }
444}
445
446impl Extend<AttributeValue> for Attributes {
447 fn extend<T: IntoIterator<Item = AttributeValue>>(&mut self, iter: T) {
448 self.extend(iter.into_iter().map(Attribute::from))
449 }
450}
451
452impl FromIterator<AttributeValue> for Attributes {
453 fn from_iter<T: IntoIterator<Item = AttributeValue>>(iter: T) -> Self {
454 let inner: Vec<Attribute> = iter.into_iter().map(Attribute::from).collect();
455 let attr_mask = compute_mask(&inner);
456 Attributes {
457 inner,
458 validation_warnings: Vec::new(),
459 attr_mask,
460 }
461 }
462}
463
464impl IntoIterator for Attributes {
465 type Item = AttributeValue;
466 type IntoIter = Map<IntoIter<Attribute>, fn(Attribute) -> AttributeValue>;
467
468 fn into_iter(self) -> Self::IntoIter {
469 self.inner.into_iter().map(|x| x.value)
470 }
471}
472
473impl<'a> IntoIterator for &'a Attributes {
474 type Item = &'a AttributeValue;
475 type IntoIter = Map<Iter<'a, Attribute>, fn(&Attribute) -> &AttributeValue>;
476
477 fn into_iter(self) -> Self::IntoIter {
478 self.inner.iter().map(|x| &x.value)
479 }
480}
481
482#[cfg(feature = "serde")]
483mod serde_impl {
484 use super::*;
485 use serde::{Deserialize, Deserializer, Serialize, Serializer};
486
487 impl Serialize for Attributes {
488 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
489 where
490 S: Serializer,
491 {
492 self.inner.serialize(serializer)
493 }
494 }
495
496 impl<'de> Deserialize<'de> for Attributes {
497 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
498 where
499 D: Deserializer<'de>,
500 {
501 let inner = <Vec<Attribute>>::deserialize(deserializer)?;
502 let attr_mask = compute_mask(&inner);
503 Ok(Attributes {
504 inner,
505 validation_warnings: Vec::new(),
506 attr_mask,
507 })
508 }
509 }
510}
511
512#[derive(Debug, PartialEq, Clone, Eq)]
514#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
515#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
516pub struct Attribute {
517 pub value: AttributeValue,
518 #[cfg_attr(feature = "ts-rs", ts(type = "string"))]
520 pub flag: AttrFlags,
521}
522
523impl Attribute {
524 pub const fn is_optional(&self) -> bool {
525 self.flag.contains(AttrFlags::OPTIONAL)
526 }
527
528 pub const fn is_transitive(&self) -> bool {
529 self.flag.contains(AttrFlags::TRANSITIVE)
530 }
531
532 pub const fn is_partial(&self) -> bool {
533 self.flag.contains(AttrFlags::PARTIAL)
534 }
535
536 pub const fn is_extended(&self) -> bool {
537 self.flag.contains(AttrFlags::EXTENDED)
538 }
539}
540
541impl From<AttributeValue> for Attribute {
542 fn from(value: AttributeValue) -> Self {
543 Attribute {
544 flag: value.default_flags(),
545 value,
546 }
547 }
548}
549
550#[derive(Debug, PartialEq, Clone, Eq)]
552#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
553pub struct AigpTlv {
554 pub tlv_type: u8,
555 pub length: u16,
556 pub value: Bytes,
557}
558
559#[derive(Debug, PartialEq, Clone, Eq)]
564#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
565pub struct Aigp {
566 pub tlvs: Vec<AigpTlv>,
567}
568
569impl Aigp {
570 pub fn accumulated_metric(&self) -> Option<u64> {
572 self.tlvs
573 .iter()
574 .find(|tlv| tlv.tlv_type == 1)
575 .and_then(|tlv| {
576 if tlv.value.len() >= 8 {
577 Some(u64::from_be_bytes([
578 tlv.value[0],
579 tlv.value[1],
580 tlv.value[2],
581 tlv.value[3],
582 tlv.value[4],
583 tlv.value[5],
584 tlv.value[6],
585 tlv.value[7],
586 ]))
587 } else {
588 None
589 }
590 })
591 }
592}
593
594#[derive(Debug, PartialEq, Clone, Eq)]
596#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597pub struct RawTlv8 {
598 pub tlv_type: u8,
599 pub value: Bytes,
600}
601
602#[derive(Debug, PartialEq, Clone, Eq)]
604#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
605pub struct RawTlv8Ext {
606 pub tlv_type: u8,
607 pub value: Bytes,
608}
609
610#[derive(Debug, PartialEq, Clone, Eq)]
612#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
613pub struct RawTlv16 {
614 pub tlv_type: u16,
615 pub value: Bytes,
616}
617
618#[derive(Debug, PartialEq, Clone, Eq)]
620#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
621pub struct BfdDiscriminatorAttribute {
622 pub mode: u8,
623 pub discriminator: u32,
624 pub tlvs: Vec<RawTlv8>,
625}
626
627#[derive(Debug, PartialEq, Clone, Eq)]
629#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
630pub struct BgpPrefixSidAttribute {
631 pub tlvs: Vec<RawTlv8Ext>,
632}
633
634#[derive(Debug, PartialEq, Clone, Eq)]
636#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
637pub struct BierAttribute {
638 pub tlvs: Vec<RawTlv16>,
639}
640
641#[derive(Debug, PartialEq, Clone, Eq)]
643#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
644pub struct SfpAttribute {
645 pub tlvs: Vec<RawTlv8Ext>,
646}
647
648#[derive(Debug, Clone)]
650#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
651pub struct TrafficEngineering {
652 pub switching_capability: u8,
654
655 pub encoding: u8,
657
658 pub reserved: u16,
663
664 pub max_lsp_bandwidth: [f32; 8],
666
667 pub switching_capability_specific: Bytes,
669}
670
671impl PartialEq for TrafficEngineering {
672 fn eq(&self, other: &Self) -> bool {
673 self.switching_capability == other.switching_capability
674 && self.encoding == other.encoding
675 && self.reserved == other.reserved
676 && self
677 .max_lsp_bandwidth
678 .iter()
679 .zip(other.max_lsp_bandwidth.iter())
680 .all(|(left, right)| left.to_bits() == right.to_bits())
681 && self.switching_capability_specific == other.switching_capability_specific
682 }
683}
684
685impl Eq for TrafficEngineering {}
686
687#[derive(Debug, PartialEq, Clone, Eq)]
695#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
696pub struct AttrSet {
697 pub origin_as: Asn,
699 pub attributes: Attributes,
701}
702
703#[derive(Debug, PartialEq, Clone, Eq)]
715#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
716#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
717pub enum AttributeValue {
718 Origin(Origin),
719 #[cfg_attr(feature = "ts-rs", ts(as = "AsPathWire"))]
725 AsPath(AsPath),
726 #[cfg_attr(feature = "ts-rs", ts(as = "AsPathWire"))]
733 As4Path(AsPath),
734 NextHop(IpAddr),
735 MultiExitDiscriminator(u32),
736 LocalPreference(u32),
737 OnlyToCustomer(Asn),
738 AtomicAggregate,
739 Aggregator {
743 asn: Asn,
744 id: BgpIdentifier,
745 },
746 As4Aggregator {
750 asn: Asn,
751 id: BgpIdentifier,
752 },
753 Communities(Vec<Community>),
754 ExtendedCommunities(Vec<ExtendedCommunity>),
755 Ipv6AddressSpecificExtendedCommunities(Vec<Ipv6AddrExtCommunity>),
756 LargeCommunities(Vec<LargeCommunity>),
757 OriginatorId(BgpIdentifier),
758 Clusters(Vec<u32>),
759 MpReachNlri(Nlri),
760 MpUnreachNlri(Nlri),
761 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
763 LinkState(crate::models::bgp::linkstate::LinkStateAttribute),
764 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
766 TunnelEncapsulation(crate::models::bgp::tunnel_encap::TunnelEncapAttribute),
767 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
769 TrafficEngineering(TrafficEngineering),
770 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
772 BfdDiscriminator(BfdDiscriminatorAttribute),
773 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
775 BgpPrefixSid(BgpPrefixSidAttribute),
776 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
778 Bier(BierAttribute),
779 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
781 Sfp(SfpAttribute),
782 Development(Vec<u8>),
783 Raw(AttrRaw),
784 Deprecated(AttrRaw),
785 Unknown(AttrRaw),
786 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
788 Aigp(Aigp),
789 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>"))]
791 AttrSet(AttrSet),
792}
793
794impl From<Origin> for AttributeValue {
795 fn from(value: Origin) -> Self {
796 AttributeValue::Origin(value)
797 }
798}
799
800impl From<AsPath> for AttributeValue {
803 fn from(path: AsPath) -> Self {
804 AttributeValue::AsPath(path)
805 }
806}
807
808#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
812#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
813pub enum AttributeCategory {
814 WellKnownMandatory,
815 WellKnownDiscretionary,
816 OptionalTransitive,
817 OptionalNonTransitive,
818}
819
820impl AttributeValue {
821 pub fn attr_type(&self) -> AttrType {
822 match self {
823 AttributeValue::Origin(_) => AttrType::ORIGIN,
824 AttributeValue::AsPath(_) => AttrType::AS_PATH,
825 AttributeValue::As4Path(_) => AttrType::AS4_PATH,
826 AttributeValue::NextHop(_) => AttrType::NEXT_HOP,
827 AttributeValue::MultiExitDiscriminator(_) => AttrType::MULTI_EXIT_DISCRIMINATOR,
828 AttributeValue::LocalPreference(_) => AttrType::LOCAL_PREFERENCE,
829 AttributeValue::OnlyToCustomer(_) => AttrType::ONLY_TO_CUSTOMER,
830 AttributeValue::AtomicAggregate => AttrType::ATOMIC_AGGREGATE,
831 AttributeValue::Aggregator { .. } => AttrType::AGGREGATOR,
832 AttributeValue::As4Aggregator { .. } => AttrType::AS4_AGGREGATOR,
833 AttributeValue::Communities(_) => AttrType::COMMUNITIES,
834 AttributeValue::ExtendedCommunities(_) => AttrType::EXTENDED_COMMUNITIES,
835 AttributeValue::Ipv6AddressSpecificExtendedCommunities(_) => {
836 AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES
837 }
838 AttributeValue::LargeCommunities(_) => AttrType::LARGE_COMMUNITIES,
839 AttributeValue::OriginatorId(_) => AttrType::ORIGINATOR_ID,
840 AttributeValue::Clusters(_) => AttrType::CLUSTER_LIST,
841 AttributeValue::MpReachNlri(_) => AttrType::MP_REACHABLE_NLRI,
842 AttributeValue::MpUnreachNlri(_) => AttrType::MP_UNREACHABLE_NLRI,
843 AttributeValue::LinkState(_) => AttrType::BGP_LS_ATTRIBUTE,
844 AttributeValue::TunnelEncapsulation(_) => AttrType::TUNNEL_ENCAPSULATION,
845 AttributeValue::TrafficEngineering(_) => AttrType::TRAFFIC_ENGINEERING,
846 AttributeValue::BfdDiscriminator(_) => AttrType::BFD_DISCRIMINATOR,
847 AttributeValue::BgpPrefixSid(_) => AttrType::BGP_PREFIX_SID,
848 AttributeValue::Bier(_) => AttrType::BIER,
849 AttributeValue::Sfp(_) => AttrType::SFP_ATTRIBUTE,
850 AttributeValue::Development(_) => AttrType::DEVELOPMENT,
851 AttributeValue::Raw(x) | AttributeValue::Deprecated(x) | AttributeValue::Unknown(x) => {
852 x.attr_type()
853 }
854 AttributeValue::Aigp(_) => AttrType::AIGP,
855 AttributeValue::AttrSet(_) => AttrType::ATTR_SET,
856 }
857 }
858
859 pub fn attr_code(&self) -> u8 {
860 match self {
861 AttributeValue::Raw(x) | AttributeValue::Deprecated(x) | AttributeValue::Unknown(x) => {
862 x.code
863 }
864 _ => self.attr_type().into(),
865 }
866 }
867
868 pub fn attr_category(&self) -> Option<AttributeCategory> {
869 use AttributeCategory::*;
870
871 match self {
872 AttributeValue::Origin(_) => Some(WellKnownMandatory),
873 AttributeValue::AsPath(_) => Some(WellKnownMandatory),
874 AttributeValue::As4Path(_) => Some(OptionalTransitive),
875 AttributeValue::NextHop(_) => Some(WellKnownMandatory),
876 AttributeValue::MultiExitDiscriminator(_) => Some(OptionalNonTransitive),
877 AttributeValue::LocalPreference(_) => Some(WellKnownMandatory),
879 AttributeValue::OnlyToCustomer(_) => Some(OptionalTransitive),
880 AttributeValue::AtomicAggregate => Some(WellKnownDiscretionary),
881 AttributeValue::Aggregator { .. } => Some(OptionalTransitive),
882 AttributeValue::As4Aggregator { .. } => Some(OptionalTransitive),
883 AttributeValue::Communities(_) => Some(OptionalTransitive),
884 AttributeValue::ExtendedCommunities(_) => Some(OptionalTransitive),
885 AttributeValue::LargeCommunities(_) => Some(OptionalTransitive),
886 AttributeValue::OriginatorId(_) => Some(OptionalNonTransitive),
887 AttributeValue::Clusters(_) => Some(OptionalNonTransitive),
888 AttributeValue::MpReachNlri(_) => Some(OptionalNonTransitive),
889 AttributeValue::MpUnreachNlri(_) => Some(OptionalNonTransitive),
890 AttributeValue::LinkState(_) => Some(OptionalNonTransitive),
891 AttributeValue::TrafficEngineering(_) => Some(OptionalNonTransitive),
892 AttributeValue::Aigp(_) => Some(OptionalNonTransitive),
893 AttributeValue::BfdDiscriminator(_) => Some(OptionalTransitive),
894 AttributeValue::BgpPrefixSid(_) => Some(OptionalTransitive),
895 AttributeValue::Bier(_) => Some(OptionalTransitive),
896 AttributeValue::Sfp(_) => Some(OptionalTransitive),
897 AttributeValue::AttrSet(_) => Some(OptionalTransitive),
898 _ => None,
899 }
900 }
901
902 pub fn default_flags(&self) -> AttrFlags {
905 match self.attr_category() {
906 None => AttrFlags::OPTIONAL | AttrFlags::PARTIAL | AttrFlags::TRANSITIVE,
907 Some(AttributeCategory::WellKnownMandatory) => AttrFlags::TRANSITIVE,
908 Some(AttributeCategory::WellKnownDiscretionary) => AttrFlags::TRANSITIVE,
909 Some(AttributeCategory::OptionalTransitive) => {
910 AttrFlags::OPTIONAL | AttrFlags::TRANSITIVE
911 }
912 Some(AttributeCategory::OptionalNonTransitive) => AttrFlags::OPTIONAL,
913 }
914 }
915}
916
917#[derive(Debug, PartialEq, Clone, Eq)]
918#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
919#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
920pub struct AttrRaw {
921 pub code: u8,
922 #[cfg_attr(feature = "ts-rs", ts(type = "number[]"))]
924 pub bytes: Bytes,
925}
926
927impl AttrRaw {
928 pub fn attr_type(&self) -> AttrType {
934 AttrType::from(self.code)
935 }
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941 use std::net::Ipv4Addr;
942 use std::str::FromStr;
943
944 #[test]
945 fn test_attr_type() {
946 let attr_value = AttributeValue::Origin(Origin::IGP);
947 assert_eq!(attr_value.attr_type(), AttrType::ORIGIN);
948 }
949
950 #[test]
951 fn test_attr_category() {
952 let attr_value = AttributeValue::Origin(Origin::IGP);
953 let category = attr_value.attr_category().unwrap();
954 assert_eq!(category, AttributeCategory::WellKnownMandatory);
955 }
956
957 #[test]
958 fn test_default_flags() {
959 let attr_value = AttributeValue::Origin(Origin::IGP);
960 let flags = attr_value.default_flags();
961 assert_eq!(flags, AttrFlags::TRANSITIVE);
962 }
963
964 #[test]
965 fn test_from_iter_attribute_value_uses_default_flags() {
966 let attributes = Attributes::from_iter(vec![
967 AttributeValue::Origin(Origin::IGP),
968 AttributeValue::AsPath(AsPath::new()),
969 ]);
970
971 assert_eq!(
972 attributes.get_attr(AttrType::ORIGIN).unwrap().flag,
973 AttrFlags::TRANSITIVE
974 );
975 assert_eq!(
976 attributes.get_attr(AttrType::AS_PATH).unwrap().flag,
977 AttrFlags::TRANSITIVE
978 );
979 }
980
981 #[test]
982 fn test_get_attr() {
983 let attribute = Attribute {
984 value: AttributeValue::Origin(Origin::IGP),
985 flag: AttrFlags::TRANSITIVE,
986 };
987
988 let mut attributes = Attributes::default();
989 attributes.add_attr(attribute.clone());
990
991 assert_eq!(attributes.get_attr(AttrType::ORIGIN), Some(attribute));
992 }
993
994 #[test]
995 fn test_has_attr() {
996 let attribute = Attribute {
997 value: AttributeValue::Origin(Origin::IGP),
998 flag: AttrFlags::TRANSITIVE,
999 };
1000
1001 let mut attributes = Attributes::default();
1002 attributes.add_attr(attribute);
1003
1004 assert!(attributes.has_attr(AttrType::ORIGIN));
1005 }
1006
1007 #[test]
1008 fn test_getting_all_attributes() {
1009 let mut attributes = Attributes::default();
1010 attributes.add_attr(Attribute {
1011 value: AttributeValue::Origin(Origin::IGP),
1012 flag: AttrFlags::TRANSITIVE,
1013 });
1014 attributes.add_attr(Attribute {
1015 value: AttributeValue::AsPath(AsPath::new()),
1016 flag: AttrFlags::TRANSITIVE,
1017 });
1018 attributes.add_attr(Attribute {
1019 value: AttributeValue::NextHop(IpAddr::from_str("10.0.0.0").unwrap()),
1020 flag: AttrFlags::TRANSITIVE,
1021 });
1022 attributes.add_attr(Attribute {
1023 value: AttributeValue::MultiExitDiscriminator(1),
1024 flag: AttrFlags::TRANSITIVE,
1025 });
1026
1027 attributes.add_attr(Attribute {
1028 value: AttributeValue::LocalPreference(1),
1029 flag: AttrFlags::TRANSITIVE,
1030 });
1031 attributes.add_attr(Attribute {
1032 value: AttributeValue::OnlyToCustomer(Asn::new_32bit(1)),
1033 flag: AttrFlags::TRANSITIVE,
1034 });
1035 attributes.add_attr(Attribute {
1036 value: AttributeValue::AtomicAggregate,
1037 flag: AttrFlags::TRANSITIVE,
1038 });
1039 attributes.add_attr(Attribute {
1040 value: AttributeValue::Clusters(vec![1, 2, 3]),
1041 flag: AttrFlags::TRANSITIVE,
1042 });
1043 attributes.add_attr(Attribute {
1044 value: AttributeValue::Aggregator {
1045 asn: Asn::new_32bit(1),
1046 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1047 },
1048 flag: AttrFlags::TRANSITIVE,
1049 });
1050 attributes.add_attr(Attribute {
1051 value: AttributeValue::OriginatorId(Ipv4Addr::from_str("0.0.0.0").unwrap()),
1052 flag: AttrFlags::TRANSITIVE,
1053 });
1054
1055 assert_eq!(attributes.origin(), Origin::IGP);
1056 assert_eq!(attributes.as_path(), Some(&AsPath::new()));
1057 assert_eq!(
1058 attributes.next_hop(),
1059 Some(IpAddr::from_str("10.0.0.0").unwrap())
1060 );
1061 assert_eq!(attributes.multi_exit_discriminator(), Some(1));
1062 assert_eq!(attributes.local_preference(), Some(1));
1063 assert_eq!(attributes.only_to_customer(), Some(Asn::new_32bit(1)));
1064 assert!(attributes.atomic_aggregate());
1065 assert_eq!(attributes.clusters(), Some(vec![1_u32, 2, 3].as_slice()));
1066 assert_eq!(
1067 attributes.aggregator(),
1068 Some((Asn::new_32bit(1), Ipv4Addr::from_str("0.0.0.0").unwrap()))
1069 );
1070 assert_eq!(
1071 attributes.origin_id(),
1072 Some(Ipv4Addr::from_str("0.0.0.0").unwrap())
1073 );
1074
1075 let aspath_attr = attributes.get_attr(AttrType::AS_PATH).unwrap();
1076 assert!(aspath_attr.is_transitive());
1077 assert!(!aspath_attr.is_extended());
1078 assert!(!aspath_attr.is_partial());
1079 assert!(!aspath_attr.is_optional());
1080
1081 for attr in attributes.iter() {
1082 println!("{attr:?}");
1083 }
1084 }
1085
1086 #[test]
1087 fn test_from() {
1088 let origin = Origin::IGP;
1089 let attr_value = AttributeValue::from(origin);
1090 assert_eq!(attr_value, AttributeValue::Origin(Origin::IGP));
1091
1092 let aspath = AsPath::new();
1093 let attr_value = AttributeValue::from(aspath);
1094 assert_eq!(attr_value, AttributeValue::AsPath(AsPath::new()));
1095 }
1096
1097 #[test]
1098 fn test_well_known_mandatory_attrs() {
1099 let origin_attr = AttributeValue::Origin(Origin::IGP);
1100 assert_eq!(
1101 origin_attr.attr_category(),
1102 Some(AttributeCategory::WellKnownMandatory)
1103 );
1104 let as_path_attr = AttributeValue::AsPath(AsPath::new());
1105 assert_eq!(
1106 as_path_attr.attr_category(),
1107 Some(AttributeCategory::WellKnownMandatory)
1108 );
1109 let next_hop_attr = AttributeValue::NextHop(IpAddr::from_str("10.0.0.0").unwrap());
1110 assert_eq!(
1111 next_hop_attr.attr_category(),
1112 Some(AttributeCategory::WellKnownMandatory)
1113 );
1114 let local_preference_attr = AttributeValue::LocalPreference(1);
1115 assert_eq!(
1116 local_preference_attr.attr_category(),
1117 Some(AttributeCategory::WellKnownMandatory)
1118 );
1119 }
1120
1121 #[test]
1122 fn test_well_known_discretionary_attrs() {
1123 let atomic_aggregate_attr = AttributeValue::AtomicAggregate;
1124 assert_eq!(
1125 atomic_aggregate_attr.attr_category(),
1126 Some(AttributeCategory::WellKnownDiscretionary)
1127 );
1128 }
1129
1130 #[test]
1131 fn test_optional_transitive_attrs() {
1132 let as4_path_attr = AttributeValue::As4Path(AsPath::new());
1133 assert_eq!(
1134 as4_path_attr.attr_type(),
1135 AttrType::AS4_PATH,
1136 "AS4_PATH must map to wire type 17"
1137 );
1138 assert_eq!(
1139 as4_path_attr.attr_category(),
1140 Some(AttributeCategory::OptionalTransitive)
1141 );
1142 let aggregator_attr = AttributeValue::Aggregator {
1143 asn: Asn::new_32bit(1),
1144 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1145 };
1146 assert_eq!(
1147 aggregator_attr.attr_category(),
1148 Some(AttributeCategory::OptionalTransitive)
1149 );
1150 let only_to_customer_attr = AttributeValue::OnlyToCustomer(Asn::new_32bit(1));
1151 assert_eq!(
1152 only_to_customer_attr.attr_category(),
1153 Some(AttributeCategory::OptionalTransitive)
1154 );
1155 let communities_attr =
1156 AttributeValue::Communities(vec![Community::Custom(Asn::new_32bit(1), 1)]);
1157 assert_eq!(
1158 communities_attr.attr_category(),
1159 Some(AttributeCategory::OptionalTransitive)
1160 );
1161 let extended_communities_attr =
1162 AttributeValue::ExtendedCommunities(vec![ExtendedCommunity::Raw([0; 8])]);
1163 assert_eq!(
1164 extended_communities_attr.attr_category(),
1165 Some(AttributeCategory::OptionalTransitive)
1166 );
1167 let large_communities_attr =
1168 AttributeValue::LargeCommunities(vec![LargeCommunity::new(1, [1, 1])]);
1169 assert_eq!(
1170 large_communities_attr.attr_category(),
1171 Some(AttributeCategory::OptionalTransitive)
1172 );
1173 let as4_aggregator_attr = AttributeValue::As4Aggregator {
1174 asn: Asn::new_32bit(1),
1175 id: Ipv4Addr::from_str("0.0.0.0").unwrap(),
1176 };
1177 assert_eq!(
1178 as4_aggregator_attr.attr_type(),
1179 AttrType::AS4_AGGREGATOR,
1180 "AS4_AGGREGATOR must map to wire type 18"
1181 );
1182 assert_eq!(
1183 as4_aggregator_attr.attr_category(),
1184 Some(AttributeCategory::OptionalTransitive)
1185 );
1186 }
1187
1188 #[test]
1189 fn test_new_attribute_attr_categories() {
1190 assert_eq!(
1192 AttributeValue::BfdDiscriminator(BfdDiscriminatorAttribute {
1193 mode: 1,
1194 discriminator: 0,
1195 tlvs: vec![],
1196 })
1197 .attr_category(),
1198 Some(AttributeCategory::OptionalTransitive)
1199 );
1200 assert_eq!(
1202 AttributeValue::BgpPrefixSid(BgpPrefixSidAttribute { tlvs: vec![] }).attr_category(),
1203 Some(AttributeCategory::OptionalTransitive)
1204 );
1205 assert_eq!(
1207 AttributeValue::Bier(BierAttribute { tlvs: vec![] }).attr_category(),
1208 Some(AttributeCategory::OptionalTransitive)
1209 );
1210 assert_eq!(
1212 AttributeValue::Sfp(SfpAttribute { tlvs: vec![] }).attr_category(),
1213 Some(AttributeCategory::OptionalTransitive)
1214 );
1215 }
1216
1217 #[test]
1218 fn test_optional_non_transitive_attrs() {
1219 let multi_exit_discriminator_attr = AttributeValue::MultiExitDiscriminator(1);
1220 assert_eq!(
1221 multi_exit_discriminator_attr.attr_category(),
1222 Some(AttributeCategory::OptionalNonTransitive)
1223 );
1224 let originator_id_attr =
1225 AttributeValue::OriginatorId(Ipv4Addr::from_str("0.0.0.0").unwrap());
1226 assert_eq!(
1227 originator_id_attr.attr_category(),
1228 Some(AttributeCategory::OptionalNonTransitive)
1229 );
1230 let clusters_attr = AttributeValue::Clusters(vec![1, 2, 3]);
1231 assert_eq!(
1232 clusters_attr.attr_category(),
1233 Some(AttributeCategory::OptionalNonTransitive)
1234 );
1235 let mp_unreach_nlri_attr = AttributeValue::MpReachNlri(Nlri::new_unreachable(
1236 NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
1237 ));
1238 assert_eq!(
1239 mp_unreach_nlri_attr.attr_category(),
1240 Some(AttributeCategory::OptionalNonTransitive)
1241 );
1242
1243 let mp_reach_nlri_attr = AttributeValue::MpUnreachNlri(Nlri::new_unreachable(
1244 NetworkPrefix::from_str("10.0.0.0/24").unwrap(),
1245 ));
1246 assert_eq!(
1247 mp_reach_nlri_attr.attr_category(),
1248 Some(AttributeCategory::OptionalNonTransitive)
1249 );
1250
1251 let traffic_engineering_attr = AttributeValue::TrafficEngineering(TrafficEngineering {
1252 switching_capability: 1,
1253 encoding: 1,
1254 reserved: 0,
1255 max_lsp_bandwidth: [0.0; 8],
1256 switching_capability_specific: Bytes::new(),
1257 });
1258
1259 assert_eq!(
1260 traffic_engineering_attr.attr_category(),
1261 Some(AttributeCategory::OptionalNonTransitive)
1262 );
1263
1264 assert_eq!(
1265 traffic_engineering_attr.default_flags(),
1266 AttrFlags::OPTIONAL
1267 );
1268 }
1269
1270 #[test]
1271 #[cfg(feature = "serde")]
1272 fn test_serde() {
1273 let attributes = Attributes::from_iter(vec![
1274 Attribute {
1275 value: AttributeValue::Origin(Origin::IGP),
1276 flag: AttrFlags::TRANSITIVE,
1277 },
1278 Attribute {
1279 value: AttributeValue::AsPath(AsPath::new()),
1280 flag: AttrFlags::TRANSITIVE,
1281 },
1282 ]);
1283
1284 let serialized = serde_json::to_string(&attributes).unwrap();
1285 let deserialized: Attributes = serde_json::from_str(&serialized).unwrap();
1286
1287 assert_eq!(attributes, deserialized);
1288 }
1289
1290 #[test]
1291 fn test_as_path_accessors() {
1292 let mut attributes = Attributes::default();
1293 assert_eq!(attributes.as_path(), None);
1294 assert_eq!(attributes.as4_path(), None);
1295 assert_eq!(attributes.effective_as_path(), None);
1296
1297 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([23456, 64497])).into());
1298 assert_eq!(
1299 attributes
1300 .as_path()
1301 .map(|p| p.to_u32_vec_opt(false).unwrap()),
1302 Some(vec![23456, 64497])
1303 );
1304 assert_eq!(attributes.as4_path(), None);
1305 assert_eq!(
1306 attributes
1307 .effective_as_path()
1308 .unwrap()
1309 .to_u32_vec_opt(false)
1310 .unwrap(),
1311 vec![23456, 64497]
1312 );
1313
1314 attributes.add_attr(AttributeValue::As4Path(AsPath::from_sequence([65536, 64497])).into());
1315 assert_eq!(
1316 attributes
1317 .as4_path()
1318 .map(|p| p.to_u32_vec_opt(false).unwrap()),
1319 Some(vec![65536, 64497])
1320 );
1321 assert_eq!(
1323 attributes
1324 .effective_as_path()
1325 .unwrap()
1326 .to_u32_vec_opt(false)
1327 .unwrap(),
1328 vec![65536, 64497]
1329 );
1330
1331 let mut attributes = Attributes::default();
1333 attributes.add_attr(AttributeValue::As4Path(AsPath::from_sequence([65536, 64497])).into());
1334 assert_eq!(attributes.as_path(), None);
1335 assert_eq!(
1336 attributes
1337 .effective_as_path()
1338 .unwrap()
1339 .to_u32_vec_opt(false)
1340 .unwrap(),
1341 vec![65536, 64497]
1342 );
1343 }
1344}