refactor: update tunnel MTU, optimize Frame parsing, and improve connection stability

This commit is contained in:
2026-06-02 16:26:02 +07:00
parent 3d26c73ee7
commit 6cd4d001ad
32 changed files with 1020 additions and 671 deletions
+2 -2
View File
@@ -97,12 +97,12 @@ impl SessionManager {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
config = config.disable_routing().with_mtu(1380);
config = config.disable_routing().with_mtu(1450);
}
#[cfg(target_os = "linux")]
{
config = config.with_mtu(1380);
config = config.with_mtu(1450);
}
NetworkConfig::init_global(config.mtu);
+67 -28
View File
@@ -11,29 +11,32 @@ use smoltcp::{
Ipv6Address,
},
};
use std::{collections::VecDeque, sync::atomic::Ordering};
use tokio::sync::{mpsc, oneshot};
use std::{collections::VecDeque, sync::{Arc, atomic::{AtomicBool, Ordering}}};
use tokio::sync::{OwnedSemaphorePermit, mpsc, oneshot};
use netrunner_logger::{debug, info, instrument};
pub struct ConnectionCore<T> {
pub handle: SocketHandle,
pub tx: mpsc::Sender<T>,
pub rx: mpsc::Receiver<Bytes>,
pub is_saturated: Arc<AtomicBool>,
}
impl<T> ConnectionCore<T> {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<T>, mpsc::Sender<Bytes>) {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<T>, mpsc::Sender<Bytes>, Arc<AtomicBool>) {
let cap = NetworkConfig::global().channel_capacity;
let (tx_to_net, rx_from_smol) = mpsc::channel::<T>(cap);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(cap);
let is_saturated = Arc::new(AtomicBool::new(false));
let core = Self {
handle,
tx: tx_to_net,
rx: rx_from_net,
is_saturated: is_saturated.clone(),
};
(core, rx_from_smol, tx_to_smol)
(core, rx_from_smol, tx_to_smol, is_saturated)
}
}
@@ -53,31 +56,44 @@ pub struct TcpConnection {
handshake_rx: Option<oneshot::Receiver<()>>,
chunk_buf: Vec<u8>,
server_eof: bool,
permit: Option<OwnedSemaphorePermit>,
total_up_bytes: u64,
total_down_bytes: u64,
rx_congested: bool,
tx_congested: bool,
}
impl TcpConnection {
pub fn new(
handle: SocketHandle,
permit: tokio::sync::OwnedSemaphorePermit,
) -> (
Self,
mpsc::Receiver<Bytes>,
mpsc::Sender<Bytes>,
oneshot::Sender<()>,
Arc<AtomicBool>
) {
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
let (core, rx_from_smol, tx_to_smol, is_saturated) = ConnectionCore::new(handle);
let (handshake_tx, handshake_rx) = oneshot::channel();
let conn = Self {
core,
state: ConnectionState::Handshaking,
permit: Some(permit),
pending_data: VecDeque::new(),
pending_bytes: 0,
handshake_rx: Some(handshake_rx),
chunk_buf: vec![0u8; NetworkConfig::global().tcp_chunk_size],
server_eof: false,
total_up_bytes: 0,
total_down_bytes: 0,
rx_congested: false,
tx_congested: false,
};
(conn, rx_from_smol, tx_to_smol, handshake_tx)
(conn, rx_from_smol, tx_to_smol, handshake_tx, is_saturated)
}
pub fn tick(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::time::Instant) -> bool {
@@ -87,6 +103,7 @@ impl TcpConnection {
match rx.try_recv() {
Ok(_) => {
debug!(%self.core.handle, "TCP Handshake successful, State -> Active");
self.permit.take();
self.state = ConnectionState::Established;
self.handshake_rx = None;
return true;
@@ -106,7 +123,12 @@ impl TcpConnection {
self.poll_and_process(socket, timestamp);
if matches!(socket.state(), tcp::State::Closed | tcp::State::TimeWait) {
debug!(%self.core.handle, "TCP Socket is finished, state -> Closed");
info!(
%self.core.handle,
UP = %self.total_up_bytes,
DOWN = %self.total_down_bytes,
"🏁 TCP Socket finished and closed"
);
self.state = ConnectionState::Closed;
return false;
}
@@ -129,7 +151,7 @@ impl TcpConnection {
true
}
fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::time::Instant) {
fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::time::Instant) {
let current_rtt = GLOBAL_MIN_RTT.load(Ordering::Relaxed);
let rtt = smoltcp::time::Duration::from_millis(current_rtt as u64);
socket.set_tunnel_rtt(rtt);
@@ -137,43 +159,61 @@ fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::tim
// 1. Читаем из браузера -> в Туннель (Upload)
while socket.can_recv() && self.core.tx.capacity() > 0 {
if let Ok(n) = socket.peek_slice(&mut self.chunk_buf, timestamp) {
if n == 0 {
break;
}
if n == 0 { break; }
let chunk = Bytes::copy_from_slice(&self.chunk_buf[..n]);
match self.core.tx.try_send(chunk) {
Ok(_) => {
socket.recv_slice(&mut self.chunk_buf[..n]).unwrap();
self.total_up_bytes += n as u64; // Учет Upload
// Если была перегрузка, а теперь прошло — пишем радостный лог
if self.tx_congested {
netrunner_logger::debug!(%self.core.handle, "🟢 Upload channel cleared. Resuming read from browser.");
self.tx_congested = false;
}
}
Err(mpsc::error::TrySendError::Full(_)) => {
if !self.tx_congested {
netrunner_logger::debug!(%self.core.handle, "🟡 Upload Congestion: Channel to Muxer is full. Pausing read from browser.");
self.tx_congested = true;
}
continue;
}
Err(mpsc::error::TrySendError::Full(_)) => break,
Err(mpsc::error::TrySendError::Closed(_)) => {
self.server_eof = true;
break;
}
}
} else {
break;
}
} else { break; }
}
// 2. Читаем из Туннеля -> в Браузер (Download)
if !self.server_eof {
loop {
if self.pending_bytes > 256 * 1024 {
netrunner_logger::warn!(%self.core.handle, "Local stream buffer >8MB! Force closing to save tunnel.");
self.server_eof = true;
socket.close();
if self.pending_bytes > 1024 * 1024 {
if !self.rx_congested {
netrunner_logger::warn!(%self.core.handle, "🟡 Download Congestion: App buffer high. Pausing tunnel read.");
self.rx_congested = true;
self.core.is_saturated.store(true, Ordering::Release); // 🔥 ОПОВЕЩАЕМ ENGINE
}
break;
}
// Нижний порог (512 КБ)
else if self.rx_congested && self.pending_bytes < 512 * 1024 {
netrunner_logger::debug!(%self.core.handle, "🟢 Download buffer relieved. Resuming tunnel read.");
self.rx_congested = false;
self.core.is_saturated.store(false, Ordering::Release); // 🔥 ДАЕМ ДОБРО ENGINE
}
match self.core.rx.try_recv() {
Ok(data) => {
self.pending_bytes += data.len();
self.total_down_bytes += data.len() as u64; // Учет Download
self.pending_data.push_back(data);
}
Err(mpsc::error::TryRecvError::Empty) => break,
Err(mpsc::error::TryRecvError::Disconnected) => {
netrunner_logger::debug!(%self.core.handle, "Tunnel channel closed (Server EOF)");
self.server_eof = true;
break;
}
@@ -181,7 +221,7 @@ fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::tim
}
}
// 3. Отправляем в smoltcp то, что накопилось (если браузер готов принять)
// 3. Отправляем в smoltcp то, что накопилось (оставляем как было)
while socket.can_send() {
if let Some(mut chunk) = self.pending_data.pop_front() {
match socket.send_slice(&chunk) {
@@ -190,7 +230,7 @@ fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::tim
if n < chunk.len() {
chunk.advance(n);
self.pending_data.push_front(chunk);
break; // Сокет браузера переполнен, ждем следующего тика
break;
}
}
Err(e) => {
@@ -199,9 +239,7 @@ fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::tim
break;
}
}
} else {
break;
}
} else { break; }
}
if self.server_eof && self.pending_data.is_empty() {
@@ -212,6 +250,7 @@ fn poll_and_process(&mut self, socket: &mut tcp::Socket, timestamp: smoltcp::tim
}
}
}
pub fn app_pending_out_size(&self) -> usize {
self.pending_bytes
}
@@ -275,14 +314,14 @@ impl UdpConnection {
handle: SocketHandle,
client_addr: smoltcp::wire::IpAddress,
client_port: u16,
) -> (Self, mpsc::Receiver<UdpPacketTarget>, mpsc::Sender<Bytes>) {
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
) -> (Self, mpsc::Receiver<UdpPacketTarget>, mpsc::Sender<Bytes>, Arc<AtomicBool>) {
let (core, rx_from_smol, tx_to_smol, is_saturated) = ConnectionCore::new(handle);
let conn = Self {
core,
last_client_endpoint: Some(IpEndpoint::new(client_addr, client_port)),
last_activity: std::time::Instant::now(),
};
(conn, rx_from_smol, tx_to_smol)
(conn, rx_from_smol, tx_to_smol, is_saturated)
}
pub fn has_client(&self, port: u16) -> bool {
+53 -24
View File
@@ -6,7 +6,7 @@ use netrunner_core::{
},
rawcast::RawCastFrame,
};
use netrunner_logger::warn;
use netrunner_logger::{debug, warn};
use smoltcp::{
iface::{SocketHandle, SocketSet},
socket::{Socket, tcp, udp},
@@ -15,7 +15,7 @@ use smoltcp::{
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::mpsc;
use tokio::sync::{Semaphore, mpsc};
use crate::net::{
connection::{IcmpResponder, TcpConnection, UdpConnection},
@@ -75,11 +75,13 @@ impl TargetResolver {
}
pub struct ConnectionManager {
pub tracker: SessionTracker, // 🔥 Теперь pub для Engine
pub tracker: SessionTracker,
resolver: TargetResolver,
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
factory: Arc<dyn SocketProvider>,
pending_connects: DashMap<FlowKey, Instant>,
connection_limiter: Arc<Semaphore>,
active_handles_cache: Vec<SocketHandle>,
}
impl ConnectionManager {
@@ -94,6 +96,9 @@ impl ConnectionManager {
tx_to_tunnel,
factory,
pending_connects: DashMap::new(),
// 🔥 ИСПРАВЛЕНИЕ: Семафор должен соответствовать лимиту сокетов
connection_limiter: Arc::new(Semaphore::new(MAX_SOCKETS)),
active_handles_cache: Vec::with_capacity(MAX_SOCKETS),
}
}
@@ -207,15 +212,29 @@ impl ConnectionManager {
}
if !self.tracker.has_connection_from(f.src, f.src_p, socket_set) {
// 🔥 ИСПРАВЛЕНИЕ: Проверяем лимит и очищаем место ДО запроса к семафору
if socket_set.iter().count() >= MAX_SOCKETS {
warn!("🔥 TCP Socket limit reached! Dropping SYN.");
return;
if !self.tracker.evict_oldest_socket(socket_set) {
warn!("🔥 TCP Socket limit reached and eviction failed! Dropping SYN.");
return;
}
}
let permit = match self.connection_limiter.clone().try_acquire_owned() {
Ok(p) => p,
Err(_) => {
// Страховка: если лимит сработал, динамически добавляем слот,
// чтобы не блокировать браузер
self.connection_limiter.add_permits(1);
self.connection_limiter.clone().try_acquire_owned().unwrap()
}
};
self.pending_connects.insert(key, Instant::now());
let socket = self.factory.create_listening_tcp(Some(f.dst), f.dst_p);
let handle = socket_set.add(socket);
self.tracker.add_pending_tcp(handle);
self.tracker.add_pending_tcp(handle, permit);
}
}
@@ -229,8 +248,10 @@ impl ConnectionManager {
}
if socket_set.iter().count() >= MAX_SOCKETS {
warn!("🔥 UDP Socket limit reached! Dropping packet.");
return;
if !self.tracker.evict_oldest_socket(socket_set) {
warn!("🔥 UDP Socket limit reached and eviction failed! Dropping packet.");
return;
}
}
let socket_id = self.tracker.next_id();
@@ -239,9 +260,10 @@ impl ConnectionManager {
if socket.is_open() {
let handle = socket_set.add(socket);
let (conn, rx_smol, tx_smol) = UdpConnection::new(handle, f.src, f.src_p);
let (conn, rx_smol, tx_smol, is_saturated) = UdpConnection::new(handle, f.src, f.src_p);
self.tracker.register_udp(handle, socket_id, conn, tx_smol);
self.tracker
.register_udp(handle, socket_id, conn, tx_smol, is_saturated);
UdpConnection::spawn(
socket_id,
dst_ip,
@@ -254,26 +276,30 @@ impl ConnectionManager {
}
pub fn process_sockets(&mut self, socket_set: &mut SocketSet, now: smoltcp::time::Instant) {
let handles: Vec<SocketHandle> = socket_set.iter().map(|(h, _)| h).collect();
self.active_handles_cache.clear();
for (h, _) in socket_set.iter() {
self.active_handles_cache.push(h);
}
for handle in handles {
let len = self.active_handles_cache.len();
for i in 0..len {
let handle = self.active_handles_cache[i];
let socket = socket_set.get_mut(handle);
match socket {
Socket::Tcp(s) => self.handle_tcp(handle, s, now), // Пробрасываем в TCP
Socket::Udp(s) => self.handle_udp(handle, s, now), // Пробрасываем в UDP
Socket::Icmp(s) => IcmpResponder::handle(s, now), // Пробрасываем в ICMP
Socket::Tcp(s) => self.handle_tcp(handle, s, now),
Socket::Udp(s) => self.handle_udp(handle, s, now),
Socket::Icmp(s) => IcmpResponder::handle(s, now),
}
}
}
// Добавляем аргумент 'now' в сигнатуру
fn handle_tcp(
&mut self,
handle: SocketHandle,
socket: &mut tcp::Socket,
now: smoltcp::time::Instant,
) {
self.tracker.update_activity(handle);
// 🔥 ИСПРАВЛЕНИЕ: Убрали update_activity. Теперь LRU работает как честный FIFO
let state = socket.state();
if state == tcp::State::Closed || state == tcp::State::TimeWait {
@@ -287,11 +313,18 @@ impl ConnectionManager {
let key = (remote.addr, remote.port, local.addr, local.port);
self.pending_connects.remove(&key);
let permit = self
.tracker
.pop_pending_permit(handle)
.expect("Permit must exist for pending TCP connection");
let socket_id = self.tracker.next_id();
let (dst_ip, target) = self.resolver.resolve_destination(local.addr, local.port);
let (conn, rx_smol, tx_smol, handshake_tx) = TcpConnection::new(handle);
self.tracker.register_tcp(handle, socket_id, conn, tx_smol);
let (conn, rx_smol, tx_smol, handshake_tx, is_saturated) =
TcpConnection::new(handle, permit);
self.tracker
.register_tcp(handle, socket_id, conn, tx_smol, is_saturated);
TcpConnection::spawn(
socket_id,
@@ -304,9 +337,7 @@ impl ConnectionManager {
);
}
}
if let Some(conn) = self.tracker.get_tcp_mut(handle) {
// Передаем 'now' в метод tick для обработки очередей и RTT
let _ = conn.tick(socket, now);
}
}
@@ -317,9 +348,8 @@ impl ConnectionManager {
socket: &mut udp::Socket,
now: smoltcp::time::Instant,
) {
self.tracker.update_activity(handle);
// 🔥 ИСПРАВЛЕНИЕ: Убрали update_activity.
if socket.endpoint().port == 53 {
// Передаем время в вызов recv для DNS-сокета
while let Ok((data, meta)) = socket.recv(now) {
if let Some(res) = self.resolver.process_dns_query(data) {
let _ = socket.send_slice(&res, meta);
@@ -328,7 +358,6 @@ impl ConnectionManager {
return;
}
if let Some(conn) = self.tracker.get_udp_mut(handle) {
// Передаем время в tick
if !conn.tick(socket, now) {
self.tracker.queue_removal(handle);
}
+86 -48
View File
@@ -1,3 +1,4 @@
use bytes::Bytes;
use netrunner_core::net::ClientHandler;
use netrunner_core::net::NetworkConfig;
use netrunner_core::rawcast::{RawCastEvent, RawCastFrame};
@@ -38,7 +39,7 @@ pub struct Engine {
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
avail: Arc<AtomicBool>,
rx_from_tunnel: Option<Receiver<RawCastFrame>>, // 🔥 Теперь Option, чтобы отдать его в таску
rx_from_tunnel: Option<Receiver<RawCastFrame>>,
factory: Arc<dyn SocketProvider>,
}
@@ -88,24 +89,43 @@ impl Engine {
let inbound_map = self.manager.tracker.inbound_tx.clone();
let mut rx_tunnel = self.rx_from_tunnel.take().unwrap();
let tunnel_waker = Arc::new(tokio::sync::Notify::new());
let waker_clone = tunnel_waker.clone();
tokio::spawn(async move {
let mut local_cache: std::collections::HashMap<
u64,
(mpsc::Sender<Bytes>, Arc<AtomicBool>),
> = std::collections::HashMap::new();
while let Some(frame) = rx_tunnel.recv().await {
if frame.event == RawCastEvent::Close {
local_cache.remove(&frame.socket_id);
inbound_map.remove(&frame.socket_id);
} else if frame.event == RawCastEvent::Data {
// 1. Берем лок, клонируем Sender, СРАЗУ отпускаем лок (конец scope `tx_opt`)
let tx_opt = inbound_map
.get(&frame.socket_id)
.map(|ref_tx| ref_tx.clone());
let tx_opt: Option<(mpsc::Sender<Bytes>, Arc<AtomicBool>)> =
if let Some(cached) = local_cache.get(&frame.socket_id) {
Some(cached.clone())
} else if let Some(ref_tx) = inbound_map.get(&frame.socket_id) {
let val = ref_tx.value().clone();
local_cache.insert(frame.socket_id, val.clone());
Some(val)
} else {
None
};
// 2. Спокойно ждем (Backpressure), не блокируя остальную систему
if let Some(tx) = tx_opt {
if tx.send(frame.payload).await.is_ok() {
// 🔥 2. БУДИМ ОСНОВНОЙ ЦИКЛ! Данные готовы к выдаче в TUN!
waker_clone.notify_one();
if let Some((tx, is_saturated)) = tx_opt {
// Pressure-Aware drop (нулевая нагрузка на ОС)
if is_saturated.load(Ordering::Relaxed) {
continue;
}
match tx.try_send(frame.payload) {
Ok(_) => {}
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
netrunner_logger::trace!(
"⚠️ Local socket {} buffer suddenly full. Dropping frame.",
frame.socket_id
);
}
Err(_) => {}
}
}
}
@@ -113,13 +133,21 @@ impl Engine {
});
loop {
let mut work_done = false;
let mut repeat_poll = true;
let now = Self::current_time();
// 1. Опрашиваем сокеты до тех пор, пока есть движение данных
while repeat_poll {
self.manager.process_sockets(&mut self.socket_set, now);
let poll_res = self.poll();
self.manager.cleanup(&mut self.socket_set);
repeat_poll = matches!(poll_res, PollResult::SocketStateChanged);
if matches!(poll_res, PollResult::SocketStateChanged) {
work_done = true;
repeat_poll = true;
} else {
repeat_poll = false;
}
}
if last_stats_log.elapsed() >= Duration::from_secs(5) {
@@ -129,32 +157,51 @@ impl Engine {
last_stats_log = StdInstant::now();
}
let delay = self
.interface
.poll_delay(Self::current_time(), &self.socket_set);
// Можно смело ставить 5ms, так как Waker разбудит нас раньше, если нужно
let sleep_time = delay
.map(|d| std::cmp::min(Duration::from_micros(d.micros()), Duration::from_millis(5)))
.unwrap_or(Duration::from_millis(5));
// Основной цикл теперь занимается ТОЛЬКО прогонкой данных: TUN <-> smoltcp
tokio::select! {
_ = sleep(sleep_time) => {}
// 🔥 3. ЛОВИМ ПРОБУЖДЕНИЕ ОТ ТУННЕЛЯ
_ = tunnel_waker.notified() => {
// Цикл пойдет на новую итерацию, вызовет self.poll()
// и мгновенно вытащит данные из канала в TUN-интерфейс
// 2. БАТЧИНГ: Выгребаем все доступные пакеты из TUN без блокировки
let mut packets_processed = 0;
while let Ok(token) = tun_to_engine_rx.try_recv() {
self.manager
.try_create_socket_from_packet(&token, &mut self.socket_set);
if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available();
}
work_done = true;
packets_processed += 1;
// Предотвращаем starvation: если пакетов слишком много, даем другим потокам подышать
if packets_processed > 250 {
break;
}
}
msg = tun_to_engine_rx.recv() => {
if let Some(token) = msg {
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available();
// 3. АДАПТИВНЫЙ ТАЙМИНГ
if work_done {
// Если мы обработали пакеты или сокеты сдвинулись - НЕ СПИМ.
// Уступаем квант времени другим задачам и моментально возвращаемся в цикл.
tokio::task::yield_now().await;
} else {
// Мы отдыхаем только если сеть полностью простаивает
let delay = self
.interface
.poll_delay(Self::current_time(), &self.socket_set);
let sleep_time = delay
.map(|d| {
std::cmp::min(Duration::from_micros(d.micros()), Duration::from_millis(5))
})
.unwrap_or(Duration::from_millis(5));
tokio::select! {
_ = sleep(sleep_time) => {}
msg = tun_to_engine_rx.recv() => {
if let Some(token) = msg {
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available();
}
} else {
break;
}
} else { break; }
}
}
}
}
@@ -250,7 +297,6 @@ pub struct EngineConfig {
pub any_ip: bool,
pub transparent_mode: bool,
pub default_gateway: Ipv4Addr,
// Новые поля
pub killswitch_enabled: bool,
pub excluded_apps: Vec<String>,
pub excluded_domains: Vec<String>,
@@ -323,14 +369,11 @@ impl EngineBuilder {
self
}
// client/src/net/engine.rs
pub async fn build(self) -> Result<(Engine, Tun), String> {
let tun = self.tun_device.ok_or("TUN device is required")?;
info!("Initializing Engine with config: {:?}", self.config);
// 1. Обновляем DnsHandler: теперь он знает про список исключенных доменов
let mut dns_handler = DnsHandler::new(
&self.config.cache_path,
self.config.excluded_domains.clone(),
@@ -340,7 +383,6 @@ impl EngineBuilder {
error!("Failed to initialize DNS blocklist: {}", e);
}
// 2. Обновляем настройку роутинга: передаем флаг Killswitch и список приложений
if self.config.setup_routing {
info!(
"Applying platform routing rules (Killswitch: {})...",
@@ -348,8 +390,8 @@ impl EngineBuilder {
);
setup_platform_routing(
&self.config.remote_address,
self.config.killswitch_enabled, // Новый параметр
&self.config.excluded_apps, // Новый параметр
self.config.killswitch_enabled,
&self.config.excluded_apps,
)
.map_err(|e| format!("Routing setup failed: {}", e))?;
}
@@ -378,13 +420,9 @@ impl EngineBuilder {
Arc::new(SmolSocketFactory::new(config))
});
// 3. Динамический резолвинг исключенных доменов.
// Чтобы Split-Tunneling работал, ОС должна знать, что к реальным IP этих доменов
// нужно идти через физический шлюз, а не через TUN.
let excluded_domains = self.config.excluded_domains.clone();
if !excluded_domains.is_empty() {
tokio::spawn(async move {
// Определяем физический шлюз (например, 192.168.1.1)
#[cfg(target_os = "linux")]
let phys_gw = crate::tun::routing::get_default_gateway_linux()
.unwrap_or_else(|| "192.168.1.1".into());
+61 -24
View File
@@ -11,7 +11,7 @@ use smoltcp::{
wire::IpAddress,
};
use std::sync::Arc;
use tokio::sync::mpsc;
use tokio::sync::{OwnedSemaphorePermit, mpsc};
use crate::net::connection::{TcpConnection, UdpConnection};
@@ -19,11 +19,11 @@ pub struct SessionTracker {
last_activity: HashMap<SocketHandle, StdInstant>,
active_tcp: HashMap<SocketHandle, TcpConnection>,
active_udp: HashMap<SocketHandle, UdpConnection>,
pub inbound_tx: Arc<DashMap<u64, mpsc::Sender<Bytes>>>, // 🔥 Теперь Arc<DashMap>
pub inbound_tx: Arc<DashMap<u64, (mpsc::Sender<Bytes>, Arc<std::sync::atomic::AtomicBool>)>>,
handle_to_id: HashMap<SocketHandle, u64>,
id_to_handle: HashMap<u64, SocketHandle>,
pending_tcp: HashMap<SocketHandle, StdInstant>,
pending_tcp: HashMap<SocketHandle, (StdInstant, OwnedSemaphorePermit)>,
to_remove: Vec<SocketHandle>,
next_socket_id: u64,
}
@@ -49,8 +49,13 @@ impl SessionTracker {
id
}
pub fn add_pending_tcp(&mut self, handle: SocketHandle) {
self.pending_tcp.insert(handle, StdInstant::now());
pub fn add_pending_tcp(&mut self, handle: SocketHandle, permit: OwnedSemaphorePermit) {
self.pending_tcp.insert(handle, (StdInstant::now(), permit));
self.last_activity.insert(handle, StdInstant::now());
}
pub fn pop_pending_permit(&mut self, handle: SocketHandle) -> Option<OwnedSemaphorePermit> {
self.pending_tcp.remove(&handle).map(|(_, permit)| permit)
}
pub fn register_tcp(
@@ -59,13 +64,12 @@ impl SessionTracker {
id: u64,
conn: TcpConnection,
tx: mpsc::Sender<Bytes>,
is_saturated: Arc<std::sync::atomic::AtomicBool>,
) {
self.pending_tcp.remove(&handle);
self.handle_to_id.insert(handle, id);
self.id_to_handle.insert(id, handle);
self.active_tcp.insert(handle, conn);
self.inbound_tx.insert(id, tx);
self.last_activity.insert(handle, StdInstant::now());
self.inbound_tx.insert(id, (tx, is_saturated));
}
pub fn register_udp(
@@ -74,11 +78,12 @@ impl SessionTracker {
id: u64,
conn: UdpConnection,
tx: mpsc::Sender<Bytes>,
is_saturated: Arc<std::sync::atomic::AtomicBool>,
) {
self.handle_to_id.insert(handle, id);
self.id_to_handle.insert(id, handle);
self.active_udp.insert(handle, conn);
self.inbound_tx.insert(id, tx);
self.inbound_tx.insert(id, (tx, is_saturated));
self.last_activity.insert(handle, StdInstant::now());
}
@@ -106,12 +111,6 @@ impl SessionTracker {
self.pending_tcp.contains_key(&handle) && !self.active_tcp.contains_key(&handle)
}
pub fn check_pending_timeout(&self, handle: SocketHandle, timeout: Duration) -> bool {
self.pending_tcp
.get(&handle)
.map_or(false, |t| t.elapsed() > timeout)
}
pub fn get_tcp_mut(&mut self, handle: SocketHandle) -> Option<&mut TcpConnection> {
self.active_tcp.get_mut(&handle)
}
@@ -124,14 +123,6 @@ impl SessionTracker {
self.last_activity.insert(handle, StdInstant::now());
}
pub fn close_tunnel_session(&mut self, id: u64) {
self.inbound_tx.remove(&id);
if let Some(handle) = self.id_to_handle.remove(&id) {
self.queue_removal(handle);
}
}
pub fn queue_removal(&mut self, handle: SocketHandle) {
if !self.to_remove.contains(&handle) {
self.to_remove.push(handle);
@@ -154,10 +145,56 @@ impl SessionTracker {
}
}
pub fn evict_oldest_socket(&mut self, socket_set: &mut SocketSet) -> bool {
let mut victim = None;
for (handle, socket) in socket_set.iter() {
if let smoltcp::socket::Socket::Tcp(tcp_socket) = socket {
let state = tcp_socket.state();
if matches!(
state,
smoltcp::socket::tcp::State::Closed
| smoltcp::socket::tcp::State::TimeWait
| smoltcp::socket::tcp::State::CloseWait
| smoltcp::socket::tcp::State::FinWait1
| smoltcp::socket::tcp::State::FinWait2
) {
victim = Some(handle);
break;
}
}
}
if victim.is_none() {
// 🔥 ИСПРАВЛЕНИЕ: Защищаем системные (слушающие) сокеты от удаления
victim = self
.last_activity
.iter()
.filter(|(h, _)| {
self.active_tcp.contains_key(h)
|| self.active_udp.contains_key(h)
|| self.pending_tcp.contains_key(h)
})
.min_by_key(|&(_, &time)| time)
.map(|(&handle, _)| handle);
}
if let Some(handle) = victim {
netrunner_logger::info!(
"🔪 LRU Eviction: Force closing socket {:?} to free up a slot",
handle
);
self.queue_removal(handle);
self.cleanup(socket_set);
true
} else {
false
}
}
pub fn cleanup(&mut self, socket_set: &mut SocketSet) {
for handle in self.to_remove.drain(..) {
socket_set.remove(handle);
self.active_tcp.remove(&handle);
self.active_udp.remove(&handle);
self.pending_tcp.remove(&handle);
+1 -1
View File
@@ -23,4 +23,4 @@ interface SessionManager {
interface Session {
void stop();
};
};