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}