use std::sync::Arc;
pub const MAX_CONNECTIONS: usize = 1024;
pub const HANDSHAKE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
pub const ROUTE_CHECK: std::time::Duration = std::time::Duration::from_millis(250);
pub const MAX_CONNECTIONS_PER_IP: usize = 64;
pub const FIRST_FRAME_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
pub const IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(900);
pub(crate) struct IpSlot {
ip: std::net::IpAddr,
map: Arc<std::sync::Mutex<std::collections::HashMap<std::net::IpAddr, usize>>>,
}
impl IpSlot {
fn key(ip: std::net::IpAddr) -> std::net::IpAddr {
match ip.to_canonical() {
std::net::IpAddr::V6(v6) => {
let mut o = v6.octets();
o[8..].fill(0);
std::net::IpAddr::V6(o.into())
}
v4 => v4,
}
}
pub(crate) fn acquire(
map: &Arc<std::sync::Mutex<std::collections::HashMap<std::net::IpAddr, usize>>>,
ip: std::net::IpAddr,
max: usize,
) -> Option<Self> {
let ip = Self::key(ip);
let mut m = map.lock().unwrap();
let n = m.entry(ip).or_insert(0);
if *n >= max {
return None;
}
*n += 1;
Some(Self { ip, map: Arc::clone(map) })
}
}
impl Drop for IpSlot {
fn drop(&mut self) {
let mut m = self.map.lock().unwrap();
if let Some(n) = m.get_mut(&self.ip) {
*n -= 1;
if *n == 0 {
m.remove(&self.ip);
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Limits {
pub first_frame: std::time::Duration,
pub idle: std::time::Duration,
pub per_ip: usize,
}
impl Default for Limits {
fn default() -> Self {
Self {
first_frame: FIRST_FRAME_TIMEOUT,
idle: IDLE_TIMEOUT,
per_ip: MAX_CONNECTIONS_PER_IP,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::IpAddr;
#[test]
fn mapped_and_prefix_addresses_share_a_slot() {
let map = Arc::new(std::sync::Mutex::new(Default::default()));
let v4: IpAddr = "192.0.2.1".parse().unwrap();
let mapped: IpAddr = "::ffff:192.0.2.1".parse().unwrap();
let _a = IpSlot::acquire(&map, v4, 1).unwrap();
assert!(IpSlot::acquire(&map, mapped, 1).is_none());
let p1: IpAddr = "2001:db8:1:2::1".parse().unwrap();
let p2: IpAddr = "2001:db8:1:2:ffff::9".parse().unwrap();
let other: IpAddr = "2001:db8:1:3::1".parse().unwrap();
let _b = IpSlot::acquire(&map, p1, 1).unwrap();
assert!(IpSlot::acquire(&map, p2, 1).is_none(), "same /64");
assert!(IpSlot::acquire(&map, other, 1).is_some(), "different /64");
}
}