start remake local proxy

This commit is contained in:
2026-03-24 21:38:43 +07:00
parent 61e88559c8
commit 743c6bf05c
10 changed files with 310 additions and 152 deletions
+43 -13
View File
@@ -3,12 +3,14 @@ uniffi::setup_scaffolding!();
mod net; mod net;
mod tun; mod tun;
pub use crate::tun::{routing, tun::Tun}; //for desktop test in main.rs pub use crate::tun::{routing, tun::Tun};
use crate::{ use crate::{
net::engine::{EngineBuilder, EngineConfig}, net::engine::{EngineBuilder, EngineConfig},
tun::routing::reset_platform_routing, tun::routing::reset_platform_routing,
}; };
use netrunner_core::proxy::connection::connection::ConnectionRole;
use netrunner_core::proxy::network::Network;
use netrunner_logger::{error, info}; use netrunner_logger::{error, info};
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@@ -16,7 +18,6 @@ use tokio_util::sync::CancellationToken;
pub static RUNTIME: OnceLock<Runtime> = OnceLock::new(); pub static RUNTIME: OnceLock<Runtime> = OnceLock::new();
// Инициализация Tokio Runtime
fn get_runtime() -> &'static Runtime { fn get_runtime() -> &'static Runtime {
RUNTIME.get_or_init(|| { RUNTIME.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
@@ -54,7 +55,7 @@ impl Drop for Session {
} }
// ========================================== // ==========================================
// SESSION MANAGER (Публичный API UniFFI) // SESSION MANAGER
// ========================================== // ==========================================
#[derive(uniffi::Object)] #[derive(uniffi::Object)]
@@ -75,14 +76,10 @@ impl SessionManager {
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format"); let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let remote_proxy_ip = addr.ip().to_string(); let remote_proxy_ip = addr.ip().to_string();
// 1. Создаем базовый конфиг
let mut config = EngineConfig::new(&remote_address).with_cache_path(&cache_dir); let mut config = EngineConfig::new(&remote_address).with_cache_path(&cache_dir);
// 2. Тонкая настройка под платформу
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
{ {
// На мобилках роутингом обычно управляет VpnService (Android) или NEPacketTunnelProvider (iOS)
// Поэтому отключаем попытки движка менять системные таблицы роутинга напрямую
config = config.disable_routing().with_mtu(1280); config = config.disable_routing().with_mtu(1280);
} }
@@ -91,10 +88,44 @@ impl SessionManager {
config = config.with_mtu(1350); config = config.with_mtu(1350);
} }
runtime.spawn(async move { // --- 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK) ---
info!("Starting VPN session thread..."); let local_proxy_port = 8080;
let local_proxy_host = "127.0.0.1".to_string();
let proxy_token = cancel_token.clone();
let remote_addr_clone = remote_address.clone();
runtime.spawn(async move {
info!(
"Starting Local Proxy (Network) on {}:{}",
local_proxy_host, local_proxy_port
);
let network = Network::new(
local_proxy_host,
local_proxy_port,
ConnectionRole::Client,
Some(remote_addr_clone),
);
// Оборачиваем в tokio::select! для жесткой отмены
tokio::select! {
_ = network.run(proxy_token.clone()) => {
info!("Local Proxy (Network) task finished normally.");
}
_ = proxy_token.cancelled() => {
info!("Local Proxy (Network) task forcefully stopped via CancellationToken.");
}
}
});
// Даем прокси немного времени на бинд порта
std::thread::sleep(std::time::Duration::from_millis(100));
// --- 2. ЗАПУСК ENGINE И TUN ---
let engine_token = cancel_token.clone();
runtime.spawn(async move {
info!("Starting VPN Engine thread...");
// 3. Инициализация TUN устройства (специфично для платформ)
let tun_device = { let tun_device = {
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
{ {
@@ -108,7 +139,7 @@ impl SessionManager {
.tun_name("netr0") .tun_name("netr0")
.address((10, 0, 0, 1)) .address((10, 0, 0, 1))
.netmask((255, 255, 255, 0)) .netmask((255, 255, 255, 0))
.mtu(config.mtu as u16) // Используем MTU из нашего конфига .mtu(config.mtu as u16)
.up(); .up();
}) })
.expect("Failed to init TUN") .expect("Failed to init TUN")
@@ -122,7 +153,6 @@ impl SessionManager {
} }
}; };
// 4. Используем обновленный билдер
let builder_result = EngineBuilder::new(config) let builder_result = EngineBuilder::new(config)
.with_tun(tun_device) .with_tun(tun_device)
.build() .build()
@@ -135,7 +165,7 @@ impl SessionManager {
res = engine.run(tun) => { res = engine.run(tun) => {
info!("Engine loop finished: {:?}", res); info!("Engine loop finished: {:?}", res);
}, },
_ = cancel_token.cancelled() => { _ = engine_token.cancelled() => {
info!("Engine task shutting down via token"); info!("Engine task shutting down via token");
} }
} }
+56 -14
View File
@@ -2,25 +2,60 @@ use netrunner_logger::{error, info};
mod net; mod net;
mod tun; mod tun;
// Импортируем и Билдер, и Конфиг // Импортируем Билдер, Конфиг и модули маршрутизации
use crate::tun::{routing::reset_platform_routing, tun::Tun}; use crate::tun::{routing::reset_platform_routing, tun::Tun};
use net::engine::{EngineBuilder, EngineConfig}; use net::engine::{EngineBuilder, EngineConfig};
// Импортируем компоненты локального прокси
use netrunner_core::proxy::connection::connection::ConnectionRole;
use netrunner_core::proxy::network::Network;
use tokio_util::sync::CancellationToken;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
netrunner_logger::Logger::init(); netrunner_logger::Logger::init();
info!("Initializing NetRunner Stack..."); info!("Initializing NetRunner Stack...");
let remote_address = "147.45.43.70:443"; let remote_address = "147.45.43.70:443".to_string();
let local_proxy_host = "127.0.0.1".to_string();
let local_proxy_port = 8080; // Локальный порт прокси
// 1. Создаем конфигурацию движка // Токен для управления жизненным циклом фоновых задач
// Здесь мы можем гибко настроить параметры, которые раньше были захардкожены let cancel_token = CancellationToken::new();
let config = EngineConfig::new(remote_address)
// ==================================================
// 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK)
// ==================================================
let proxy_token = cancel_token.clone();
let remote_addr_clone = remote_address.clone();
tokio::spawn(async move {
info!(
"Starting Local Proxy (Network) on {}:{}",
local_proxy_host, local_proxy_port
);
let network = Network::new(
local_proxy_host,
local_proxy_port,
ConnectionRole::Client,
Some(remote_addr_clone),
);
// Эта функция заблокирует поток, пока не сработает proxy_token
network.run(proxy_token).await;
info!("Local Proxy (Network) task stopped.");
});
// Даем локальному прокси немного времени на бинд порта и установку соединения
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
// ==================================================
// 2. ИНИЦИАЛИЗАЦИЯ ДВИЖКА И TUN
// ==================================================
let config = EngineConfig::new(&remote_address)
.with_cache_path(".") .with_cache_path(".")
.with_mtu(1350); // Указываем MTU здесь, чтобы использовать его и для TUN, и для стека .with_mtu(1350);
// 2. Создаем TUN интерфейс
// Используем значение MTU из конфига, чтобы данные были синхронизированы
let tun_device = Tun::create(|tun_cfg| { let tun_device = Tun::create(|tun_cfg| {
tun_cfg tun_cfg
.tun_name("netr0") .tun_name("netr0")
@@ -34,34 +69,38 @@ async fn main() -> anyhow::Result<()> {
info!("TUN interface is UP: 10.0.0.1/24 (MTU: {})", config.mtu); info!("TUN interface is UP: 10.0.0.1/24 (MTU: {})", config.mtu);
// 3. Собираем движок, передавая объект конфигурации
let builder_result = EngineBuilder::new(config) let builder_result = EngineBuilder::new(config)
.with_tun(tun_device) .with_tun(tun_device)
.build() .build()
.await; .await;
// 4. Обрабатываем результат и запускаем цикл // ==================================================
// 3. ГЛАВНЫЙ ЦИКЛ ENGINE
// ==================================================
match builder_result { match builder_result {
Ok((mut engine, tun)) => { Ok((mut engine, tun)) => {
info!("Engine starting process loop..."); info!("Engine starting process loop...");
let ctrl_c = tokio::signal::ctrl_c();
tokio::select! { tokio::select! {
_res = engine.run(tun) => { _res = engine.run(tun) => {
info!("Engine loop finished"); info!("Engine loop finished");
}, },
_ = ctrl_c => { _ = tokio::signal::ctrl_c() => {
info!("Ctrl+C received, shutting down..."); info!("Ctrl+C received, shutting down...");
// Отменяем токен, чтобы Network.run завершился
cancel_token.cancel();
} }
} }
} }
Err(e) => { Err(e) => {
error!("Failed to build VPN Engine: {}", e); error!("Failed to build VPN Engine: {}", e);
cancel_token.cancel();
} }
} }
// 5. Очистка системных роутов // ==================================================
// 4. ОЧИСТКА РОУТИНГА
// ==================================================
info!("Restoring system routing..."); info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format"); let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let p_ip = addr.ip().to_string(); let p_ip = addr.ip().to_string();
@@ -72,5 +111,8 @@ async fn main() -> anyhow::Result<()> {
info!("System routing restored successfully."); info!("System routing restored successfully.");
} }
// Даем таске Network время на graceful shutdown (чтобы сокеты успели закрыться)
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(()) Ok(())
} }
+30 -11
View File
@@ -263,22 +263,35 @@ impl UdpConnection {
} }
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool { pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
// Проверка таймаута
if self.last_activity.elapsed() > UDP_TIMEOUT { if self.last_activity.elapsed() > UDP_TIMEOUT {
debug!(%self.core.handle, "UDP Session closed due to {}s timeout", UDP_TIMEOUT.as_secs()); debug!(%self.core.handle, "UDP Session closed due to {}s timeout", UDP_TIMEOUT.as_secs());
socket.close(); socket.close();
return false; return false;
} }
// Читаем из smoltcp и шлем в сеть (через Muxer)
if socket.can_recv() { if socket.can_recv() {
let target_endpoint = socket.endpoint();
while let Ok((data, metadata)) = socket.recv() { while let Ok((data, metadata)) = socket.recv() {
if self.client_endpoint.is_none() { let source_endpoint = metadata.endpoint;
debug!(%self.core.handle, endpoint = %metadata.endpoint, "UDP Endpoint pinned");
} if self.client_endpoint.is_none() {
self.client_endpoint = Some(metadata.endpoint); info!(
%self.core.handle,
source = %source_endpoint,
target = %target_endpoint,
"UDP Session Established. Pinning endpoint."
);
}
self.client_endpoint = Some(source_endpoint);
trace!(
%self.core.handle,
source = %source_endpoint,
target = %target_endpoint,
bytes = data.len(),
"Forwarded UDP datagram from smoltcp to Muxer"
);
trace!(%self.core.handle, "Forwarded UDP datagram ({} bytes) from smoltcp to Muxer", data.len());
if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() { if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
self.last_activity = Instant::now(); self.last_activity = Instant::now();
} else { } else {
@@ -287,9 +300,8 @@ impl UdpConnection {
} }
} }
// Читаем из сети (от Muxer) и шлем в smoltcp
if socket.can_send() { if socket.can_send() {
if let Some(endpoint) = self.client_endpoint { if let Some(client_endpoint) = self.client_endpoint {
while let Ok(data) = self.core.rx.try_recv() { while let Ok(data) = self.core.rx.try_recv() {
if data.is_empty() { if data.is_empty() {
debug!(%self.core.handle, "Received empty datagram (Close signal) from Muxer, closing UDP socket"); debug!(%self.core.handle, "Received empty datagram (Close signal) from Muxer, closing UDP socket");
@@ -297,9 +309,16 @@ impl UdpConnection {
return false; return false;
} }
match socket.send_slice(&data, endpoint) { match socket.send_slice(&data, client_endpoint) {
Ok(_) => { Ok(_) => {
trace!(%self.core.handle, "Wrote UDP datagram ({} bytes) to smoltcp", data.len()); let proxy_endpoint = socket.endpoint();
info!(
%self.core.handle,
source = %proxy_endpoint,
target = %client_endpoint,
bytes = data.len(),
"Wrote UDP reply from Muxer back to smoltcp"
);
self.last_activity = Instant::now(); self.last_activity = Instant::now();
} }
Err(e) => { Err(e) => {
+76 -98
View File
@@ -1,8 +1,5 @@
use bytes::Bytes; use bytes::Bytes;
use netrunner_core::{ use netrunner_core::protocol::codec::socks::TargetAddress;
protocol::codec::{frame::FrameType, socks::TargetAddress},
proxy::connection::muxer::{MuxMessage, Muxer},
};
use netrunner_logger::{debug, error, info, trace, warn}; use netrunner_logger::{debug, error, info, trace, warn};
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
@@ -10,10 +7,10 @@ use smoltcp::{
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket}, wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
}; };
use std::{collections::HashMap, time::Duration, time::Instant as StdInstant}; use std::{collections::HashMap, time::Duration, time::Instant as StdInstant};
use tokio::sync::mpsc; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, UdpSocket};
use crate::net::{ use crate::net::{
CHANNEL_CAPACITY,
connection::{TcpConnection, UdpConnection}, connection::{TcpConnection, UdpConnection},
dns::DnsHandler, dns::DnsHandler,
ip_store::FakeIpStore, ip_store::FakeIpStore,
@@ -193,15 +190,13 @@ impl SocketFactory {
pub struct ConnectionManager { pub struct ConnectionManager {
tracker: SessionTracker, tracker: SessionTracker,
resolver: TargetResolver, resolver: TargetResolver,
muxer: Muxer,
} }
impl ConnectionManager { impl ConnectionManager {
pub fn new(dns_handler: DnsHandler, muxer: Muxer) -> Self { pub fn new(dns_handler: DnsHandler) -> Self {
Self { Self {
tracker: SessionTracker::new(), tracker: SessionTracker::new(),
resolver: TargetResolver::new(dns_handler), resolver: TargetResolver::new(dns_handler),
muxer,
} }
} }
@@ -244,7 +239,6 @@ impl ConnectionManager {
match ip_packet.next_header() { match ip_packet.next_header() {
IpProtocol::Tcp => { IpProtocol::Tcp => {
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) { if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
// Реагируем только на чистые SYN-пакеты (начало нового соединения)
if tcp_packet.syn() && !tcp_packet.ack() { if tcp_packet.syn() && !tcp_packet.ack() {
let dst_port = tcp_packet.dst_port(); let dst_port = tcp_packet.dst_port();
trace!(%dst_addr, dst_port, "Received TCP SYN"); trace!(%dst_addr, dst_port, "Received TCP SYN");
@@ -354,45 +348,45 @@ impl ConnectionManager {
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.tracker.active_tcp.insert(handle, conn); self.tracker.active_tcp.insert(handle, conn);
let muxer = self.muxer.clone(); // Конвертируем TargetAddress в строку, понятную для tokio::net
let stream_id = muxer.next_id(); let target_str = match target {
let connect_payload = target.to_string(); TargetAddress::Domain(d, p) => format!("{}:{}", d, p),
TargetAddress::Ipv4(ip, p) => format!("{}:{}", ip, p),
TargetAddress::Ipv6(ip, p) => format!("{}:{}", ip, p),
};
tokio::spawn(async move { tokio::spawn(async move {
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY); let mut upstream = match TcpStream::connect(&target_str).await {
muxer.register_stream(stream_id, v_tx); Ok(s) => s,
Err(e) => {
if muxer error!("Failed to connect to upstream TCP {}: {}", target_str, e);
.send_to_netwrok(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: Bytes::from(connect_payload),
})
.await
.is_err()
{
muxer.remove_stream(stream_id);
return;
}
match tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await {
Ok(Some(data)) if data.len() >= 2 && data[1] == 0x00 => {
let _ = handshake_tx.send(());
}
_ => {
muxer.remove_stream(stream_id);
return; return;
} }
} };
let to_proxy = async { // Сообщаем соединению smoltcp, что мы готовы (рукопожатие выполнено)
let _ = handshake_tx.send(());
let (mut r, mut w) = upstream.into_split();
// Читаем из tun (smoltcp) и пишем во внешнюю сеть
let to_upstream = async {
while let Some(data) = rx_from_smol.recv().await { while let Some(data) = rx_from_smol.recv().await {
if muxer if w.write_all(&data).await.is_err() {
.send_to_netwrok(MuxMessage { break;
stream_id, }
frame_type: FrameType::Data, }
data, };
})
// Читаем из внешней сети и пишем в tun (smoltcp)
let from_upstream = async {
let mut buf = vec![0u8; 8192]; // Читаем чанками
while let Ok(n) = r.read(&mut buf).await {
if n == 0 {
break;
} // EOF
if tx_to_smol
.send(Bytes::copy_from_slice(&buf[..n]))
.await .await
.is_err() .is_err()
{ {
@@ -401,24 +395,7 @@ impl ConnectionManager {
} }
}; };
let from_proxy = async { tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
while let Some(data) = v_rx.recv().await {
if data.is_empty() || tx_to_smol.send(data).await.is_err() {
break;
}
}
};
tokio::select! { _ = to_proxy => {}, _ = from_proxy => {} }
let _ = muxer
.send_to_netwrok(MuxMessage {
stream_id,
frame_type: FrameType::Close,
data: Bytes::new(),
})
.await;
muxer.remove_stream(stream_id);
}); });
} }
@@ -464,30 +441,48 @@ impl ConnectionManager {
let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle); let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle);
self.tracker.active_udp.insert(handle, conn); self.tracker.active_udp.insert(handle, conn);
let muxer = self.muxer.clone(); // Конвертируем для tokio::net::UdpSocket
let stream_id = muxer.next_id(); let target_str = match target {
let connect_payload = target.to_string(); TargetAddress::Domain(d, p) => format!("{}:{}", d, p),
TargetAddress::Ipv4(ip, p) => format!("{}:{}", ip, p),
TargetAddress::Ipv6(ip, p) => format!("{}:{}", ip, p),
};
tokio::spawn(async move { tokio::spawn(async move {
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY); // Создаем локальный UDP сокет со случайным портом
muxer.register_stream(stream_id, v_tx); let upstream = match UdpSocket::bind("0.0.0.0:0").await {
Ok(s) => s,
Err(e) => {
error!("Failed to bind local UDP socket: {}", e);
return;
}
};
let _ = muxer // "Подключаем" UDP сокет к цели (включает фильтр пакетов и позволяет использовать обычные send/recv)
.send_to_netwrok(MuxMessage { if let Err(e) = upstream.connect(&target_str).await {
stream_id, error!("Failed to connect UDP to {}: {}", target_str, e);
frame_type: FrameType::UdpConnect, return;
data: Bytes::from(connect_payload), }
})
.await;
let to_proxy = async { let upstream = std::sync::Arc::new(upstream);
let upstream_rx = upstream.clone();
let upstream_tx = upstream;
// Из smoltcp наружу
let to_upstream = async {
while let Some(data) = rx_from_smol.recv().await { while let Some(data) = rx_from_smol.recv().await {
if muxer if upstream_tx.send(&data).await.is_err() {
.send_to_netwrok(MuxMessage { break;
stream_id, }
frame_type: FrameType::UdpData, }
data, };
})
// Извне в smoltcp
let from_upstream = async {
let mut buf = vec![0u8; 65536]; // Максимальный размер UDP датаграммы
while let Ok(n) = upstream_rx.recv(&mut buf).await {
if tx_to_smol
.send(Bytes::copy_from_slice(&buf[..n]))
.await .await
.is_err() .is_err()
{ {
@@ -496,24 +491,7 @@ impl ConnectionManager {
} }
}; };
let from_proxy = async { tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
while let Some(data) = v_rx.recv().await {
if data.is_empty() || tx_to_smol.send(data).await.is_err() {
break;
}
}
};
tokio::select! { _ = to_proxy => {}, _ = from_proxy => {} }
let _ = muxer
.send_to_netwrok(MuxMessage {
stream_id,
frame_type: FrameType::Close,
data: Bytes::new(),
})
.await;
muxer.remove_stream(stream_id);
}); });
} }
+3 -8
View File
@@ -39,19 +39,14 @@ pub struct Engine {
} }
impl Engine { impl Engine {
pub fn new( pub fn new(config: Config, caps: DeviceCapabilities, dns_handler: DnsHandler) -> Self {
config: Config,
caps: DeviceCapabilities,
dns_handler: DnsHandler,
muxer: Muxer,
) -> Self {
let now = Engine::current_time(); let now = Engine::current_time();
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps); let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
let interface = Interface::new(config, &mut device, now); let interface = Interface::new(config, &mut device, now);
let socket_set = ConnectionManager::setup_sockets(2); let socket_set = ConnectionManager::setup_sockets(2);
let manager = ConnectionManager::new(dns_handler, muxer); let manager = ConnectionManager::new(dns_handler);
Self { Self {
interface, interface,
@@ -322,7 +317,7 @@ impl EngineBuilder {
info!("Secure tunnel established, Muxer is ready."); info!("Secure tunnel established, Muxer is ready.");
// 5. Инициализация и настройка Engine // 5. Инициализация и настройка Engine
let mut engine = Engine::new(smol_config, caps, dns_handler, muxer); let mut engine = Engine::new(smol_config, caps, dns_handler);
engine.set_any_ip(self.config.any_ip); engine.set_any_ip(self.config.any_ip);
+2
View File
@@ -3,3 +3,5 @@ pub mod codec;
pub mod frame; pub mod frame;
mod padding; mod padding;
pub mod socks; pub mod socks;
pub const MAX_PADDING_SIZE: u32 = 255;
+3 -1
View File
@@ -1,6 +1,8 @@
use bytes::Bytes; use bytes::Bytes;
use rand::Rng; use rand::Rng;
use crate::protocol::codec::MAX_PADDING_SIZE;
pub struct Padding { pub struct Padding {
pub len: u16, pub len: u16,
pub data: Bytes, pub data: Bytes,
@@ -10,7 +12,7 @@ impl Padding {
pub fn generate_padding() -> Padding { pub fn generate_padding() -> Padding {
let mut rng = rand::rng(); let mut rng = rand::rng();
let random_u32: u32 = rng.next_u32(); let random_u32: u32 = rng.next_u32();
let padding_len: u16 = (random_u32 % 255) as u16; let padding_len: u16 = (random_u32 % MAX_PADDING_SIZE) as u16;
let mut padding = vec![0u8; padding_len as usize]; let mut padding = vec![0u8; padding_len as usize];
rng.fill_bytes(&mut padding); rng.fill_bytes(&mut padding);
Padding { Padding {
+4 -4
View File
@@ -32,7 +32,7 @@ pub async fn run_tcp_bridge<R, W>(
frame_type: FrameType::Data, frame_type: FrameType::Data,
data: buf.split().freeze(), data: buf.split().freeze(),
}; };
if muxer.send_to_netwrok(msg).await.is_err() { break; } if muxer.send_to_network(msg).await.is_err() { break; }
} }
Err(e) => { Err(e) => {
error!(stream_id, error = %e, "Socket read error"); error!(stream_id, error = %e, "Socket read error");
@@ -60,7 +60,7 @@ pub async fn run_tcp_bridge<R, W>(
} }
let _ = muxer let _ = muxer
.send_to_netwrok(MuxMessage { .send_to_network(MuxMessage {
stream_id, stream_id,
frame_type: FrameType::Close, frame_type: FrameType::Close,
data: Bytes::new(), data: Bytes::new(),
@@ -91,7 +91,7 @@ pub async fn run_udp_bridge(
frame_type: FrameType::UdpData, frame_type: FrameType::UdpData,
data: Bytes::copy_from_slice(&buf[..n]), data: Bytes::copy_from_slice(&buf[..n]),
}; };
if muxer.send_to_netwrok(msg).await.is_err() { break; } if muxer.send_to_network(msg).await.is_err() { break; }
} }
Err(e) => { Err(e) => {
error!(stream_id, error = %e, "UDP socket read error"); error!(stream_id, error = %e, "UDP socket read error");
@@ -119,7 +119,7 @@ pub async fn run_udp_bridge(
} }
let _ = muxer let _ = muxer
.send_to_netwrok(MuxMessage { .send_to_network(MuxMessage {
stream_id, stream_id,
frame_type: FrameType::Close, frame_type: FrameType::Close,
data: Bytes::new(), data: Bytes::new(),
+37 -1
View File
@@ -54,10 +54,46 @@ impl Muxer {
self.id_gen.next() self.id_gen.next()
} }
pub async fn send_to_netwrok(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> { pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> {
self.data_tx.send(message).await self.data_tx.send(message).await
} }
pub async fn send_data_safe(&self, stream_id: u32, mut data: Bytes) -> Result<(), String> {
// Лимит полезной нагрузки, чтобы вместе с заголовком и паддингом
// пакет оставался в пределах ~1400-1450 байт.
const MAX_PAYLOAD_CHUNK: usize = 1300;
if data.len() <= MAX_PAYLOAD_CHUNK {
return self
.send_to_network(MuxMessage {
stream_id,
frame_type: FrameType::Data,
data,
})
.await
.map_err(|e| e.to_string());
}
// Если данных много (например, те самые 4096 байт), режем их на куски
while !data.is_empty() {
let chunk_size = std::cmp::min(data.len(), MAX_PAYLOAD_CHUNK);
let chunk = data.split_to(chunk_size);
self.send_to_network(MuxMessage {
stream_id,
frame_type: FrameType::Data,
data: chunk,
})
.await
.map_err(|e| e.to_string())?;
// Небольшая уступка планировщику, чтобы не забить канал мгновенно
tokio::task::yield_now().await;
}
Ok(())
}
pub async fn send_control( pub async fn send_control(
&self, &self,
stream_id: u32, stream_id: u32,
+56 -2
View File
@@ -1,5 +1,8 @@
use crate::proxy::connection::connection::{ use crate::{
ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler, protocol::codec::{frame::FRAME_HEADER_SIZE, MAX_PADDING_SIZE},
proxy::connection::connection::{
ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler,
},
}; };
use netrunner_logger::{error, info}; use netrunner_logger::{error, info};
use tokio::net::TcpListener; use tokio::net::TcpListener;
@@ -86,3 +89,54 @@ impl Network {
} }
} }
} }
pub const IP_UDP_OVERHEAD: usize = 28;
pub struct NetworkConfig {
pub mtu: usize,
pub max_wire_frame_size: usize,
pub safe_payload_size: usize,
pub tcp_rx_buffer_size: usize,
pub tcp_tx_buffer_size: usize,
pub udp_rx_buffer_size: usize,
pub udp_tx_buffer_size: usize,
pub channel_capacity: usize,
}
impl NetworkConfig {
pub fn new(system_mtu: usize) -> Self {
let transport_overhead = 28; // IPv4 + UDP
let max_wire_frame = system_mtu.saturating_sub(transport_overhead);
let safe_payload = max_wire_frame
.saturating_sub(FRAME_HEADER_SIZE as usize)
.saturating_sub((MAX_PADDING_SIZE - 1) as usize);
let tcp_chunks_count = 65536 / safe_payload;
let tcp_buffer = safe_payload * tcp_chunks_count;
let udp_chunks_count = 16384 / safe_payload;
let udp_buffer = safe_payload * udp_chunks_count;
let channel_cap = 1024;
netrunner_logger::info!(
mtu = system_mtu,
payload = safe_payload,
tcp_buf = tcp_buffer,
"Network Optimizer: Calculations complete for current MTU"
);
Self {
mtu: system_mtu,
max_wire_frame_size: max_wire_frame,
safe_payload_size: safe_payload,
tcp_rx_buffer_size: tcp_buffer,
tcp_tx_buffer_size: tcp_buffer,
udp_rx_buffer_size: udp_buffer,
udp_tx_buffer_size: udp_buffer,
channel_capacity: channel_cap,
}
}
}