mirror of
https://github.com/telemt/telemt.git
synced 2026-09-12 21:44:08 +03:00
cf0cd08387
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
369 lines
12 KiB
Rust
369 lines
12 KiB
Rust
use std::net::IpAddr;
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
use std::time::{Duration, Instant};
|
|
|
|
use tokio::sync::{RwLock, Semaphore, watch};
|
|
use tracing::{info, warn};
|
|
|
|
use crate::config::{LogLevel, ProxyConfig};
|
|
use crate::conntrack_control;
|
|
use crate::crypto::SecureRandom;
|
|
use crate::ip_tracker::UserIpTracker;
|
|
use crate::network::probe::{NetworkDecision, NetworkProbe};
|
|
use crate::proxy::direct_buffer_budget::{DirectBufferBudget, run_direct_buffer_budget_controller};
|
|
use crate::proxy::route_mode::RouteRuntimeController;
|
|
use crate::proxy::shared_state::ProxySharedState;
|
|
use crate::startup::{
|
|
COMPONENT_DC_CONNECTIVITY_PING, COMPONENT_ME_CONNECTIVITY_PING, COMPONENT_ME_POOL_CONSTRUCT,
|
|
COMPONENT_ME_POOL_INIT_STAGE1, COMPONENT_ME_PROXY_CONFIG_V4, COMPONENT_ME_PROXY_CONFIG_V6,
|
|
COMPONENT_ME_SECRET_FETCH, StartupMeStatus, StartupTracker,
|
|
};
|
|
use crate::stats::beobachten::BeobachtenStore;
|
|
use crate::stats::{ReplayChecker, Stats};
|
|
use crate::stream::BufferPool;
|
|
use crate::transport::UpstreamManager;
|
|
use crate::transport::middle_proxy::MePool;
|
|
|
|
use super::admission;
|
|
use super::generation::RuntimeTaskScope;
|
|
use super::{connectivity, me_startup, runtime_tasks};
|
|
|
|
pub(super) struct RuntimeStartupState {
|
|
pub(super) config: Arc<ProxyConfig>,
|
|
pub(super) beobachten: Arc<BeobachtenStore>,
|
|
pub(super) rng: Arc<SecureRandom>,
|
|
pub(super) max_connections: Arc<Semaphore>,
|
|
pub(super) me_pool: Option<Arc<MePool>>,
|
|
pub(super) replay_checker: Arc<ReplayChecker>,
|
|
pub(super) buffer_pool: Arc<BufferPool>,
|
|
pub(super) config_rx: watch::Receiver<Arc<ProxyConfig>>,
|
|
pub(super) detected_ip_v4: Option<IpAddr>,
|
|
pub(super) detected_ip_v6: Option<IpAddr>,
|
|
pub(super) admission_tx: watch::Sender<bool>,
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(super) async fn prepare_runtime(
|
|
mut config: ProxyConfig,
|
|
config_path: &Path,
|
|
probe: &NetworkProbe,
|
|
decision: &NetworkDecision,
|
|
process_started_at: Instant,
|
|
startup_tracker: &Arc<StartupTracker>,
|
|
stats: Arc<Stats>,
|
|
upstream_manager: Arc<UpstreamManager>,
|
|
ip_tracker: Arc<UserIpTracker>,
|
|
shared_state: Arc<ProxySharedState>,
|
|
direct_buffer_budget: Arc<DirectBufferBudget>,
|
|
route_runtime: Arc<RouteRuntimeController>,
|
|
api_me_pool: Arc<RwLock<Option<Arc<MePool>>>>,
|
|
runtime_task_scope: RuntimeTaskScope,
|
|
admission_tx: watch::Sender<bool>,
|
|
runtime_log_filter: &runtime_tasks::RuntimeLogFilter,
|
|
has_rust_log: bool,
|
|
effective_log_level: &LogLevel,
|
|
) -> RuntimeStartupState {
|
|
let prefer_ipv6 = decision.prefer_ipv6();
|
|
let mut use_middle_proxy = config.general.use_middle_proxy;
|
|
let beobachten = Arc::new(BeobachtenStore::new());
|
|
let rng = Arc::new(SecureRandom::new());
|
|
|
|
let max_connections_limit = if config.server.max_connections == 0 {
|
|
Semaphore::MAX_PERMITS
|
|
} else {
|
|
config.server.max_connections as usize
|
|
};
|
|
let max_connections = Arc::new(Semaphore::new(max_connections_limit));
|
|
|
|
let me2dc_fallback = config.general.me2dc_fallback;
|
|
let me_init_retry_attempts = config.general.me_init_retry_attempts;
|
|
if use_middle_proxy && !decision.ipv4_me && !decision.ipv6_me {
|
|
if me2dc_fallback {
|
|
warn!(
|
|
"No usable IP family for Middle Proxy detected; Direct-DC startup fallback is active while ME init retries continue"
|
|
);
|
|
} else {
|
|
warn!(
|
|
"No usable IP family for Middle Proxy detected; me2dc_fallback=false, ME init retries stay active"
|
|
);
|
|
}
|
|
}
|
|
|
|
if use_middle_proxy {
|
|
startup_tracker
|
|
.set_me_status(StartupMeStatus::Initializing, COMPONENT_ME_SECRET_FETCH)
|
|
.await;
|
|
startup_tracker
|
|
.start_component(
|
|
COMPONENT_ME_SECRET_FETCH,
|
|
Some("fetch proxy-secret from source/cache".to_string()),
|
|
)
|
|
.await;
|
|
startup_tracker
|
|
.set_me_retry_limit(if !me2dc_fallback || me_init_retry_attempts == 0 {
|
|
"unlimited".to_string()
|
|
} else {
|
|
me_init_retry_attempts.to_string()
|
|
})
|
|
.await;
|
|
} else {
|
|
startup_tracker
|
|
.set_me_status(StartupMeStatus::Skipped, "skipped")
|
|
.await;
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_ME_SECRET_FETCH,
|
|
Some("middle proxy mode disabled".to_string()),
|
|
)
|
|
.await;
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_ME_PROXY_CONFIG_V4,
|
|
Some("middle proxy mode disabled".to_string()),
|
|
)
|
|
.await;
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_ME_PROXY_CONFIG_V6,
|
|
Some("middle proxy mode disabled".to_string()),
|
|
)
|
|
.await;
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_ME_POOL_CONSTRUCT,
|
|
Some("middle proxy mode disabled".to_string()),
|
|
)
|
|
.await;
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_ME_POOL_INIT_STAGE1,
|
|
Some("middle proxy mode disabled".to_string()),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
let (me_ready_tx, me_ready_rx) = watch::channel(0_u64);
|
|
let direct_first_startup = use_middle_proxy && me2dc_fallback;
|
|
|
|
let me_pool: Option<Arc<MePool>> = if direct_first_startup {
|
|
None
|
|
} else {
|
|
me_startup::initialize_me_pool(
|
|
use_middle_proxy,
|
|
&config,
|
|
decision,
|
|
probe,
|
|
startup_tracker,
|
|
upstream_manager.clone(),
|
|
rng.clone(),
|
|
stats.clone(),
|
|
api_me_pool.clone(),
|
|
me_ready_tx.clone(),
|
|
runtime_task_scope.clone(),
|
|
)
|
|
.await
|
|
};
|
|
|
|
if direct_first_startup {
|
|
startup_tracker.set_transport_mode("direct").await;
|
|
startup_tracker.set_degraded(true).await;
|
|
info!(
|
|
"Transport: Direct DC startup fallback active; Middle-End bootstrap continues in background"
|
|
);
|
|
} else if me_pool.is_some() {
|
|
startup_tracker.set_transport_mode("middle_proxy").await;
|
|
startup_tracker.set_degraded(false).await;
|
|
info!("Transport: Middle-End Proxy - all DC-over-RPC");
|
|
} else {
|
|
let _ = use_middle_proxy;
|
|
use_middle_proxy = false;
|
|
config.general.use_middle_proxy = false;
|
|
startup_tracker.set_transport_mode("direct").await;
|
|
startup_tracker.set_degraded(true).await;
|
|
if me2dc_fallback {
|
|
startup_tracker
|
|
.set_me_status(StartupMeStatus::Failed, "fallback_to_direct")
|
|
.await;
|
|
} else {
|
|
startup_tracker
|
|
.set_me_status(StartupMeStatus::Skipped, "skipped")
|
|
.await;
|
|
}
|
|
info!("Transport: Direct DC - TCP - standard DC-over-TCP");
|
|
}
|
|
|
|
let config = Arc::new(config);
|
|
let replay_checker = Arc::new(ReplayChecker::new(
|
|
config.access.replay_check_len,
|
|
Duration::from_secs(config.access.replay_window_secs),
|
|
));
|
|
let buffer_pool = Arc::new(BufferPool::with_config(64 * 1024, 4096));
|
|
|
|
if direct_first_startup {
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_ME_CONNECTIVITY_PING,
|
|
Some("deferred by direct-first startup".to_string()),
|
|
)
|
|
.await;
|
|
startup_tracker
|
|
.skip_component(
|
|
COMPONENT_DC_CONNECTIVITY_PING,
|
|
Some("background health checks active".to_string()),
|
|
)
|
|
.await;
|
|
} else {
|
|
connectivity::run_startup_connectivity(
|
|
&config,
|
|
&me_pool,
|
|
rng.clone(),
|
|
startup_tracker,
|
|
upstream_manager.clone(),
|
|
prefer_ipv6,
|
|
decision,
|
|
process_started_at,
|
|
api_me_pool.clone(),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
let runtime_watches = runtime_tasks::spawn_runtime_tasks(
|
|
&config,
|
|
config_path,
|
|
probe,
|
|
prefer_ipv6,
|
|
decision.ipv4_dc,
|
|
decision.ipv6_dc,
|
|
startup_tracker,
|
|
stats.clone(),
|
|
upstream_manager.clone(),
|
|
replay_checker.clone(),
|
|
me_pool.clone(),
|
|
rng.clone(),
|
|
ip_tracker.clone(),
|
|
beobachten.clone(),
|
|
me_pool.clone(),
|
|
shared_state.clone(),
|
|
me_ready_tx.clone(),
|
|
runtime_task_scope.clone(),
|
|
None,
|
|
)
|
|
.await;
|
|
let config_rx = runtime_watches.config_rx;
|
|
let log_level_rx = runtime_watches.log_level_rx;
|
|
let detected_ip_v4 = runtime_watches.detected_ip_v4;
|
|
let detected_ip_v6 = runtime_watches.detected_ip_v6;
|
|
runtime_log_filter.start(
|
|
has_rust_log,
|
|
effective_log_level,
|
|
log_level_rx,
|
|
runtime_task_scope.clone(),
|
|
);
|
|
|
|
if direct_first_startup {
|
|
let config_bg = config.clone();
|
|
let decision_bg = decision.clone();
|
|
let probe_bg = probe.clone();
|
|
let startup_tracker_bg = startup_tracker.clone();
|
|
let upstream_manager_bg = upstream_manager.clone();
|
|
let rng_bg = rng.clone();
|
|
let stats_bg = stats.clone();
|
|
let api_me_pool_bg = api_me_pool.clone();
|
|
let me_ready_tx_bg = me_ready_tx.clone();
|
|
let config_rx_bg = config_rx.clone();
|
|
let task_scope_bg = runtime_task_scope.clone();
|
|
runtime_task_scope.spawn(async move {
|
|
let mut bootstrap_attempt: u32 = 0;
|
|
loop {
|
|
bootstrap_attempt = bootstrap_attempt.saturating_add(1);
|
|
let pool = me_startup::initialize_me_pool(
|
|
true,
|
|
config_bg.as_ref(),
|
|
&decision_bg,
|
|
&probe_bg,
|
|
&startup_tracker_bg,
|
|
upstream_manager_bg.clone(),
|
|
rng_bg.clone(),
|
|
stats_bg.clone(),
|
|
api_me_pool_bg.clone(),
|
|
me_ready_tx_bg.clone(),
|
|
task_scope_bg.clone(),
|
|
)
|
|
.await;
|
|
if let Some(pool) = pool {
|
|
runtime_tasks::spawn_middle_proxy_runtime_tasks(
|
|
config_bg.as_ref(),
|
|
config_rx_bg,
|
|
pool,
|
|
rng_bg,
|
|
me_ready_tx_bg,
|
|
task_scope_bg,
|
|
);
|
|
break;
|
|
}
|
|
if me_init_retry_attempts > 0 && bootstrap_attempt >= me_init_retry_attempts {
|
|
break;
|
|
}
|
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
}
|
|
});
|
|
|
|
let startup_tracker_ready = startup_tracker.clone();
|
|
let api_me_pool_ready = api_me_pool.clone();
|
|
let mut me_ready_rx_transport = me_ready_tx.subscribe();
|
|
runtime_task_scope.spawn(async move {
|
|
if me_ready_rx_transport.changed().await.is_ok() {
|
|
if let Some(pool) = api_me_pool_ready.read().await.as_ref() {
|
|
pool.set_runtime_ready(true);
|
|
}
|
|
startup_tracker_ready
|
|
.set_transport_mode("middle_proxy")
|
|
.await;
|
|
startup_tracker_ready.set_degraded(false).await;
|
|
info!("Transport: Middle-End Proxy restored for new sessions");
|
|
}
|
|
});
|
|
}
|
|
|
|
admission::configure_admission_gate(
|
|
&config,
|
|
me_pool.clone(),
|
|
api_me_pool,
|
|
route_runtime,
|
|
&admission_tx,
|
|
config_rx.clone(),
|
|
me_ready_rx,
|
|
runtime_task_scope.clone(),
|
|
)
|
|
.await;
|
|
let conntrack_scope = runtime_task_scope.clone();
|
|
runtime_task_scope.spawn(conntrack_control::run_conntrack_controller(
|
|
config_rx.clone(),
|
|
stats.clone(),
|
|
shared_state.clone(),
|
|
conntrack_scope.cancellation_token(),
|
|
));
|
|
runtime_task_scope.spawn(run_direct_buffer_budget_controller(
|
|
direct_buffer_budget,
|
|
buffer_pool.clone(),
|
|
stats,
|
|
shared_state,
|
|
config.server.max_connections,
|
|
));
|
|
|
|
RuntimeStartupState {
|
|
config,
|
|
beobachten,
|
|
rng,
|
|
max_connections,
|
|
me_pool,
|
|
replay_checker,
|
|
buffer_pool,
|
|
config_rx,
|
|
detected_ip_v4,
|
|
detected_ip_v6,
|
|
admission_tx,
|
|
}
|
|
}
|