pb_mapper_protocol/secure/
limiter.rs1use pb_mapper_auth::KeyId;
13
14#[derive(Clone, Copy, Debug)]
15pub struct FailureLogDecision {
16 pub emit: bool,
17 pub suppressed: u64,
18}
19
20pub(super) struct FailureLogEntry {
21 window_started_at: u64,
22 emitted: u8,
23 suppressed: u64,
24}
25
26#[derive(Default)]
27pub(super) struct FailureLogLimiter {
28 pub(super) entries:
29 std::collections::HashMap<(std::net::IpAddr, KeyId, String), FailureLogEntry>,
30 pub(super) overflow: Option<FailureLogEntry>,
31}
32
33impl FailureLogLimiter {
34 pub(super) fn record(
35 &mut self,
36 peer_ip: std::net::IpAddr,
37 key_id: KeyId,
38 reason: &str,
39 now: u64,
40 ) -> FailureLogDecision {
41 let key = (peer_ip, key_id, reason.to_string());
42 if !self.entries.contains_key(&key) && self.entries.len() >= 4096 {
43 self.entries
44 .retain(|_, entry| now.saturating_sub(entry.window_started_at) < 120);
45 if self.entries.len() >= 4096 {
46 let entry = self.overflow.get_or_insert(FailureLogEntry {
47 window_started_at: now,
48 emitted: 0,
49 suppressed: 0,
50 });
51 return record_failure_entry(entry, now);
52 }
53 }
54 let entry = self.entries.entry(key).or_insert(FailureLogEntry {
55 window_started_at: now,
56 emitted: 0,
57 suppressed: 0,
58 });
59 record_failure_entry(entry, now)
60 }
61}
62
63fn record_failure_entry(entry: &mut FailureLogEntry, now: u64) -> FailureLogDecision {
64 if now.saturating_sub(entry.window_started_at) >= 60 {
65 let suppressed = entry.suppressed;
66 *entry = FailureLogEntry {
67 window_started_at: now,
68 emitted: 1,
69 suppressed: 0,
70 };
71 return FailureLogDecision {
72 emit: true,
73 suppressed,
74 };
75 }
76 if entry.emitted < 5 {
77 entry.emitted += 1;
78 FailureLogDecision {
79 emit: true,
80 suppressed: 0,
81 }
82 } else {
83 entry.suppressed = entry.suppressed.saturating_add(1);
84 FailureLogDecision {
85 emit: false,
86 suppressed: 0,
87 }
88 }
89}