diff --git a/radicle-node/src/service.rs b/radicle-node/src/service.rs index 4eb946ab..eb5b1119 100644 --- a/radicle-node/src/service.rs +++ b/radicle-node/src/service.rs @@ -738,7 +738,10 @@ where } let host: HostName = addr.into(); - if self.limiter.limit(host.clone(), &Link::Inbound, self.clock) { + if self + .limiter + .limit(host.clone(), &self.config.limits.rate.inbound, self.clock) + { trace!(target: "service", "Rate limitting inbound connection from {host}.."); return false; } @@ -1132,9 +1135,13 @@ where warn!(target: "service", "Session not found for {remote}"); return Ok(()); }; + let limit = match peer.link { + Link::Outbound => &self.config.limits.rate.outbound, + Link::Inbound => &self.config.limits.rate.inbound, + }; if self .limiter - .limit(peer.addr.clone().into(), &peer.link, self.clock) + .limit(peer.addr.clone().into(), limit, self.clock) { trace!(target: "service", "Rate limiting message from {remote} ({})", peer.addr); return Ok(()); diff --git a/radicle-node/src/service/limitter.rs b/radicle-node/src/service/limitter.rs index cd1354b0..57995987 100644 --- a/radicle-node/src/service/limitter.rs +++ b/radicle-node/src/service/limitter.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use localtime::LocalTime; -use radicle::node::HostName; +use radicle::node::{config, HostName}; /// Peer rate limitter. /// @@ -38,19 +38,13 @@ pub trait AsTokens { fn rate(&self) -> f64; } -impl AsTokens for crate::Link { +impl AsTokens for config::RateLimit { fn rate(&self) -> f64 { - match self { - Self::Inbound => 0.1, - Self::Outbound => 1.0, - } + self.fill_rate } fn capacity(&self) -> usize { - match self { - Self::Inbound => 16, - Self::Outbound => 64, - } + self.capacity } } diff --git a/radicle/src/node/config.rs b/radicle/src/node/config.rs index db986efe..db528ce2 100644 --- a/radicle/src/node/config.rs +++ b/radicle/src/node/config.rs @@ -57,6 +57,9 @@ pub struct Limits { pub routing_max_age: LocalDuration, /// Maximum number of concurrent fetches per per connection. pub fetch_concurrency: usize, + /// Rate limitter settings. + #[serde(default)] + pub rate: RateLimits, } impl Default for Limits { @@ -65,6 +68,38 @@ impl Default for Limits { routing_max_size: 1000, routing_max_age: LocalDuration::from_mins(7 * 24 * 60), fetch_concurrency: 1, + rate: RateLimits::default(), + } + } +} + +/// Rate limts for a single connection. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RateLimit { + pub fill_rate: f64, + pub capacity: usize, +} + +/// Rate limits for inbound and outbound connections. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RateLimits { + pub inbound: RateLimit, + pub outbound: RateLimit, +} + +impl Default for RateLimits { + fn default() -> Self { + Self { + inbound: RateLimit { + fill_rate: 0.1, + capacity: 16, + }, + outbound: RateLimit { + fill_rate: 1.0, + capacity: 64, + }, } } }