iris_core/conntrack/conn/
conn_state.rs1use serde::{Deserialize, Serialize};
3use std::{cmp::Ordering, str::FromStr};
4use strum::IntoEnumIterator;
5use strum_macros::EnumIter;
6
7use crate::{
8 conntrack::Layer,
9 protocols::{stream::SessionProto, Session},
10};
11
12#[doc(hidden)]
13#[derive(PartialEq, Eq, Debug, Copy, Clone, Ord, PartialOrd, Hash, EnumIter)]
16pub enum LayerState {
17 Discovery,
20 Headers,
23 Payload,
25 None,
29}
30
31#[derive(
35 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumIter, Serialize, Deserialize,
36)]
37#[repr(u8)]
38pub enum StateTransition {
39 L4FirstPacket = 0,
41 L4EndHshk,
46
47 InL4Conn,
51 InL4Stream,
54
55 L7OnDisc,
57 L7EndHdrs,
59 L4Terminated,
61
62 Packet,
78}
79
80impl std::fmt::Display for StateTransition {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 Self::InL4Conn => write!(f, "InL4Conn"),
84 Self::InL4Stream => write!(f, "InL4Stream"),
85 _ => write!(f, "{:?}", self),
86 }
87 }
88}
89
90impl StateTransition {
92 #[doc(hidden)]
93 pub fn as_usize(&self) -> usize {
94 *self as u8 as usize
95 }
96
97 #[doc(hidden)]
98 pub fn from_usize(i: usize) -> Self {
99 for level in StateTransition::iter() {
100 if level.as_usize() == i {
101 return level;
102 }
103 }
104 panic!("Cannot build StateTransition from {}", i);
105 }
106
107 #[doc(hidden)]
108 pub fn name(&self) -> &str {
109 match self {
110 StateTransition::L4FirstPacket => "L4FirstPacket",
111 StateTransition::L4EndHshk => "L4EndHshk",
112 StateTransition::InL4Conn => "InL4Conn",
113 StateTransition::InL4Stream => "InL4Stream",
114 StateTransition::L4Terminated => "L4Terminated",
115 StateTransition::L7OnDisc => "L7OnDisc",
116 StateTransition::L7EndHdrs => "L7EndHdrs",
117 StateTransition::Packet => "L4Pdu",
118 }
119 }
120
121 #[doc(hidden)]
122 pub fn in_transport(&self) -> bool {
123 self.name().contains("L4")
124 }
125
126 #[doc(hidden)]
127 pub fn is_streaming(&self) -> bool {
128 self.name().contains("In")
129 }
130
131 #[doc(hidden)]
132 pub fn layer_idx(&self) -> Option<usize> {
133 if self.name().contains("L7") {
134 return Some(0);
135 }
136 None
137 }
138
139 #[doc(hidden)]
142 pub fn compare(&self, other: &StateTransition) -> StateTxOrd {
143 if self == other {
144 return StateTxOrd::Equal;
145 }
146 if matches!(self, StateTransition::Packet) || matches!(other, StateTransition::Packet) {
148 return StateTxOrd::Any;
149 }
150
151 if matches!(self, StateTransition::L4Terminated)
153 || matches!(other, StateTransition::L4Terminated)
154 {
155 return StateTxOrd::from_ord(self.cmp(other));
156 }
157 if matches!(self, StateTransition::L4FirstPacket)
159 || matches!(other, StateTransition::L4FirstPacket)
160 {
161 return StateTxOrd::from_ord(self.cmp(other));
162 }
163
164 if (matches!(self, StateTransition::L4EndHshk)
167 || matches!(other, StateTransition::L4EndHshk))
168 && (matches!(self, StateTransition::L7EndHdrs)
169 || matches!(other, StateTransition::L7EndHdrs))
170 {
171 return StateTxOrd::from_ord(self.cmp(other));
172 }
173
174 if self.name().contains("L4") && !other.name().contains("L4")
176 || self.name().contains("L7") && !other.name().contains("L7")
177 {
178 return StateTxOrd::Unknown;
179 }
180
181 if matches!(self, StateTransition::L4EndHshk) {
183 return StateTxOrd::Unknown;
184 }
185
186 if matches!(
188 self,
189 StateTransition::InL4Conn | StateTransition::InL4Stream
190 ) || matches!(
191 self,
192 StateTransition::InL4Stream | StateTransition::InL4Conn
193 ) {
194 return StateTxOrd::Unknown;
195 }
196
197 StateTxOrd::from_ord(self.cmp(other))
199 }
200}
201
202#[doc(hidden)]
206#[derive(Debug, Eq, PartialEq)]
207pub enum StateTxOrd {
208 Unknown,
210 Any,
212 Greater,
214 Less,
216 Equal,
218}
219
220impl StateTxOrd {
221 pub(crate) fn from_ord(ordering: Ordering) -> StateTxOrd {
222 match ordering {
223 Ordering::Greater => StateTxOrd::Greater,
224 Ordering::Less => StateTxOrd::Less,
225 Ordering::Equal => StateTxOrd::Equal,
226 }
227 }
228}
229
230pub(crate) const NUM_STATE_TRANSITIONS: usize = 7;
236
237#[derive(Debug)]
240pub enum StateTxData<'a> {
241 L7OnDisc(SessionProto),
242 L7EndHdrs(&'a Session),
243 Null,
244}
245
246impl<'a> StateTxData<'a> {
247 #[doc(hidden)]
248 pub fn from_tx(state: &StateTransition, layer: &'a Layer) -> Self {
249 match layer {
250 Layer::L7(layer) => match state {
251 StateTransition::L7OnDisc => Self::L7OnDisc(layer.get_protocol()),
252 StateTransition::L7EndHdrs => {
253 Self::L7EndHdrs(layer.sessions.last().expect("L7EndHdrs without session"))
254 }
255 _ => Self::Null,
256 },
257 }
258 }
259
260 #[allow(dead_code)]
262 pub(crate) fn as_usize(&self) -> usize {
263 match self {
264 StateTxData::L7OnDisc(_) => StateTransition::L7OnDisc.as_usize(),
265 StateTxData::L7EndHdrs(_) => StateTransition::L7EndHdrs.as_usize(),
266 StateTxData::Null => panic!("Invalid StateTxData"),
267 }
268 }
269}
270
271#[doc(hidden)]
272impl FromStr for StateTransition {
273 type Err = String;
274 fn from_str(s: &str) -> Result<Self, String> {
275 match s {
276 "L4FirstPacket" => Ok(StateTransition::L4FirstPacket),
277 "L4EndHshk" => Ok(StateTransition::L4EndHshk),
278 "InL4Conn" => Ok(StateTransition::InL4Conn),
279 "InL4Stream" => Ok(StateTransition::InL4Stream),
280 "InL4Conn(reassemble)" => Ok(StateTransition::InL4Stream),
282 "L4Terminated" => Ok(StateTransition::L4Terminated),
283 "L7OnDisc" => Ok(StateTransition::L7OnDisc),
284 "L7EndHdrs" => Ok(StateTransition::L7EndHdrs),
285 "Packet" => Ok(StateTransition::Packet),
286 _ => Err(format!("Invalid StateTransition: {}", s)),
287 }
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn test_data_level_raw() {
297 assert_eq!(
298 StateTransition::Packet.as_usize(),
299 NUM_STATE_TRANSITIONS,
300 "{} != {}",
301 StateTransition::Packet.as_usize(),
302 NUM_STATE_TRANSITIONS
303 );
304 }
305}