add connection limits
This commit is contained in:
John Smith 2022-05-04 20:40:10 -04:00
parent 6ad1f60a61
commit 3b2f4d184f
15 changed files with 433 additions and 60 deletions

View File

@ -0,0 +1,214 @@
use crate::xx::*;
use crate::*;
use alloc::collections::btree_map::Entry;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressFilterError {
CountExceeded,
RateExceeded,
}
impl fmt::Display for AddressFilterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
Self::CountExceeded => "Count exceeded",
Self::RateExceeded => "Rate exceeded",
}
)
}
}
impl std::error::Error for AddressFilterError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AddressNotInTableError {}
impl fmt::Display for AddressNotInTableError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Address not in table")
}
}
impl std::error::Error for AddressNotInTableError {}
#[derive(Debug)]
pub struct ConnectionLimits {
max_connections_per_ip4: usize,
max_connections_per_ip6_prefix: usize,
max_connections_per_ip6_prefix_size: usize,
max_connection_frequency_per_min: usize,
conn_count_by_ip4: BTreeMap<Ipv4Addr, usize>,
conn_count_by_ip6_prefix: BTreeMap<Ipv6Addr, usize>,
conn_timestamps_by_ip4: BTreeMap<Ipv4Addr, Vec<u64>>,
conn_timestamps_by_ip6_prefix: BTreeMap<Ipv6Addr, Vec<u64>>,
}
impl ConnectionLimits {
pub fn new(config: VeilidConfig) -> Self {
let c = config.get();
Self {
max_connections_per_ip4: c.network.max_connections_per_ip4 as usize,
max_connections_per_ip6_prefix: c.network.max_connections_per_ip6_prefix as usize,
max_connections_per_ip6_prefix_size: c.network.max_connections_per_ip6_prefix_size
as usize,
max_connection_frequency_per_min: c.network.max_connection_frequency_per_min as usize,
conn_count_by_ip4: BTreeMap::new(),
conn_count_by_ip6_prefix: BTreeMap::new(),
conn_timestamps_by_ip4: BTreeMap::new(),
conn_timestamps_by_ip6_prefix: BTreeMap::new(),
}
}
// Converts an ip to a ip block by applying a netmask
// to the host part of the ip address
// ipv4 addresses are treated as single hosts
// ipv6 addresses are treated as prefix allocated blocks
fn ip_to_ipblock(&self, addr: IpAddr) -> IpAddr {
match addr {
IpAddr::V4(_) => addr,
IpAddr::V6(v6) => {
let mut hostlen = 128usize.saturating_sub(self.max_connections_per_ip6_prefix_size);
let mut out = v6.octets();
for i in (0..16).rev() {
if hostlen >= 8 {
out[i] = 0xFF;
hostlen -= 8;
} else {
out[i] |= !(0xFFu8 << hostlen);
break;
}
}
IpAddr::V6(Ipv6Addr::from(out))
}
}
}
fn purge_old_timestamps(&mut self, cur_ts: u64) {
// v4
{
let mut dead_keys = Vec::<Ipv4Addr>::new();
for (key, value) in &mut self.conn_timestamps_by_ip4 {
value.retain(|v| {
// keep timestamps that are less than a minute away
cur_ts.saturating_sub(*v) < 60_000_000u64
});
if value.is_empty() {
dead_keys.push(*key);
}
}
for key in dead_keys {
self.conn_timestamps_by_ip4.remove(&key);
}
}
// v6
{
let mut dead_keys = Vec::<Ipv6Addr>::new();
for (key, value) in &mut self.conn_timestamps_by_ip6_prefix {
value.retain(|v| {
// keep timestamps that are less than a minute away
cur_ts.saturating_sub(*v) < 60_000_000u64
});
if value.is_empty() {
dead_keys.push(*key);
}
}
for key in dead_keys {
self.conn_timestamps_by_ip6_prefix.remove(&key);
}
}
}
pub fn add(&mut self, addr: IpAddr) -> Result<(), AddressFilterError> {
let ipblock = self.ip_to_ipblock(addr);
let ts = intf::get_timestamp();
self.purge_old_timestamps(ts);
match ipblock {
IpAddr::V4(v4) => {
// See if we have too many connections from this ip block
let cnt = &mut *self.conn_count_by_ip4.entry(v4).or_default();
assert!(*cnt <= self.max_connections_per_ip4);
if *cnt == self.max_connections_per_ip4 {
return Err(AddressFilterError::CountExceeded);
}
// See if this ip block has connected too frequently
let tstamps = &mut self.conn_timestamps_by_ip4.entry(v4).or_default();
tstamps.retain(|v| {
// keep timestamps that are less than a minute away
ts.saturating_sub(*v) < 60_000_000u64
});
assert!(tstamps.len() <= self.max_connection_frequency_per_min);
if tstamps.len() == self.max_connection_frequency_per_min {
return Err(AddressFilterError::RateExceeded);
}
// If it's okay, add the counts and timestamps
*cnt += 1;
tstamps.push(ts);
}
IpAddr::V6(v6) => {
// See if we have too many connections from this ip block
let cnt = &mut *self.conn_count_by_ip6_prefix.entry(v6).or_default();
assert!(*cnt <= self.max_connections_per_ip6_prefix);
if *cnt == self.max_connections_per_ip6_prefix {
return Err(AddressFilterError::CountExceeded);
}
// See if this ip block has connected too frequently
let tstamps = &mut self.conn_timestamps_by_ip6_prefix.entry(v6).or_default();
assert!(tstamps.len() <= self.max_connection_frequency_per_min);
if tstamps.len() == self.max_connection_frequency_per_min {
return Err(AddressFilterError::RateExceeded);
}
// If it's okay, add the counts and timestamps
*cnt += 1;
tstamps.push(ts);
}
}
Ok(())
}
pub fn remove(&mut self, addr: IpAddr) -> Result<(), AddressNotInTableError> {
let ipblock = self.ip_to_ipblock(addr);
let ts = intf::get_timestamp();
self.purge_old_timestamps(ts);
match ipblock {
IpAddr::V4(v4) => {
match self.conn_count_by_ip4.entry(v4) {
Entry::Vacant(_) => {
return Err(AddressNotInTableError {});
}
Entry::Occupied(mut o) => {
let cnt = o.get_mut();
assert!(*cnt > 0);
if *cnt == 0 {
self.conn_count_by_ip4.remove(&v4);
} else {
*cnt -= 1;
}
}
};
}
IpAddr::V6(v6) => {
match self.conn_count_by_ip6_prefix.entry(v6) {
Entry::Vacant(_) => {
return Err(AddressNotInTableError {});
}
Entry::Occupied(mut o) => {
let cnt = o.get_mut();
assert!(*cnt > 0);
if *cnt == 0 {
self.conn_count_by_ip6_prefix.remove(&v6);
} else {
*cnt -= 1;
}
}
};
}
}
Ok(())
}
}

