Skip to content

Commit f2a4922

Browse files
committed
feat: [torrust#1096] ban client IP when exceeds connection ID errors limit
If the client does not send the rigth conenction ID more than 10 times it's banned. In this first implementation after sending 10 times a wrong connection ID. They are only unbanned when the tracker is restarted.
1 parent 87401e8 commit f2a4922

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed

cSpell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"alekitto",
66
"appuser",
77
"Arvid",
8+
"ASMS",
89
"asyn",
910
"autoclean",
1011
"AUTOINCREMENT",

src/servers/udp/server/launcher.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ use std::sync::Arc;
33
use std::time::Duration;
44

55
use bittorrent_tracker_client::udp::client::check;
6+
use bloom::CountingBloomFilter;
67
use derive_more::Constructor;
78
use futures_util::StreamExt;
89
use tokio::select;
9-
use tokio::sync::oneshot;
10+
use tokio::sync::{oneshot, RwLock};
1011
use tracing::instrument;
1112

1213
use super::request_buffer::ActiveRequests;
@@ -20,6 +21,10 @@ use crate::servers::udp::server::processor::Processor;
2021
use crate::servers::udp::server::receiver::Receiver;
2122
use crate::servers::udp::UDP_TRACKER_LOG_TARGET;
2223

24+
/// The maximum number of connection id errors per ip. Clients will be banned if
25+
/// they exceed this limit.
26+
const MAX_CONNECTION_ID_ERRORS_PER_IP: u32 = 10;
27+
2328
/// A UDP server instance launcher.
2429
#[derive(Constructor)]
2530
pub struct Launcher;
@@ -114,6 +119,10 @@ impl Launcher {
114119
async fn run_udp_server_main(mut receiver: Receiver, tracker: Arc<Tracker>, cookie_lifetime: Duration) {
115120
let active_requests = &mut ActiveRequests::default();
116121

122+
// Create a counting bloom filter that uses 4 bits per element and has a
123+
// false positive rate of 0.01 when 100 items have been inserted
124+
let cbf = Arc::new(RwLock::new(CountingBloomFilter::with_rate(4, 0.01, 100)));
125+
117126
let addr = receiver.bound_socket_address();
118127
let local_addr = format!("udp://{addr}");
119128

@@ -149,6 +158,13 @@ impl Launcher {
149158
}
150159
}
151160

161+
let connection_id_errors_from_ip = cbf.read().await.estimate_count(&req.from.ip().to_string());
162+
163+
if connection_id_errors_from_ip > MAX_CONNECTION_ID_ERRORS_PER_IP {
164+
tracing::debug!(target: UDP_TRACKER_LOG_TARGET, local_addr, "Udp::run_udp_server::loop continue: (banned ip)");
165+
continue;
166+
}
167+
152168
// We spawn the new task even if there active requests buffer is
153169
// full. This could seem counterintuitive because we are accepting
154170
// more request and consuming more memory even if the server is
@@ -160,7 +176,8 @@ impl Launcher {
160176
// are only adding and removing tasks without given them the
161177
// chance to finish. However, the buffer is yielding before
162178
// aborting one tasks, giving it the chance to finish.
163-
let abort_handle: tokio::task::AbortHandle = tokio::task::spawn(processor.process_request(req)).abort_handle();
179+
let abort_handle: tokio::task::AbortHandle =
180+
tokio::task::spawn(processor.process_request(req, cbf.clone())).abort_handle();
164181

165182
if abort_handle.is_finished() {
166183
continue;

src/servers/udp/server/processor.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::net::{IpAddr, SocketAddr};
33
use std::sync::Arc;
44

55
use aquatic_udp_protocol::Response;
6+
use bloom::{CountingBloomFilter, ASMS};
7+
use tokio::sync::RwLock;
68
use tracing::{instrument, Level};
79

810
use super::bound_socket::BoundSocket;
@@ -25,8 +27,8 @@ impl Processor {
2527
}
2628
}
2729

28-
#[instrument(skip(self, request))]
29-
pub async fn process_request(self, request: RawRequest) {
30+
#[instrument(skip(self, request, cbf))]
31+
pub async fn process_request(self, request: RawRequest, cbf: Arc<RwLock<CountingBloomFilter>>) {
3032
let from = request.from;
3133
let response = handlers::handle_packet(
3234
request,
@@ -35,6 +37,13 @@ impl Processor {
3537
CookieTimeValues::new(self.cookie_lifetime),
3638
)
3739
.await;
40+
41+
if let Response::Error(err) = &response {
42+
if err.message.contains("cookie value is expired") || err.message.contains("cookie value is from future") {
43+
cbf.write().await.insert(&from.ip().to_string());
44+
}
45+
}
46+
3847
self.send_response(from, response).await;
3948
}
4049

0 commit comments

Comments
 (0)