1use crate::error::HuginnNetError;
2use crate::ip_options::IpOptions;
3use crate::observable_signals::ObservableMtu;
4use crate::observable_signals::ObservableTcp;
5use crate::observable_signals::ObservableUptime;
6use crate::tcp::{IpVersion, PayloadSize, Quirk, TcpOption, Ttl, WindowSize};
7use crate::uptime::check_ts_tcp;
8use crate::uptime::{Connection, SynData};
9use crate::window_size::detect_win_multiplicator;
10use crate::{mtu, ttl};
11use pnet::packet::ip::IpNextHeaderProtocols;
12use pnet::packet::{
13 ipv4::{Ipv4Flags, Ipv4Packet},
14 ipv6::Ipv6Packet,
15 tcp::{TcpFlags, TcpOptionNumbers::*, TcpOptionPacket, TcpPacket},
16 Packet, PacketSize,
17};
18use std::convert::TryInto;
19use std::net::IpAddr;
20use ttl_cache::TtlCache;
21
22const IP_TOS_CE: u8 = 0x01;
24const IP_TOS_ECT: u8 = 0x02;
26const IP4_MBZ: u8 = 0b0100;
28
29pub struct ObservableTCPPackage {
31 pub tcp_request: Option<ObservableTcp>,
32 pub tcp_response: Option<ObservableTcp>,
33 pub mtu: Option<ObservableMtu>,
34 pub uptime: Option<ObservableUptime>,
35}
36
37fn from_client(tcp_flags: u8) -> bool {
38 use TcpFlags::*;
39 tcp_flags & SYN != 0 && tcp_flags & ACK == 0
40}
41
42fn from_server(tcp_flags: u8) -> bool {
43 use TcpFlags::*;
44 tcp_flags & SYN != 0 && tcp_flags & ACK != 0
45}
46
47fn is_valid(tcp_flags: u8, tcp_type: u8) -> bool {
48 use TcpFlags::*;
49
50 !(((tcp_flags & SYN) == SYN && (tcp_flags & (FIN | RST)) != 0)
51 || (tcp_flags & (FIN | RST)) == (FIN | RST)
52 || tcp_type == 0)
53}
54
55pub fn process_tcp_ipv4(
56 packet: &Ipv4Packet,
57 cache: &mut TtlCache<Connection, SynData>,
58) -> Result<ObservableTCPPackage, HuginnNetError> {
59 if packet.get_next_level_protocol() != IpNextHeaderProtocols::Tcp {
60 return Err(HuginnNetError::UnsupportedProtocol("IPv4".to_string()));
61 }
62
63 if packet.get_fragment_offset() > 0
64 || (packet.get_flags() & Ipv4Flags::MoreFragments) == Ipv4Flags::MoreFragments
65 {
66 return Err(HuginnNetError::UnexpectedPackage("IPv4".to_string()));
67 }
68
69 let version = IpVersion::V4;
70 let ttl_observed: u8 = packet.get_ttl();
71 let ttl: Ttl = ttl::calculate_ttl(ttl_observed);
72 let olen: u8 = IpOptions::calculate_ipv4_length(packet);
73 let mut quirks = vec![];
74
75 if (packet.get_ecn() & (IP_TOS_CE | IP_TOS_ECT)) != 0 {
76 quirks.push(Quirk::Ecn);
77 }
78
79 if (packet.get_flags() & IP4_MBZ) != 0 {
80 quirks.push(Quirk::MustBeZero);
81 }
82
83 if (packet.get_flags() & Ipv4Flags::DontFragment) != 0 {
84 quirks.push(Quirk::Df);
85
86 if packet.get_identification() != 0 {
87 quirks.push(Quirk::NonZeroID);
88 }
89 } else if packet.get_identification() == 0 {
90 quirks.push(Quirk::ZeroID);
91 }
92
93 let source_ip: IpAddr = IpAddr::V4(packet.get_source());
94 let destination_ip = IpAddr::V4(packet.get_destination());
95
96 let tcp_payload = packet.payload(); let ip_package_header_length: u8 = packet.get_header_length();
99
100 TcpPacket::new(tcp_payload)
101 .ok_or_else(|| HuginnNetError::UnexpectedPackage("TCP packet too short".to_string()))
102 .and_then(|tcp_packet| {
103 visit_tcp(
104 cache,
105 &tcp_packet,
106 version,
107 ttl,
108 ip_package_header_length,
109 olen,
110 quirks,
111 source_ip,
112 destination_ip,
113 )
114 })
115}
116
117pub fn process_tcp_ipv6(
118 packet: &Ipv6Packet,
119 cache: &mut TtlCache<Connection, SynData>,
120) -> Result<ObservableTCPPackage, HuginnNetError> {
121 if packet.get_next_header() != IpNextHeaderProtocols::Tcp {
122 return Err(HuginnNetError::UnsupportedProtocol("IPv6".to_string()));
123 }
124 let version = IpVersion::V6;
125 let ttl_observed: u8 = packet.get_hop_limit();
126 let ttl: Ttl = ttl::calculate_ttl(ttl_observed);
127 let olen: u8 = IpOptions::calculate_ipv6_length(packet);
128 let mut quirks = vec![];
129
130 if packet.get_flow_label() != 0 {
131 quirks.push(Quirk::FlowID);
132 }
133 if (packet.get_traffic_class() & (IP_TOS_CE | IP_TOS_ECT)) != 0 {
134 quirks.push(Quirk::Ecn);
135 }
136
137 let source_ip: IpAddr = IpAddr::V6(packet.get_source());
138 let destination_ip = IpAddr::V6(packet.get_destination());
139
140 let ip_package_header_length: u8 = 40; TcpPacket::new(packet.payload())
143 .ok_or_else(|| HuginnNetError::UnexpectedPackage("TCP packet too short".to_string()))
144 .and_then(|tcp_packet| {
145 visit_tcp(
146 cache,
147 &tcp_packet,
148 version,
149 ttl,
150 ip_package_header_length,
151 olen,
152 quirks,
153 source_ip,
154 destination_ip,
155 )
156 })
157}
158
159#[allow(clippy::too_many_arguments)]
160fn visit_tcp(
161 cache: &mut TtlCache<Connection, SynData>,
162 tcp: &TcpPacket,
163 version: IpVersion,
164 ittl: Ttl,
165 ip_package_header_length: u8,
166 olen: u8,
167 mut quirks: Vec<Quirk>,
168 source_ip: IpAddr,
169 destination_ip: IpAddr,
170) -> Result<ObservableTCPPackage, HuginnNetError> {
171 use TcpFlags::*;
172
173 let flags: u8 = tcp.get_flags();
174 let from_client: bool = from_client(flags);
175 let from_server: bool = from_server(flags);
176
177 if !from_client && !from_server {
178 return Ok(ObservableTCPPackage {
179 tcp_request: None,
180 tcp_response: None,
181 mtu: None,
182 uptime: None,
183 });
184 }
185 let tcp_type: u8 = flags & (SYN | ACK | FIN | RST);
186 if !is_valid(flags, tcp_type) {
187 return Err(HuginnNetError::InvalidTcpFlags(flags));
188 }
189
190 if (flags & (ECE | CWR)) != 0 {
191 quirks.push(Quirk::Ecn);
192 }
193 if tcp.get_sequence() == 0 {
194 quirks.push(Quirk::SeqNumZero);
195 }
196 if flags & ACK == ACK {
197 if tcp.get_acknowledgement() == 0 {
198 quirks.push(Quirk::AckNumZero);
199 }
200 } else if tcp.get_acknowledgement() != 0 && flags & RST == 0 {
201 quirks.push(Quirk::AckNumNonZero);
202 }
203
204 if flags & URG == URG {
205 quirks.push(Quirk::Urg);
206 } else if tcp.get_urgent_ptr() != 0 {
207 quirks.push(Quirk::NonZeroURG);
208 }
209
210 if flags & PSH == PSH {
211 quirks.push(Quirk::Push);
212 }
213
214 let mut buf = tcp.get_options_raw();
215 let mut mss = None;
216 let mut wscale = None;
217 let mut olayout = vec![];
218 let mut uptime: Option<ObservableUptime> = None;
219
220 while let Some(opt) = TcpOptionPacket::new(buf) {
221 buf = &buf[opt.packet_size().min(buf.len())..];
222
223 let data: &[u8] = opt.payload();
224
225 match opt.get_number() {
226 EOL => {
227 olayout.push(TcpOption::Eol(buf.len() as u8));
228
229 if buf.iter().any(|&b| b != 0) {
230 quirks.push(Quirk::TrailinigNonZero);
231 }
232 }
233 NOP => {
234 olayout.push(TcpOption::Nop);
235 }
236 MSS => {
237 olayout.push(TcpOption::Mss);
238 if data.len() >= 2 {
239 let mss_value: u16 = u16::from_be_bytes([data[0], data[1]]);
240 mss = Some(mss_value);
242 }
243
244 }
248 WSCALE => {
249 olayout.push(TcpOption::Ws);
250
251 wscale = Some(data[0]);
252
253 if data[0] > 14 {
254 quirks.push(Quirk::ExcessiveWindowScaling);
255 }
256 }
260 SACK_PERMITTED => {
261 olayout.push(TcpOption::Sok);
262
263 }
267 SACK => {
268 olayout.push(TcpOption::Sack);
269
270 }
275 TIMESTAMPS => {
276 olayout.push(TcpOption::TS);
277
278 if data.len() >= 4 {
279 let ts_val_bytes: [u8; 4] = data[..4].try_into().map_err(|_| {
280 HuginnNetError::Parse(
281 "Failed to convert slice to array for timestamp value".to_string(),
282 )
283 })?;
284 if u32::from_ne_bytes(ts_val_bytes) == 0 {
285 quirks.push(Quirk::OwnTimestampZero);
286 }
287 }
288
289 if data.len() >= 8 && tcp_type == SYN {
290 let ts_peer_bytes: [u8; 4] = data[4..8].try_into().map_err(|_| {
291 HuginnNetError::Parse(
292 "Failed to convert slice to array for peer timestamp value".to_string(),
293 )
294 })?;
295 if u32::from_ne_bytes(ts_peer_bytes) != 0 {
296 quirks.push(Quirk::PeerTimestampNonZero);
297 }
298 }
299
300 if data.len() >= 8 {
301 let ts_val_bytes: [u8; 4] = data[..4].try_into().map_err(|_| {
302 HuginnNetError::Parse(
303 "Failed to convert slice to array for timestamp value".to_string(),
304 )
305 })?;
306 let ts_val: u32 = u32::from_ne_bytes(ts_val_bytes);
307 let connection: Connection = Connection {
308 src_ip: source_ip,
309 src_port: tcp.get_source(),
310 dst_ip: destination_ip,
311 dst_port: tcp.get_destination(),
312 };
313 uptime = check_ts_tcp(cache, &connection, from_client, ts_val);
314 }
315
316 }
320 _ => {
321 olayout.push(TcpOption::Unknown(opt.get_number().0));
322 }
323 }
324 }
325
326 let mtu: Option<ObservableMtu> = match (mss, &version) {
327 (Some(mss_value), IpVersion::V4) => {
328 mtu::extract_from_ipv4(tcp, ip_package_header_length, mss_value)
329 }
330 (Some(mss_value), IpVersion::V6) => {
331 mtu::extract_from_ipv6(tcp, ip_package_header_length, mss_value)
332 }
333 _ => None,
334 };
335
336 let wsize: WindowSize = detect_win_multiplicator(
337 tcp.get_window(),
338 mss.unwrap_or(0),
339 ip_package_header_length as u16,
340 olayout.contains(&TcpOption::TS),
341 &version,
342 );
343
344 let tcp_signature: ObservableTcp = ObservableTcp {
345 version,
346 ittl,
347 olen,
348 mss,
349 wsize,
350 wscale,
351 olayout,
352 quirks,
353 pclass: if tcp.payload().is_empty() {
354 PayloadSize::Zero
355 } else {
356 PayloadSize::NonZero
357 },
358 };
359
360 Ok(ObservableTCPPackage {
361 tcp_request: if from_client {
362 Some(tcp_signature.clone())
363 } else {
364 None
365 },
366 tcp_response: if !from_client {
367 Some(tcp_signature)
368 } else {
369 None
370 },
371 mtu: if from_client { mtu } else { None },
372 uptime: if !from_client { uptime } else { None },
373 })
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379
380 #[test]
381 fn test_from_client() {
382 assert!(from_client(TcpFlags::SYN));
383 assert!(!from_client(TcpFlags::SYN | TcpFlags::ACK));
384 assert!(!from_client(TcpFlags::ACK));
385 }
386
387 #[test]
388 fn test_from_server() {
389 assert!(from_server(TcpFlags::SYN | TcpFlags::ACK));
390 assert!(!from_server(TcpFlags::SYN));
391 assert!(!from_server(TcpFlags::ACK));
392 assert!(!from_server(TcpFlags::RST));
393 }
394
395 #[test]
396 fn test_is_valid() {
397 assert!(is_valid(TcpFlags::SYN, TcpFlags::SYN));
398 assert!(!is_valid(TcpFlags::SYN | TcpFlags::FIN, TcpFlags::SYN));
399 assert!(!is_valid(TcpFlags::SYN | TcpFlags::RST, TcpFlags::SYN));
400 assert!(!is_valid(
401 TcpFlags::FIN | TcpFlags::RST,
402 TcpFlags::FIN | TcpFlags::RST
403 ));
404 assert!(!is_valid(TcpFlags::SYN, 0));
405 }
406}