value updates and logs
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
use netrunner_core::proxy::connection::TCP_BUF_SIZE;
|
||||
use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::socket::tcp;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
use crate::connections::CHANNEL_CAPACITY;
|
||||
|
||||
pub enum ConnectionState {
|
||||
Established,
|
||||
Handshaking,
|
||||
@@ -14,16 +15,14 @@ pub enum ConnectionState {
|
||||
pub struct TcpConnection {
|
||||
pub handle: SocketHandle,
|
||||
state: ConnectionState,
|
||||
// UPLOAD: Ограниченный канал для передачи данных наружу
|
||||
tx: mpsc::Sender<Bytes>,
|
||||
// DOWNLOAD: Канал для приема данных из сети
|
||||
rx: mpsc::Receiver<Bytes>,
|
||||
pending_data: BytesMut,
|
||||
handshake_rx: Option<oneshot::Receiver<()>>,
|
||||
}
|
||||
|
||||
const MAX_PENDING: usize = 32 * 512 * 1024;
|
||||
const TCP_CHUNK_SIZE: usize = 65536;
|
||||
const MAX_PENDING: usize = 64 * 1024;
|
||||
const TCP_CHUNK_SIZE: usize = 1024 * 16;
|
||||
|
||||
impl TcpConnection {
|
||||
pub fn new(
|
||||
@@ -34,8 +33,8 @@ impl TcpConnection {
|
||||
mpsc::Sender<Bytes>,
|
||||
oneshot::Sender<()>,
|
||||
) {
|
||||
let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(TCP_BUF_SIZE);
|
||||
let (tx_to_smol, rx_from_net) = 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>(CHANNEL_CAPACITY);
|
||||
let (handshake_tx, handshake_rx) = oneshot::channel();
|
||||
|
||||
let conn = Self {
|
||||
@@ -107,74 +106,92 @@ impl TcpConnection {
|
||||
}
|
||||
|
||||
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
|
||||
// 1. UPLOAD: Чанкинг и Backpressure
|
||||
if socket.can_recv() {
|
||||
while socket.can_recv() {
|
||||
let mut channel_full = false;
|
||||
let mut channel_closed = false;
|
||||
while socket.can_recv() {
|
||||
let mut full = false;
|
||||
|
||||
let _ = socket.recv(|data| {
|
||||
if data.is_empty() {
|
||||
return (0, ());
|
||||
// Используем recv_slice для контроля размера чанка
|
||||
let mut temp = [0u8; TCP_CHUNK_SIZE];
|
||||
|
||||
// Peek, чтобы проверить, сможем ли мы отправить данные, прежде чем извлечь их
|
||||
if let Ok(n) = socket.peek_slice(&mut temp) {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let chunk = Bytes::copy_from_slice(&temp[..n]);
|
||||
match self.tx.try_send(chunk) {
|
||||
Ok(_) => {
|
||||
// Только если успешно отправили в канал, удаляем данные из сокета
|
||||
socket.recv_slice(&mut temp[..n]).unwrap();
|
||||
}
|
||||
|
||||
let chunk_size = std::cmp::min(data.len(), TCP_CHUNK_SIZE);
|
||||
let chunk = &data[..chunk_size];
|
||||
|
||||
match self.tx.try_send(Bytes::copy_from_slice(chunk)) {
|
||||
Ok(_) => (chunk_size, ()),
|
||||
Err(mpsc::error::TrySendError::Full(_)) => {
|
||||
channel_full = true;
|
||||
(0, ()) // Оставляем данные в smoltcp
|
||||
}
|
||||
Err(_) => {
|
||||
channel_closed = true;
|
||||
(0, ())
|
||||
}
|
||||
Err(mpsc::error::TrySendError::Full(_)) => {
|
||||
full = true; // Канал забит, сработает Backpressure в smoltcp
|
||||
}
|
||||
});
|
||||
Err(_) => {
|
||||
self.state = ConnectionState::Closed;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. DOWNLOAD: Сброс ограниченного канала в буфер
|
||||
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
|
||||
// 3. DOWNLOAD: Отправка накопленного буфера в smoltcp
|
||||
if !self.pending_data.is_empty() && socket.can_send() {
|
||||
match socket.send_slice(&self.pending_data) {
|
||||
Ok(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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use bytes::Bytes;
|
||||
use netrunner_core::{
|
||||
protocol::codec::{frame::FrameType, socks::TargetAddress},
|
||||
proxy::connection::{
|
||||
TCP_BUF_SIZE,
|
||||
muxer::{MuxMessage, Muxer},
|
||||
},
|
||||
proxy::connection::muxer::{MuxMessage, Muxer},
|
||||
};
|
||||
use netrunner_logger::{debug, info, warn};
|
||||
use netrunner_logger::{debug, error, info, warn};
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
socket::{AnySocket, icmp, tcp, udp},
|
||||
@@ -74,11 +71,10 @@ impl ConnectionManager {
|
||||
let local_endpoint = match socket.local_endpoint() {
|
||||
Some(ep) => ep,
|
||||
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);
|
||||
}
|
||||
};
|
||||
debug!(remote_addr = %local_endpoint.addr, remote_port = %local_endpoint.port, "SMOLTCP RAW REMOTE ENDPOINT");
|
||||
|
||||
let port = local_endpoint.port;
|
||||
let ip = local_endpoint.addr;
|
||||
@@ -86,27 +82,30 @@ impl ConnectionManager {
|
||||
match ip {
|
||||
smoltcp::wire::IpAddress::Ipv4(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) {
|
||||
debug!(target=%domain, port=%port, "Resolved fake IP to domain");
|
||||
return TargetAddress::Domain(domain, port);
|
||||
debug!(ip=%std_ip, domain=%domain, elapsed=?start.elapsed(), "FakeIP match found");
|
||||
TargetAddress::Domain(domain, port)
|
||||
} 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)");
|
||||
TargetAddress::Ipv4(std_ip, port)
|
||||
}
|
||||
|
||||
debug!(ip=%std_ip, port=%port, "Using raw IP target");
|
||||
TargetAddress::Ipv4(std_ip, port)
|
||||
}
|
||||
smoltcp::wire::IpAddress::Ipv6(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
if let Some(tcp) = tcp::Socket::downcast_mut(socket) {
|
||||
self.handle_tcp(handle, tcp);
|
||||
@@ -122,11 +121,9 @@ impl ConnectionManager {
|
||||
use tcp::State;
|
||||
|
||||
if socket.state() == State::Closed {
|
||||
if self.active_tcp_sessions.contains_key(&handle) {
|
||||
debug!(%handle, "TCP session closed, removing from active sessions");
|
||||
self.active_tcp_sessions.remove(&handle);
|
||||
if self.active_tcp_sessions.remove(&handle).is_some() {
|
||||
info!(%handle, "TCP Session removed: local socket closed");
|
||||
}
|
||||
|
||||
if !self.sockets_to_remove.contains(&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) {
|
||||
let start_establish = StdInstant::now();
|
||||
let target = self.resolve_target(socket);
|
||||
|
||||
if let TargetAddress::Domain(d, _) = &target {
|
||||
if d == "disconnected" {
|
||||
warn!(%handle, "Aborting socket: target unresolved");
|
||||
socket.abort();
|
||||
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);
|
||||
self.active_tcp_sessions.insert(handle, conn);
|
||||
|
||||
// 2. Выносим логику мультиплексирования в фоновую задачу
|
||||
let muxer = self.muxer.clone();
|
||||
let stream_id = muxer.next_id();
|
||||
let connect_payload = target.to_string();
|
||||
|
||||
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);
|
||||
|
||||
let send_start = StdInstant::now();
|
||||
if muxer
|
||||
.send_to_netwrok(MuxMessage {
|
||||
stream_id,
|
||||
@@ -167,24 +165,25 @@ impl ConnectionManager {
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
error!(stream_id, "Muxer: Failed to send Connect frame");
|
||||
muxer.remove_stream(stream_id);
|
||||
return;
|
||||
}
|
||||
|
||||
let first_payload =
|
||||
tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await;
|
||||
match first_payload {
|
||||
// Ждем подтверждения от прокси (handshake)
|
||||
match tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await {
|
||||
Ok(Some(data)) => {
|
||||
if data.len() >= 2 && data[1] == 0x00 {
|
||||
info!(stream_id, %handle, elapsed=?send_start.elapsed(), "Muxer: Handshake success");
|
||||
let _ = handshake_tx.send(());
|
||||
} else {
|
||||
netrunner_logger::warn!(stream_id, "Server rejected TCP connection");
|
||||
warn!(stream_id, "Muxer: Connection rejected by proxy server");
|
||||
muxer.remove_stream(stream_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
netrunner_logger::error!(stream_id, "Timeout waiting for proxy response");
|
||||
error!(stream_id, "Muxer: Handshake timeout (10s)");
|
||||
muxer.remove_stream(stream_id);
|
||||
return;
|
||||
}
|
||||
@@ -192,12 +191,15 @@ impl ConnectionManager {
|
||||
|
||||
let to_proxy = async {
|
||||
while let Some(data) = rx_from_smol.recv().await {
|
||||
let msg = MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Data,
|
||||
data,
|
||||
};
|
||||
if muxer.send_to_netwrok(msg).await.is_err() {
|
||||
if muxer
|
||||
.send_to_netwrok(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Data,
|
||||
data,
|
||||
})
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -205,18 +207,15 @@ impl ConnectionManager {
|
||||
|
||||
let from_proxy = async {
|
||||
while let Some(data) = v_rx.recv().await {
|
||||
if data.is_empty() {
|
||||
break;
|
||||
}
|
||||
if tx_to_smol.send(data).await.is_err() {
|
||||
if data.is_empty() || tx_to_smol.send(data).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokio::select! {
|
||||
_ = to_proxy => {}
|
||||
_ = from_proxy => {}
|
||||
_ = to_proxy => debug!(stream_id, "Stream direction: SMOL -> Proxy closed"),
|
||||
_ = from_proxy => debug!(stream_id, "Stream direction: Proxy -> SMOL closed"),
|
||||
}
|
||||
|
||||
let _ = muxer
|
||||
@@ -227,17 +226,19 @@ impl ConnectionManager {
|
||||
})
|
||||
.await;
|
||||
muxer.remove_stream(stream_id);
|
||||
debug!(stream_id, "Muxer stream fully removed");
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) {
|
||||
if !conn.tick(socket) {
|
||||
debug!(%handle, "Connection tick failed, aborting.");
|
||||
debug!(%handle, "TCP Connection tick failed (buffer full or error), aborting");
|
||||
socket.abort();
|
||||
}
|
||||
}
|
||||
|
||||
if socket.state() == State::CloseWait {
|
||||
debug!(%handle, "Socket in CloseWait, closing locally");
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
@@ -362,7 +363,7 @@ impl ConnectionManager {
|
||||
|
||||
fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> {
|
||||
let buf_size = match port {
|
||||
443 | 80 => 512 * 1024,
|
||||
443 | 80 => 1024 * 1024 * 2,
|
||||
22 => 32 * 1024,
|
||||
53 => 16 * 1024,
|
||||
_ => 128 * 1024,
|
||||
@@ -377,7 +378,6 @@ impl ConnectionManager {
|
||||
socket.set_ack_delay(None);
|
||||
socket
|
||||
}
|
||||
|
||||
pub fn try_create_socket_from_packet(&mut self, packet: &[u8], socket_set: &mut SocketSet) {
|
||||
let Ok(ip_packet) = Ipv4Packet::new_checked(packet) else {
|
||||
return;
|
||||
@@ -394,16 +394,18 @@ impl ConnectionManager {
|
||||
let dst_addr = ip_packet.dst_addr();
|
||||
|
||||
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 endpoint = IpListenEndpoint {
|
||||
addr: Some(dst_addr.into()),
|
||||
port: dst_port,
|
||||
};
|
||||
|
||||
if let Ok(_) = socket.listen(endpoint) {
|
||||
socket_set.add(socket);
|
||||
if socket.listen(endpoint).is_ok() {
|
||||
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) {
|
||||
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 endpoint = IpListenEndpoint {
|
||||
addr: Some(dst_addr.into()),
|
||||
port: dst_port,
|
||||
};
|
||||
|
||||
if let Ok(_) = socket.bind(endpoint) {
|
||||
socket_set.add(socket);
|
||||
if socket.bind(endpoint).is_ok() {
|
||||
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> {
|
||||
let (buf_size, packet_count) = match port {
|
||||
443 => (512 * 1024, 380), // Большой буфер для QUIC (YouTube)
|
||||
53 => (16 * 1024, 32), // DNS
|
||||
443 => (512 * 1024, 390), // Большой буфер для QUIC (YouTube)
|
||||
53 => (64 * 1024, 32), // DNS
|
||||
_ => (128 * 1024, 100),
|
||||
};
|
||||
|
||||
@@ -501,10 +503,12 @@ impl ConnectionManager {
|
||||
|
||||
pub fn cleanup(&mut self, socket_set: &mut SocketSet) {
|
||||
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);
|
||||
self.last_activity.remove(&handle);
|
||||
self.failed_until.remove(&handle);
|
||||
self.active_tcp_sessions.remove(&handle);
|
||||
self.active_udp_sessions.remove(&handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user