Skip to main content

flowparser_sflow/samples/
mod.rs

1pub mod counter_sample;
2pub mod flow_sample;
3
4use nom::number::complete::be_u32;
5use serde::{Deserialize, Serialize};
6
7use crate::error::{ParseContext, ParseErrorKind, SflowError};
8pub use counter_sample::{CounterSample, ExpandedCounterSample};
9pub use flow_sample::{ExpandedFlowSample, FlowSample};
10
11/// An sFlow sample carried within a datagram.
12///
13/// Each datagram can contain a mix of flow samples (packet-level data)
14/// and counter samples (interface statistics). Expanded variants use
15/// separate fields for source ID type and index instead of a packed u32.
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub enum SflowSample {
18    /// Standard flow sample (enterprise=0, format=1).
19    Flow(FlowSample),
20    /// Standard counter sample (enterprise=0, format=2).
21    Counter(CounterSample),
22    /// Expanded flow sample with unpacked source ID (enterprise=0, format=3).
23    ExpandedFlow(ExpandedFlowSample),
24    /// Expanded counter sample with unpacked source ID (enterprise=0, format=4).
25    ExpandedCounter(ExpandedCounterSample),
26    /// Unrecognized sample type, preserved as raw bytes.
27    Unknown {
28        /// Enterprise code from the sample header.
29        enterprise: u32,
30        /// Format code from the sample header.
31        format: u32,
32        /// Raw sample data.
33        data: Vec<u8>,
34    },
35}
36
37pub(crate) fn parse_samples(
38    mut input: &[u8],
39    num_samples: u32,
40) -> Result<(&[u8], Vec<SflowSample>), SflowError> {
41    // Cap capacity to prevent DoS: each sample needs at least 8 bytes (format + length)
42    let cap = (num_samples as usize).min(input.len() / 8);
43    let mut samples = Vec::with_capacity(cap);
44
45    for _ in 0..num_samples {
46        let (rest, data_format) =
47            be_u32(input).map_err(|_: nom::Err<nom::error::Error<&[u8]>>| {
48                SflowError::Incomplete {
49                    available: input.len(),
50                    expected: None,
51                    context: ParseContext::SampleDataFormat,
52                }
53            })?;
54
55        let enterprise = data_format >> 12;
56        let format = data_format & 0xFFF;
57
58        let (rest, sample_length) =
59            be_u32(rest).map_err(|_: nom::Err<nom::error::Error<&[u8]>>| {
60                SflowError::Incomplete {
61                    available: rest.len(),
62                    expected: None,
63                    context: ParseContext::SampleLength,
64                }
65            })?;
66
67        let sample_length = sample_length as usize;
68        if rest.len() < sample_length {
69            return Err(SflowError::Incomplete {
70                available: rest.len(),
71                expected: Some(sample_length),
72                context: ParseContext::SampleData,
73            });
74        }
75
76        let sample_data = &rest[..sample_length];
77        let after_sample = &rest[sample_length..];
78
79        let sample = if enterprise == 0 {
80            match format {
81                1 => {
82                    let (_, fs) = flow_sample::parse_flow_sample(sample_data).map_err(|e| {
83                        SflowError::ParseError {
84                            offset: 0,
85                            context: ParseContext::FlowSample,
86                            kind: nom_err_to_kind(&e),
87                        }
88                    })?;
89                    SflowSample::Flow(fs)
90                }
91                2 => {
92                    let (_, cs) =
93                        counter_sample::parse_counter_sample(sample_data).map_err(|e| {
94                            SflowError::ParseError {
95                                offset: 0,
96                                context: ParseContext::CounterSample,
97                                kind: nom_err_to_kind(&e),
98                            }
99                        })?;
100                    SflowSample::Counter(cs)
101                }
102                3 => {
103                    let (_, efs) = flow_sample::parse_expanded_flow_sample(sample_data)
104                        .map_err(|e| SflowError::ParseError {
105                            offset: 0,
106                            context: ParseContext::ExpandedFlowSample,
107                            kind: nom_err_to_kind(&e),
108                        })?;
109                    SflowSample::ExpandedFlow(efs)
110                }
111                4 => {
112                    let (_, ecs) = counter_sample::parse_expanded_counter_sample(sample_data)
113                        .map_err(|e| SflowError::ParseError {
114                        offset: 0,
115                        context: ParseContext::ExpandedCounterSample,
116                        kind: nom_err_to_kind(&e),
117                    })?;
118                    SflowSample::ExpandedCounter(ecs)
119                }
120                _ => SflowSample::Unknown {
121                    enterprise,
122                    format,
123                    data: sample_data.to_vec(),
124                },
125            }
126        } else {
127            SflowSample::Unknown {
128                enterprise,
129                format,
130                data: sample_data.to_vec(),
131            }
132        };
133
134        samples.push(sample);
135        input = after_sample;
136    }
137
138    Ok((input, samples))
139}
140
141fn nom_err_to_kind(e: &nom::Err<nom::error::Error<&[u8]>>) -> ParseErrorKind {
142    match e {
143        nom::Err::Error(e) | nom::Err::Failure(e) => ParseErrorKind::NomError(e.code),
144        nom::Err::Incomplete(_) => ParseErrorKind::NomError(nom::error::ErrorKind::Complete),
145    }
146}