Skip to main content

flowparser_sflow/counter_records/
mod.rs

1pub mod ethernet_interface;
2pub mod generic_interface;
3pub mod processor;
4pub mod token_ring;
5pub mod vlan;
6
7use nom::IResult;
8use nom::number::complete::be_u32;
9use serde::{Deserialize, Serialize};
10
11pub use ethernet_interface::EthernetInterface;
12pub use generic_interface::GenericInterface;
13pub use processor::Processor;
14pub use token_ring::TokenRing;
15pub use vlan::Vlan;
16
17/// A counter record within a counter sample.
18///
19/// Counter records contain periodic interface and system statistics
20/// reported by the sFlow agent.
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22pub enum CounterRecord {
23    /// Generic interface counters (enterprise=0, format=1).
24    GenericInterface(GenericInterface),
25    /// Ethernet-specific interface counters (enterprise=0, format=2).
26    EthernetInterface(EthernetInterface),
27    /// Token Ring interface counters (enterprise=0, format=3).
28    TokenRing(TokenRing),
29    /// VLAN counters (enterprise=0, format=5).
30    Vlan(Vlan),
31    /// Processor/CPU counters (enterprise=0, format=1001).
32    Processor(Processor),
33    /// Unrecognized counter record type, preserved as raw bytes.
34    Unknown {
35        /// Enterprise code from the record header.
36        enterprise: u32,
37        /// Format code from the record header.
38        format: u32,
39        /// Raw record data.
40        data: Vec<u8>,
41    },
42}
43
44pub(crate) fn parse_counter_records(
45    mut input: &[u8],
46    num_records: u32,
47) -> IResult<&[u8], Vec<CounterRecord>> {
48    // Cap capacity to prevent DoS: each record needs at least 8 bytes (format + length)
49    let cap = (num_records as usize).min(input.len() / 8);
50    let mut records = Vec::with_capacity(cap);
51
52    for _ in 0..num_records {
53        let (rest, data_format) = be_u32(input)?;
54        let enterprise = data_format >> 12;
55        let format = data_format & 0xFFF;
56
57        let (rest, record_length) = be_u32(rest)?;
58        let record_length = record_length as usize;
59
60        if rest.len() < record_length {
61            return Err(nom::Err::Error(nom::error::Error::new(
62                rest,
63                nom::error::ErrorKind::Eof,
64            )));
65        }
66
67        let record_data = &rest[..record_length];
68        let after_record = &rest[record_length..];
69
70        let record = if enterprise == 0 {
71            match format {
72                1 => {
73                    let (_, r) = generic_interface::parse_generic_interface(record_data)?;
74                    CounterRecord::GenericInterface(r)
75                }
76                2 => {
77                    let (_, r) = ethernet_interface::parse_ethernet_interface(record_data)?;
78                    CounterRecord::EthernetInterface(r)
79                }
80                3 => {
81                    let (_, r) = token_ring::parse_token_ring(record_data)?;
82                    CounterRecord::TokenRing(r)
83                }
84                5 => {
85                    let (_, r) = vlan::parse_vlan(record_data)?;
86                    CounterRecord::Vlan(r)
87                }
88                1001 => {
89                    let (_, r) = processor::parse_processor(record_data)?;
90                    CounterRecord::Processor(r)
91                }
92                _ => CounterRecord::Unknown {
93                    enterprise,
94                    format,
95                    data: record_data.to_vec(),
96                },
97            }
98        } else {
99            CounterRecord::Unknown {
100                enterprise,
101                format,
102                data: record_data.to_vec(),
103            }
104        };
105
106        records.push(record);
107        input = after_record;
108    }
109
110    Ok((input, records))
111}