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 #[num_enum(catch_all)]
57 Other(u16) = 65535,
58}
59
60pub fn parse_stats_report(data: &mut Bytes) -> Result<StatsReport, ParserBmpError> {
61 let stats_count = data.read_u32()?;
62 let mut counters = vec![];
63 for _ in 0..stats_count {
64 let stat_type = StatType::from(data.read_u16()?);
65 let stat_len = data.read_u16()?;
66 data.has_n_remaining(stat_len as usize)?;
67 let stat_data = match stat_len {
68 4 => StatsData::Counter(data.read_u32()?),
69 8 => StatsData::Gauge(data.read_u64()?),
70 11 => {
71 let afi = data.read_u16()?;
72 let safi = data.read_u8()?;
73 let value = data.read_u64()?;
74 StatsData::AfiSafiGauge(afi, safi, value)
75 }
76 _ => {
77 let mut unknown = vec![0; stat_len as usize];
78 data.copy_to_slice(&mut unknown);
79 StatsData::Unknown(unknown)
80 }
81 };
82 counters.push(StatCounter {
83 stat_type,
84 stat_len,
85 stat_data,
86 })
87 }
88
89 Ok(StatsReport {
90 stats_count,
91 counters,
92 })
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use bytes::{BufMut, BytesMut};
99
100 #[test]
101 fn test_parse_stats_report_counter() {
102 let mut data = BytesMut::new();
103 data.put_u32(1);
104 data.put_u16(0);
105 data.put_u16(4);
106 data.put_u32(1234);
107
108 let result = parse_stats_report(&mut data.freeze());
109 match result {
110 Ok(report) => {
111 assert_eq!(report.stats_count, 1);
112 assert_eq!(
113 report.counters[0].stat_type,
114 StatType::PrefixesRejectedByInboundPolicy
115 );
116 assert_eq!(report.counters[0].stat_len, 4);
117 match report.counters[0].stat_data {
118 StatsData::Counter(value) => assert_eq!(value, 1234),
119 _ => panic!("Unexpected StatsData!"),
120 }
121 }
122 Err(_) => panic!("Error parsing stats!"),
123 }
124 }
125
126 #[test]
127 fn test_parse_stats_report_gauge() {
128 let mut data = BytesMut::new();
129 data.put_u32(1);
130 data.put_u16(0);
131 data.put_u16(8);
132 data.put_u64(1234);
133
134 let result = parse_stats_report(&mut data.freeze());
135 match result {
136 Ok(report) => {
137 assert_eq!(report.stats_count, 1);
138 assert_eq!(
139 report.counters[0].stat_type,
140 StatType::PrefixesRejectedByInboundPolicy
141 );
142 assert_eq!(report.counters[0].stat_len, 8);
143 match report.counters[0].stat_data {
144 StatsData::Gauge(value) => assert_eq!(value, 1234),
145 _ => panic!("Unexpected StatsData!"),
146 }
147 }
148 Err(_) => panic!("Error parsing stats!"),
149 }
150 }
151
152 #[test]
153 fn test_parse_stats_report_afi_safi_gauge() {
154 let mut data = BytesMut::new();
155 data.put_u32(1);
156 data.put_u16(9);
157 data.put_u16(11);
158 data.put_u16(1);
159 data.put_u8(2);
160 data.put_u64(1234);
161
162 let result = parse_stats_report(&mut data.freeze());
163 match result {
164 Ok(report) => {
165 assert_eq!(report.stats_count, 1);
166 assert_eq!(
167 report.counters[0].stat_type,
168 StatType::RoutesInPerAfiSafiAdjRibIn
169 );
170 assert_eq!(report.counters[0].stat_len, 11);
171 match report.counters[0].stat_data {
172 StatsData::AfiSafiGauge(afi, safi, value) => {
173 assert_eq!(afi, 1);
174 assert_eq!(safi, 2);
175 assert_eq!(value, 1234)
176 }
177 _ => panic!("Unexpected StatsData!"),
178 }
179 }
180 Err(_) => panic!("Error parsing stats!"),
181 }
182 }
183
184 #[test]
185 fn test_parse_stats_report_unknown() {
186 let mut data = BytesMut::new();
187 data.put_u32(1);
188 data.put_u16(100);
189 data.put_u16(1);
190 data.put_u8(3);
191
192 let result = parse_stats_report(&mut data.freeze());
193 match result {
194 Ok(report) => {
195 assert_eq!(report.stats_count, 1);
196 assert_eq!(report.counters[0].stat_type, StatType::Other(100));
197 assert_eq!(report.counters[0].stat_len, 1);
198 match &report.counters[0].stat_data {
199 StatsData::Unknown(data_vec) => {
200 assert_eq!(data_vec.len(), 1);
201 assert_eq!(data_vec[0], 3);
202 }
203 _ => panic!("Unexpected StatsData!"),
204 }
205 }
206 Err(_) => panic!("Error parsing stats!"),
207 }
208 }
209
210 #[test]
212 fn test_parse_stats_report_error() {
213 let mut data = BytesMut::new();
214 data.put_u32(1);
215 data.put_u16(0);
216 data.put_u16(6);
217 data.put_u32(1234);
218
219 let result = parse_stats_report(&mut data.freeze());
220 assert!(result.is_err());
221 }
222}