bgpkit_parser/parser/bmp/messages/
stats_report.rs1use crate::parser::bmp::error::ParserBmpError;
2use crate::parser::ReadUtils;
3use bytes::{Buf, Bytes};
4use num_enum::{FromPrimitive, IntoPrimitive};
5
6#[derive(Debug, PartialEq, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct StatsReport {
9 pub stats_count: u32,
10 pub counters: Vec<StatCounter>,
11}
12
13#[derive(Debug, PartialEq, Clone)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct StatCounter {
17 pub stat_type: StatType,
18 pub stat_len: u16,
19 pub stat_data: StatsData,
20}
21
22#[derive(Debug, PartialEq, Clone)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub enum StatsData {
25 Counter(u32),
26 Gauge(u64),
27 AfiSafiGauge(u16, u8, u64),
28 Unknown(Vec<u8>),
29}
30
31#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36#[repr(u16)]
37pub enum StatType {
38 PrefixesRejectedByInboundPolicy = 0,
39 DuplicatePrefixAdvertisements = 1,
40 DuplicateWithdrawnPrefixes = 2,
41 UpdatesInvalidatedDueToClusterListLoop = 3,
42 UpdatesInvalidatedDueToASPathLoop = 4,
43 UpdatesInvalidatedDueToOriginatorId = 5,
44 UpdatesInvalidatedDueToASConfedLoop = 6,
45 RoutesInAdjRibsIn = 7,
46 RoutesInLocRib = 8,
47 RoutesInPerAfiSafiAdjRibIn = 9,
48 RoutesInPerAfiSafiLocRib = 10,
49 UpdatesSubjectedToTreatAsWithdraw = 11,
50 PrefixesSubjectedToTreatAsWithdraw = 12,
51 DuplicateUpdateMessagesReceived = 13,
52 RoutesInPrePolicyAdjRibOut = 14,
53 RoutesInPostPolicyAdjRibOut = 15,
54 RoutesInPerAfiSafiPrePolicyAdjRibOut = 16,
55 RoutesInPerAfiSafiPostPolicyAdjRibOut = 17,
56 RoutesInPrePolicyAdjRibIn = 18,
57 RoutesInPerAfiSafiPrePolicyAdjRibIn = 19,
58 RoutesInPostPolicyAdjRibIn = 20,
59 RoutesInPerAfiSafiPostPolicyAdjRibIn = 21,
60 RoutesInPerAfiSafiPrePolicyAdjRibInRejectedByPolicy = 22,
61 RoutesInPerAfiSafiPostPolicyAdjRibInAcceptedByPolicy = 23,
62 RoutesInPerAfiSafiPostPolicyAdjRibInOrLocRibSuppressed = 26,
63 RoutesInPerAfiSafiPostPolicyAdjRibInOrLocRibStaleByGracefulRestart = 27,
64 RoutesInPerAfiSafiPostPolicyAdjRibInOrLocRibStaleByLongLivedGracefulRestart = 28,
65 RoutesInPostPolicyAdjRibInLeftBeforeThreshold = 29,
66 RoutesInPerAfiSafiPostPolicyAdjRibInLeftBeforeThreshold = 30,
67 RoutesInPostPolicyAdjRibInOrLocRibLeftBeforeLicenseThreshold = 31,
68 RoutesInPerAfiSafiPostPolicyAdjRibInOrLocRibLeftBeforeLicenseThreshold = 32,
69 RoutesInPrePolicyAdjRibInRejectedDueToMaxAsPathLength = 33,
70 RoutesInPerAfiSafiPrePolicyAdjRibInRejectedDueToMaxAsPathLength = 34,
71 RoutesInPerAfiSafiPostPolicyAdjRibInInvalidatedByRoa = 35,
72 RoutesInPerAfiSafiPostPolicyAdjRibInValidatedByRoa = 36,
73 RoutesInPerAfiSafiPostPolicyAdjRibInRpkiStateNotFound = 37,
74 RoutesInPerAfiSafiPrePolicyAdjRibOutRejectedByPolicy = 38,
75 RoutesInPrePolicyAdjRibOutFilteredDueToMaxAsPathLength = 39,
76 RoutesInPerAfiSafiPrePolicyAdjRibOutFilteredDueToMaxAsPathLength = 40,
77 RoutesInPerAfiSafiPostPolicyAdjRibOutInvalidatedByRoa = 41,
78 RoutesInPerAfiSafiPostPolicyAdjRibOutValidatedByRoa = 42,
79 RoutesInPerAfiSafiPostPolicyAdjRibOutRpkiStateNotFound = 43,
80 #[num_enum(catch_all)]
81 Other(u16) = 65535,
82}
83
84pub fn parse_stats_report(data: &mut Bytes) -> Result<StatsReport, ParserBmpError> {
85 let stats_count = data.read_u32()?;
86 let mut counters = vec![];
87 for _ in 0..stats_count {
88 let stat_type = StatType::from(data.read_u16()?);
89 let stat_len = data.read_u16()?;
90 data.has_n_remaining(stat_len as usize)?;
91 let stat_data = match stat_len {
92 4 => StatsData::Counter(data.read_u32()?),
93 8 => StatsData::Gauge(data.read_u64()?),
94 11 => {
95 let afi = data.read_u16()?;
96 let safi = data.read_u8()?;
97 let value = data.read_u64()?;
98 StatsData::AfiSafiGauge(afi, safi, value)
99 }
100 _ => {
101 let mut unknown = vec![0; stat_len as usize];
102 data.copy_to_slice(&mut unknown);
103 StatsData::Unknown(unknown)
104 }
105 };
106 counters.push(StatCounter {
107 stat_type,
108 stat_len,
109 stat_data,
110 })
111 }
112
113 Ok(StatsReport {
114 stats_count,
115 counters,
116 })
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use bytes::{BufMut, BytesMut};
123
124 #[test]
125 fn test_parse_stats_report_counter() {
126 let mut data = BytesMut::new();
127 data.put_u32(1);
128 data.put_u16(0);
129 data.put_u16(4);
130 data.put_u32(1234);
131
132 let result = parse_stats_report(&mut data.freeze());
133 match result {
134 Ok(report) => {
135 assert_eq!(report.stats_count, 1);
136 assert_eq!(
137 report.counters[0].stat_type,
138 StatType::PrefixesRejectedByInboundPolicy
139 );
140 assert_eq!(report.counters[0].stat_len, 4);
141 match report.counters[0].stat_data {
142 StatsData::Counter(value) => assert_eq!(value, 1234),
143 _ => panic!("Unexpected StatsData!"),
144 }
145 }
146 Err(_) => panic!("Error parsing stats!"),
147 }
148 }
149
150 #[test]
151 fn test_parse_stats_report_gauge() {
152 let mut data = BytesMut::new();
153 data.put_u32(1);
154 data.put_u16(0);
155 data.put_u16(8);
156 data.put_u64(1234);
157
158 let result = parse_stats_report(&mut data.freeze());
159 match result {
160 Ok(report) => {
161 assert_eq!(report.stats_count, 1);
162 assert_eq!(
163 report.counters[0].stat_type,
164 StatType::PrefixesRejectedByInboundPolicy
165 );
166 assert_eq!(report.counters[0].stat_len, 8);
167 match report.counters[0].stat_data {
168 StatsData::Gauge(value) => assert_eq!(value, 1234),
169 _ => panic!("Unexpected StatsData!"),
170 }
171 }
172 Err(_) => panic!("Error parsing stats!"),
173 }
174 }
175
176 #[test]
177 fn test_parse_stats_report_afi_safi_gauge() {
178 let mut data = BytesMut::new();
179 data.put_u32(1);
180 data.put_u16(9);
181 data.put_u16(11);
182 data.put_u16(1);
183 data.put_u8(2);
184 data.put_u64(1234);
185
186 let result = parse_stats_report(&mut data.freeze());
187 match result {
188 Ok(report) => {
189 assert_eq!(report.stats_count, 1);
190 assert_eq!(
191 report.counters[0].stat_type,
192 StatType::RoutesInPerAfiSafiAdjRibIn
193 );
194 assert_eq!(report.counters[0].stat_len, 11);
195 match report.counters[0].stat_data {
196 StatsData::AfiSafiGauge(afi, safi, value) => {
197 assert_eq!(afi, 1);
198 assert_eq!(safi, 2);
199 assert_eq!(value, 1234)
200 }
201 _ => panic!("Unexpected StatsData!"),
202 }
203 }
204 Err(_) => panic!("Error parsing stats!"),
205 }
206 }
207
208 #[test]
209 fn test_parse_stats_report_unknown() {
210 let mut data = BytesMut::new();
211 data.put_u32(1);
212 data.put_u16(100);
213 data.put_u16(1);
214 data.put_u8(3);
215
216 let result = parse_stats_report(&mut data.freeze());
217 match result {
218 Ok(report) => {
219 assert_eq!(report.stats_count, 1);
220 assert_eq!(report.counters[0].stat_type, StatType::Other(100));
221 assert_eq!(report.counters[0].stat_len, 1);
222 match &report.counters[0].stat_data {
223 StatsData::Unknown(data_vec) => {
224 assert_eq!(data_vec.len(), 1);
225 assert_eq!(data_vec[0], 3);
226 }
227 _ => panic!("Unexpected StatsData!"),
228 }
229 }
230 Err(_) => panic!("Error parsing stats!"),
231 }
232 }
233
234 #[test]
236 fn test_parse_stats_report_error() {
237 let mut data = BytesMut::new();
238 data.put_u32(1);
239 data.put_u16(0);
240 data.put_u16(6);
241 data.put_u32(1234);
242
243 let result = parse_stats_report(&mut data.freeze());
244 assert!(result.is_err());
245 }
246}