diff --git a/Cargo.toml b/Cargo.toml index eb6b5fc..55bf85d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "netflow_parser" description = "Parser for Netflow Cisco V5, V7, V9, IPFIX" -version = "0.3.6" +version = "0.4.0" edition = "2021" author = "michael.mileusnich@gmail.com" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 295df44..7e240d8 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ See: ## V5: ```rust -use netflow_parser::{NetflowParser, NetflowPacketResult}; +use netflow_parser::{NetflowParser, NetflowPacket}; let v5_packet = [0, 5, 2, 0, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,]; match NetflowParser::default().parse_bytes(&v5_packet).first() { - Some(NetflowPacketResult::V5(v5)) => assert_eq!(v5.header.version, 5), - Some(NetflowPacketResult::Error(e)) => println!("{:?}", e), + Some(NetflowPacket::V5(v5)) => assert_eq!(v5.header.version, 5), + Some(NetflowPacket::Error(e)) => println!("{:?}", e), _ => (), } ``` @@ -40,12 +40,12 @@ println!("{}", json!(NetflowParser::default().parse_bytes(&v5_packet)).to_string ## Filtering for a specific version ```rust -use netflow_parser::{NetflowParser, NetflowPacketResult}; +use netflow_parser::{NetflowParser, NetflowPacket}; let v5_packet = [0, 5, 2, 0, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,]; let parsed = NetflowParser::default().parse_bytes(&v5_packet); -let v5_parsed: Vec = parsed.iter().filter(|p| p.is_v5()).map(|p| p.clone()).collect(); +let v5_parsed: Vec = parsed.into_iter().filter(|p| p.is_v5()).collect(); ``` ## Re-Exporting flows @@ -57,7 +57,7 @@ let packet = [ 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, ]; -if let NetflowPacketResult::V5(v5) = NetflowParser::default() +if let NetflowPacket::V5(v5) = NetflowParser::default() .parse_bytes(&packet) .first() .unwrap() diff --git a/RELEASES.md b/RELEASES.md index c661536..c595acc 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,8 @@ +# 0.4.0 + * NetflowPacketResult now simply NetflowPacket. + * General parser cleanup and removal of uneeded code. + * Small performance optimization in lib parse_bytes. + # 0.3.6 * Added V9 Post NAT fields 225-228. * Added Tokio Async Example diff --git a/src/lib.rs b/src/lib.rs index ce2eb2e..99833c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,12 +11,12 @@ //! ## V5: //! //! ```rust -//! use netflow_parser::{NetflowParser, NetflowPacketResult}; +//! use netflow_parser::{NetflowParser, NetflowPacket}; //! //! let v5_packet = [0, 5, 2, 0, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,]; //! match NetflowParser::default().parse_bytes(&v5_packet).first() { -//! Some(NetflowPacketResult::V5(v5)) => assert_eq!(v5.header.version, 5), -//! Some(NetflowPacketResult::Error(e)) => println!("{:?}", e), +//! Some(NetflowPacket::V5(v5)) => assert_eq!(v5.header.version, 5), +//! Some(NetflowPacket::Error(e)) => println!("{:?}", e), //! _ => (), //! } //! ``` @@ -40,25 +40,25 @@ //! ## Filtering for a specific version //! //! ```rust -//! use netflow_parser::{NetflowParser, NetflowPacketResult}; +//! use netflow_parser::{NetflowParser, NetflowPacket}; //! //! let v5_packet = [0, 5, 2, 0, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,]; //! let parsed = NetflowParser::default().parse_bytes(&v5_packet); //! -//! let v5_parsed: Vec = parsed.iter().filter(|p| p.is_v5()).map(|p| p.clone()).collect(); +//! let v5_parsed: Vec = parsed.into_iter().filter(|p| p.is_v5()).collect(); //! ``` //! //! ## Re-Exporting flows //! Netflow Parser now supports parsed V5, V7, V9, IPFix can be re-exported back into bytes. //! ```rust -//! use netflow_parser::{NetflowParser, NetflowPacketResult}; +//! use netflow_parser::{NetflowParser, NetflowPacket}; //! //! let packet = [ //! 0, 5, 0, 1, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, //! 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, //! 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, //! ]; -//! if let NetflowPacketResult::V5(v5) = NetflowParser::default() +//! if let NetflowPacket::V5(v5) = NetflowParser::default() //! .parse_bytes(&packet) //! .first() //! .unwrap() @@ -97,22 +97,26 @@ //! //! ```cargo run --example netflow_udp_listener_tokio``` -mod parser; pub mod protocol; pub mod static_versions; mod tests; pub mod variable_versions; -use parser::NetflowParseError; use static_versions::{v5::V5, v7::V7}; use variable_versions::ipfix::{IPFix, IPFixParser}; use variable_versions::v9::{V9Parser, V9}; +use crate::static_versions::v5; +use crate::static_versions::v7; +use crate::variable_versions::ipfix; +use crate::variable_versions::v9; + +use nom_derive::{Nom, Parse}; use serde::Serialize; /// Enum of supported Netflow Versions #[derive(Debug, Clone, Serialize)] -pub enum NetflowPacketResult { +pub enum NetflowPacket { /// Version 5 V5(V5), /// Version 7 @@ -125,7 +129,7 @@ pub enum NetflowPacketResult { Error(NetflowPacketError), } -impl NetflowPacketResult { +impl NetflowPacket { pub fn is_v5(&self) -> bool { matches!(self, Self::V5(_v)) } @@ -143,10 +147,10 @@ impl NetflowPacketResult { } } -#[derive(Debug, Clone, Serialize)] -pub struct NetflowPacketError { - pub error: NetflowParseError, - pub remaining: Vec, +#[derive(Nom)] +/// Generic Netflow Header for shared versions +struct GenericNetflowHeader { + version: u16, } #[derive(Default, Debug)] @@ -155,6 +159,38 @@ pub struct NetflowParser { pub ipfix_parser: IPFixParser, } +#[derive(Debug, Clone)] +pub(crate) struct ParsedNetflow { + pub(crate) remaining: Vec, + /// Parsed Netflow Packet + pub(crate) result: NetflowPacket, +} + +impl ParsedNetflow { + fn new(remaining: &[u8], result: NetflowPacket) -> Self { + Self { + remaining: remaining.to_vec(), + result, + } + } +} + +#[derive(Debug, Clone, Serialize)] +pub struct NetflowPacketError { + pub error: NetflowParseError, + pub remaining: Vec, +} + +#[derive(Debug, Clone, Serialize)] +pub enum NetflowParseError { + V5(String), + V7(String), + V9(String), + IPFix(String), + Incomplete(String), + UnknownVersion(Vec), +} + impl NetflowParser { /// Takes a Netflow packet slice and returns a vector of Parsed Netflows. /// If we reach some parse error we return what items be have. @@ -176,21 +212,45 @@ impl NetflowParser { /// ``` /// #[inline] - pub fn parse_bytes(&mut self, packet: &[u8]) -> Vec { + pub fn parse_bytes(&mut self, packet: &[u8]) -> Vec { if packet.is_empty() { return vec![]; } - self.parse(packet) + self.parse_packet_by_version(packet) .map(|parsed_netflow| { - let mut parsed = vec![parsed_netflow.result]; - parsed.append(&mut self.parse_bytes(parsed_netflow.remaining.as_slice())); - parsed + let parsed_result = vec![parsed_netflow.result]; + if !parsed_netflow.remaining.is_empty() { + let parsed_remaining = self.parse_bytes(&parsed_netflow.remaining); + [parsed_result, parsed_remaining].concat() + } else { + parsed_result + } }) .unwrap_or_else(|e| { - vec![NetflowPacketResult::Error(NetflowPacketError { + vec![NetflowPacket::Error(NetflowPacketError { error: e, remaining: packet.to_vec(), })] }) } + + /// Checks the first u16 of the packet to determine the version. Parses the packet based on the version. + /// If the version is unknown it returns an error. If the packet is incomplete it returns an error. + /// If the packet is parsed successfully it returns the parsed Netflow packet and the remaining bytes. + fn parse_packet_by_version<'a>( + &'a mut self, + packet: &'a [u8], + ) -> Result { + let (packet, version) = GenericNetflowHeader::parse(packet) + .map(|(remaining, header)| (remaining, header.version)) + .map_err(|e| NetflowParseError::Incomplete(e.to_string()))?; + + match version { + 5 => v5::parse_netflow_v5(packet), + 7 => v7::parse_netflow_v7(packet), + 9 => v9::parse_netflow_v9(packet, &mut self.v9_parser), + 10 => ipfix::parse_netflow_ipfix(packet, &mut self.ipfix_parser), + _ => Err(NetflowParseError::UnknownVersion(packet.to_vec())), + } + } } diff --git a/src/parser.rs b/src/parser.rs deleted file mode 100644 index eb56f94..0000000 --- a/src/parser.rs +++ /dev/null @@ -1,115 +0,0 @@ -use nom_derive::{Nom, Parse}; - -use crate::static_versions::{v5::V5, v7::V7}; -use crate::variable_versions::ipfix::IPFix; -use crate::variable_versions::v9::V9; -use crate::{NetflowPacketResult, NetflowParser}; - -use serde::Serialize; - -/// Struct is used simply to match how to handle the result of the packet -#[derive(Nom)] -pub struct NetflowHeader { - /// Netflow Version - pub version: u16, -} - -pub enum NetflowHeaderResult<'a> { - V5(&'a [u8]), - V7(&'a [u8]), - V9(&'a [u8]), - IPFix(&'a [u8]), -} - -impl NetflowHeader { - pub fn parse_header(packet: &[u8]) -> Result { - match NetflowHeader::parse_be(packet) { - Ok((i, header)) if header.version == 5 => Ok(NetflowHeaderResult::V5(i)), - Ok((i, header)) if header.version == 7 => Ok(NetflowHeaderResult::V7(i)), - Ok((i, header)) if header.version == 9 => Ok(NetflowHeaderResult::V9(i)), - Ok((i, header)) if header.version == 10 => Ok(NetflowHeaderResult::IPFix(i)), - _ => Err(NetflowParseError::UnknownVersion(packet.to_vec())), - } - } -} - -pub type V5ParsedResult<'a> = (&'a [u8], V5); -pub type V7ParsedResult<'a> = (&'a [u8], V7); -pub type V9ParsedResult<'a> = (&'a [u8], V9); -pub type IPFixParsedResult<'a> = (&'a [u8], IPFix); - -impl<'a> From> for ParsedNetflow { - fn from((remaining, v5_parsed): V5ParsedResult) -> ParsedNetflow { - Self::new(remaining, NetflowPacketResult::V5(v5_parsed)) - } -} - -impl<'a> From> for ParsedNetflow { - fn from((remaining, v7_parsed): V7ParsedResult) -> ParsedNetflow { - Self::new(remaining, NetflowPacketResult::V7(v7_parsed)) - } -} - -impl<'a> From> for ParsedNetflow { - fn from((remaining, v9_parsed): V9ParsedResult) -> ParsedNetflow { - Self::new(remaining, NetflowPacketResult::V9(v9_parsed)) - } -} - -impl<'a> From> for ParsedNetflow { - fn from((remaining, ipfix_parsed): IPFixParsedResult) -> ParsedNetflow { - Self::new(remaining, NetflowPacketResult::IPFix(ipfix_parsed)) - } -} - -#[derive(Debug, Clone)] -pub struct ParsedNetflow { - pub remaining: Vec, - /// Parsed Netflow Packet - pub result: NetflowPacketResult, -} - -impl ParsedNetflow { - fn new(remaining: &[u8], result: NetflowPacketResult) -> Self { - Self { - remaining: remaining.to_vec(), - result, - } - } -} - -#[derive(Debug, Clone, Serialize)] -pub enum NetflowParseError { - V5(String), - V7(String), - V9(String), - IPFix(String), - UnknownVersion(Vec), - Unknown(String), -} - -impl NetflowParser { - /// Parses a Netflow by version packet and returns a Parsed Netflow. - pub fn parse<'a>( - &'a mut self, - packet: &'a [u8], - ) -> Result { - match NetflowHeader::parse_header(packet) { - Ok(NetflowHeaderResult::V5(v5_packet)) => V5::parse(v5_packet) - .map(|r: V5ParsedResult| r.into()) - .map_err(|e| NetflowParseError::V5(e.to_string())), - Ok(NetflowHeaderResult::V7(v7_packet)) => V7::parse(v7_packet) - .map(|r: V7ParsedResult| r.into()) - .map_err(|e| NetflowParseError::V7(e.to_string())), - Ok(NetflowHeaderResult::V9(v9_packet)) => V9::parse(v9_packet, &mut self.v9_parser) - .map(|r: V9ParsedResult| r.into()) - .map_err(|e| NetflowParseError::V9(e.to_string())), - Ok(NetflowHeaderResult::IPFix(ipfix_packet)) => { - IPFix::parse(ipfix_packet, &mut self.ipfix_parser) - .map(|r: IPFixParsedResult| r.into()) - .map_err(|e| NetflowParseError::IPFix(e.to_string())) - } - Err(e) => Err(e), - } - } -} diff --git a/src/snapshots/netflow_parser__tests__base_tests__it_creates_error.snap b/src/snapshots/netflow_parser__tests__base_tests__it_creates_error.snap index f10eff2..d655cf2 100644 --- a/src/snapshots/netflow_parser__tests__base_tests__it_creates_error.snap +++ b/src/snapshots/netflow_parser__tests__base_tests__it_creates_error.snap @@ -5,8 +5,6 @@ expression: "NetflowParser::default().parse_bytes(&packet)" - Error: error: UnknownVersion: - - 12 - - 13 - 14 remaining: - 12 diff --git a/src/snapshots/netflow_parser__tests__base_tests__it_parses_v5_incomplete.snap b/src/snapshots/netflow_parser__tests__base_tests__it_parses_v5_incomplete.snap new file mode 100644 index 0000000..4145771 --- /dev/null +++ b/src/snapshots/netflow_parser__tests__base_tests__it_parses_v5_incomplete.snap @@ -0,0 +1,17 @@ +--- +source: src/tests.rs +expression: "NetflowParser::default().parse_bytes(&packet)" +--- +- Error: + error: + V5: Parsing requires 4 bytes/chars + remaining: + - 0 + - 5 + - 0 + - 0 + - 1 + - 1 + - 1 + - 1 + diff --git a/src/static_versions/v5.rs b/src/static_versions/v5.rs index 9446cf9..8a445e8 100644 --- a/src/static_versions/v5.rs +++ b/src/static_versions/v5.rs @@ -4,6 +4,7 @@ //! - use crate::protocol::ProtocolTypes; +use crate::{NetflowPacket, NetflowParseError, ParsedNetflow}; use nom::number::complete::be_u32; use nom_derive::*; @@ -13,6 +14,12 @@ use Nom; use std::net::Ipv4Addr; use std::time::Duration; +pub(crate) fn parse_netflow_v5(packet: &[u8]) -> Result { + V5::parse(packet) + .map(|(remaining, v5)| ParsedNetflow::new(remaining, NetflowPacket::V5(v5))) + .map_err(|e| NetflowParseError::V5(e.to_string())) +} + #[derive(Nom, Debug, Clone, Serialize)] pub struct V5 { /// V5 Header @@ -99,7 +106,7 @@ pub struct FlowSet { } impl V5 { - /// Convert the V5 struct to a Vec of bytes in big-endian order for exporting + /// Convert the V5 struct to a `Vec` of bytes in big-endian order for exporting pub fn to_be_bytes(&self) -> Vec { let header_version = self.header.version.to_be_bytes(); let header_count = self.header.count.to_be_bytes(); diff --git a/src/static_versions/v7.rs b/src/static_versions/v7.rs index 86e0b82..4ead8ed 100644 --- a/src/static_versions/v7.rs +++ b/src/static_versions/v7.rs @@ -4,6 +4,7 @@ //! - use crate::protocol::ProtocolTypes; +use crate::{NetflowPacket, NetflowParseError, ParsedNetflow}; use nom::number::complete::be_u32; use nom_derive::*; @@ -13,6 +14,12 @@ use Nom; use std::net::Ipv4Addr; use std::time::Duration; +pub(crate) fn parse_netflow_v7(packet: &[u8]) -> Result { + V7::parse(packet) + .map(|(remaining, v7)| ParsedNetflow::new(remaining, NetflowPacket::V7(v7))) + .map_err(|e| NetflowParseError::V7(e.to_string())) +} + #[derive(Debug, Nom, Clone, Serialize)] pub struct V7 { /// V7 Header @@ -98,7 +105,7 @@ pub struct FlowSet { } impl V7 { - /// Convert the V7 struct to a Vec of bytes in big-endian order for exporting + /// Convert the V7 struct to a `Vec` of bytes in big-endian order for exporting pub fn to_be_bytes(&self) -> Vec { let header_version = self.header.version.to_be_bytes(); let header_count = self.header.count.to_be_bytes(); diff --git a/src/tests.rs b/src/tests.rs index f187499..e575d49 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -7,7 +7,7 @@ mod base_tests { use crate::variable_versions::v9::{ Template as V9Template, TemplateField as V9TemplateField, }; - use crate::{NetflowPacketResult, NetflowParser}; + use crate::{NetflowPacket, NetflowParser}; use hex; use insta::assert_yaml_snapshot; @@ -48,6 +48,12 @@ mod base_tests { assert_yaml_snapshot!(NetflowParser::default().parse_bytes(&packet)); } + #[test] + fn it_parses_v5_incomplete() { + let packet = [0, 5, 0, 0, 1, 1, 1, 1]; + assert_yaml_snapshot!(NetflowParser::default().parse_bytes(&packet)); + } + #[test] fn it_parses_v5_and_re_exports() { let packet = [ @@ -55,7 +61,7 @@ mod base_tests { 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, ]; - if let NetflowPacketResult::V5(v5) = NetflowParser::default() + if let NetflowPacket::V5(v5) = NetflowParser::default() .parse_bytes(&packet) .first() .unwrap() @@ -88,7 +94,7 @@ mod base_tests { 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, ]; - if let NetflowPacketResult::V7(v7) = NetflowParser::default() + if let NetflowPacket::V7(v7) = NetflowParser::default() .parse_bytes(&packet) .first() .unwrap() @@ -122,7 +128,7 @@ mod base_tests { 0, 9, 0, 2, 0, 0, 9, 9, 0, 1, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 16, 1, 2, 0, 2, 0, 1, 0, 4, 0, 8, 0, 4, 1, 2, 0, 12, 9, 2, 3, 4, 9, 9, 9, 8, ]; - if let NetflowPacketResult::V9(v9) = NetflowParser::default() + if let NetflowPacket::V9(v9) = NetflowParser::default() .parse_bytes(&packet) .first() .unwrap() @@ -244,7 +250,7 @@ mod base_tests { 4, 0, 12, 0, 4, 0, 2, 0, 4, 1, 0, 0, 28, 1, 2, 3, 4, 1, 2, 3, 3, 1, 2, 3, 2, 0, 2, 0, 2, 0, 1, 2, 3, 4, 5, 6, 7, ]; - if let NetflowPacketResult::IPFix(ipfix) = NetflowParser::default() + if let NetflowPacket::IPFix(ipfix) = NetflowParser::default() .parse_bytes(&packet) .first() .unwrap() diff --git a/src/variable_versions/ipfix.rs b/src/variable_versions/ipfix.rs index cd27f4e..27764a0 100644 --- a/src/variable_versions/ipfix.rs +++ b/src/variable_versions/ipfix.rs @@ -8,6 +8,7 @@ use super::common::*; use crate::variable_versions::ipfix_lookup::*; +use crate::{NetflowPacket, NetflowParseError, ParsedNetflow}; use nom::bytes::complete::take; use nom::error::{Error as NomError, ErrorKind}; @@ -29,6 +30,15 @@ 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 { + 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, @@ -339,7 +349,7 @@ fn parse_fields<'a, T: CommonTemplate>( } impl IPFix { - /// Convert the IPFix to a Vec of bytes in big-endian order for exporting + /// Convert the IPFix to a `Vec` of bytes in big-endian order for exporting pub fn to_be_bytes(&self) -> Vec { let mut result = vec![]; diff --git a/src/variable_versions/v9.rs b/src/variable_versions/v9.rs index 3f366f8..b66a86c 100644 --- a/src/variable_versions/v9.rs +++ b/src/variable_versions/v9.rs @@ -6,6 +6,7 @@ use super::common::*; use crate::variable_versions::v9_lookup::*; +use crate::{NetflowPacket, NetflowParseError, ParsedNetflow}; use nom::bytes::complete::take; use nom::error::{Error as NomError, ErrorKind}; @@ -25,6 +26,15 @@ const FLOW_SET_MIN_RANGE: u16 = 255; type TemplateId = u16; type V9FieldPair = (V9Field, FieldValue); +pub(crate) fn parse_netflow_v9( + packet: &[u8], + parser: &mut V9Parser, +) -> Result { + V9::parse(packet, parser) + .map(|(remaining, v9)| ParsedNetflow::new(remaining, NetflowPacket::V9(v9))) + .map_err(|e| NetflowParseError::V9(e.to_string())) +} + #[derive(Default, Debug)] pub struct V9Parser { pub templates: HashMap, @@ -433,7 +443,7 @@ fn parse_scope_data_fields<'a>( } impl V9 { - /// Convert the V9 struct to a Vec of bytes in big-endian order for exporting + /// Convert the V9 struct to a `Vec` of bytes in big-endian order for exporting pub fn to_be_bytes(&self) -> Vec { let mut result = vec![];