all unbounded and remove AI hallucinations

This commit is contained in:
2026-04-12 15:14:42 +07:00
parent 9233641e67
commit 11b810ad65
16 changed files with 315 additions and 436 deletions
+61 -70
View File
@@ -6,6 +6,7 @@ use netrunner_core::{
use smoltcp::{
iface::SocketHandle,
socket::{tcp, udp},
time::Duration,
wire::{
Icmpv4Message, Icmpv4Packet, Icmpv6Message, Icmpv6Packet, IpAddress, IpEndpoint,
Ipv6Address,
@@ -18,17 +19,20 @@ use netrunner_logger::{debug, info};
pub struct ConnectionCore<T> {
pub handle: SocketHandle,
pub tx: mpsc::Sender<T>,
pub rx: mpsc::Receiver<Bytes>,
pub tx: mpsc::UnboundedSender<T>, // 🔥 Полностью Unbounded
pub rx: mpsc::UnboundedReceiver<Bytes>, // 🔥 Полностью Unbounded
}
impl<T> ConnectionCore<T> {
pub fn new(
handle: SocketHandle,
capacity: usize,
) -> (Self, mpsc::Receiver<T>, mpsc::Sender<Bytes>) {
let (tx_to_net, rx_from_smol) = mpsc::channel::<T>(capacity);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(capacity);
) -> (
Self,
mpsc::UnboundedReceiver<T>,
mpsc::UnboundedSender<Bytes>,
) {
let (tx_to_net, rx_from_smol) = mpsc::unbounded_channel::<T>();
let (tx_to_smol, rx_from_net) = mpsc::unbounded_channel::<Bytes>();
let core = Self {
handle,
@@ -51,7 +55,6 @@ pub enum ConnectionState {
pub struct TcpConnection {
core: ConnectionCore<Bytes>,
state: ConnectionState,
// 🔥 ФИКС: Используем очередь чанков, чтобы не копировать память
pending_data: VecDeque<Bytes>,
handshake_rx: Option<oneshot::Receiver<()>>,
chunk_buf: Vec<u8>,
@@ -63,12 +66,11 @@ impl TcpConnection {
handle: SocketHandle,
) -> (
Self,
mpsc::Receiver<Bytes>,
mpsc::Sender<Bytes>,
mpsc::UnboundedReceiver<Bytes>,
mpsc::UnboundedSender<Bytes>,
oneshot::Sender<()>,
) {
let capacity = NetworkConfig::global().client_virtual_stream_capacity;
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle, capacity);
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
let (handshake_tx, handshake_rx) = oneshot::channel();
let conn = Self {
@@ -133,51 +135,36 @@ impl TcpConnection {
}
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
while socket.can_recv() {
let mut full = false;
let rtt = Duration::from_millis(250);
socket.set_tunnel_rtt(rtt);
while socket.can_recv() {
if let Ok(n) = socket.peek_slice(&mut self.chunk_buf) {
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();
}
Err(mpsc::error::TrySendError::Full(_)) => {
full = true;
}
Err(_) => {
self.server_eof = true;
break;
}
if self.core.tx.send(chunk).is_ok() {
socket.recv_slice(&mut self.chunk_buf[..n]).unwrap();
} else {
self.server_eof = true;
break;
}
} else {
break;
}
if full {
break;
}
}
if !self.server_eof {
loop {
let pending_size: usize = self.pending_data.iter().map(|b| b.len()).sum();
let max_pending = 128 * 1024;
if pending_size >= max_pending {
break;
}
match self.core.rx.try_recv() {
Ok(data) => {
self.pending_data.push_back(data);
}
Err(mpsc::error::TryRecvError::Empty) => break,
Err(mpsc::error::TryRecvError::Disconnected) => {
debug!(%self.core.handle, "Server sent EOF");
debug!(%self.core.handle, "Tunnel channel closed (Server EOF)");
self.server_eof = true;
break;
}
@@ -185,33 +172,33 @@ impl TcpConnection {
}
}
// 🔥 ФИКС: Отправляем данные кусками без копирования памяти
while socket.can_send() {
if let Some(mut chunk) = self.pending_data.pop_front() {
match socket.send_slice(&chunk) {
Ok(n) => {
if n < chunk.len() {
// Если сокет проглотил не всё, отрезаем отправленное,
// а остаток возвращаем в начало очереди
chunk.advance(n);
self.pending_data.push_front(chunk);
break; // Буфер сокета заполнен
break;
}
}
Err(e) => {
debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
self.pending_data.push_front(chunk); // Возвращаем кусок обратно при ошибке
self.pending_data.push_front(chunk);
break;
}
}
} else {
break; // Нет больше данных для отправки
break;
}
}
if self.server_eof && self.pending_data.is_empty() && socket.may_send() {
debug!(%self.core.handle, "All data flushed after server EOF, sending FIN to browser");
socket.close();
if self.server_eof && self.pending_data.is_empty() {
let state = socket.state();
if state == tcp::State::Established || state == tcp::State::CloseWait {
debug!(%self.core.handle, "All data flushed after server EOF, sending FIN to browser");
socket.close();
}
}
}
@@ -220,15 +207,17 @@ impl TcpConnection {
dst_ip: std::net::Ipv4Addr,
dst_port: u16,
target: String,
mut rx_smol: mpsc::Receiver<Bytes>,
mut rx_smol: mpsc::UnboundedReceiver<Bytes>,
handshake_tx: oneshot::Sender<()>,
tx_tunnel: mpsc::Sender<RawCastFrame>,
// 🔥 ФИКС: Тип изменен на UnboundedSender
tx_tunnel: mpsc::UnboundedSender<RawCastFrame>,
) {
tokio::spawn(async move {
let mut frame = RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
frame.payload = Bytes::from(target);
if tx_tunnel.send(frame).await.is_err() {
// send() на UnboundedSender не асинхронный и возвращает Result<(), T>
if tx_tunnel.send(frame).is_err() {
netrunner_logger::error!("❌ [TCP {}] Failed to send CONNECT to tunnel", socket_id);
return;
}
@@ -244,20 +233,25 @@ impl TcpConnection {
data.to_vec(),
);
if tx_tunnel.send(data_frame).await.is_err() {
if tx_tunnel.send(data_frame).is_err() {
break;
}
}
let close_frame = RawCastFrame::close(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
let _ = tx_tunnel.send(close_frame).await;
let _ = tx_tunnel.send(close_frame);
debug!("🏁 [TCP {}] Spawned task finished", socket_id);
});
}
pub fn app_pending_out_size(&self) -> usize {
self.pending_data.iter().map(|b| b.len()).sum()
}
}
pub type UdpPacketTarget = (Bytes, std::net::Ipv4Addr, u16);
pub struct UdpConnection {
core: ConnectionCore<UdpPacketTarget>,
last_client_endpoint: Option<IpEndpoint>,
@@ -269,16 +263,17 @@ impl UdpConnection {
handle: SocketHandle,
client_addr: smoltcp::wire::IpAddress,
client_port: u16,
) -> (Self, mpsc::Receiver<UdpPacketTarget>, mpsc::Sender<Bytes>) {
let capacity = NetworkConfig::global().client_virtual_stream_capacity;
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle, capacity);
) -> (
Self,
mpsc::UnboundedReceiver<UdpPacketTarget>,
mpsc::UnboundedSender<Bytes>,
) {
let (core, rx_from_smol, tx_to_smol) = 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)
}
@@ -286,6 +281,7 @@ impl UdpConnection {
self.last_client_endpoint
.map_or(false, |ep| ep.port == port)
}
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
if self.last_activity.elapsed() > UDP_IDLE_TIMEOUT {
socket.close();
@@ -296,12 +292,11 @@ impl UdpConnection {
while let Ok((data, metadata)) = socket.recv() {
if let smoltcp::wire::IpAddress::Ipv4(ip) = metadata.endpoint.addr {
self.last_client_endpoint = Some(metadata.endpoint);
let target_ip = std::net::Ipv4Addr::from(ip);
let target_port = metadata.endpoint.port;
let payload = (Bytes::copy_from_slice(data), target_ip, target_port);
if self.core.tx.try_send(payload).is_ok() {
if self.core.tx.send(payload).is_ok() {
self.last_activity = std::time::Instant::now();
}
}
@@ -313,18 +308,15 @@ impl UdpConnection {
match self.core.rx.try_recv() {
Ok(data) => {
if let Err(e) = socket.send_slice(&data, client_endpoint) {
debug!("Dropped UDP packet due to smoltcp error: {:?}", e);
debug!("Dropped UDP packet: {:?}", e);
} else {
self.last_activity = std::time::Instant::now();
}
}
Err(_) => {
break;
}
Err(_) => break,
}
}
}
true
}
@@ -333,16 +325,16 @@ impl UdpConnection {
dst_ip: std::net::Ipv4Addr,
dst_port: u16,
target: String,
mut rx_smol: mpsc::Receiver<UdpPacketTarget>,
tx_tunnel: mpsc::Sender<RawCastFrame>,
mut rx_smol: mpsc::UnboundedReceiver<UdpPacketTarget>,
// 🔥 ФИКС: Тип изменен на UnboundedSender
tx_tunnel: mpsc::UnboundedSender<RawCastFrame>,
) {
tokio::spawn(async move {
debug!("📡 [UDP {}] Task started for {}", socket_id, target);
let mut frame = RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
frame.payload = Bytes::from(target);
if tx_tunnel.send(frame).await.is_err() {
if tx_tunnel.send(frame).is_err() {
netrunner_logger::error!("❌ [UDP {}] Failed to send CONNECT to tunnel", socket_id);
return;
}
@@ -350,20 +342,19 @@ impl UdpConnection {
while let Some((data, ip, port)) = rx_smol.recv().await {
let data_frame =
RawCastFrame::data(LocalProtocol::Udp, socket_id, ip, port, data.to_vec());
if tx_tunnel.send(data_frame).await.is_err() {
if tx_tunnel.send(data_frame).is_err() {
break;
}
}
let close_frame = RawCastFrame::close(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
let _ = tx_tunnel.send(close_frame).await;
let _ = tx_tunnel.send(close_frame);
info!("🛑 [UDP {}] Task stopped", socket_id);
});
}
}
// IcmpResponder оставляем как был (он не использует каналы)
use smoltcp::socket::icmp;
pub struct IcmpResponder;
+16 -42
View File
@@ -78,16 +78,15 @@ impl TargetResolver {
pub struct ConnectionManager {
tracker: SessionTracker,
resolver: TargetResolver,
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
tx_to_tunnel: mpsc::UnboundedSender<RawCastFrame>,
factory: Arc<dyn SocketProvider>,
// 🔥 ТРЕКЕР ОЖИДАЮЩИХ СОЕДИНЕНИЙ: предотвращает SYN-шторм
pending_connects: DashMap<FlowKey, Instant>,
}
impl ConnectionManager {
pub fn new(
dns_handler: DnsHandler,
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
tx_to_tunnel: mpsc::UnboundedSender<RawCastFrame>, // 🔥 Unbounded
factory: Arc<dyn SocketProvider>,
) -> Self {
Self {
@@ -101,7 +100,6 @@ impl ConnectionManager {
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
if frame.event == RawCastEvent::Close {
info!("💀 [Stream {}] Received CLOSE from tunnel", frame.socket_id);
self.tracker.close_tunnel_session(frame.socket_id);
return Ok(());
}
@@ -111,14 +109,12 @@ impl ConnectionManager {
}
if let Some(tx) = self.tracker.get_inbound_tx(frame.socket_id) {
tx.try_send(frame.payload.clone()).map_err(|e| {
if matches!(e, mpsc::error::TrySendError::Closed(_)) {
self.tracker.close_tunnel_session(frame.socket_id);
}
frame
})
// 🔥 UnboundedSender::send не требует await и не возвращает Full
if let Err(_) = tx.send(frame.payload.clone()) {
self.tracker.close_tunnel_session(frame.socket_id);
}
Ok(())
} else {
trace!("👻 [Stream {}] Orphan packet", frame.socket_id);
Ok(())
}
}
@@ -229,21 +225,17 @@ impl ConnectionManager {
fn intercept_tcp(&mut self, f: Flow, socket_set: &mut SocketSet) {
let key = f.to_key();
// 🔥 ФИКС 1: Проверка в таблице ожидающих соединений
if self.pending_connects.contains_key(&key) {
return;
}
if !self.tracker.has_connection_from(f.src, f.src_p, socket_set) {
if socket_set.iter().count() >= 2048 {
if socket_set.iter().count() >= MAX_SOCKETS {
warn!("🔥 TCP Socket limit reached! Dropping SYN.");
return;
}
// Помечаем поток как "в процессе создания"
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);
@@ -266,10 +258,11 @@ impl ConnectionManager {
let socket_id = self.tracker.next_id();
let (dst_ip, target) = self.resolver.resolve_destination(f.dst, f.dst_p);
let socket = self.factory.create_bound_udp(Some(f.dst), f.dst_p);
if socket.is_open() {
let handle = socket_set.add(socket);
// 🔥 Принимаем Unbounded части из конструктора
let (conn, rx_smol, tx_smol) = UdpConnection::new(handle, f.src, f.src_p);
self.tracker.register_udp(handle, socket_id, conn, tx_smol);
@@ -309,17 +302,16 @@ impl ConnectionManager {
return;
}
// Инициализация туннеля при успешном коннекте
if state == tcp::State::Established && self.tracker.should_init_tcp(handle) {
if let (Some(local), Some(remote)) = (socket.local_endpoint(), socket.remote_endpoint())
{
// 🔥 ФИКС 2: Удаляем из pending при установке соединения
let key = (remote.addr, remote.port, local.addr, local.port);
self.pending_connects.remove(&key);
let socket_id = self.tracker.next_id();
let (dst_ip, target) = self.resolver.resolve_destination(local.addr, local.port);
// 🔥 Принимаем Unbounded части
let (conn, rx_smol, tx_smol, handshake_tx) = TcpConnection::new(handle);
self.tracker.register_tcp(handle, socket_id, conn, tx_smol);
@@ -335,30 +327,8 @@ impl ConnectionManager {
}
}
// Очистка мертвых сокетов
if state != tcp::State::Listen && !is_handshaking(state) {
if !socket.may_recv() && !socket.may_send() {
socket.abort();
self.tracker.queue_removal(handle);
return;
}
}
if self
.tracker
.check_pending_timeout(handle, TCP_HANDSHAKE_TIMEOUT)
{
// Если таймаут, нужно тоже попытаться очистить pending_connects (по возрасту в cleanup)
socket.abort();
self.tracker.queue_removal(handle);
return;
}
if let Some(conn) = self.tracker.get_tcp_mut(handle) {
if !conn.tick(socket) {
socket.abort();
self.tracker.queue_removal(handle);
}
let _ = conn.tick(socket);
}
}
@@ -387,6 +357,10 @@ impl ConnectionManager {
self.tracker.enforce_idle_timeouts(GLOBAL_IDLE_TIMEOUT);
self.tracker.cleanup(socket_set);
}
pub fn get_buf_info(&self, handle: SocketHandle) -> usize {
self.tracker.get_app_buffer_info(handle)
}
}
fn is_handshaking(state: tcp::State) -> bool {
+20 -23
View File
@@ -16,7 +16,7 @@ use std::{
};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::mpsc::{self, UnboundedSender}; // 🔥 Добавили UnboundedSender
use tokio::time::{Duration, sleep};
use tun::{DeviceReader, DeviceWriter};
@@ -36,11 +36,11 @@ pub struct Engine {
socket_set: SocketSet<'static>,
manager: ConnectionManager,
device: VirtTunDevice,
to_smoltcp_tx: Sender<TokenBuffer>,
to_smoltcp_tx: UnboundedSender<TokenBuffer>, // 🔥 Стало Unbounded
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
avail: Arc<AtomicBool>,
rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
factory: Arc<dyn SocketProvider>, // 🔥 Сохраняем фабрику для доступа к логам статистики
rx_from_tunnel: mpsc::UnboundedReceiver<RawCastFrame>, // 🔥 Стало Unbounded
factory: Arc<dyn SocketProvider>,
}
impl Engine {
@@ -48,12 +48,13 @@ impl Engine {
config: Config,
caps: DeviceCapabilities,
dns_handler: DnsHandler,
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
tx_to_tunnel: mpsc::UnboundedSender<RawCastFrame>, // 🔥 Стало Unbounded
rx_from_tunnel: mpsc::UnboundedReceiver<RawCastFrame>, // 🔥 Стало Unbounded
factory: Arc<dyn SocketProvider>,
) -> Self {
let now = Engine::current_time();
// VirtTunDevice::new возвращает UnboundedSender и UnboundedReceiver
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
let interface = Interface::new(config, &mut device, now);
@@ -78,19 +79,17 @@ impl Engine {
info!("Current routes: {:?}", self.interface.routes());
let (writer, reader) = tun.split().expect("Failed to split TUN");
let (tun_to_engine_tx, mut tun_to_engine_rx) =
mpsc::channel(NetworkConfig::global().client_tun_capacity);
// TUN Reader теперь тоже Unbounded
let (tun_to_engine_tx, mut tun_to_engine_rx) = mpsc::unbounded_channel::<TokenBuffer>();
Self::spawn_tun_reader(reader, tun_to_engine_tx, self.avail.clone());
let from_smoltcp_rx = self.from_smoltcp_rx.take().expect("Engine started twice");
Self::spawn_tun_writer(writer, from_smoltcp_rx);
// 🔥 Таймер для периодического вывода статистики буферов
let mut last_stats_log = StdInstant::now();
loop {
// 1. Сначала обрабатываем всё, что накопилось в стеке
let mut repeat_poll = true;
while repeat_poll {
self.manager.process_sockets(&mut self.socket_set);
@@ -99,13 +98,13 @@ impl Engine {
repeat_poll = matches!(poll_res, PollResult::SocketStateChanged);
}
// 🔥 Логируем состояние буферов раз в 5 секунд
if last_stats_log.elapsed() >= Duration::from_secs(5) {
self.factory.log_stats(&self.socket_set);
let manager_ref = &self.manager;
self.factory
.log_stats(&self.socket_set, &|handle| manager_ref.get_buf_info(handle));
last_stats_log = StdInstant::now();
}
// 2. Считаем задержку
let delay = self
.interface
.poll_delay(Self::current_time(), &self.socket_set);
@@ -119,11 +118,12 @@ impl Engine {
msg = self.rx_from_tunnel.recv() => {
if let Some(frame) = msg {
let _ = self.manager.try_inject_inbound(frame);
// Выгребаем пачку
let mut count = 0;
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
let _ = self.manager.try_inject_inbound(frame);
count += 1;
if count >= 32 { break; }
if count >= 64 { break; }
}
} else { break; }
}
@@ -131,8 +131,8 @@ impl Engine {
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).await.is_ok() {
// Отправка в Unbounded всегда мгновенная
if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available();
}
} else { break; }
@@ -149,7 +149,7 @@ impl Engine {
fn spawn_tun_reader(
mut reader: DeviceReader,
to_engine: mpsc::Sender<TokenBuffer>,
to_engine: mpsc::UnboundedSender<TokenBuffer>, // 🔥 Unbounded
is_avail: Arc<AtomicBool>,
) {
tokio::spawn(async move {
@@ -162,7 +162,7 @@ impl Engine {
let mut token = TokenBuffer::with_capacity(n);
token.extend_from_slice(&buf[..n]);
if to_engine.send(token).await.is_ok() {
if to_engine.send(token).is_ok() {
is_avail.store(true, Ordering::Release);
} else {
break;
@@ -303,11 +303,8 @@ impl EngineBuilder {
caps.max_transmission_unit = self.config.mtu;
caps.medium = smoltcp::phy::Medium::Ip;
let (tx_to_tunnel, rx_for_client_handler) =
mpsc::channel(NetworkConfig::global().client_tun_capacity);
let (tx_for_client_handler, rx_from_tunnel) =
mpsc::channel(NetworkConfig::global().client_tun_capacity);
let (tx_to_tunnel, rx_for_client_handler) = mpsc::unbounded_channel::<RawCastFrame>();
let (tx_for_client_handler, rx_from_tunnel) = mpsc::unbounded_channel::<RawCastFrame>();
info!("Establishing secure tunnel to proxy server...");
ClientHandler::connect(
+12 -4
View File
@@ -17,7 +17,8 @@ pub struct SessionTracker {
last_activity: HashMap<SocketHandle, StdInstant>,
active_tcp: HashMap<SocketHandle, TcpConnection>,
active_udp: HashMap<SocketHandle, UdpConnection>,
inbound_tx: HashMap<u64, mpsc::Sender<Bytes>>,
// 🔥 Теперь храним UnboundedSender
inbound_tx: HashMap<u64, mpsc::UnboundedSender<Bytes>>,
handle_to_id: HashMap<SocketHandle, u64>,
id_to_handle: HashMap<u64, SocketHandle>,
@@ -56,7 +57,7 @@ impl SessionTracker {
handle: SocketHandle,
id: u64,
conn: TcpConnection,
tx: mpsc::Sender<Bytes>,
tx: mpsc::UnboundedSender<Bytes>, // 🔥 Unbounded
) {
self.pending_tcp.remove(&handle);
self.handle_to_id.insert(handle, id);
@@ -71,7 +72,7 @@ impl SessionTracker {
handle: SocketHandle,
id: u64,
conn: UdpConnection,
tx: mpsc::Sender<Bytes>,
tx: mpsc::UnboundedSender<Bytes>, // 🔥 Unbounded
) {
self.handle_to_id.insert(handle, id);
self.id_to_handle.insert(id, handle);
@@ -122,7 +123,7 @@ impl SessionTracker {
self.last_activity.insert(handle, StdInstant::now());
}
pub fn get_inbound_tx(&self, id: u64) -> Option<&mpsc::Sender<Bytes>> {
pub fn get_inbound_tx(&self, id: u64) -> Option<&mpsc::UnboundedSender<Bytes>> {
self.inbound_tx.get(&id)
}
@@ -171,4 +172,11 @@ impl SessionTracker {
}
}
}
pub fn get_app_buffer_info(&self, handle: SocketHandle) -> usize {
self.active_tcp
.get(&handle)
.map(|conn| conn.app_pending_out_size())
.unwrap_or(0)
}
}
+50 -12
View File
@@ -1,5 +1,5 @@
use netrunner_core::net::NetworkConfig;
use netrunner_logger::info;
use netrunner_core::net::{MAX_SOCKETS, NetworkConfig};
use netrunner_logger::{info, warn};
use smoltcp::{
iface::SocketSet,
socket::{
@@ -45,7 +45,11 @@ pub trait SocketProvider: Send + Sync {
fn reconfigure_tcp(&self, socket: &mut tcp::Socket, profile: TrafficProfile);
// 🔥 НОВЫЙ МЕТОД: Логирование статистики всех сокетов в сете
fn log_stats(&self, sockets: &SocketSet);
fn log_stats(
&self,
sockets: &SocketSet,
get_app_pending: &dyn Fn(smoltcp::iface::SocketHandle) -> usize,
);
}
pub struct SmolSocketFactory {
@@ -103,22 +107,56 @@ impl SocketProvider for SmolSocketFactory {
socket.set_congestion_control(CongestionControl::Bbr);
}
fn log_stats(&self, sockets: &SocketSet) {
fn log_stats(
&self,
sockets: &SocketSet,
get_app_pending: &dyn Fn(smoltcp::iface::SocketHandle) -> usize,
) {
for (handle, socket) in sockets.iter() {
if let smoltcp::socket::Socket::Tcp(tcp_socket) = socket {
if tcp_socket.is_active() {
// Извлекаем текущие емкости динамических буферов
let rx_cap = tcp_socket.recv_capacity();
let tx_cap = tcp_socket.send_capacity();
// 1. Статистика стека (facing the browser)
let tcp_rx_len = tcp_socket.recv_queue(); // Данные от браузера к нам
let tcp_rx_cap = tcp_socket.recv_capacity();
let tcp_tx_len = tcp_socket.send_queue(); // Данные от нас к браузеру
let tcp_tx_cap = tcp_socket.send_capacity();
// 2. Статистика приложения (теневая очередь из туннеля)
// Это данные, которые уже прилетели из Германии/Финляндии,
// но еще не влезли в tcp_socket.send_slice()
let app_pending_len = get_app_pending(handle);
let state = tcp_socket.state();
// Выбираем иконку для статуса
let status_icon = match state {
tcp::State::Established => "",
tcp::State::CloseWait => "", // Сервер закрылся, мы дочищаем хвосты
tcp::State::FinWait1 | tcp::State::FinWait2 => "👋",
_ => "",
};
// Логируем одной строкой для удобства чтения в Logcat
info!(
"📊 [TCP {}] Buffers: RX: {:>4} KB, TX: {:>4} KB | State: {:?}",
"📊 [TCP {}] {:<12} {} | RX_BR: {:>4}/{} KB | TX_BR: {:>4}/{} KB | APP_WAIT: {:>4} KB",
handle,
rx_cap / 1024,
tx_cap / 1024,
state
format!("{:?}", state),
status_icon,
tcp_rx_len / 1024,
tcp_rx_cap / 1024, // Пришло от браузера
tcp_tx_len / 1024,
tcp_tx_cap / 1024, // Ушло браузеру (из стека)
app_pending_len / 1024 // Ждет входа в стек (из туннеля)
);
// Если очередь приложения раздута — это красный флаг
if app_pending_len > 1024 * 1024 {
warn!(
"⚠️ [TCP {}] Bufferbloat detected! Application queue is > 1MB",
handle
);
}
}
}
}
@@ -168,7 +206,7 @@ impl SocketProvider for SmolSocketFactory {
}
fn create_base_set(&self, n_icmp: usize) -> SocketSet<'static> {
let mut sockets = SocketSet::new(Vec::with_capacity(128));
let mut sockets = SocketSet::new(Vec::with_capacity(MAX_SOCKETS));
sockets.add(self.create_bound_udp(None, 53));
for _ in 0..n_icmp {
sockets.add(self.create_icmp(TrafficProfile::Default));