1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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<NetflowHeaderResult, NetflowParseError> {
        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<V5ParsedResult<'a>> for ParsedNetflow {
    fn from((remaining, v5_parsed): V5ParsedResult) -> ParsedNetflow {
        Self::new(remaining, NetflowPacketResult::V5(v5_parsed))
    }
}

impl<'a> From<V7ParsedResult<'a>> for ParsedNetflow {
    fn from((remaining, v7_parsed): V7ParsedResult) -> ParsedNetflow {
        Self::new(remaining, NetflowPacketResult::V7(v7_parsed))
    }
}

impl<'a> From<V9ParsedResult<'a>> for ParsedNetflow {
    fn from((remaining, v9_parsed): V9ParsedResult) -> ParsedNetflow {
        Self::new(remaining, NetflowPacketResult::V9(v9_parsed))
    }
}

impl<'a> From<IPFixParsedResult<'a>> 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<u8>,
    /// 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<u8>),
    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<ParsedNetflow, NetflowParseError> {
        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),
        }
    }
}