all unbounded and remove AI hallucinations
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -79,8 +79,8 @@ impl DerefMut for TokenBuffer {
|
||||
|
||||
pub struct VirtTunDevice {
|
||||
capabilities: DeviceCapabilities,
|
||||
rx_queue: mpsc::Receiver<TokenBuffer>, // Входящие из TUN оставляем ограниченными (Backpressure)
|
||||
tx_queue: mpsc::UnboundedSender<TokenBuffer>, // 🔥 ФИКС: Исходящие в TUN делаем БЕЗЛИМИТНЫМИ
|
||||
rx_queue: mpsc::UnboundedReceiver<TokenBuffer>,
|
||||
tx_queue: mpsc::UnboundedSender<TokenBuffer>,
|
||||
rx_avail: Arc<AtomicBool>,
|
||||
|
||||
rx_bytes: u64,
|
||||
@@ -102,12 +102,12 @@ impl VirtTunDevice {
|
||||
capabilities: DeviceCapabilities,
|
||||
) -> (
|
||||
Self,
|
||||
mpsc::Sender<TokenBuffer>,
|
||||
mpsc::UnboundedSender<TokenBuffer>,
|
||||
mpsc::UnboundedReceiver<TokenBuffer>,
|
||||
Arc<AtomicBool>,
|
||||
) {
|
||||
let (to_smoltcp_tx, to_smoltcp_rx) = mpsc::channel(128);
|
||||
let (from_smoltcp_tx, from_smoltcp_rx) = mpsc::unbounded_channel(); // 🔥 Безлимитный канал
|
||||
let (to_smoltcp_tx, to_smoltcp_rx) = mpsc::unbounded_channel();
|
||||
let (from_smoltcp_tx, from_smoltcp_rx) = mpsc::unbounded_channel();
|
||||
let rx_avail = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let now = StdInstant::now();
|
||||
@@ -252,4 +252,4 @@ impl phy::TxToken for VirtTxToken<'_> {
|
||||
let _ = self.0.tx_queue.send(buffer);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user