Skip to content

Commit e3364ec

Browse files
mikemiles-devMichael Mileusnich
and
Michael Mileusnich
authored
Removed some clones & Optimized template lookups (#35)
Co-authored-by: Michael Mileusnich <[email protected]>
1 parent 59dc66e commit e3364ec

8 files changed

+60
-70
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "netflow_parser"
33
description = "Parser for Netflow Cisco V5, V7, V9, IPFIX"
4-
version = "0.2.1"
4+
version = "0.2.2"
55
edition = "2021"
66
77
license = "MIT OR Apache-2.0"

RELEASES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 0.2.2
2+
* Optimizations in V9/IPFIX, removed some clone/cloned.
3+
* Reworked Template Fields/Option Template Fields into single struct.
4+
This avoids having to make an additional clone for each parse.
5+
16
# 0.2.1
27
* Fixed issue where v9/ipfix template fields can infinite loop.
38

SECURITY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
| Version | Supported |
66
| ------- | ------------------ |
7+
| 0.2.2 | :white_check_mark: |
78
| 0.2.1 | :white_check_mark: |
89
| 0.2.0 | :white_check_mark: |
910
| 0.1.9 | :white_check_mark: |

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,12 +390,14 @@ mod tests {
390390
field_type:
391391
super::variable_versions::ipfix_lookup::IPFixField::PacketDeltaCount,
392392
field_length: 2,
393+
enterprise_number: None,
393394
},
394395
IPFixTemplateField {
395396
field_type_number: 8,
396397
field_type:
397398
super::variable_versions::ipfix_lookup::IPFixField::SourceIpv4address,
398399
field_length: 4,
400+
enterprise_number: None,
399401
},
400402
];
401403
let template = IPFixTemplate {

src/snapshots/netflow_parser__tests__it_parses_ipfix_options_template.snap

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@ expression: "NetflowParser::default().parse_bytes(&packet)"
1818
template_id: 260
1919
field_count: 3
2020
scope_field_count: 1
21-
scope_field_specifiers:
21+
fields:
2222
- field_type_number: 123
2323
field_type: Enterprise
2424
field_length: 4
2525
enterprise_number: 2
26-
field_specifiers:
2726
- field_type_number: 32809
2827
field_type: ExportedMessageTotalCount
2928
field_length: 2
3029
- field_type_number: 32810
3130
field_type: ExportedFlowRecordTotalCount
3231
field_length: 2
33-

src/snapshots/netflow_parser__tests__it_parses_ipfix_options_template_with_data.snap

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ expression: "NetflowParser::default().parse_bytes(&packet)"
1818
template_id: 260
1919
field_count: 3
2020
scope_field_count: 1
21-
scope_field_specifiers:
21+
fields:
2222
- field_type_number: 123
2323
field_type: Enterprise
2424
field_length: 4
2525
enterprise_number: 2
26-
field_specifiers:
2726
- field_type_number: 32809
2827
field_type: ExportedMessageTotalCount
2928
field_length: 2
@@ -46,4 +45,3 @@ expression: "NetflowParser::default().parse_bytes(&packet)"
4645
DataNumber: 7710
4746
Enterprise:
4847
DataNumber: 2
49-

src/variable_versions/ipfix.rs

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::{NetflowByteParserVariable, NetflowPacketResult, ParsedNetflow};
1313

1414
use nom::bytes::complete::take;
1515
use nom::error::{Error as NomError, ErrorKind};
16+
use nom::multi::count;
1617
use nom::number::complete::{be_u128, be_u32};
1718
use nom::Err as NomErr;
1819
use nom::IResult;
@@ -100,17 +101,15 @@ pub struct Set {
100101
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
101102
#[nom(ExtraArgs(parser: &mut IPFixParser, set_id: u16))]
102103
pub struct Data {
103-
#[nom(
104-
Parse = "{ |i| parse_fields::<Template>(i, parser.templates.get(&set_id).cloned()) }"
105-
)]
104+
#[nom(Parse = "{ |i| parse_fields::<Template>(i, parser.templates.get(&set_id)) }")]
106105
pub data_fields: Vec<BTreeMap<IPFixField, FieldValue>>,
107106
}
108107

