value updates and logs

This commit is contained in:
2026-03-22 00:21:32 +07:00
parent 46e8b4ae73
commit d0576bddf3
5 changed files with 142 additions and 121 deletions
+72 -55
View File
@@ -1,9 +1,10 @@
use bytes::{Buf, Bytes, BytesMut}; use bytes::{Buf, Bytes, BytesMut};
use netrunner_core::proxy::connection::TCP_BUF_SIZE;
use smoltcp::iface::SocketHandle; use smoltcp::iface::SocketHandle;
use smoltcp::socket::tcp; use smoltcp::socket::tcp;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
use crate::connections::CHANNEL_CAPACITY;
pub enum ConnectionState { pub enum ConnectionState {
Established, Established,
Handshaking, Handshaking,
@@ -14,16 +15,14 @@ pub enum ConnectionState {
pub struct TcpConnection { pub struct TcpConnection {
pub handle: SocketHandle, pub handle: SocketHandle,
state: ConnectionState, state: ConnectionState,
// UPLOAD: Ограниченный канал для передачи данных наружу
tx: mpsc::Sender<Bytes>, tx: mpsc::Sender<Bytes>,
// DOWNLOAD: Канал для приема данных из сети
rx: mpsc::Receiver<Bytes>, rx: mpsc::Receiver<Bytes>,
pending_data: BytesMut, pending_data: BytesMut,
handshake_rx: Option<oneshot::Receiver<()>>, handshake_rx: Option<oneshot::Receiver<()>>,
} }
const MAX_PENDING: usize = 32 * 512 * 1024; const MAX_PENDING: usize = 64 * 1024;
const TCP_CHUNK_SIZE: usize = 65536; const TCP_CHUNK_SIZE: usize = 1024 * 16;
impl TcpConnection { impl TcpConnection {
pub fn new( pub fn new(
@@ -34,8 +33,8 @@ impl TcpConnection {
mpsc::Sender<Bytes>, mpsc::Sender<Bytes>,
oneshot::Sender<()>, oneshot::Sender<()>,
) { ) {
let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(TCP_BUF_SIZE); let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(TCP_BUF_SIZE); let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (handshake_tx, handshake_rx) = oneshot::channel(); let (handshake_tx, handshake_rx) = oneshot::channel();
let conn = Self { let conn = Self {
@@ -107,74 +106,92 @@ impl TcpConnection {
} }
fn poll_and_process(&mut self, socket: &mut tcp::Socket) { fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
// 1. UPLOAD: Чанкинг и Backpressure
if socket.can_recv() {
while socket.can_recv() { while socket.can_recv() {
let mut channel_full = false; let mut full = false;
let mut channel_closed = false;
let _ = socket.recv(|data| { // Используем recv_slice для контроля размера чанка
if data.is_empty() { let mut temp = [0u8; TCP_CHUNK_SIZE];
return (0, ());
// Peek, чтобы проверить, сможем ли мы отправить данные, прежде чем извлечь их
if let Ok(n) = socket.peek_slice(&mut temp) {
if n == 0 {
break;
} }
let chunk_size = std::cmp::min(data.len(), TCP_CHUNK_SIZE); let chunk = Bytes::copy_from_slice(&temp[..n]);
let chunk = &data[..chunk_size]; match self.tx.try_send(chunk) {
Ok(_) => {
match self.tx.try_send(Bytes::copy_from_slice(chunk)) { // Только если успешно отправили в канал, удаляем данные из сокета
Ok(_) => (chunk_size, ()), socket.recv_slice(&mut temp[..n]).unwrap();
}
Err(mpsc::error::TrySendError::Full(_)) => { Err(mpsc::error::TrySendError::Full(_)) => {
channel_full = true; full = true; // Канал забит, сработает Backpressure в smoltcp
(0, ()) // Оставляем данные в smoltcp
} }
Err(_) => { Err(_) => {
channel_closed = true; self.state = ConnectionState::Closed;
(0, ()) return;
} }
} }
}); } else {
break;
}
if channel_full || channel_closed { if full {
break;
}
}
let current_pending = self.pending_data.len();
// Считаем % заполненности для логирования
let fill_ratio = (current_pending as f32 / MAX_PENDING as f32) * 100.0;
if current_pending >= MAX_PENDING {
// Состояние активного Backpressure
netrunner_logger::warn!(
%self.handle,
"Backpressure ACTIVE: Buffer is FULL ({} bytes). Stalling RX channel.",
current_pending
);
} else if fill_ratio > 80.0 {
// Состояние Bufferbloat (буфер почти полон, пакеты задерживаются)
netrunner_logger::info!(
%self.handle,
"Bufferbloat Warning: Buffer {:.1}% full ({} bytes). Latency increasing.",
fill_ratio, current_pending
);
// Продолжаем читать, пока есть хоть какое-то место
while let Ok(data) = self.rx.try_recv() {
self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING {
break;
}
}
} else {
// Обычный режим
while let Ok(data) = self.rx.try_recv() {
self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING {
break; break;
} }
} }
} }
// 2. DOWNLOAD: Сброс ограниченного канала в буфер // 3. DOWNLOAD: Отправка накопленного буфера в smoltcp
while let Ok(data) = self.rx.try_recv() {
if self.pending_data.is_empty() && socket.can_send() {
match socket.send_slice(&data) {
Ok(n) if n < data.len() => {
self.pending_data.extend_from_slice(&data[n..]);
}
Ok(_) => {}
Err(_) => {
self.pending_data.extend_from_slice(&data);
}
}
} else {
self.pending_data.extend_from_slice(&data);
}
if self.pending_data.len() > MAX_PENDING {
netrunner_logger::error!(
%self.handle,
"TCP Buffer overflow ({} bytes). Dropping connection.",
self.pending_data.len()
);
socket.abort();
self.state = ConnectionState::Closed;
return;
}
}
// 3. DOWNLOAD: Отправка буфера в smoltcp
if !self.pending_data.is_empty() && socket.can_send() { if !self.pending_data.is_empty() && socket.can_send() {
match socket.send_slice(&self.pending_data) { match socket.send_slice(&self.pending_data) {
Ok(n) => { Ok(n) => {
self.pending_data.advance(n); self.pending_data.advance(n);
// Лог освобождения (опционально, чтобы видеть, что пробка рассасывается)
if n > 0 && self.pending_data.len() < (MAX_PENDING / 2) && fill_ratio > 90.0 {
netrunner_logger::info!(%self.handle, "Backpressure RELIEVED: Buffer drained to {} bytes", self.pending_data.len());
}
}
Err(e) => {
netrunner_logger::debug!(%self.handle, "Smoltcp socket send error: {:?}", e);
} }
Err(_) => {}
} }
} }
} }
+55 -51
View File
@@ -1,12 +1,9 @@
use bytes::Bytes; use bytes::Bytes;
use netrunner_core::{ use netrunner_core::{
protocol::codec::{frame::FrameType, socks::TargetAddress}, protocol::codec::{frame::FrameType, socks::TargetAddress},
proxy::connection::{ proxy::connection::muxer::{MuxMessage, Muxer},
TCP_BUF_SIZE,
muxer::{MuxMessage, Muxer},
},
}; };
use netrunner_logger::{debug, info, warn}; use netrunner_logger::{debug, error, info, warn};
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
socket::{AnySocket, icmp, tcp, udp}, socket::{AnySocket, icmp, tcp, udp},
@@ -74,11 +71,10 @@ impl ConnectionManager {
let local_endpoint = match socket.local_endpoint() { let local_endpoint = match socket.local_endpoint() {
Some(ep) => ep, Some(ep) => ep,
None => { None => {
warn!(handle=?socket, "Attempted to resolve target for an unconnected socket"); warn!(handle=?socket, "Target resolution failed: socket disconnected or no local endpoint");
return TargetAddress::Domain("disconnected".to_string(), 0); return TargetAddress::Domain("disconnected".to_string(), 0);
} }
}; };
debug!(remote_addr = %local_endpoint.addr, remote_port = %local_endpoint.port, "SMOLTCP RAW REMOTE ENDPOINT");
let port = local_endpoint.port; let port = local_endpoint.port;
let ip = local_endpoint.addr; let ip = local_endpoint.addr;
@@ -86,27 +82,30 @@ impl ConnectionManager {
match ip { match ip {
smoltcp::wire::IpAddress::Ipv4(ipv4_addr) => { smoltcp::wire::IpAddress::Ipv4(ipv4_addr) => {
let std_ip = std::net::Ipv4Addr::from(ipv4_addr); let std_ip = std::net::Ipv4Addr::from(ipv4_addr);
let start = StdInstant::now();
debug!(ip=%std_ip, "Trying to resolve IP in FakeIpStore");
if let Some(domain) = self.fake_ip_store.lookup_by_ip(&std_ip) { if let Some(domain) = self.fake_ip_store.lookup_by_ip(&std_ip) {
debug!(target=%domain, port=%port, "Resolved fake IP to domain"); debug!(ip=%std_ip, domain=%domain, elapsed=?start.elapsed(), "FakeIP match found");
return TargetAddress::Domain(domain, port); TargetAddress::Domain(domain, port)
} else { } else {
warn!(ip=%std_ip, "IP not found in FakeIpStore! SOCKS request will fail."); warn!(ip=%std_ip, port=%port, "IP not found in FakeIpStore. Using raw IP (possible DNS lag)");
}
debug!(ip=%std_ip, port=%port, "Using raw IP target");
TargetAddress::Ipv4(std_ip, port) TargetAddress::Ipv4(std_ip, port)
} }
}
smoltcp::wire::IpAddress::Ipv6(ipv6_addr) => { smoltcp::wire::IpAddress::Ipv6(ipv6_addr) => {
let std_ip = std::net::Ipv6Addr::from(ipv6_addr); let std_ip = std::net::Ipv6Addr::from(ipv6_addr);
debug!(ip=%std_ip, port=%port, "Using IPv6 target"); debug!(ip=%std_ip, port=%port, "Using raw IPv6 target");
TargetAddress::Ipv6(std_ip, port) TargetAddress::Ipv6(std_ip, port)
} }
} }
} }
pub fn process_sockets(&mut self, socket_set: &mut SocketSet) { pub fn process_sockets(&mut self, socket_set: &mut SocketSet) {
// Добавим лог в начало цикла, если сессий много, чтобы видеть нагрузку
if !self.active_tcp_sessions.is_empty() || !self.active_udp_sessions.is_empty() {
// trace!("Processing sockets: TCP={}, UDP={}", self.active_tcp_sessions.len(), self.active_udp_sessions.len());
}
for (handle, socket) in socket_set.iter_mut() { for (handle, socket) in socket_set.iter_mut() {
if let Some(tcp) = tcp::Socket::downcast_mut(socket) { if let Some(tcp) = tcp::Socket::downcast_mut(socket) {
self.handle_tcp(handle, tcp); self.handle_tcp(handle, tcp);
@@ -122,11 +121,9 @@ impl ConnectionManager {
use tcp::State; use tcp::State;
if socket.state() == State::Closed { if socket.state() == State::Closed {
if self.active_tcp_sessions.contains_key(&handle) { if self.active_tcp_sessions.remove(&handle).is_some() {
debug!(%handle, "TCP session closed, removing from active sessions"); info!(%handle, "TCP Session removed: local socket closed");
self.active_tcp_sessions.remove(&handle);
} }
if !self.sockets_to_remove.contains(&handle) { if !self.sockets_to_remove.contains(&handle) {
self.sockets_to_remove.push(handle); self.sockets_to_remove.push(handle);
} }
@@ -134,30 +131,31 @@ impl ConnectionManager {
} }
if socket.state() == State::Established && !self.active_tcp_sessions.contains_key(&handle) { if socket.state() == State::Established && !self.active_tcp_sessions.contains_key(&handle) {
let start_establish = StdInstant::now();
let target = self.resolve_target(socket); let target = self.resolve_target(socket);
if let TargetAddress::Domain(d, _) = &target { if let TargetAddress::Domain(d, _) = &target {
if d == "disconnected" { if d == "disconnected" {
warn!(%handle, "Aborting socket: target unresolved");
socket.abort(); socket.abort();
return; return;
} }
} }
info!(%handle, "New TCP session established for target: {:?}", target); info!(%handle, target = %target, "New TCP session: Local established, starting Muxer handshake");
// 1. Создаем соединение и получаем каналы
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle); let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
self.active_tcp_sessions.insert(handle, conn); self.active_tcp_sessions.insert(handle, conn);
// 2. Выносим логику мультиплексирования в фоновую задачу
let muxer = self.muxer.clone(); let muxer = self.muxer.clone();
let stream_id = muxer.next_id(); let stream_id = muxer.next_id();
let connect_payload = target.to_string(); let connect_payload = target.to_string();
tokio::spawn(async move { tokio::spawn(async move {
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(TCP_BUF_SIZE); let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
muxer.register_stream(stream_id, v_tx); muxer.register_stream(stream_id, v_tx);
let send_start = StdInstant::now();
if muxer if muxer
.send_to_netwrok(MuxMessage { .send_to_netwrok(MuxMessage {
stream_id, stream_id,
@@ -167,24 +165,25 @@ impl ConnectionManager {
.await .await
.is_err() .is_err()
{ {
error!(stream_id, "Muxer: Failed to send Connect frame");
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
return; return;
} }
let first_payload = // Ждем подтверждения от прокси (handshake)
tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await; match tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await {
match first_payload {
Ok(Some(data)) => { Ok(Some(data)) => {
if data.len() >= 2 && data[1] == 0x00 { if data.len() >= 2 && data[1] == 0x00 {
info!(stream_id, %handle, elapsed=?send_start.elapsed(), "Muxer: Handshake success");
let _ = handshake_tx.send(()); let _ = handshake_tx.send(());
} else { } else {
netrunner_logger::warn!(stream_id, "Server rejected TCP connection"); warn!(stream_id, "Muxer: Connection rejected by proxy server");
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
return; return;
} }
} }
_ => { _ => {
netrunner_logger::error!(stream_id, "Timeout waiting for proxy response"); error!(stream_id, "Muxer: Handshake timeout (10s)");
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
return; return;
} }
@@ -192,12 +191,15 @@ impl ConnectionManager {
let to_proxy = async { let to_proxy = async {
while let Some(data) = rx_from_smol.recv().await { while let Some(data) = rx_from_smol.recv().await {
let msg = MuxMessage { if muxer
.send_to_netwrok(MuxMessage {
stream_id, stream_id,
frame_type: FrameType::Data, frame_type: FrameType::Data,
data, data,
}; })
if muxer.send_to_netwrok(msg).await.is_err() { .await
.is_err()
{
break; break;
} }
} }
@@ -205,18 +207,15 @@ impl ConnectionManager {
let from_proxy = async { let from_proxy = async {
while let Some(data) = v_rx.recv().await { while let Some(data) = v_rx.recv().await {
if data.is_empty() { if data.is_empty() || tx_to_smol.send(data).await.is_err() {
break;
}
if tx_to_smol.send(data).await.is_err() {
break; break;
} }
} }
}; };
tokio::select! { tokio::select! {
_ = to_proxy => {} _ = to_proxy => debug!(stream_id, "Stream direction: SMOL -> Proxy closed"),
_ = from_proxy => {} _ = from_proxy => debug!(stream_id, "Stream direction: Proxy -> SMOL closed"),
} }
let _ = muxer let _ = muxer
@@ -227,17 +226,19 @@ impl ConnectionManager {
}) })
.await; .await;
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
debug!(stream_id, "Muxer stream fully removed");
}); });
} }
if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) { if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) {
if !conn.tick(socket) { if !conn.tick(socket) {
debug!(%handle, "Connection tick failed, aborting."); debug!(%handle, "TCP Connection tick failed (buffer full or error), aborting");
socket.abort(); socket.abort();
} }
} }
if socket.state() == State::CloseWait { if socket.state() == State::CloseWait {
debug!(%handle, "Socket in CloseWait, closing locally");
socket.close(); socket.close();
} }
} }
@@ -362,7 +363,7 @@ impl ConnectionManager {
fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> { fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> {
let buf_size = match port { let buf_size = match port {
443 | 80 => 512 * 1024, 443 | 80 => 1024 * 1024 * 2,
22 => 32 * 1024, 22 => 32 * 1024,
53 => 16 * 1024, 53 => 16 * 1024,
_ => 128 * 1024, _ => 128 * 1024,
@@ -377,7 +378,6 @@ impl ConnectionManager {
socket.set_ack_delay(None); socket.set_ack_delay(None);
socket socket
} }
pub fn try_create_socket_from_packet(&mut self, packet: &[u8], socket_set: &mut SocketSet) { pub fn try_create_socket_from_packet(&mut self, packet: &[u8], socket_set: &mut SocketSet) {
let Ok(ip_packet) = Ipv4Packet::new_checked(packet) else { let Ok(ip_packet) = Ipv4Packet::new_checked(packet) else {
return; return;
@@ -394,16 +394,18 @@ impl ConnectionManager {
let dst_addr = ip_packet.dst_addr(); let dst_addr = ip_packet.dst_addr();
if !self.has_active_tcp_session(socket_set, dst_addr.into(), dst_port) { if !self.has_active_tcp_session(socket_set, dst_addr.into(), dst_port) {
debug!(target: "netrunner", "Dynamic TCP: Creating socket for {}:{}", dst_addr, dst_port); let start_create = StdInstant::now();
let mut socket = Self::create_dynamic_tcp_socket(dst_port); let mut socket = Self::create_dynamic_tcp_socket(dst_port);
let endpoint = IpListenEndpoint { let endpoint = IpListenEndpoint {
addr: Some(dst_addr.into()), addr: Some(dst_addr.into()),
port: dst_port, port: dst_port,
}; };
if let Ok(_) = socket.listen(endpoint) { if socket.listen(endpoint).is_ok() {
socket_set.add(socket); let handle = socket_set.add(socket);
debug!(%handle, "Dynamic TCP: Created for {}:{} in {:?}", dst_addr, dst_port, start_create.elapsed());
} else {
warn!("Dynamic TCP: Failed to listen on {}:{}", dst_addr, dst_port);
} }
} }
} }
@@ -420,16 +422,16 @@ impl ConnectionManager {
} }
if !self.has_active_udp_session(socket_set, dst_addr.into(), dst_port) { if !self.has_active_udp_session(socket_set, dst_addr.into(), dst_port) {
netrunner_logger::debug!(target: "netrunner", "Dynamic UDP: Creating socket for {}:{}", dst_addr, dst_port); let start_create = StdInstant::now();
let mut socket = Self::create_dynamic_udp_socket(dst_port); let mut socket = Self::create_dynamic_udp_socket(dst_port);
let endpoint = IpListenEndpoint { let endpoint = IpListenEndpoint {
addr: Some(dst_addr.into()), addr: Some(dst_addr.into()),
port: dst_port, port: dst_port,
}; };
if let Ok(_) = socket.bind(endpoint) { if socket.bind(endpoint).is_ok() {
socket_set.add(socket); let handle = socket_set.add(socket);
debug!(%handle, "Dynamic UDP: Created for {}:{} in {:?}", dst_addr, dst_port, start_create.elapsed());
} }
} }
} }
@@ -439,8 +441,8 @@ impl ConnectionManager {
fn create_dynamic_udp_socket<'a>(port: u16) -> udp::Socket<'a> { fn create_dynamic_udp_socket<'a>(port: u16) -> udp::Socket<'a> {
let (buf_size, packet_count) = match port { let (buf_size, packet_count) = match port {
443 => (512 * 1024, 380), // Большой буфер для QUIC (YouTube) 443 => (512 * 1024, 390), // Большой буфер для QUIC (YouTube)
53 => (16 * 1024, 32), // DNS 53 => (64 * 1024, 32), // DNS
_ => (128 * 1024, 100), _ => (128 * 1024, 100),
}; };
@@ -501,10 +503,12 @@ impl ConnectionManager {
pub fn cleanup(&mut self, socket_set: &mut SocketSet) { pub fn cleanup(&mut self, socket_set: &mut SocketSet) {
for handle in self.sockets_to_remove.drain(..) { for handle in self.sockets_to_remove.drain(..) {
debug!(%handle, "Memory free: removing socket from set"); debug!(%handle, "Cleanup: Removing socket from SocketSet and internal maps");
socket_set.remove(handle); socket_set.remove(handle);
self.last_activity.remove(&handle); self.last_activity.remove(&handle);
self.failed_until.remove(&handle); self.failed_until.remove(&handle);
self.active_tcp_sessions.remove(&handle);
self.active_udp_sessions.remove(&handle);
} }
} }
+1 -1
View File
@@ -179,7 +179,7 @@ impl TunnelEngine {
codec: &Arc<Mutex<Codec>>, codec: &Arc<Mutex<Codec>>,
msg: MuxMessage, msg: MuxMessage,
) -> Result<(), String> { ) -> Result<(), String> {
const MAX_CHUNK_SIZE: usize = 16000; const MAX_CHUNK_SIZE: usize = 1024 * 4;
let mut data = msg.data; let mut data = msg.data;
let stream_id = msg.stream_id; let stream_id = msg.stream_id;
+3 -3
View File
@@ -4,6 +4,6 @@ pub mod engine;
pub mod handler; pub mod handler;
pub mod muxer; pub mod muxer;
pub const TCP_BUF_SIZE: usize = 1024 * 512; pub const TCP_BUF_SIZE: usize = 1024 * 32;
pub const UDP_BUF_SIZE: usize = 1024 * 64; pub const UDP_BUF_SIZE: usize = 1024 * 8;
pub const MESSAGE_CHANNEL_SIZE: usize = 1024 * 16; pub const MESSAGE_CHANNEL_SIZE: usize = 4;
+2 -2
View File
@@ -133,8 +133,8 @@ impl Network {
} }
// --- ИЗМЕНЕНИЯ ЗДЕСЬ: Создаем два канала --- // --- ИЗМЕНЕНИЯ ЗДЕСЬ: Создаем два канала ---
let (control_tx, control_rx) = tokio::sync::mpsc::channel(100_000); let (control_tx, control_rx) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_SIZE * 4);
let (data_tx, data_rx) = tokio::sync::mpsc::channel(100_000); let (data_tx, data_rx) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_SIZE * 4);
// Передаем оба трансмиттера в Muxer // Передаем оба трансмиттера в Muxer
let muxer = Muxer::new(control_tx, data_tx, true); let muxer = Muxer::new(control_tx, data_tx, true);