Skip to content

Commit 1ce2e33

Browse files
committed
feat: [#1145] add banned ips total for UDP to stats
```json { "torrents": 0, "seeders": 0, "completed": 0, "leechers": 0, "tcp4_connections_handled": 0, "tcp4_announces_handled": 0, "tcp4_scrapes_handled": 0, "tcp6_connections_handled": 0, "tcp6_announces_handled": 0, "tcp6_scrapes_handled": 0, "udp_requests_aborted": 0, "udp_requests_banned": 0, "udp_banned_ips_total": 0, "udp4_requests": 0, "udp4_connections_handled": 0, "udp4_announces_handled": 0, "udp4_scrapes_handled": 0, "udp4_responses": 0, "udp4_errors_handled": 0, "udp6_requests": 0, "udp6_connections_handled": 0, "udp6_announces_handled": 0, "udp6_scrapes_handled": 0, "udp6_responses": 0, "udp6_errors_handled": 0 } ``` The new metric: `udp_banned_ips_total`. It's the total number of IPs that have been banned for sending wrong connection IDs.
1 parent 1299f17 commit 1ce2e33

File tree

16 files changed

+124
-50
lines changed

16 files changed

+124
-50
lines changed

src/app.rs

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub async fn start(
113113
if let Some(job) = tracker_apis::start_job(
114114
http_api_config,
115115
tracker.clone(),
116+
ban_service.clone(),
116117
registar.give_form(),
117118
servers::apis::Version::V1,
118119
)

src/bootstrap/app.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ pub fn setup() -> (Configuration, Arc<Tracker>, Arc<RwLock<BanService>>) {
4747

4848
let tracker = initialize_with_configuration(&configuration);
4949

50-
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
50+
let udp_ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
5151

5252
tracing::info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json());
5353

54-
(configuration, tracker, ban_service)
54+
(configuration, tracker, udp_ban_service)
5555
}
5656

5757
/// checks if the seed is the instance seed in production.

src/bootstrap/jobs/tracker_apis.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::net::SocketAddr;
2424
use std::sync::Arc;
2525

2626
use axum_server::tls_rustls::RustlsConfig;
27+
use tokio::sync::RwLock;
2728
use tokio::task::JoinHandle;
2829
use torrust_tracker_configuration::{AccessTokens, HttpApi};
2930
use tracing::instrument;
@@ -33,6 +34,7 @@ use crate::core;
3334
use crate::servers::apis::server::{ApiServer, Launcher};
3435
use crate::servers::apis::Version;
3536
use crate::servers::registar::ServiceRegistrationForm;
37+
use crate::servers::udp::server::banning::BanService;
3638

3739
/// This is the message that the "launcher" spawned task sends to the main
3840
/// application process to notify the API server was successfully started.
@@ -54,10 +56,11 @@ pub struct ApiServerJobStarted();
5456
/// It would panic if unable to send the `ApiServerJobStarted` notice.
5557
///
5658
///
57-
#[instrument(skip(config, tracker, form))]
59+
#[instrument(skip(config, tracker, ban_service, form))]
5860
pub async fn start_job(
5961
config: &HttpApi,
6062
tracker: Arc<core::Tracker>,
63+
ban_service: Arc<RwLock<BanService>>,
6164
form: ServiceRegistrationForm,
6265
version: Version,
6366
) -> Option<JoinHandle<()>> {
@@ -70,21 +73,22 @@ pub async fn start_job(
7073
let access_tokens = Arc::new(config.access_tokens.clone());
7174

7275
match version {
73-
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone(), form, access_tokens).await),
76+
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone(), ban_service.clone(), form, access_tokens).await),
7477
}
7578
}
7679