109108
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
110109
#[nom(ExtraArgs(parser: &mut IPFixParser, set_id: u16))]
111110
pub struct OptionsData {
112111
#[nom(
113-
Parse = "{ |i| parse_fields::<OptionsTemplate>(i, parser.options_templates.get(&set_id).cloned()) }"
112+
Parse = "{ |i| parse_fields::<OptionsTemplate>(i, parser.options_templates.get(&set_id)) }"
114113
)]
115114
pub data_fields: Vec<BTreeMap<IPFixField, FieldValue>>,
116115
}
@@ -120,12 +119,12 @@ pub struct OptionsTemplate {
120119
pub template_id: u16,
121120
pub field_count: u16,
122121
pub scope_field_count: u16,
123-
#[nom(Count = "scope_field_count")]
124-
pub scope_field_specifiers: Vec<OptionsTemplateField>,
125122
#[nom(
126-
Count = "(field_count.checked_sub(scope_field_count).unwrap_or(field_count)) as usize"
123+
PreExec = "let combined_count = scope_field_count as usize +
124+
field_count.checked_sub(scope_field_count).unwrap_or(field_count) as usize;",
125+
Parse = "count(|i| TemplateField::parse(i, true), combined_count)"
127126
)]
128-
pub field_specifiers: Vec<OptionsTemplateField>,
127+
pub fields: Vec<TemplateField>,
129128
#[nom(Cond = "!i.is_empty()")]
130129
#[serde(skip_serializing)]
131130
padding: Option<u16>,
@@ -135,23 +134,28 @@ pub struct OptionsTemplate {
135134
pub struct Template {
136135
pub template_id: u16,
137136
pub field_count: u16,
138-
#[nom(Count = "field_count")]
137+
#[nom(Parse = "count(|i| TemplateField::parse(i, false), field_count as usize)")]
139138
pub fields: Vec<TemplateField>,
140139
}
141140

142141
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Nom)]
143-
pub struct OptionsTemplateField {
142+
#[nom(ExtraArgs(options_template: bool))]
143+
pub struct TemplateField {
144144
pub field_type_number: u16,
145145
#[nom(Value(IPFixField::from(field_type_number)))]
146146
pub field_type: IPFixField,
147-
field_length: u16,
147+
pub field_length: u16,
148148
#[nom(
149-
Cond = "field_type_number > 32767",
150-
PostExec = "let field_type_number = field_type_number.overflowing_sub(32768).0;",
151-
PostExec = "let field_type = set_entperprise_field(field_type, enterprise_number);"
149+
Cond = "options_template && field_type_number > 32767",
150+
PostExec = "let field_type_number = if options_template {
151+
field_type_number.overflowing_sub(32768).0
152+
} else { field_type_number };",
153+
PostExec = "let field_type = if options_template {
154+
set_entperprise_field(field_type, enterprise_number)
155+
} else { field_type };"
152156
)]
153157
#[serde(skip_serializing_if = "Option::is_none")]
154-
enterprise_number: Option<u32>,
158+
pub enterprise_number: Option<u32>,
155159
}
156160

157161
fn set_entperprise_field(field_type: IPFixField, enterprise_number: Option<u32>) -> IPFixField {
@@ -161,14 +165,6 @@ fn set_entperprise_field(field_type: IPFixField, enterprise_number: Option<u32>)
161165
}
162166
}
163167

