bgpkit_parser/models/bgp/
capabilities.rs

1use num_enum::{FromPrimitive, IntoPrimitive};
2
3#[allow(non_camel_case_types)]
4#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[repr(u8)]
7pub enum BgpCapabilityType {
8    MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4 = 1,
9    ROUTE_REFRESH_CAPABILITY_FOR_BGP_4 = 2,
10    OUTBOUND_ROUTE_FILTERING_CAPABILITY = 3,
11    EXTENDED_NEXT_HOP_ENCODING = 5,
12    BGP_EXTENDED_MESSAGE = 6,
13    BGPSEC_CAPABILITY = 7,
14    MULTIPLE_LABELS_CAPABILITY = 8,
15    BGP_ROLE = 9,
16    GRACEFUL_RESTART_CAPABILITY = 64,
17    SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY = 65,
18    SUPPORT_FOR_DYNAMIC_CAPABILITY = 67,
19    MULTISESSION_BGP_CAPABILITY = 68,
20    ADD_PATH_CAPABILITY = 69,
21    ENHANCED_ROUTE_REFRESH_CAPABILITY = 70,
22    LONG_LIVED_GRACEFUL_RESTART_CAPABILITY = 71,
23    ROUTING_POLICY_DISTRIBUTION = 72,
24    FQDN_CAPABILITY = 73,
25
26    /// Catch-all type for any deprecated, unassigned, or reserved codes
27    #[num_enum(catch_all)]
28    Unknown(u8),
29}
30
31impl BgpCapabilityType {
32    pub const fn is_deprecated(&self) -> bool {
33        matches!(
34            self,
35            BgpCapabilityType::Unknown(4 | 66 | 128 | 129 | 130 | 131 | 184 | 185)
36        )
37    }
38
39    pub const fn is_reserved(&self) -> bool {
40        matches!(self, BgpCapabilityType::Unknown(0 | 255))
41    }
42
43    pub const fn is_reserved_for_experimental_use(&self) -> bool {
44        matches!(self, BgpCapabilityType::Unknown(239..=254))
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn test_parsing_capability() {
54        // reserved
55        assert!(BgpCapabilityType::from(0).is_reserved());
56        assert!(BgpCapabilityType::from(255).is_reserved());
57
58        // deprecated
59        for code in [4, 66, 128, 129, 130, 131, 184, 185] {
60            assert!(BgpCapabilityType::from(code).is_deprecated());
61        }
62
63        // unassigned
64        let unassigned_ranges = [10..=63, 74..=127, 132..=183, 186..=238];
65        for code in <[_; 4]>::into_iter(unassigned_ranges).flatten() {
66            let ty = BgpCapabilityType::from(code);
67            assert_eq!(ty, BgpCapabilityType::Unknown(code));
68            assert!(!ty.is_deprecated() && !ty.is_reserved());
69        }
70
71        // valid capabilities
72        assert_eq!(
73            BgpCapabilityType::from(1),
74            BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4
75        );
76        assert_eq!(
77            BgpCapabilityType::from(2),
78            BgpCapabilityType::ROUTE_REFRESH_CAPABILITY_FOR_BGP_4
79        );
80        assert_eq!(
81            BgpCapabilityType::from(3),
82            BgpCapabilityType::OUTBOUND_ROUTE_FILTERING_CAPABILITY
83        );
84        assert_eq!(
85            BgpCapabilityType::from(5),
86            BgpCapabilityType::EXTENDED_NEXT_HOP_ENCODING
87        );
88        assert_eq!(
89            BgpCapabilityType::from(6),
90            BgpCapabilityType::BGP_EXTENDED_MESSAGE
91        );
92        assert_eq!(
93            BgpCapabilityType::from(7),
94            BgpCapabilityType::BGPSEC_CAPABILITY
95        );
96        assert_eq!(
97            BgpCapabilityType::from(8),
98            BgpCapabilityType::MULTIPLE_LABELS_CAPABILITY
99        );
100        assert_eq!(BgpCapabilityType::from(9), BgpCapabilityType::BGP_ROLE);
101
102        assert_eq!(
103            BgpCapabilityType::from(64),
104            BgpCapabilityType::GRACEFUL_RESTART_CAPABILITY
105        );
106        assert_eq!(
107            BgpCapabilityType::from(65),
108            BgpCapabilityType::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY
109        );
110        assert_eq!(
111            BgpCapabilityType::from(67),
112            BgpCapabilityType::SUPPORT_FOR_DYNAMIC_CAPABILITY
113        );
114        assert_eq!(
115            BgpCapabilityType::from(68),
116            BgpCapabilityType::MULTISESSION_BGP_CAPABILITY
117        );
118        assert_eq!(
119            BgpCapabilityType::from(69),
120            BgpCapabilityType::ADD_PATH_CAPABILITY
121        );
122        assert_eq!(
123            BgpCapabilityType::from(70),
124            BgpCapabilityType::ENHANCED_ROUTE_REFRESH_CAPABILITY
125        );
126        assert_eq!(
127            BgpCapabilityType::from(71),
128            BgpCapabilityType::LONG_LIVED_GRACEFUL_RESTART_CAPABILITY
129        );
130        assert_eq!(
131            BgpCapabilityType::from(72),
132            BgpCapabilityType::ROUTING_POLICY_DISTRIBUTION
133        );
134        assert_eq!(
135            BgpCapabilityType::from(73),
136            BgpCapabilityType::FQDN_CAPABILITY
137        );
138    }
139
140    #[test]
141    fn test_reserved_for_experimental() {
142        let experimental_ranges = [239..=254];
143        for code in <[_; 1]>::into_iter(experimental_ranges).flatten() {
144            let ty = BgpCapabilityType::from(code);
145            assert_eq!(ty, BgpCapabilityType::Unknown(code));
146            assert!(ty.is_reserved_for_experimental_use());
147        }
148    }
149
150    #[test]
151    #[cfg(feature = "serde")]
152    fn test_serde() {
153        let ty = BgpCapabilityType::MULTIPROTOCOL_EXTENSIONS_FOR_BGP_4;
154        let serialized = serde_json::to_string(&ty).unwrap();
155        let deserialized: BgpCapabilityType = serde_json::from_str(&serialized).unwrap();
156        assert_eq!(ty, deserialized);
157    }
158}