nprint_rs/
lib.rs

1//! nPrint is a standard data representation for network traffic, designed for direct use with machine learning algorithms, eliminating the need for feature engineering in various traffic analysis tasks. Developing a Rust implementation of nPrint will simplify the creation of network systems that leverage real-world ML deployments, rather than just training and deploying models offline.
2pub mod protocols;
3use crate::protocols::ipv4::Ipv4Header;
4use crate::protocols::packet::PacketHeader;
5use crate::protocols::tcp::TcpHeader;
6use crate::protocols::udp::UdpHeader;
7
8use pnet::packet::ethernet::{EtherTypes, EthernetPacket};
9use pnet::packet::ip::IpNextHeaderProtocols;
10use pnet::packet::ipv4::Ipv4Packet;
11use pnet::packet::vlan::VlanPacket;
12use pnet::packet::Packet;
13
14/// The `Nprint` structure stores a collection of parsed packet headers,
15/// associated with a single network flow (e.g., a connection or tuple).
16///
17/// It maintains the list of protocols used for parsing and tracks the number of packets processed.
18#[derive(Debug)]
19pub struct Nprint {
20    /// Vector that contains all the parsed headers for each packet.
21    data: Vec<Headers>,
22    /// Ordered list of Protocol selected for this Nprint.
23    protocols: Vec<ProtocolType>,
24    /// Number of packets processed.
25    nb_pkt: usize,
26}
27
28/// Internal structure handling the extracted information of ONE single packet.
29#[derive(Debug)]
30pub(crate) struct Headers {
31    /// Vector that contains ordered values extracted informations
32    pub data: Vec<Box<dyn PacketHeader>>,
33}
34
35/// Enum that contains the current implemented type extractable
36#[derive(Debug, PartialEq, Eq)]
37pub enum ProtocolType {
38    Ipv4,
39    Tcp,
40    Udp,
41}
42
43impl Nprint {
44    /// Creates a new `Nprint` based the first packet of the connection and the vector of protocols.
45    ///
46    /// # Arguments
47    ///
48    /// * `packet` - A byte slice representing the raw packet data.
49    /// * `protocols` - A vector of `ProtocolType` specifying the protocol stack to parse.
50    ///
51    /// # Returns
52    ///
53    /// A new `Nprint` instance containing the parsed headers of the packet.
54    /// # Example
55    ///
56    /// ```
57    /// use nprint_rs::ProtocolType;
58    /// use nprint_rs::Nprint;
59    ///
60    /// let packet = vec![
61    ///      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x08, 0x00, 0x45, 0x00,
62    ///      0x00, 0x3c, 0xf5, 0x1b, 0x40, 0x00, 0x40, 0x06, 0x1b, 0x24, 0xc0, 0xa8, 0x2b, 0x25,
63    ///      0xc6, 0x26, 0x78, 0x88, 0x97, 0xa4, 0x01, 0xbb, 0x96, 0x2e, 0x5e, 0x0b, 0x00, 0x00,
64    ///      0x00, 0x00, 0xa0, 0x02, 0x72, 0x10, 0x25, 0xd4, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4,
65    ///      0x04, 0x02, 0x08, 0x0a, 0xe3, 0xe2, 0x14, 0x23, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
66    ///      0x03, 0x07];
67    ///
68    /// let nprint = Nprint::new(&packet, vec![ProtocolType::Ipv4, ProtocolType::Tcp,ProtocolType::Udp]);
69    /// ```    
70    pub fn new(packet: &[u8], protocols: Vec<ProtocolType>) -> Nprint {
71        Nprint {
72            data: vec![Headers::new(packet, &protocols)],
73            protocols,
74            nb_pkt: 1,
75        }
76    }
77
78    /// Return all the nprint values in a vector of f32.
79    ///
80    /// This is useful for exporting structured packet data for ML models or analytics.
81    ///
82    /// # Returns
83    ///
84    /// A `Vec<f32>` containing all protocol data from each parsed packet in order.
85    pub fn print(&self) -> Vec<f32> {
86        let mut output = vec![];
87        for header in &self.data {
88            for proto in &header.data {
89                output.extend((*proto).get_data());
90            }
91        }
92        output
93    }
94
95    /// Adds a new packet to the `Nprint` structure, parsing it using the existing protocols.
96    ///
97    /// # Arguments
98    ///
99    /// * `packet` - A byte slice representing the new raw packet.
100    pub fn add(&mut self, packet: &[u8]) {
101        self.data.push(Headers::new(packet, &self.protocols));
102        self.nb_pkt += 1;
103    }
104
105    /// Returns the number of packets.
106    ///
107    /// # Returns
108    ///
109    /// A `usize` representing the number of packets within the structure.
110    pub fn count(&self) -> usize {
111        self.nb_pkt
112    }
113
114    /// Return the name list of all fields of all the protocols present in this Nprint
115    ///
116    /// # Returns
117    ///
118    /// A list of header names that are prefixed by the protocol and suffixed with an index (e.g., `tcp_sprt_0`, `tcp_sprt_1`).
119    pub fn get_headers(&self) -> Vec<String> {
120        let mut output = vec![];
121        for proto in &self.protocols {
122            match proto {
123                ProtocolType::Ipv4 => {
124                    output.extend(Ipv4Header::get_headers());
125                }
126                ProtocolType::Tcp => {
127                    output.extend(TcpHeader::get_headers());
128                }
129                ProtocolType::Udp => {
130                    output.extend(UdpHeader::get_headers());
131                }
132            }
133        }
134        output
135    }
136
137    /// Remove sensitive data from the captured header
138    pub fn anonymize(&mut self) {
139        for packet in self.data.iter_mut() {
140            for header in packet.data.iter_mut() {
141                header.anonymize();
142            }
143        }
144    }
145}
146
147impl Headers {
148    /// Creates a new `Headers` instance by parsing the given packet data
149    /// according to the specified list of protocols.
150    ///
151    /// # Arguments
152    ///
153    /// * `packet` - A byte slice representing the raw packet.
154    /// * `protocols` - A slice of `ProtocolType` enums specifying the protocol to parsed.
155    ///
156    /// # Returns
157    ///
158    /// A `Headers` struct containing the parsed protocol headers as specified.
159    ///
160    pub fn new(packet: &[u8], protocols: &[ProtocolType]) -> Headers {
161        let mut data: Vec<Box<dyn PacketHeader>> = Vec::with_capacity(protocols.len());
162        let mut ipv4 = None;
163        let mut tcp = None;
164        let mut udp = None;
165
166        if let Some(ethernet) = EthernetPacket::new(packet) {
167            let mut ethertype = ethernet.get_ethertype();
168            let mut payload = ethernet.payload().to_vec();
169
170            // Pop VLAN's Header
171            if ethertype == EtherTypes::Vlan {
172                if let Some(vlan_packet) = VlanPacket::new(&payload) {
173                    ethertype = vlan_packet.get_ethertype();
174                    payload = vlan_packet.payload().to_vec();
175                }
176            }
177
178            if ethertype == EtherTypes::Ipv4 {
179                if let Some(ipv4_packet) = Ipv4Packet::new(&payload) {
180                    ipv4 = Some(Ipv4Header::new(&payload));
181
182                    match ipv4_packet.get_next_level_protocol() {
183                        IpNextHeaderProtocols::Tcp => {
184                            tcp = Some(TcpHeader::new(ipv4_packet.payload()));
185                        }
186                        IpNextHeaderProtocols::Udp => {
187                            udp = Some(UdpHeader::new(ipv4_packet.payload()));
188                        }
189                        _ => {}
190                    }
191                }
192            }
193        } else {
194            eprintln!("Not an EthernetPacket packet, returning default...");
195        }
196
197        for proto in protocols {
198            match proto {
199                ProtocolType::Ipv4 => {
200                    data.push(Box::new(ipv4.clone().unwrap_or_else(Ipv4Header::default)));
201                }
202                ProtocolType::Tcp => {
203                    data.push(Box::new(tcp.clone().unwrap_or_else(TcpHeader::default)));
204                }
205                ProtocolType::Udp => {
206                    data.push(Box::new(udp.clone().unwrap_or_else(UdpHeader::default)));
207                }
208            }
209        }
210        Headers { data }
211    }
212}