Skip to main content

iris_core/conntrack/conn/tcp_conn/
mod.rs

1pub mod reassembly;
2
3use self::reassembly::TcpFlow;
4use crate::conntrack::conn::conn_info::ConnInfo;
5use crate::conntrack::pdu::{L4Context, L4Pdu};
6use crate::protocols::packet::tcp::{ACK, SYN};
7use crate::protocols::packet::tcp::{FIN, RST};
8use crate::protocols::stream::ParserRegistry;
9use crate::subscription::{Subscription, Trackable};
10
11pub(crate) struct TcpConn {
12    pub(crate) ctos: TcpFlow,
13    pub(crate) stoc: TcpFlow,
14    handshake_done: bool,
15}
16
17impl TcpConn {
18    pub(crate) fn new_on_syn(ctxt: L4Context, max_ooo: usize) -> Self {
19        let flags = ctxt.flags;
20        let next_seq = ctxt.seq_no.wrapping_add(1 + ctxt.length as u32);
21        let ack = ctxt.ack_no;
22        TcpConn {
23            ctos: TcpFlow::new(max_ooo, next_seq, flags, ack),
24            stoc: TcpFlow::default(max_ooo),
25            handshake_done: false,
26        }
27    }
28
29    /// Insert TCP segment ordered into ctos or stoc flow
30    #[inline]
31    pub(crate) fn reassemble<T: Trackable>(
32        &mut self,
33        segment: L4Pdu,
34        info: &mut ConnInfo<T>,
35        subscription: &Subscription<T::Subscribed>,
36        registry: &ParserRegistry,
37    ) {
38        if segment.dir {
39            self.ctos
40                .insert_segment::<T>(segment, info, subscription, registry);
41        } else {
42            self.stoc
43                .insert_segment::<T>(segment, info, subscription, registry);
44        }
45        if self.handshake_done() {
46            self.handshake_done = true;
47            info.handshake_done(subscription);
48        }
49    }
50
51    /// Returns true if the PDU currently being processed is the last
52    /// packet in the TCP handshake.
53    /// Note: we define this pretty loosely -- we just require that both sides have sent SYNs and ACKs,
54    /// but we don't check the sequence numbers of those SYNs/ACKs.
55    #[inline]
56    pub(crate) fn handshake_done(&self) -> bool {
57        !self.handshake_done
58            && self.ctos.consumed_flags & (SYN | ACK) != 0
59            && self.stoc.consumed_flags & (SYN | ACK) != 0
60    }
61
62    #[inline]
63    pub(crate) fn flow_len(&self, dir: bool) -> usize {
64        if dir {
65            self.ctos.observed
66        } else {
67            self.stoc.observed
68        }
69    }
70
71    #[inline]
72    pub(crate) fn total_len(&self) -> usize {
73        self.ctos.observed + self.stoc.observed
74    }
75
76    /// Returns `true` if the connection should be terminated
77    #[inline]
78    pub(crate) fn is_terminated(&self) -> bool {
79        // Both sides have sent, reassembled, and acknowledged FIN, or RST has been sent
80        (self.ctos.consumed_flags & self.stoc.consumed_flags & FIN != 0
81            && self.ctos.last_ack == self.stoc.next_seq
82            && self.stoc.last_ack == self.ctos.next_seq)
83            || (self.ctos.consumed_flags & RST | self.stoc.consumed_flags & RST) != 0
84    }
85
86    /// Returns the correct inactivity timeout
87    /// (reassembly timeout if there are out-of-order segments, default otherwise)
88    #[inline]
89    pub(crate) fn inactivity_timeout(
90        &self,
91        default_inactivity_timeout: usize,
92        reassembly_timeout: usize,
93    ) -> usize {
94        match self.ctos.ooo_buf.is_empty() && self.stoc.ooo_buf.is_empty() {
95            true => default_inactivity_timeout,
96            false => reassembly_timeout,
97        }
98    }
99
100    /// Updates connection termination flags
101    // Useful if desired to track TCP connections without reassembly
102    #[inline]
103    pub(super) fn update_flags(&mut self, flags: u8, dir: bool) {
104        if dir {
105            self.ctos.consumed_flags |= flags;
106        } else {
107            self.stoc.consumed_flags |= flags;
108        }
109    }
110}