fips_core/upper/
icmp_rate_limit.rs1use std::collections::HashMap;
7use std::net::Ipv6Addr;
8use std::time::{Duration, Instant};
9
10pub struct IcmpRateLimiter {
15 last_sent: HashMap<Ipv6Addr, Instant>,
17 min_interval: Duration,
19 max_age: Duration,
21}
22
23impl IcmpRateLimiter {
24 pub fn new() -> Self {
28 Self {
29 last_sent: HashMap::new(),
30 min_interval: Duration::from_millis(100),
31 max_age: Duration::from_secs(10),
32 }
33 }
34
35 pub fn with_interval(min_interval: Duration) -> Self {
37 Self {
38 last_sent: HashMap::new(),
39 min_interval,
40 max_age: Duration::from_secs(10),
41 }
42 }
43
44 pub fn should_send(&mut self, src_addr: Ipv6Addr) -> bool {
51 let now = Instant::now();
52
53 if let Some(&last) = self.last_sent.get(&src_addr)
55 && now.duration_since(last) < self.min_interval
56 {
57 return false; }
59
60 self.last_sent.insert(src_addr, now);
62
63 self.cleanup(now);
65
66 true
67 }
68
69 fn cleanup(&mut self, now: Instant) {
71 self.last_sent
72 .retain(|_, &mut last| now.duration_since(last) < self.max_age);
73 }
74
75 #[cfg(test)]
77 pub fn len(&self) -> usize {
78 self.last_sent.len()
79 }
80
81 #[cfg(test)]
83 pub fn is_empty(&self) -> bool {
84 self.last_sent.is_empty()
85 }
86}
87
88impl Default for IcmpRateLimiter {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use std::thread;
98
99 #[test]
100 fn test_first_send_allowed() {
101 let mut limiter = IcmpRateLimiter::new();
102 let addr: Ipv6Addr = "fd00::1".parse().unwrap();
103
104 assert!(limiter.should_send(addr));
105 }
106
107 #[test]
108 fn test_rapid_sends_rate_limited() {
109 let mut limiter = IcmpRateLimiter::new();
110 let addr: Ipv6Addr = "fd00::1".parse().unwrap();
111
112 assert!(limiter.should_send(addr));
114
115 assert!(!limiter.should_send(addr));
117 assert!(!limiter.should_send(addr));
118 }
119
120 #[test]
121 fn test_different_sources_independent() {
122 let mut limiter = IcmpRateLimiter::new();
123 let addr1: Ipv6Addr = "fd00::1".parse().unwrap();
124 let addr2: Ipv6Addr = "fd00::2".parse().unwrap();
125
126 assert!(limiter.should_send(addr1));
128 assert!(limiter.should_send(addr2));
129
130 assert!(!limiter.should_send(addr1));
132 assert!(!limiter.should_send(addr2));
133 }
134
135 #[test]
136 fn test_send_allowed_after_interval() {
137 let mut limiter = IcmpRateLimiter::with_interval(Duration::from_millis(50));
138 let addr: Ipv6Addr = "fd00::1".parse().unwrap();
139
140 assert!(limiter.should_send(addr));
142
143 thread::sleep(Duration::from_millis(60));
145
146 assert!(limiter.should_send(addr));
148 }
149
150 #[test]
151 fn test_cleanup_removes_old_entries() {
152 let mut limiter = IcmpRateLimiter::new();
153 let addr1: Ipv6Addr = "fd00::1".parse().unwrap();
154 let addr2: Ipv6Addr = "fd00::2".parse().unwrap();
155
156 assert!(limiter.should_send(addr1));
158 assert!(limiter.should_send(addr2));
159 assert_eq!(limiter.len(), 2);
160
161 let future = Instant::now() + Duration::from_secs(11);
163 limiter.cleanup(future);
164
165 assert_eq!(limiter.len(), 0);
167 }
168
169 #[test]
170 fn test_cleanup_preserves_recent_entries() {
171 let mut limiter = IcmpRateLimiter::new();
172 let addr: Ipv6Addr = "fd00::1".parse().unwrap();
173
174 assert!(limiter.should_send(addr));
175 assert_eq!(limiter.len(), 1);
176
177 limiter.cleanup(Instant::now());
179 assert_eq!(limiter.len(), 1);
180 }
181}