Skip to main content

stackforge_core/flow/
udp_state.rs

1use std::time::Duration;
2
3use super::config::FlowConfig;
4use super::state::ConversationStatus;
5
6/// UDP pseudo-conversation state.
7///
8/// UDP is connectionless, so conversations are tracked purely via
9/// timeout heuristics. A conversation is considered active as long as
10/// packets continue arriving within the configured timeout window.
11#[derive(Debug, Clone)]
12pub struct UdpFlowState {
13    pub status: ConversationStatus,
14}
15
16impl UdpFlowState {
17    pub fn new() -> Self {
18        Self {
19            status: ConversationStatus::Active,
20        }
21    }
22
23    /// Update state when a new packet is received.
24    pub fn process_packet(&mut self) {
25        self.status = ConversationStatus::Active;
26    }
27
28    /// Check whether this flow has timed out.
29    pub fn check_timeout(&self, last_seen: Duration, now: Duration, config: &FlowConfig) -> bool {
30        now.saturating_sub(last_seen) > config.udp_timeout
31    }
32}
33
34impl Default for UdpFlowState {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_udp_state_new() {
46        let state = UdpFlowState::new();
47        assert_eq!(state.status, ConversationStatus::Active);
48    }
49
50    #[test]
51    fn test_udp_timeout_check() {
52        let config = FlowConfig::default(); // 120s UDP timeout
53        let state = UdpFlowState::new();
54
55        // Not timed out
56        assert!(!state.check_timeout(Duration::from_secs(100), Duration::from_secs(200), &config));
57
58        // Timed out
59        assert!(state.check_timeout(Duration::from_secs(100), Duration::from_secs(300), &config));
60    }
61
62    #[test]
63    fn test_udp_process_packet() {
64        let mut state = UdpFlowState::new();
65        state.status = ConversationStatus::TimedOut;
66        state.process_packet();
67        assert_eq!(state.status, ConversationStatus::Active);
68    }
69}