View File

@ -44,17 +44,18 @@ pub struct ConnectionManager {
} }
impl ConnectionManager { impl ConnectionManager {
fn new_inner() -> ConnectionManagerInner { fn new_inner(config: VeilidConfig) -> ConnectionManagerInner {
ConnectionManagerInner { ConnectionManagerInner {
connection_table: ConnectionTable::new(), connection_table: ConnectionTable::new(config),
connection_processor_jh: None, connection_processor_jh: None,
connection_add_channel_tx: None, connection_add_channel_tx: None,
} }
} }
fn new_arc(network_manager: NetworkManager) -> ConnectionManagerArc { fn new_arc(network_manager: NetworkManager) -> ConnectionManagerArc {
let config = network_manager.config();
ConnectionManagerArc { ConnectionManagerArc {
network_manager, network_manager,
inner: AsyncMutex::new(Self::new_inner()), inner: AsyncMutex::new(Self::new_inner(config)),
} }
} }
pub fn new(network_manager: NetworkManager) -> Self { pub fn new(network_manager: NetworkManager) -> Self {
@ -78,16 +79,15 @@ impl ConnectionManager {
} }
pub async fn shutdown(&self) { pub async fn shutdown(&self) {
*self.arc.inner.lock().await = Self::new_inner(); *self.arc.inner.lock().await = Self::new_inner(self.arc.network_manager.config());
} }
// Returns a network connection if one already is established // Returns a network connection if one already is established
pub async fn get_connection( pub async fn get_connection(
&self, &self,
descriptor: ConnectionDescriptor, descriptor: ConnectionDescriptor,
) -> Option<NetworkConnection> { ) -> Option<NetworkConnection> {
let inner = self.arc.inner.lock().await; let mut inner = self.arc.inner.lock().await;
inner.connection_table.get_connection(descriptor) inner.connection_table.get_connection(descriptor)
} }

View File

@ -1,73 +1,120 @@
use crate::connection_limits::*;
use crate::network_connection::*; use crate::network_connection::*;
use crate::xx::*; use crate::xx::*;
use crate::*; use crate::*;
use alloc::collections::btree_map::Entry; use alloc::collections::btree_map::Entry;
use hashlink::LruCache;
#[derive(Debug)] #[derive(Debug)]
pub struct ConnectionTable { pub struct ConnectionTable {
conn_by_descriptor: BTreeMap<ConnectionDescriptor, NetworkConnection>, max_connections: Vec<usize>,
conn_by_descriptor: Vec<LruCache<ConnectionDescriptor, NetworkConnection>>,
conns_by_remote: BTreeMap<PeerAddress, Vec<NetworkConnection>>, conns_by_remote: BTreeMap<PeerAddress, Vec<NetworkConnection>>,
address_filter: ConnectionLimits,
}
fn protocol_to_index(protocol: ProtocolType) -> usize {
match protocol {
ProtocolType::TCP => 0,
ProtocolType::WS => 1,
ProtocolType::WSS => 2,
ProtocolType::UDP => panic!("not a connection-oriented protocol"),
}
} }
impl ConnectionTable { impl ConnectionTable {
pub fn new() -> Self { pub fn new(config: VeilidConfig) -> Self {
let max_connections = {
let c = config.get();
vec![
c.network.protocol.tcp.max_connections as usize,
c.network.protocol.ws.max_connections as usize,
c.network.protocol.wss.max_connections as usize,
]
};
Self { Self {
conn_by_descriptor: BTreeMap::new(), max_connections,
conn_by_descriptor: vec![
LruCache::new_unbounded(),
LruCache::new_unbounded(),
LruCache::new_unbounded(),
],
conns_by_remote: BTreeMap::new(), conns_by_remote: BTreeMap::new(),
address_filter: ConnectionLimits::new(config),
} }
} }
pub fn add_connection(&mut self, conn: NetworkConnection) -> Result<(), String> { pub fn add_connection(&mut self, conn: NetworkConnection) -> Result<(), String> {
let descriptor = conn.connection_descriptor(); let descriptor = conn.connection_descriptor();
assert_ne!( let ip_addr = descriptor.remote.socket_address.to_ip_addr();
descriptor.protocol_type(),
ProtocolType::UDP, let index = protocol_to_index(descriptor.protocol_type());
"Only connection oriented protocols go in the table!" if self.conn_by_descriptor[index].contains_key(&descriptor) {
);
if self.conn_by_descriptor.contains_key(&descriptor) {
return Err(format!( return Err(format!(
"Connection already added to table: {:?}", "Connection already added to table: {:?}",
descriptor descriptor
)); ));
} }
let res = self.conn_by_descriptor.insert(descriptor, conn.clone());
// Filter by ip for connection limits
self.address_filter.add(ip_addr).map_err(map_to_string)?;
// Add the connection to the table
let res = self.conn_by_descriptor[index].insert(descriptor, conn.clone());
assert!(res.is_none()); assert!(res.is_none());
// if we have reached the maximum number of connections per protocol type
// then drop the least recently used connection
if self.conn_by_descriptor[index].len() > self.max_connections[index] {
if let Some((lruk, _)) = self.conn_by_descriptor[index].remove_lru() {
self.remove_connection_records(lruk);
}
}
// add connection records
let conns = self.conns_by_remote.entry(descriptor.remote).or_default(); let conns = self.conns_by_remote.entry(descriptor.remote).or_default();
//warn!("add_connection: {:?}", conn); //warn!("add_connection: {:?}", conn);
conns.push(conn); conns.push(conn);
Ok(()) Ok(())
} }
pub fn get_connection(&self, descriptor: ConnectionDescriptor) -> Option<NetworkConnection> { pub fn get_connection(
let out = self.conn_by_descriptor.get(&descriptor).cloned(); &mut self,
descriptor: ConnectionDescriptor,
) -> Option<NetworkConnection> {
let index = protocol_to_index(descriptor.protocol_type());
let out = self.conn_by_descriptor[index].get(&descriptor).cloned();
//warn!("get_connection: {:?} -> {:?}", descriptor, out); //warn!("get_connection: {:?} -> {:?}", descriptor, out);
out out
} }
pub fn get_last_connection_by_remote(&self, remote: PeerAddress) -> Option<NetworkConnection> {
pub fn get_last_connection_by_remote(
&mut self,
remote: PeerAddress,
) -> Option<NetworkConnection> {
let out = self let out = self
.conns_by_remote .conns_by_remote
.get(&remote) .get(&remote)
.map(|v| v[(v.len() - 1)].clone()); .map(|v| v[(v.len() - 1)].clone());
//warn!("get_last_connection_by_remote: {:?} -> {:?}", remote, out); //warn!("get_last_connection_by_remote: {:?} -> {:?}", remote, out);
if let Some(connection) = &out {
// lru bump
let index = protocol_to_index(connection.connection_descriptor().protocol_type());
let _ = self.conn_by_descriptor[index].get(&connection.connection_descriptor());
}
out out
} }
pub fn connection_count(&self) -> usize { pub fn connection_count(&self) -> usize {
self.conn_by_descriptor.len() self.conn_by_descriptor.iter().fold(0, |b, c| b + c.len())
} }
pub fn remove_connection( fn remove_connection_records(&mut self, descriptor: ConnectionDescriptor) {
&mut self, let ip_addr = descriptor.remote.socket_address.to_ip_addr();
descriptor: ConnectionDescriptor,
) -> Result<NetworkConnection, String> {
//warn!("remove_connection: {:?}", descriptor);
let out = self
.conn_by_descriptor
.remove(&descriptor)
.ok_or_else(|| format!("Connection not in table: {:?}", descriptor))?;
// conns_by_remote
match self.conns_by_remote.entry(descriptor.remote) { match self.conns_by_remote.entry(descriptor.remote) {
Entry::Vacant(_) => { Entry::Vacant(_) => {
panic!("inconsistency in connection table") panic!("inconsistency in connection table")
@ -88,6 +135,22 @@ impl ConnectionTable {
} }
} }
} }
self.address_filter
.remove(ip_addr)
.expect("Inconsistency in connection table");
}
pub fn remove_connection(
&mut self,
descriptor: ConnectionDescriptor,
) -> Result<NetworkConnection, String> {
//warn!("remove_connection: {:?}", descriptor);
let index = protocol_to_index(descriptor.protocol_type());
let out = self.conn_by_descriptor[index]
.remove(&descriptor)
.ok_or_else(|| format!("Connection not in table: {:?}", descriptor))?;
self.remove_connection_records(descriptor);
Ok(out) Ok(out)
} }

View File

@ -469,7 +469,10 @@ impl Network {
.await?; .await?;
} }
self.inner.lock().network_class = context.inner.lock().network_class; let network_class = context.inner.lock().network_class;
self.inner.lock().network_class = network_class;
log_net!(debug "network class set to {:?}", network_class);
Ok(()) Ok(())
} }

View File

@ -43,6 +43,10 @@ impl Network {
} }
} }
fn network_manager(&self) -> NetworkManager {
self.inner.lock().network_manager.clone()
}
fn connection_manager(&self) -> ConnectionManager { fn connection_manager(&self) -> ConnectionManager {
self.inner.lock().network_manager.connection_manager() self.inner.lock().network_manager.connection_manager()
} }
@ -54,6 +58,8 @@ impl Network {
dial_info: DialInfo, dial_info: DialInfo,
data: Vec<u8>, data: Vec<u8>,
) -> Result<(), String> { ) -> Result<(), String> {
let data_len = data.len();
let res = match dial_info.protocol_type() { let res = match dial_info.protocol_type() {
ProtocolType::UDP => { ProtocolType::UDP => {
return Err("no support for UDP protocol".to_owned()).map_err(logthru_net!(error)) return Err("no support for UDP protocol".to_owned()).map_err(logthru_net!(error))
@ -62,7 +68,7 @@ impl Network {
return Err("no support for TCP protocol".to_owned()).map_err(logthru_net!(error)) return Err("no support for TCP protocol".to_owned()).map_err(logthru_net!(error))
} }
ProtocolType::WS | ProtocolType::WSS => { ProtocolType::WS | ProtocolType::WSS => {
WebsocketProtocolHandler::send_unbound_message(dial_info, data) WebsocketProtocolHandler::send_unbound_message(dial_info.clone(), data)
.await .await
.map_err(logthru_net!()) .map_err(logthru_net!())
} }
@ -80,6 +86,7 @@ impl Network {
descriptor: ConnectionDescriptor, descriptor: ConnectionDescriptor,
data: Vec<u8>, data: Vec<u8>,
) -> Result<Option<Vec<u8>>, String> { ) -> Result<Option<Vec<u8>>, String> {
let data_len = data.len();
match descriptor.protocol_type() { match descriptor.protocol_type() {
ProtocolType::UDP => { ProtocolType::UDP => {
return Err("no support for udp protocol".to_owned()).map_err(logthru_net!(error)) return Err("no support for udp protocol".to_owned()).map_err(logthru_net!(error))
@ -115,6 +122,7 @@ impl Network {
dial_info: DialInfo, dial_info: DialInfo,
data: Vec<u8>, data: Vec<u8>,
) -> Result<(), String> { ) -> Result<(), String> {
let data_len = data.len();
if dial_info.protocol_type() == ProtocolType::UDP { if dial_info.protocol_type() == ProtocolType::UDP {
return Err("no support for UDP protocol".to_owned()).map_err(logthru_net!(error)) return Err("no support for UDP protocol".to_owned()).map_err(logthru_net!(error))
} }
@ -125,7 +133,7 @@ impl Network {
// Handle connection-oriented protocols // Handle connection-oriented protocols
let conn = self let conn = self
.connection_manager() .connection_manager()
.get_or_create_connection(None, dial_info) .get_or_create_connection(None, dial_info.clone())
.await?; .await?;
let res = conn.send(data).await.map_err(logthru_net!(error)); let res = conn.send(data).await.map_err(logthru_net!(error));
@ -143,15 +151,17 @@ impl Network {
// get protocol config // get protocol config
self.inner.lock().protocol_config = Some({ self.inner.lock().protocol_config = Some({
let c = self.config.get(); let c = self.config.get();
ProtocolConfig { let inbound = ProtocolSet::new();
udp_enabled: false, //c.network.protocol.udp.enabled && c.capabilities.protocol_udp, let mut outbound = ProtocolSet::new();
tcp_connect: false, //c.network.protocol.tcp.connect && c.capabilities.protocol_connect_tcp,
tcp_listen: false, //c.network.protocol.tcp.listen && c.capabilities.protocol_accept_tcp, if c.network.protocol.ws.connect && c.capabilities.protocol_connect_ws {
ws_connect: c.network.protocol.ws.connect && c.capabilities.protocol_connect_ws, outbound.insert(ProtocolType::WS);
ws_listen: c.network.protocol.ws.listen && c.capabilities.protocol_accept_ws,
wss_connect: c.network.protocol.wss.connect && c.capabilities.protocol_connect_wss,
wss_listen: c.network.protocol.wss.listen && c.capabilities.protocol_accept_wss,
} }
if c.network.protocol.wss.connect && c.capabilities.protocol_connect_wss {
outbound.insert(ProtocolType::WSS);
}
ProtocolConfig { inbound, outbound }
}); });
self.inner.lock().network_started = true; self.inner.lock().network_started = true;
@ -174,7 +184,8 @@ impl Network {
let routing_table = network_manager.routing_table(); let routing_table = network_manager.routing_table();
// Drop all dial info // Drop all dial info
routing_table.clear_dial_info_details(); routing_table.clear_dial_info_details(RoutingDomain::PublicInternet);
routing_table.clear_dial_info_details(RoutingDomain::LocalNetwork);
// Cancels all async background tasks by dropping join handles // Cancels all async background tasks by dropping join handles
*self.inner.lock() = Self::new_inner(network_manager); *self.inner.lock() = Self::new_inner(network_manager);
@ -203,6 +214,12 @@ impl Network {
None None
}; };
} }
pub fn reset_network_class(&self) {
//let mut inner = self.inner.lock();
//inner.network_class = None;
}
pub fn get_protocol_config(&self) -> Option<ProtocolConfig> { pub fn get_protocol_config(&self) -> Option<ProtocolConfig> {
self.inner.lock().protocol_config.clone() self.inner.lock().protocol_config.clone()
} }

View File

@ -7,6 +7,7 @@ extern crate alloc;
mod api_logger; mod api_logger;
mod attachment_manager; mod attachment_manager;
mod callback_state_machine; mod callback_state_machine;
mod connection_limits;
mod connection_manager; mod connection_manager;
mod connection_table; mod connection_table;
mod core_context; mod core_context;

View File

@ -59,7 +59,6 @@ impl DummyNetworkConnection {
pub struct NetworkConnectionStats { pub struct NetworkConnectionStats {
last_message_sent_time: Option<u64>, last_message_sent_time: Option<u64>,
last_message_recv_time: Option<u64>, last_message_recv_time: Option<u64>,
_established_time: u64,
} }
#[derive(Debug)] #[derive(Debug)]
@ -71,6 +70,7 @@ struct NetworkConnectionInner {
struct NetworkConnectionArc { struct NetworkConnectionArc {
descriptor: ConnectionDescriptor, descriptor: ConnectionDescriptor,
protocol_connection: ProtocolNetworkConnection, protocol_connection: ProtocolNetworkConnection,
established_time: u64,
inner: Mutex<NetworkConnectionInner>, inner: Mutex<NetworkConnectionInner>,
} }
@ -92,7 +92,6 @@ impl NetworkConnection {
stats: NetworkConnectionStats { stats: NetworkConnectionStats {
last_message_sent_time: None, last_message_sent_time: None,
last_message_recv_time: None, last_message_recv_time: None,
_established_time: intf::get_timestamp(),
}, },
} }
} }
@ -103,6 +102,7 @@ impl NetworkConnection {
NetworkConnectionArc { NetworkConnectionArc {
descriptor, descriptor,
protocol_connection, protocol_connection,
established_time: intf::get_timestamp(),
inner: Mutex::new(Self::new_inner()), inner: Mutex::new(Self::new_inner()),
} }
} }
@ -161,4 +161,8 @@ impl NetworkConnection {
let inner = self.arc.inner.lock(); let inner = self.arc.inner.lock();
inner.stats.clone() inner.stats.clone()
} }
pub fn established_time(&self) -> u64 {
self.arc.established_time
}
} }

View File

@ -1,10 +1,13 @@
use super::test_veilid_config::*;
use crate::connection_table::*; use crate::connection_table::*;
use crate::network_connection::*; use crate::network_connection::*;
use crate::xx::*; use crate::xx::*;
use crate::*; use crate::*;
pub async fn test_add_get_remove() { pub async fn test_add_get_remove() {
let mut table = ConnectionTable::new(); let config = get_config();
let mut table = ConnectionTable::new(config);
let a1 = ConnectionDescriptor::new_no_local(PeerAddress::new( let a1 = ConnectionDescriptor::new_no_local(PeerAddress::new(
SocketAddress::new(Address::IPV4(Ipv4Addr::new(127, 0, 0, 1)), 8080), SocketAddress::new(Address::IPV4(Ipv4Addr::new(127, 0, 0, 1)), 8080),

View File

@ -185,9 +185,12 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"protected_store.always_use_insecure_storage" => Ok(Box::new(false)), "protected_store.always_use_insecure_storage" => Ok(Box::new(false)),
"protected_store.insecure_fallback_directory" => Ok(Box::new(get_protected_store_path())), "protected_store.insecure_fallback_directory" => Ok(Box::new(get_protected_store_path())),
"protected_store.delete" => Ok(Box::new(false)), "protected_store.delete" => Ok(Box::new(false)),
"network.max_connections" => Ok(Box::new(16u32)),
"network.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)), "network.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)),
"network.connection_inactivity_timeout_ms" => Ok(Box::new(60_000u32)), "network.connection_inactivity_timeout_ms" => Ok(Box::new(60_000u32)),
"network.max_connections_per_ip4" => Ok(Box::new(8u32)),
"network.max_connections_per_ip6_prefix" => Ok(Box::new(8u32)),
"network.max_connections_per_ip6_prefix_size" => Ok(Box::new(56u32)),
"network.max_connection_frequency_per_min" => Ok(Box::new(8u32)),
"network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)), "network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)),
"network.node_id" => Ok(Box::new(dht::key::DHTKey::default())), "network.node_id" => Ok(Box::new(dht::key::DHTKey::default())),
"network.node_id_secret" => Ok(Box::new(dht::key::DHTKeySecret::default())), "network.node_id_secret" => Ok(Box::new(dht::key::DHTKeySecret::default())),
@ -264,6 +267,18 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
} }
} }
pub fn get_config() -> VeilidConfig {
let mut vc = VeilidConfig::new();
match vc.setup(Arc::new(config_callback)) {
Ok(()) => (),
Err(e) => {
error!("Error: {}", e);
unreachable!();
}
};
vc
}
pub async fn test_config() { pub async fn test_config() {
let mut vc = VeilidConfig::new(); let mut vc = VeilidConfig::new();
match vc.setup(Arc::new(config_callback)) { match vc.setup(Arc::new(config_callback)) {
@ -296,9 +311,12 @@ pub async fn test_config() {
get_protected_store_path() get_protected_store_path()
); );
assert_eq!(inner.protected_store.delete, false); assert_eq!(inner.protected_store.delete, false);
assert_eq!(inner.network.max_connections, 16);
assert_eq!(inner.network.connection_initial_timeout_ms, 2_000u32); assert_eq!(inner.network.connection_initial_timeout_ms, 2_000u32);
assert_eq!(inner.network.connection_inactivity_timeout_ms, 60_000u32); assert_eq!(inner.network.connection_inactivity_timeout_ms, 60_000u32);
assert_eq!(inner.network.max_connections_per_ip4, 8u32);
assert_eq!(inner.network.max_connections_per_ip6_prefix, 8u32);
assert_eq!(inner.network.max_connections_per_ip6_prefix_size, 56u32);
assert_eq!(inner.network.max_connection_frequency_per_min, 8u32);
assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32); assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32);
assert!(!inner.network.node_id.valid); assert!(!inner.network.node_id.valid);
assert!(!inner.network.node_id_secret.valid); assert!(!inner.network.node_id_secret.valid);

View File

@ -135,9 +135,12 @@ pub struct VeilidConfigRoutingTable {
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct VeilidConfigNetwork { pub struct VeilidConfigNetwork {
pub max_connections: u32,
pub connection_initial_timeout_ms: u32, pub connection_initial_timeout_ms: u32,
pub connection_inactivity_timeout_ms: u32, pub connection_inactivity_timeout_ms: u32,
pub max_connections_per_ip4: u32,
pub max_connections_per_ip6_prefix: u32,
pub max_connections_per_ip6_prefix_size: u32,
pub max_connection_frequency_per_min: u32,
pub client_whitelist_timeout_ms: u32, pub client_whitelist_timeout_ms: u32,
pub reverse_connection_receipt_time_ms: u32, pub reverse_connection_receipt_time_ms: u32,
pub hole_punch_receipt_time_ms: u32, pub hole_punch_receipt_time_ms: u32,
@ -294,9 +297,12 @@ impl VeilidConfig {
get_config!(inner.protected_store.delete); get_config!(inner.protected_store.delete);
get_config!(inner.network.node_id); get_config!(inner.network.node_id);
get_config!(inner.network.node_id_secret); get_config!(inner.network.node_id_secret);
get_config!(inner.network.max_connections);
get_config!(inner.network.connection_initial_timeout_ms); get_config!(inner.network.connection_initial_timeout_ms);
get_config!(inner.network.connection_inactivity_timeout_ms); get_config!(inner.network.connection_inactivity_timeout_ms);
get_config!(inner.network.max_connections_per_ip4);
get_config!(inner.network.max_connections_per_ip6_prefix);
get_config!(inner.network.max_connections_per_ip6_prefix_size);
get_config!(inner.network.max_connection_frequency_per_min);
get_config!(inner.network.client_whitelist_timeout_ms); get_config!(inner.network.client_whitelist_timeout_ms);
get_config!(inner.network.bootstrap); get_config!(inner.network.bootstrap);
get_config!(inner.network.routing_table.limit_over_attached); get_config!(inner.network.routing_table.limit_over_attached);

View File

@ -33,6 +33,14 @@ macro_rules! log_net {
(warn $fmt:literal, $($arg:expr),+) => { (warn $fmt:literal, $($arg:expr),+) => {
warn!(target:"net", $fmt, $($arg),+); warn!(target:"net", $fmt, $($arg),+);
}; };
(debug $text:expr) => {debug!(
target: "net",
"{}",
$text,
)};
(debug $fmt:literal, $($arg:expr),+) => {
debug!(target:"net", $fmt, $($arg),+);
};
($text:expr) => {trace!( ($text:expr) => {trace!(
target: "net", target: "net",
"{}", "{}",

View File

@ -38,9 +38,12 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
delete: false, delete: false,
), ),
network: VeilidConfigNetwork( network: VeilidConfigNetwork(
maxConnections: 16,
connectionInitialTimeoutMs: 2000, connectionInitialTimeoutMs: 2000,
connectionInactivityTimeoutMs: 60000, connectionInactivityTimeoutMs: 60000,
maxConnectionsPerIp4: 8,
maxConnectionsPerIp6Prefix: 8,
maxConnectionsPerIp6PrefixSize: 56,
maxConnectionFrequencyPerMin: 8,
clientWhitelistTimeoutMs: 300000, clientWhitelistTimeoutMs: 300000,
nodeId: "", nodeId: "",
nodeIdSecret: "", nodeIdSecret: "",

View File

@ -549,9 +549,12 @@ class VeilidConfigLeases {
//////////// ////////////
class VeilidConfigNetwork { class VeilidConfigNetwork {
int maxConnections;
int connectionInitialTimeoutMs; int connectionInitialTimeoutMs;
int connectionInactivityTimeoutMs; int connectionInactivityTimeoutMs;
int maxConnectionsPerIp4;
int maxConnectionsPerIp6Prefix;
int maxConnectionsPerIp6PrefixSize;
int maxConnectionFrequencyPerMin;
int clientWhitelistTimeoutMs; int clientWhitelistTimeoutMs;
String nodeId; String nodeId;
String nodeIdSecret; String nodeIdSecret;
@ -569,9 +572,12 @@ class VeilidConfigNetwork {
VeilidConfigLeases leases; VeilidConfigLeases leases;
VeilidConfigNetwork({ VeilidConfigNetwork({
required this.maxConnections,
required this.connectionInitialTimeoutMs, required this.connectionInitialTimeoutMs,
required this.connectionInactivityTimeoutMs, required this.connectionInactivityTimeoutMs,
required this.maxConnectionsPerIp4,
required this.maxConnectionsPerIp6Prefix,
required this.maxConnectionsPerIp6PrefixSize,
required this.maxConnectionFrequencyPerMin,
required this.clientWhitelistTimeoutMs, required this.clientWhitelistTimeoutMs,
required this.nodeId, required this.nodeId,
required this.nodeIdSecret, required this.nodeIdSecret,
@ -591,9 +597,12 @@ class VeilidConfigNetwork {
Map<String, dynamic> get json { Map<String, dynamic> get json {
return { return {
'max_connections': maxConnections,
'connection_initial_timeout_ms': connectionInitialTimeoutMs, 'connection_initial_timeout_ms': connectionInitialTimeoutMs,
'connection_inactivity_timeout_ms': connectionInactivityTimeoutMs, 'connection_inactivity_timeout_ms': connectionInactivityTimeoutMs,
'max_connections_per_ip4': maxConnectionsPerIp4,
'max_connections_per_ip6_prefix': maxConnectionsPerIp6Prefix,
'max_connections_per_ip6_prefix_size': maxConnectionsPerIp6PrefixSize,
'max_connection_frequency_per_min': maxConnectionFrequencyPerMin,
'client_whitelist_timeout_ms': clientWhitelistTimeoutMs, 'client_whitelist_timeout_ms': clientWhitelistTimeoutMs,
'node_id': nodeId, 'node_id': nodeId,
'node_id_secret': nodeIdSecret, 'node_id_secret': nodeIdSecret,
@ -613,10 +622,14 @@ class VeilidConfigNetwork {
} }
VeilidConfigNetwork.fromJson(Map<String, dynamic> json) VeilidConfigNetwork.fromJson(Map<String, dynamic> json)
: maxConnections = json['max_connections'], : connectionInitialTimeoutMs = json['connection_initial_timeout_ms'],
connectionInitialTimeoutMs = json['connection_initial_timeout_ms'],
connectionInactivityTimeoutMs = connectionInactivityTimeoutMs =
json['connection_inactivity_timeout_ms'], json['connection_inactivity_timeout_ms'],
maxConnectionsPerIp4 = json['max_connections_per_ip4'],
maxConnectionsPerIp6Prefix = json['max_connections_per_ip6_prefix'],
maxConnectionsPerIp6PrefixSize =
json['max_connections_per_ip6_prefix_size'],
maxConnectionFrequencyPerMin = json['max_connection_frequency_per_min'],
clientWhitelistTimeoutMs = json['client_whitelist_timeout_ms'], clientWhitelistTimeoutMs = json['client_whitelist_timeout_ms'],
nodeId = json['node_id'], nodeId = json['node_id'],
nodeIdSecret = json['node_id_secret'], nodeIdSecret = json['node_id_secret'],

View File

@ -48,9 +48,12 @@ core:
directory: '%BLOCK_STORE_DIRECTORY%' directory: '%BLOCK_STORE_DIRECTORY%'
delete: false delete: false
network: network:
max_connections: 16
connection_initial_timeout_ms: 2000 connection_initial_timeout_ms: 2000
connection_inactivity_timeout_ms: 60000 connection_inactivity_timeout_ms: 60000
max_connections_per_ip4: 8
max_connections_per_ip6_prefix: 8
max_connections_per_ip6_prefix_size: 56
max_connection_frequency_per_min: 8
client_whitelist_timeout_ms: 300000 client_whitelist_timeout_ms: 300000
node_id: '' node_id: ''
node_id_secret: '' node_id_secret: ''
@ -538,9 +541,12 @@ pub struct RoutingTable {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Network { pub struct Network {
pub max_connections: u32,
pub connection_initial_timeout_ms: u32, pub connection_initial_timeout_ms: u32,
pub connection_inactivity_timeout_ms: u32, pub connection_inactivity_timeout_ms: u32,
pub max_connections_per_ip4: u32,
pub max_connections_per_ip6_prefix: u32,
pub max_connections_per_ip6_prefix_size: u32,
pub max_connection_frequency_per_min: u32,
pub client_whitelist_timeout_ms: u32, pub client_whitelist_timeout_ms: u32,
pub node_id: veilid_core::DHTKey, pub node_id: veilid_core::DHTKey,
pub node_id_secret: veilid_core::DHTKeySecret, pub node_id_secret: veilid_core::DHTKeySecret,
@ -810,13 +816,25 @@ impl Settings {
)), )),
"block_store.delete" => Ok(Box::new(inner.core.block_store.delete)), "block_store.delete" => Ok(Box::new(inner.core.block_store.delete)),
"network.max_connections" => Ok(Box::new(inner.core.network.max_connections)),
"network.connection_initial_timeout_ms" => { "network.connection_initial_timeout_ms" => {
Ok(Box::new(inner.core.network.connection_initial_timeout_ms)) Ok(Box::new(inner.core.network.connection_initial_timeout_ms))
} }
"network.connection_inactivity_timeout_ms" => Ok(Box::new( "network.connection_inactivity_timeout_ms" => Ok(Box::new(
inner.core.network.connection_inactivity_timeout_ms, inner.core.network.connection_inactivity_timeout_ms,
)), )),
"network.max_connections_per_ip4" => {
Ok(Box::new(inner.core.network.max_connections_per_ip4))
}
"network.max_connections_per_ip6_prefix" => {
Ok(Box::new(inner.core.network.max_connections_per_ip6_prefix))
}
"network.max_connections_per_ip6_prefix_size" => Ok(Box::new(
inner.core.network.max_connections_per_ip6_prefix_size,
)),
"network.max_connection_frequency_per_min" => Ok(Box::new(
inner.core.network.max_connection_frequency_per_min,
)),
"network.client_whitelist_timeout_ms" => { "network.client_whitelist_timeout_ms" => {
Ok(Box::new(inner.core.network.client_whitelist_timeout_ms)) Ok(Box::new(inner.core.network.client_whitelist_timeout_ms))
} }
@ -1170,9 +1188,12 @@ mod tests {
); );
assert_eq!(s.core.protected_store.delete, false); assert_eq!(s.core.protected_store.delete, false);
assert_eq!(s.core.network.max_connections, 16);
assert_eq!(s.core.network.connection_initial_timeout_ms, 2_000u32); assert_eq!(s.core.network.connection_initial_timeout_ms, 2_000u32);
assert_eq!(s.core.network.connection_inactivity_timeout_ms, 60_000u32); assert_eq!(s.core.network.connection_inactivity_timeout_ms, 60_000u32);
assert_eq!(s.core.network.max_connections_per_ip4, 8u32);
assert_eq!(s.core.network.max_connections_per_ip6_prefix, 8u32);
assert_eq!(s.core.network.max_connections_per_ip6_prefix_size, 56u32);
assert_eq!(s.core.network.max_connection_frequency_per_min, 8u32);
assert_eq!(s.core.network.client_whitelist_timeout_ms, 300_000u32); assert_eq!(s.core.network.client_whitelist_timeout_ms, 300_000u32);
assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default()); assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default());
assert_eq!( assert_eq!(

View File

@ -37,7 +37,6 @@ fn init_callbacks() {
case "capabilities.protocol_connect_wss": return true; case "capabilities.protocol_connect_wss": return true;
case "capabilities.protocol_accept_wss": return false; case "capabilities.protocol_accept_wss": return false;
case "tablestore.directory": return ""; case "tablestore.directory": return "";
case "network.max_connections": return 16;
case "network.node_id": return "ZLd4uMYdP4qYLtxF6GqrzBb32Z6T3rE2FWMkWup1pdY"; case "network.node_id": return "ZLd4uMYdP4qYLtxF6GqrzBb32Z6T3rE2FWMkWup1pdY";
case "network.node_id_secret": return "s2Gvq6HJOxgQh-3xIgfWSL3I-DWZ2c1RjZLJl2Xmg2E"; case "network.node_id_secret": return "s2Gvq6HJOxgQh-3xIgfWSL3I-DWZ2c1RjZLJl2Xmg2E";
case "network.bootstrap": return []; case "network.bootstrap": return [];