164-
#[derive(Debug, PartialEq, Eq, Clone, Serialize, PartialOrd, Ord, Nom)]
165-
pub struct TemplateField {
166-
pub field_type_number: u16,
167-
#[nom(Value(IPFixField::from(field_type_number)))]
168-
pub field_type: IPFixField,
169-
pub field_length: u16,
170-
}
171-
172168
/// Parses options template
173169
fn parse_options_template(i: &[u8], length: u16) -> IResult<&[u8], OptionsTemplate> {
174170
let (remaining, taken) = take(length.checked_sub(4).unwrap_or(length))(i)?;
@@ -179,57 +175,47 @@ fn parse_options_template(i: &[u8], length: u16) -> IResult<&[u8], OptionsTempla
179175
// Hacky way when using Template as generic T to cast to a common field type.
180176
// We use OptionsTemplateField as it is the same as type Template Field but
181177
// with enterprise_field. In TemplateField tpe enterprise_field is just None.
182-
trait CommonTemplateFields {
183-
fn get_fields(&self) -> Vec<OptionsTemplateField>;
178+
trait CommonTemplate {
179+
fn get_fields(&self) -> &Vec<TemplateField>;
184180
}
185181

186-
impl CommonTemplateFields for Template {
187-
fn get_fields(&self) -> Vec<OptionsTemplateField> {
188-
self.fields
189-
.iter()
190-
.map(|f| OptionsTemplateField {
191-
field_length: f.field_length,
192-
field_type: f.field_type,
193-
field_type_number: f.field_type_number,
194-
enterprise_number: None,
195-
})
196-
.collect()
182+
impl CommonTemplate for Template {
183+
fn get_fields(&self) -> &Vec<TemplateField> {
184+
&self.fields
197185
}
198186
}
199187

200-
impl CommonTemplateFields for OptionsTemplate {
201-
fn get_fields(&self) -> Vec<OptionsTemplateField> {
202-
let mut temp = vec![];
203-
temp.append(&mut self.scope_field_specifiers.clone());
204-
temp.append(&mut self.field_specifiers.clone());
205-
temp
188+
impl CommonTemplate for OptionsTemplate {
189+
fn get_fields(&self) -> &Vec<TemplateField> {
190+
&self.fields
206191
}
207192
}
208193

209194
/// Takes a byte stream and a cached template.
210195
/// Fields get matched to static types.
211196
/// Returns BTree of IPFix Types & Fields or IResult Error.
212-
fn parse_fields<T: CommonTemplateFields>(
213-
i: &[u8],
214-
template: Option<T>,
215-
) -> IResult<&[u8], Vec<BTreeMap<IPFixField, FieldValue>>> {
197+
fn parse_fields<'a, T: CommonTemplate>(
198+
i: &'a [u8],
199+
template: Option<&T>,
200+
) -> IResult<&'a [u8], Vec<BTreeMap<IPFixField, FieldValue>>> {
216201
let template = match template {
217202
Some(t) => t,
218203
None => {
219204
// dbg!("Could not fetch any v10 templates!");
220205
return Err(NomErr::Error(NomError::new(i, ErrorKind::Fail)));
221206
}
222207
};
208+
let template_fields = template.get_fields();
223209
// If no fields there are no fields to parse
224-
if template.get_fields().is_empty() {
210+
if template_fields.is_empty() {
225211
// dbg!("Template without fields!");
226212
return Err(NomErr::Error(NomError::new(i, ErrorKind::Fail)));
227213
};
228214
let mut fields = vec![];
229215
let mut remaining = i;
230216
while !remaining.is_empty() {
231217
let mut data_field = BTreeMap::new();
232-
for template_field in template.get_fields().iter() {
218+
for template_field in template_fields.iter() {
233219
let field_type: FieldDataType = template_field.field_type.into();
234220
// Enterprise Number
235221
if template_field.enterprise_number.is_some() {
@@ -349,7 +335,7 @@ impl NetflowByteParserVariable for IPFixParser {
349335
.checked_sub(remaining.len())
350336
.unwrap_or(total_left);
351337
total_left -= parsed;
352-
sets.push(v10_set.clone());
338+
sets.push(v10_set);
353339
}
354340

355341
let v10_parsed = IPFix {

src/variable_versions/v9.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ pub struct OptionsData {
224224
pub length: u16,
225225
// Scope Data
226226
#[nom(
227-
Parse = "{ |i| parse_scope_data_fields(i, flow_set_id, parser.options_templates.clone()) }"
227+
Parse = "{ |i| parse_scope_data_fields(i, flow_set_id, &parser.options_templates) }"
228228
)]
229229
pub scope_fields: Vec<ScopeDataField>,
230230
// Options Data Fields
@@ -276,7 +276,7 @@ fn get_total_options_length(flow_set_id: u16, length: u16, parser: &mut V9Parser
276276
}
277277

278278
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
279-
#[nom(ExtraArgs(field: OptionsTemplateScopeField))]
279+
#[nom(ExtraArgs(field: &OptionsTemplateScopeField))]
280280
pub struct ScopeDataField {
281281
/// System
282282
#[nom(
@@ -328,23 +328,23 @@ pub struct Data {
328328
/// themselves, as well as the combined lengths of any included data records.
329329
pub length: u16,
330330
// Data Fields
331-
#[nom(Parse = "{ |i| parse_fields(i, parser.templates.get(&flow_set_id).cloned()) }")]
331+
#[nom(Parse = "{ |i| parse_fields(i, parser.templates.get(&flow_set_id)) }")]
332332
pub data_fields: Vec<BTreeMap<V9Field, FieldValue>>,
333333
}
334334

335335
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
336-
#[nom(ExtraArgs(field: TemplateField))]
336+
#[nom(ExtraArgs(field: &TemplateField))]
337337
pub struct OptionDataField {
338338
#[nom(Value(field.field_type))]
339339
pub field_type: V9Field,
340340
#[nom(Map = "|i: &[u8]| i.to_vec()", Take = "field.field_length")]
341341
pub field_value: Vec<u8>,
342342
}
343343

344-
fn parse_fields(
345-
i: &[u8],
346-
template: Option<Template>,
347-
) -> IResult<&[u8], Vec<BTreeMap<V9Field, FieldValue>>> {
344+
fn parse_fields<'a>(
345+
i: &'a [u8],
346+
template: Option<&Template>,
347+
) -> IResult<&'a [u8], Vec<BTreeMap<V9Field, FieldValue>>> {
348348
let template = match template {
349349
Some(t) => t,
350350
None => {
@@ -457,26 +457,26 @@ fn parse_options_data_fields(
457457
let mut fields = vec![];
458458
let mut remaining = i;
459459
for field in template.option_fields.iter() {
460-
let (i, v9_data_field) = OptionDataField::parse(remaining, field.clone())?;
460+
let (i, v9_data_field) = OptionDataField::parse(remaining, field)?;
461461
remaining = i;
462462
fields.push(v9_data_field)
463463
}
464464
Ok((remaining, fields))
465465
}
466466

467-
fn parse_scope_data_fields(
468-
i: &[u8],
467+
fn parse_scope_data_fields<'a>(
468+
i: &'a [u8],
469469
flow_set_id: u16,
470-
templates: HashMap<u16, OptionsTemplate>,
471-
) -> IResult<&[u8], Vec<ScopeDataField>> {
470+
templates: &HashMap<u16, OptionsTemplate>,
471+
) -> IResult<&'a [u8], Vec<ScopeDataField>> {
472472
let template = templates.get(&flow_set_id).ok_or_else(|| {
473473
// dbg!("Could not fetch any v9 options templates!");
474474
NomErr::Error(NomError::new(i, ErrorKind::Fail))
475475
})?;
476476
let mut fields = vec![];
477477
let mut remaining = i;
478478
for field in template.scope_fields.iter() {
479-
let (i, v9_data_field) = ScopeDataField::parse(remaining, field.clone())?;
479+
let (i, v9_data_field) = ScopeDataField::parse(remaining, field)?;
480480
remaining = i;
481481
fields.push(v9_data_field)
482482
}

0 commit comments

Comments
 (0)