use super::data_number::*;
use crate::variable_versions::ipfix_lookup::*;
use crate::{NetflowPacket, NetflowParseError, ParsedNetflow};
use nom::bytes::complete::take;
use nom::error::{Error as NomError, ErrorKind};
use nom::multi::count;
use nom::Err as NomErr;
use nom::IResult;
use nom_derive::*;
use serde::Serialize;
use Nom;
use std::collections::BTreeMap;
const TEMPLATE_ID: u16 = 2;
const OPTIONS_TEMPLATE_ID: u16 = 3;
const SET_MIN_RANGE: u16 = 255;
type TemplateId = u16;
type IPFixFieldPair = (IPFixField, FieldValue);
pub(crate) fn parse_netflow_ipfix(
packet: &[u8],
parser: &mut IPFixParser,
) -> Result<ParsedNetflow, NetflowParseError> {
IPFix::parse(packet, parser)
.map(|(remaining, ipfix)| ParsedNetflow::new(remaining, NetflowPacket::IPFix(ipfix)))
.map_err(|e| NetflowParseError::IPFix(e.to_string()))
}
#[derive(Default, Debug)]
pub struct IPFixParser {
pub templates: BTreeMap<TemplateId, Template>,
pub options_templates: BTreeMap<TemplateId, OptionsTemplate>,
}
#[derive(Nom, Debug, PartialEq, Clone, Serialize)]
#[nom(ExtraArgs(parser: &mut IPFixParser))]
pub struct IPFix {
pub header: Header,
#[nom(Parse = "{ |i| parse_sets(i, parser, header.length) }")]
pub flowsets: Vec<FlowSet>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Nom)]
pub struct Header {
#[nom(Value = "10")]
pub version: u16,
pub length: u16,
pub export_time: u32,
pub sequence_number: u32,
pub observation_domain_id: u32,
}
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
#[nom(ExtraArgs(parser: &mut IPFixParser))]
pub struct FlowSet {
pub header: FlowSetHeader,
#[nom(Parse = "{ |i| parse_set_body(i, parser, header.length, header.header_id) }")]
pub body: FlowSetBody,
}
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
pub struct FlowSetHeader {
pub header_id: u16,
pub length: u16,
}
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
#[nom(ExtraArgs(parser: &mut IPFixParser, id: u16, length: u16))]
pub struct FlowSetBody {
#[nom(
Cond = "id == TEMPLATE_ID",
PostExec = "if let Some(templates) = templates.clone() { parser.templates.insert(templates.template_id, templates); }"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub templates: Option<Template>,
#[nom(
Cond = "id == OPTIONS_TEMPLATE_ID",
PreExec = "let set_length = length.checked_sub(4).unwrap_or(length);",
Parse = "{ |i| OptionsTemplate::parse(i, set_length) }",
PostExec = "if let Some(options_templates) = options_templates.clone() {
parser.options_templates.insert(options_templates.template_id, options_templates);
}"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub options_templates: Option<OptionsTemplate>,
#[nom(
Cond = "id > SET_MIN_RANGE && parser.templates.contains_key(&id)",
Parse = "{ |i| Data::parse(i, parser, id) }"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Data>,
#[nom(
Cond = "id > SET_MIN_RANGE && parser.options_templates.contains_key(&id)",
Parse = "{ |i| OptionsData::parse(i, parser, id) }"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub options_data: Option<OptionsData>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
#[nom(ExtraArgs(parser: &mut IPFixParser, set_id: u16))]
pub struct Data {
#[nom(Parse = "{ |i| parse_fields::<Template>(i, parser.templates.get(&set_id)) }")]
pub data_fields: Vec<BTreeMap<usize, (IPFixField, FieldValue)>>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Nom)]
#[nom(ExtraArgs(parser: &mut IPFixParser, set_id: u16))]
pub struct OptionsData {
#[nom(
Parse = "{ |i| parse_fields::<OptionsTemplate>(i, parser.options_templates.get(&set_id)) }"
)]
pub data_fields: Vec<BTreeMap<usize, (IPFixField, FieldValue)>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Nom)]
#[nom(ExtraArgs(set_length: u16))]
pub struct OptionsTemplate {
pub template_id: u16,
pub field_count: u16,
pub scope_field_count: u16,
#[nom(
PreExec = "let combined_count = scope_field_count as usize +
field_count.checked_sub(scope_field_count).unwrap_or(field_count) as usize;",
Parse = "count(|i| TemplateField::parse(i, true), combined_count)",
PostExec = "let options_remaining = set_length.checked_sub(field_count * 4).unwrap_or(set_length) > 0;"
)]
pub fields: Vec<TemplateField>,
#[nom(Cond = "options_remaining && !i.is_empty()")]
#[serde(skip_serializing)]
padding: Option<u16>,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Nom)]
pub struct Template {
pub template_id: u16,
pub field_count: u16,
#[nom(Parse = "{ |i| parse_template_fields(i, field_count) } ")]
pub fields: Vec<TemplateField>,
}
fn parse_template_fields(i: &[u8], count: u16) -> IResult<&[u8], Vec<TemplateField>> {
let mut result = vec![];
let mut remaining = i;
for _ in 0..count {
let (i, field) = TemplateField::parse(remaining, false)?;
result.push(field);
remaining = i;
}
Ok((remaining, result))
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Nom)]
#[nom(ExtraArgs(options_template: bool))]
pub struct TemplateField {
pub field_type_number: u16,
#[nom(Value(IPFixField::from(field_type_number)))]
pub field_type: IPFixField,
pub field_length: u16,
#[nom(
Cond = "options_template && field_type_number > 32767",
PostExec = "let field_type_number = if options_template {
field_type_number.overflowing_sub(32768).0
} else { field_type_number };",
PostExec = "let field_type = if options_template && enterprise_number.is_some() {
IPFixField::Enterprise
} else { field_type };"
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub enterprise_number: Option<u32>,
}
trait CommonTemplate {
fn get_fields(&self) -> &Vec<TemplateField>;
}
impl CommonTemplate for Template {
fn get_fields(&self) -> &Vec<TemplateField> {
&self.fields
}
}
impl CommonTemplate for OptionsTemplate {
fn get_fields(&self) -> &Vec<TemplateField> {
&self.fields
}
}
fn parse_sets<'a>(
i: &'a [u8],
parser: &mut IPFixParser,
length: u16,
) -> IResult<&'a [u8], Vec<FlowSet>> {
let length = length.checked_sub(16).unwrap_or(length);
let (_, taken) = take(length)(i)?;
let mut sets = vec![];
let mut remaining = taken;
while !remaining.is_empty() {
let (i, set) = FlowSet::parse(remaining, parser)?;
sets.push(set);
remaining = i;
}
Ok((remaining, sets))
}
fn parse_set_body<'a>(
i: &'a [u8],
parser: &mut IPFixParser,
length: u16,
id: u16,
) -> IResult<&'a [u8], FlowSetBody> {
let length = length.checked_sub(4).unwrap_or(length);
let (remaining, taken) = take(length)(i)?;
let (_, set_body) = FlowSetBody::parse(taken, parser, id, length)?;
Ok((remaining, set_body))
}
fn parse_fields<'a, T: CommonTemplate>(
i: &'a [u8],
template: Option<&T>,
) -> IResult<&'a [u8], Vec<BTreeMap<usize, IPFixFieldPair>>> {
fn parse_field<'a>(
i: &'a [u8],
template_field: &TemplateField,
) -> IResult<&'a [u8], FieldValue> {
if template_field.enterprise_number.is_some() {
let (remaining, data_number) = DataNumber::parse(i, 4, false)?;
Ok((remaining, FieldValue::DataNumber(data_number)))
} else {
Ok(DataNumber::from_field_type(
i,
template_field.field_type.into(),
template_field.field_length,
))?
}
}
let template_fields = template
.ok_or(NomErr::Error(NomError::new(i, ErrorKind::Fail)))?
.get_fields();
if template_fields.is_empty() {
return Err(NomErr::Error(NomError::new(i, ErrorKind::Fail)));
};
let mut fields = vec![];
let mut remaining = i;
let total_size = template_fields
.iter()
.map(|m| m.field_length as usize)
.sum::<usize>();
if total_size == 0 {
return Ok((&[], fields));
}
let count: usize = i.len() / total_size;
let mut error = false;
for _ in 0..count {
let mut data_field = BTreeMap::new();
for (c, template_field) in template_fields.iter().enumerate() {
let (i, field_value) = parse_field(remaining, template_field)?;
if i.len() == remaining.len() {
error = true;
break;
}
remaining = i;
data_field.insert(c, (template_field.field_type, field_value));
}
fields.push(data_field);
}
if error {
Err(NomErr::Error(NomError::new(remaining, ErrorKind::Fail)))
} else {
Ok((&[], fields))
}
}
impl IPFix {
pub fn to_be_bytes(&self) -> Vec<u8> {
let mut result = vec![];
result.extend_from_slice(&self.header.version.to_be_bytes());
result.extend_from_slice(&self.header.length.to_be_bytes());
result.extend_from_slice(&(self.header.export_time).to_be_bytes());
result.extend_from_slice(&self.header.sequence_number.to_be_bytes());
result.extend_from_slice(&self.header.observation_domain_id.to_be_bytes());
for flow in &self.flowsets {
result.extend_from_slice(&flow.header.header_id.to_be_bytes());
result.extend_from_slice(&flow.header.length.to_be_bytes());
let mut result_flowset = vec![];
if let Some(template) = &flow.body.templates {
result_flowset.extend_from_slice(&template.template_id.to_be_bytes());
result_flowset.extend_from_slice(&template.field_count.to_be_bytes());
for field in template.fields.iter() {
result_flowset.extend_from_slice(&field.field_type_number.to_be_bytes());
result_flowset.extend_from_slice(&field.field_length.to_be_bytes());
if let Some(enterprise) = field.enterprise_number {
result_flowset.extend_from_slice(&enterprise.to_be_bytes());
}
}
}
if let Some(options_template) = &flow.body.options_templates {
result_flowset.extend_from_slice(&options_template.template_id.to_be_bytes());
result_flowset.extend_from_slice(&options_template.field_count.to_be_bytes());
result_flowset
.extend_from_slice(&options_template.scope_field_count.to_be_bytes());
for field in options_template.fields.iter() {
result_flowset.extend_from_slice(&field.field_type_number.to_be_bytes());
result_flowset.extend_from_slice(&field.field_length.to_be_bytes());
if let Some(enterprise) = field.enterprise_number {
result_flowset.extend_from_slice(&enterprise.to_be_bytes());
}
}
if let Some(padding) = &options_template.padding {
result_flowset.extend_from_slice(&padding.to_be_bytes());
}
}
if let Some(data) = &flow.body.data {
for item in data.data_fields.iter() {
for (_, (_, v)) in item.iter() {
result_flowset.extend_from_slice(&v.to_be_bytes());
}
}
}
if let Some(data) = &flow.body.options_data {
for item in data.data_fields.iter() {
for (_, (_, v)) in item.iter() {
result_flowset.extend_from_slice(&v.to_be_bytes());
}
}
}
result.append(&mut result_flowset);
}
result
}
}