7780
#[allow(clippy::async_yields_async)]
78-
#[instrument(skip(socket, tls, tracker, form, access_tokens))]
81+
#[instrument(skip(socket, tls, tracker, ban_service, form, access_tokens))]
7982
async fn start_v1(
8083
socket: SocketAddr,
8184
tls: Option<RustlsConfig>,
8285
tracker: Arc<core::Tracker>,
86+
ban_service: Arc<RwLock<BanService>>,
8387
form: ServiceRegistrationForm,
8488
access_tokens: Arc<AccessTokens>,
8589
) -> JoinHandle<()> {
8690
let server = ApiServer::new(Launcher::new(socket, tls))
87-
.start(tracker, form, access_tokens)
91+
.start(tracker, ban_service, form, access_tokens)
8892
.await
8993
.expect("it should be able to start to the tracker api");
9094

@@ -98,21 +102,25 @@ async fn start_v1(
98102
mod tests {
99103
use std::sync::Arc;
100104

105+
use tokio::sync::RwLock;
101106
use torrust_tracker_test_helpers::configuration::ephemeral_public;
102107

103108
use crate::bootstrap::app::initialize_with_configuration;
104109
use crate::bootstrap::jobs::tracker_apis::start_job;
105110
use crate::servers::apis::Version;
106111
use crate::servers::registar::Registar;
112+
use crate::servers::udp::server::banning::BanService;
113+
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
107114

108115
#[tokio::test]
109116
async fn it_should_start_http_tracker() {
110117
let cfg = Arc::new(ephemeral_public());
111118
let config = &cfg.http_api.clone().unwrap();
112119
let tracker = initialize_with_configuration(&cfg);
120+
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
113121
let version = Version::V1;
114122

115-
start_job(config, tracker, Registar::default().give_form(), version)
123+
start_job(config, tracker, ban_service, Registar::default().give_form(), version)
116124
.await
117125
.expect("it should be able to join to the tracker api start-job");
118126
}

src/core/services/statistics/mod.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ pub mod setup;
4040

4141
use std::sync::Arc;
4242

43+
use tokio::sync::RwLock;
4344
use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics;
4445

4546
use crate::core::statistics::metrics::Metrics;
4647
use crate::core::Tracker;
48+
use crate::servers::udp::server::banning::BanService;
4749

4850
/// All the metrics collected by the tracker.
4951
#[derive(Debug, PartialEq)]
@@ -60,9 +62,10 @@ pub struct TrackerMetrics {
6062
}
6163

6264
/// It returns all the [`TrackerMetrics`]
63-
pub async fn get_metrics(tracker: Arc<Tracker>) -> TrackerMetrics {
65+
pub async fn get_metrics(tracker: Arc<Tracker>, ban_service: Arc<RwLock<BanService>>) -> TrackerMetrics {
6466
let torrents_metrics = tracker.get_torrents_metrics();
6567
let stats = tracker.get_stats().await;
68+
let udp_banned_ips_total = ban_service.read().await.get_banned_ips_total();
6669

6770
TrackerMetrics {
6871
torrents_metrics,
@@ -77,6 +80,7 @@ pub async fn get_metrics(tracker: Arc<Tracker>) -> TrackerMetrics {
7780
// UDP
7881
udp_requests_aborted: stats.udp_requests_aborted,
7982
udp_requests_banned: stats.udp_requests_banned,
83+
udp_banned_ips_total: udp_banned_ips_total as u64,
8084
udp4_requests: stats.udp4_requests,
8185
udp4_connections_handled: stats.udp4_connections_handled,
8286
udp4_announces_handled: stats.udp4_announces_handled,
@@ -97,13 +101,16 @@ pub async fn get_metrics(tracker: Arc<Tracker>) -> TrackerMetrics {
97101
mod tests {
98102
use std::sync::Arc;
99103

104+
use tokio::sync::RwLock;
100105
use torrust_tracker_configuration::Configuration;
101106
use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics;
102107
use torrust_tracker_test_helpers::configuration;
103108

104109
use crate::core;
105110
use crate::core::services::statistics::{get_metrics, TrackerMetrics};
106111
use crate::core::services::tracker_factory;
112+
use crate::servers::udp::server::banning::BanService;
113+
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
107114

108115
pub fn tracker_configuration() -> Configuration {
109116
configuration::ephemeral()
@@ -112,8 +119,9 @@ mod tests {
112119
#[tokio::test]
113120
async fn the_statistics_service_should_return_the_tracker_metrics() {
114121
let tracker = Arc::new(tracker_factory(&tracker_configuration()));
122+
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
115123

116-
let tracker_metrics = get_metrics(tracker.clone()).await;
124+
let tracker_metrics = get_metrics(tracker.clone(), ban_service.clone()).await;
117125

118126
assert_eq!(
119127
tracker_metrics,

src/core/statistics/metrics.rs

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ pub struct Metrics {
3434
/// Total number of UDP (UDP tracker) requests banned.
3535
pub udp_requests_banned: u64,
3636

37+
/// Total number of banned IPs.
38+
pub udp_banned_ips_total: u64,
39+
3740
/// Total number of UDP (UDP tracker) requests from IPv4 peers.
3841
pub udp4_requests: u64,
3942

src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use torrust_tracker_lib::{app, bootstrap};
22

33
#[tokio::main]
44
async fn main() {
5-
let (config, tracker, ban_service) = bootstrap::app::setup();
5+
let (config, tracker, udp_ban_service) = bootstrap::app::setup();
66

7-
let jobs = app::start(&config, tracker, ban_service).await;
7+
let jobs = app::start(&config, tracker, udp_ban_service).await;
88

99
// handle the signals
1010
tokio::select! {

src/servers/apis/routes.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use axum::response::Response;
1515
use axum::routing::get;
1616
use axum::{middleware, BoxError, Router};
1717
use hyper::{Request, StatusCode};
18+
use tokio::sync::RwLock;
1819
use torrust_tracker_configuration::{AccessTokens, DEFAULT_TIMEOUT};
1920
use tower::timeout::TimeoutLayer;
2021
use tower::ServiceBuilder;
@@ -32,16 +33,22 @@ use super::v1::middlewares::auth::State;
3233
use crate::core::Tracker;
3334
use crate::servers::apis::API_LOG_TARGET;
3435
use crate::servers::logging::Latency;
36+
use crate::servers::udp::server::banning::BanService;
3537

3638
/// Add all API routes to the router.
3739
#[allow(clippy::needless_pass_by_value)]
38-
#[instrument(skip(tracker, access_tokens))]
39-
pub fn router(tracker: Arc<Tracker>, access_tokens: Arc<AccessTokens>, server_socket_addr: SocketAddr) -> Router {
40+
#[instrument(skip(tracker, ban_service, access_tokens))]
41+
pub fn router(
42+
tracker: Arc<Tracker>,
43+
ban_service: Arc<RwLock<BanService>>,
44+
access_tokens: Arc<AccessTokens>,
45+
server_socket_addr: SocketAddr,
46+
) -> Router {
4047
let router = Router::new();
4148

4249
let api_url_prefix = "/api";
4350

44-
let router = v1::routes::add(api_url_prefix, router, tracker.clone());
51+
let router = v1::routes::add(api_url_prefix, router, tracker.clone(), ban_service.clone());
4552

4653
let state = State { access_tokens };
4754

src/servers/apis/server.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use derive_more::Constructor;
3333
use futures::future::BoxFuture;
3434
use thiserror::Error;
3535
use tokio::sync::oneshot::{Receiver, Sender};
36+
use tokio::sync::RwLock;
3637
use torrust_tracker_configuration::AccessTokens;
3738
use tracing::{instrument, Level};
3839

@@ -44,6 +45,7 @@ use crate::servers::custom_axum_server::{self, TimeoutAcceptor};
4445
use crate::servers::logging::STARTED_ON;
4546
use crate::servers::registar::{ServiceHealthCheckJob, ServiceRegistration, ServiceRegistrationForm};
4647
use crate::servers::signals::{graceful_shutdown, Halted};
48+
use crate::servers::udp::server::banning::BanService;
4749

4850
/// Errors that can occur when starting or stopping the API server.
4951
#[derive(Debug, Error)]
@@ -122,10 +124,11 @@ impl ApiServer<Stopped> {
122124
/// # Panics
123125
///
124126
/// It would panic if the bound socket address cannot be sent back to this starter.
125-
#[instrument(skip(self, tracker, form, access_tokens), err, ret(Display, level = Level::INFO))]
127+
#[instrument(skip(self, tracker, ban_service, form, access_tokens), err, ret(Display, level = Level::INFO))]
126128
pub async fn start(
127129
self,
128130
tracker: Arc<Tracker>,
131+
ban_service: Arc<RwLock<BanService>>,
129132
form: ServiceRegistrationForm,
130133
access_tokens: Arc<AccessTokens>,
131134
) -> Result<ApiServer<Running>, Error> {
@@ -137,7 +140,7 @@ impl ApiServer<Stopped> {
137140
let task = tokio::spawn(async move {
138141
tracing::debug!(target: API_LOG_TARGET, "Starting with launcher in spawned task ...");
139142

140-
let _task = launcher.start(tracker, access_tokens, tx_start, rx_halt).await;
143+
let _task = launcher.start(tracker, ban_service, access_tokens, tx_start, rx_halt).await;
141144

142145
tracing::debug!(target: API_LOG_TARGET, "Started with launcher in spawned task");
143146

@@ -235,18 +238,19 @@ impl Launcher {
235238
///
236239
/// Will panic if unable to bind to the socket, or unable to get the address of the bound socket.
237240
/// Will also panic if unable to send message regarding the bound socket address.
238-
#[instrument(skip(self, tracker, access_tokens, tx_start, rx_halt))]
241+
#[instrument(skip(self, tracker, ban_service, access_tokens, tx_start, rx_halt))]
239242
pub fn start(
240243
&self,
241244
tracker: Arc<Tracker>,
245+
ban_service: Arc<RwLock<BanService>>,
242246
access_tokens: Arc<AccessTokens>,
243247
tx_start: Sender<Started>,
244248
rx_halt: Receiver<Halted>,
245249
) -> BoxFuture<'static, ()> {
246250
let socket = std::net::TcpListener::bind(self.bind_to).expect("Could not bind tcp_listener to address.");
247251
let address = socket.local_addr().expect("Could not get local_addr from tcp_listener.");
248252

249-
let router = router(tracker, access_tokens, address);
253+
let router = router(tracker, ban_service, access_tokens, address);
250254

251255
let handle = Handle::new();
252256

@@ -294,19 +298,23 @@ impl Launcher {
294298
mod tests {
295299
use std::sync::Arc;
296300

301+
use tokio::sync::RwLock;
297302
use torrust_tracker_test_helpers::configuration::ephemeral_public;
298303

299304
use crate::bootstrap::app::initialize_with_configuration;
300305
use crate::bootstrap::jobs::make_rust_tls;
301306
use crate::servers::apis::server::{ApiServer, Launcher};
302307
use crate::servers::registar::Registar;
308+
use crate::servers::udp::server::banning::BanService;
309+
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
303310

304311
#[tokio::test]
305312
async fn it_should_be_able_to_start_and_stop() {
306313
let cfg = Arc::new(ephemeral_public());
307314
let config = &cfg.http_api.clone().unwrap();
308315

309316
let tracker = initialize_with_configuration(&cfg);
317+
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
310318

311319
let bind_to = config.bind_address;
312320

@@ -321,7 +329,7 @@ mod tests {
321329
let register = &Registar::default();
322330

323331
let started = stopped
324-
.start(tracker, register.give_form(), access_tokens)
332+
.start(tracker, ban_service, register.give_form(), access_tokens)
325333
.await
326334
.expect("it should start the server");
327335
let stopped = started.stop().await.expect("it should stop the server");

src/servers/apis/v1/context/stats/handlers.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ use axum::extract::State;
66
use axum::response::Response;
77
use axum_extra::extract::Query;
88
use serde::Deserialize;
9+
use tokio::sync::RwLock;
910

1011
use super::responses::{metrics_response, stats_response};
1112
use crate::core::services::statistics::get_metrics;
1213
use crate::core::Tracker;
14+
use crate::servers::udp::server::banning::BanService;
1315

1416
#[derive(Deserialize, Debug, Default)]
1517
#[serde(rename_all = "lowercase")]
@@ -35,8 +37,11 @@ pub struct QueryParams {
3537
///
3638
/// Refer to the [API endpoint documentation](crate::servers::apis::v1::context::stats#get-tracker-statistics)
3739
/// for more information about this endpoint.
38-
pub async fn get_stats_handler(State(tracker): State<Arc<Tracker>>, params: Query<QueryParams>) -> Response {
39-
let metrics = get_metrics(tracker.clone()).await;
40+
pub async fn get_stats_handler(
41+
State(state): State<(Arc<Tracker>, Arc<RwLock<BanService>>)>,
42+
params: Query<QueryParams>,
43+
) -> Response {
44+
let metrics = get_metrics(state.0.clone(), state.1.clone()).await;
4045

4146
match params.0.format {
4247
Some(format) => match format {

0 commit comments

Comments
 (0)