renetcode/
replay_protection.rs

1const NETCODE_REPLAY_BUFFER_SIZE: usize = 256;
2const EMPTY: u64 = u64::MAX;
3
4#[derive(Debug, Clone)]
5pub struct ReplayProtection {
6    most_recent_sequence: u64,
7    received_packet: [u64; NETCODE_REPLAY_BUFFER_SIZE],
8}
9
10impl Default for ReplayProtection {
11    fn default() -> Self {
12        Self::new()
13    }
14}
15
16impl ReplayProtection {
17    pub fn new() -> Self {
18        Self {
19            most_recent_sequence: 0,
20            received_packet: [EMPTY; NETCODE_REPLAY_BUFFER_SIZE],
21        }
22    }
23
24    pub fn already_received(&self, sequence: u64) -> bool {
25        if sequence + NETCODE_REPLAY_BUFFER_SIZE as u64 <= self.most_recent_sequence {
26            return true;
27        }
28
29        let index = sequence as usize % NETCODE_REPLAY_BUFFER_SIZE;
30        if self.received_packet[index] == EMPTY {
31            return false;
32        }
33
34        if self.received_packet[index] >= sequence {
35            return true;
36        }
37
38        false
39    }
40
41    pub fn advance_sequence(&mut self, sequence: u64) {
42        if sequence > self.most_recent_sequence {
43            self.most_recent_sequence = sequence;
44        }
45
46        let index = sequence as usize % NETCODE_REPLAY_BUFFER_SIZE;
47        self.received_packet[index] = sequence;
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn replay_protection() {
57        let mut replay_protection = ReplayProtection::new();
58        assert_eq!(replay_protection.most_recent_sequence, 0);
59
60        // New packets aren't already received
61        let max_sequence = (NETCODE_REPLAY_BUFFER_SIZE * 4) as u64;
62        for i in 0..max_sequence {
63            assert!(!replay_protection.already_received(i));
64            replay_protection.advance_sequence(i);
65        }
66
67        // Old packes outside the buffer should be considered already received
68        assert!(replay_protection.already_received(0));
69
70        // Packets received a second time should be already received
71        for i in max_sequence - 10..max_sequence {
72            assert!(replay_protection.already_received(i));
73        }
74
75        // Jumping to a higher sequence should be considered not already received
76        assert!(!replay_protection.already_received(max_sequence + NETCODE_REPLAY_BUFFER_SIZE as u64));
77
78        // Old packets should be considered received
79        for i in 0..max_sequence {
80            assert!(replay_protection.already_received(i));
81        }
82    }
83}