vpncloud/src/table.rs

161 lines
4.6 KiB
Rust
Raw Normal View History

2020-09-24 17:48:13 +00:00
// VpnCloud - Peer-to-Peer VPN
2021-02-08 09:11:20 +00:00
// Copyright (C) 2015-2021 Dennis Schwerdel
2020-09-24 17:48:13 +00:00
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use fnv::FnvHasher;
use std::{
2021-04-06 10:28:31 +00:00
cmp::min, collections::HashMap, hash::BuildHasherDefault, io, io::Write, marker::PhantomData, net::SocketAddr,
2020-09-24 17:48:13 +00:00
};
use crate::{
types::{Address, Range, RangeList},
2021-04-06 10:28:31 +00:00
util::{addr_nice, Duration, Time, TimeSource},
2020-09-24 17:48:13 +00:00
};
type Hash = BuildHasherDefault<FnvHasher>;
struct CacheValue {
peer: SocketAddr,
2021-04-06 10:28:31 +00:00
timeout: Time,
2020-09-24 17:48:13 +00:00
}
struct ClaimEntry {
peer: SocketAddr,
claim: Range,
2021-04-06 10:28:31 +00:00
timeout: Time,
2020-09-24 17:48:13 +00:00
}
pub struct ClaimTable<TS: TimeSource> {
cache: HashMap<Address, CacheValue, Hash>,
cache_timeout: Duration,
claims: Vec<ClaimEntry>,
claim_timeout: Duration,
2021-04-06 10:28:31 +00:00
_dummy: PhantomData<TS>,
2020-09-24 17:48:13 +00:00
}
impl<TS: TimeSource> ClaimTable<TS> {
pub fn new(cache_timeout: Duration, claim_timeout: Duration) -> Self {
Self { cache: HashMap::default(), cache_timeout, claims: vec![], claim_timeout, _dummy: PhantomData }
}
pub fn cache(&mut self, addr: Address, peer: SocketAddr) {
2021-01-28 22:19:20 +00:00
// HOT PATH
2020-09-24 17:48:13 +00:00
self.cache.insert(addr, CacheValue { peer, timeout: TS::now() + self.cache_timeout as Time });
}
2021-01-28 21:54:29 +00:00
pub fn clear_cache(&mut self) {
self.cache.clear()
}
2020-09-24 17:48:13 +00:00
pub fn set_claims(&mut self, peer: SocketAddr, mut claims: RangeList) {
2021-12-20 07:50:12 +00:00
let mut removed_claim = false;
2020-09-24 17:48:13 +00:00
for entry in &mut self.claims {
if entry.peer == peer {
let pos = claims.iter().position(|r| r == &entry.claim);
if let Some(pos) = pos {
entry.timeout = TS::now() + self.claim_timeout as Time;
claims.swap_remove(pos);
if claims.is_empty() {
2021-04-06 10:28:31 +00:00
break;
2020-09-24 17:48:13 +00:00
}
} else {
2021-12-20 07:50:12 +00:00
entry.timeout = 0;
removed_claim = true;
2020-09-24 17:48:13 +00:00
}
}
}
for claim in claims {
self.claims.push(ClaimEntry { peer, claim, timeout: TS::now() + self.claim_timeout as Time })
}
2021-12-20 07:50:12 +00:00
if removed_claim {
for entry in self.cache.values_mut() {
if entry.peer == peer {
entry.timeout = 0
}
2020-10-28 23:09:40 +00:00
}
}
2020-09-24 17:48:13 +00:00
self.housekeep()
}
pub fn remove_claims(&mut self, peer: SocketAddr) {
for entry in &mut self.claims {
if entry.peer == peer {
entry.timeout = 0
}
}
2020-10-28 23:09:40 +00:00
for entry in self.cache.values_mut() {
if entry.peer == peer {
entry.timeout = 0
}
}
2020-09-24 17:48:13 +00:00
self.housekeep()
}
pub fn lookup(&mut self, addr: Address) -> Option<SocketAddr> {
2021-01-28 22:19:20 +00:00
// HOT PATH
2020-09-24 17:48:13 +00:00
if let Some(entry) = self.cache.get(&addr) {
2021-04-06 10:28:31 +00:00
return Some(entry.peer);
2020-09-24 17:48:13 +00:00
}
2021-01-28 22:19:20 +00:00
// COLD PATH
2020-11-02 19:44:05 +00:00
let mut found = None;
let mut prefix_len = -1;
2020-09-24 17:48:13 +00:00
for entry in &self.claims {
2020-11-02 19:44:05 +00:00
if entry.claim.prefix_len as isize > prefix_len && entry.claim.matches(addr) {
found = Some(entry);
prefix_len = entry.claim.prefix_len as isize;
2020-09-24 17:48:13 +00:00
}
}
2020-11-02 19:44:05 +00:00
if let Some(entry) = found {
2021-04-06 10:28:31 +00:00
self.cache.insert(
addr,
CacheValue { peer: entry.peer, timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) },
);
return Some(entry.peer);
2020-11-02 19:44:05 +00:00
}
2020-09-24 17:48:13 +00:00
None
}
pub fn housekeep(&mut self) {
let now = TS::now();
self.cache.retain(|_, v| v.timeout >= now);
self.claims.retain(|e| e.timeout >= now);
}
pub fn cache_len(&self) -> usize {
self.cache.len()
}
pub fn claim_len(&self) -> usize {
self.claims.len()
}
/// Write out the table
pub fn write_out<W: Write>(&self, out: &mut W) -> Result<(), io::Error> {
let now = TS::now();
writeln!(out, "forwarding_table:")?;
writeln!(out, " claims:")?;
for entry in &self.claims {
writeln!(
out,
" - \"{}\": {{ peer: \"{}\", timeout: {} }}",
entry.claim,
addr_nice(entry.peer),
entry.timeout - now
)?;
}
writeln!(out, " cache:")?;
for (addr, entry) in &self.cache {
writeln!(
out,
" - \"{}\": {{ peer: \"{}\", timeout: {} }}",
addr,
addr_nice(entry.peer),
entry.timeout - now
)?;
}
Ok(())
}
}
2020-10-19 22:19:38 +00:00
2021-04-06 10:28:31 +00:00
// TODO: test