Skip to content

fix: * Simplify V9/IPFix Parse function. #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "netflow_parser"
description = "Parser for Netflow Cisco V5, V7, V9, IPFIX"
version = "0.5.5"
version = "0.5.6"
edition = "2024"
authors = ["[email protected]"]
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -31,7 +31,6 @@ serde_json = "1.0.100"
[[bench]]
name = "netflow_parser_bench"
harness = false

[[bench]]
name = "netflow_common_bench"
harness = false
Expand Down
3 changes: 3 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 0.5.6
* Simplify V9/IPFix Parse function.

# 0.5.5
* More IPFIx/V9 Cleanup.
* Reworked FlowSetBody for V9/IPFIX into an enum since a flowset can only contain a single type.
Expand Down
2 changes: 1 addition & 1 deletion src/variable_versions/data_number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl FieldValue {
FieldValue::DataNumber(d) => d.to_be_bytes(),
FieldValue::Float64(f) => Ok(f.to_be_bytes().to_vec()),
FieldValue::Duration(d) => Ok((u32::try_from(d.as_secs())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?)
.map_err(std::io::Error::other)?)
.to_be_bytes()
.to_vec()),
FieldValue::Ip4Addr(ip) => Ok(ip.octets().to_vec()),
Expand Down
39 changes: 18 additions & 21 deletions src/variable_versions/ipfix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub enum FlowSetBody {
///
/// - `i`: A byte slice (`&[u8]`) representing the input data to be parsed.
/// - `parser`: A mutable reference to an `IPFixParser` instance, which maintains the state including
/// registered templates and options templates.
/// registered templates and options templates.
/// - `id`: A 16-bit unsigned integer (`u16`) representing the identifier for the flowset to be parsed.
///
/// # Returns
Expand Down Expand Up @@ -287,6 +287,7 @@ pub struct TemplateField {
// Common trait for both templates. Mainly for fetching fields.
trait CommonTemplate {
fn get_fields(&self) -> &Vec<TemplateField>;

fn get_field_count(&self) -> usize {
self.get_fields().len()
}
Expand Down Expand Up @@ -319,29 +320,25 @@ impl FieldParser {
i: &[u8],
template: T,
) -> IResult<&[u8], Vec<BTreeMap<usize, IPFixFieldPair>>> {
let mut total_taken = 0;

// If no fields there are no fields to parse, return an error.
let mut fields = vec![];
let mut remaining = i;
for (c, field) in template.get_fields().iter().enumerate() {
// Iter through template fields and push them to a vec. If we encouter any zero length fields we return an error.
let mut data_field = BTreeMap::new();
let (i, field_value) = field.parse_as_field_value(remaining)?;
let taken = remaining.len().saturating_sub(i.len());
total_taken += taken;
remaining = i;
data_field.insert(c, (field.field_type, field_value));
fields.push(data_field);
}

let remaining = if !remaining.is_empty() && remaining.len() >= total_taken {
let (remaining, mut fields, total_taken) =
template.get_fields().iter().enumerate().try_fold(
(i, vec![], 0usize),
|(remaining, mut fields, total_taken), (c, field)| {
let mut data_field = BTreeMap::new();
let (i, field_value) = field.parse_as_field_value(remaining)?;
let taken = remaining.len().saturating_sub(i.len());
data_field.insert(c, (field.field_type, field_value));
fields.push(data_field);
Ok((i, fields, total_taken.saturating_add(taken)))
},
)?;

if remaining.len() >= total_taken {
let (remaining, more) = Self::parse(remaining, template)?;
fields.extend(more);
remaining
} else {
remaining
};
return Ok((remaining, fields));
}

Ok((remaining, fields))
}
Expand Down
22 changes: 10 additions & 12 deletions src/variable_versions/v9.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ pub enum ScopeDataField {
///
/// * `input` - A byte slice that contains the data to be parsed.
/// * `template_field` - A reference to an `OptionsTemplateScopeField` which holds the metadata describing the expected field,
/// including its length and type.
/// including its length and type.
///
/// # Returns
///
Expand Down Expand Up @@ -433,17 +433,15 @@ impl FlowSetParser {
parser: &mut V9Parser,
record_count: u16,
) -> IResult<&'a [u8], Vec<FlowSet>> {
let mut flowsets = vec![];
let mut remaining = i;
let mut record_count_index = 0;

// Header.count represents total number of records in data + records in templates
while !remaining.is_empty() && record_count_index < record_count {
let (i, flowset) = FlowSet::parse(remaining, parser)?;
remaining = i;
flowsets.push(flowset);
record_count_index += 1;
}
let (remaining, flowsets) =
(0..record_count).try_fold((i, Vec::new()), |(remaining, mut flowsets), _| {
if remaining.is_empty() {
return Ok((remaining, flowsets));
}
let (i, flowset) = FlowSet::parse(remaining, parser)?;
flowsets.push(flowset);
Ok((i, flowsets))
})?;

Ok((remaining, flowsets))
}
Expand Down