updates muxer legs select, buf sizes and constantsa

This commit is contained in:
2026-04-11 22:48:33 +07:00
parent 6009eca8a7
commit 732f55c01d
12 changed files with 441 additions and 245 deletions
+4 -4
View File
@@ -1,5 +1,4 @@
use bytes::{Buf, Bytes};
use std::collections::VecDeque;
use netrunner_core::{
net::{NetworkConfig, UDP_IDLE_TIMEOUT},
rawcast::{LocalProtocol, RawCastFrame},
@@ -12,6 +11,7 @@ use smoltcp::{
Ipv6Address,
},
};
use std::collections::VecDeque;
use tokio::sync::{mpsc, oneshot};
use netrunner_logger::{debug, info};
@@ -165,9 +165,9 @@ impl TcpConnection {
if !self.server_eof {
loop {
let pending_size: usize = self.pending_data.iter().map(|b| b.len()).sum();
if pending_size >= 4 * NetworkConfig::global().mtu {
let max_pending = 128 * 1024;
if pending_size >= max_pending {
break;
}
@@ -413,4 +413,4 @@ impl IcmpResponder {
}
}
}
}
}
+86 -47
View File
@@ -1,14 +1,19 @@
use dashmap::DashMap;
use netrunner_core::{
net::{DNS_PORT, GLOBAL_IDLE_TIMEOUT, HTTPS_PORT, MAX_SOCKETS, NETBIOS_PORTS, TCP_HANDSHAKE_TIMEOUT},
net::{
DNS_PORT, GLOBAL_IDLE_TIMEOUT, HTTPS_PORT, MAX_SOCKETS, NETBIOS_PORTS,
TCP_HANDSHAKE_TIMEOUT,
},
rawcast::{RawCastEvent, RawCastFrame},
};
use netrunner_logger::{info, trace};
use netrunner_logger::{info, trace, warn};
use smoltcp::{
iface::{SocketHandle, SocketSet},
socket::{Socket, tcp, udp},
wire::{IpAddress, IpListenEndpoint, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket},
};
use std::{sync::Arc};
use std::sync::Arc;
use std::time::Instant; // Используем DashMap для потокобезопасного трекинга
use tokio::sync::mpsc;
@@ -19,6 +24,9 @@ use crate::net::{
socket_factory::SocketProvider,
};
// Ключ для идентификации уникального потока (4-tuple)
type FlowKey = (IpAddress, u16, IpAddress, u16);
struct Flow {
src: IpAddress,
dst: IpAddress,
@@ -26,6 +34,12 @@ struct Flow {
dst_p: u16,
}
impl Flow {
fn to_key(&self) -> FlowKey {
(self.src, self.src_p, self.dst, self.dst_p)
}
}
struct TargetResolver {
dns_handler: DnsHandler,
fake_ip_store: FakeIpStore,
@@ -66,6 +80,8 @@ pub struct ConnectionManager {
resolver: TargetResolver,
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
factory: Arc<dyn SocketProvider>,
// 🔥 ТРЕКЕР ОЖИДАЮЩИХ СОЕДИНЕНИЙ: предотвращает SYN-шторм
pending_connects: DashMap<FlowKey, Instant>,
}
impl ConnectionManager {
@@ -79,6 +95,7 @@ impl ConnectionManager {
resolver: TargetResolver::new(dns_handler),
tx_to_tunnel,
factory,
pending_connects: DashMap::new(),
}
}
@@ -114,7 +131,6 @@ impl ConnectionManager {
for (_, socket) in socket_set.iter_mut() {
match socket {
Socket::Tcp(tcp) => {
// 🛡️ ИСПРАВЛЕНО: Только пустые базовые сокеты слушают 443
if !tcp.is_open() && tcp.local_endpoint().is_none() {
let _ = tcp.listen(IpListenEndpoint {
addr: None,
@@ -123,7 +139,6 @@ impl ConnectionManager {
}
}
Socket::Udp(udp) => {
// 🛡️ ИСПРАВЛЕНО: Только пустые базовые сокеты биндят 53
if !udp.is_open() && udp.endpoint().port == 0 {
let _ = udp.bind(IpListenEndpoint {
addr: None,
@@ -163,7 +178,10 @@ impl ConnectionManager {
if let Ok(p) = TcpPacket::new_checked(ip.payload()) {
flow.src_p = p.src_port();
flow.dst_p = p.dst_port();
self.intercept_tcp(flow, socket_set);
// Игнорируем пакеты, если это не SYN (новое соединение)
if p.syn() && !p.ack() {
self.intercept_tcp(flow, socket_set);
}
}
}
IpProtocol::Udp => {
@@ -193,7 +211,9 @@ impl ConnectionManager {
if let Ok(p) = TcpPacket::new_checked(ip.payload()) {
flow.src_p = p.src_port();
flow.dst_p = p.dst_port();
self.intercept_tcp(flow, socket_set);
if p.syn() && !p.ack() {
self.intercept_tcp(flow, socket_set);
}
}
}
IpProtocol::Udp => {
@@ -208,12 +228,22 @@ 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 {
netrunner_logger::warn!("🔥 TCP Socket limit reached! Dropping SYN.");
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);
@@ -221,16 +251,16 @@ impl ConnectionManager {
}
fn intercept_udp(&mut self, f: Flow, socket_set: &mut SocketSet) {
if f.dst_p == 0
|| f.dst_p == DNS_PORT
|| NETBIOS_PORTS.contains(&f.dst_p) // 👈 Используй массив
|| self.tracker.is_client_known(f.src_p)
if f.dst_p == 0
|| f.dst_p == DNS_PORT
|| NETBIOS_PORTS.contains(&f.dst_p)
|| self.tracker.is_client_known(f.src_p)
{
return;
}
if socket_set.iter().count() >= MAX_SOCKETS {
netrunner_logger::warn!("🔥 UDP Socket limit reached! Dropping packet.");
warn!("🔥 UDP Socket limit reached! Dropping packet.");
return;
}
@@ -255,7 +285,13 @@ impl ConnectionManager {
}
pub fn process_sockets(&mut self, socket_set: &mut SocketSet) {
for (handle, socket) in socket_set.iter_mut() {
// Собираем хендлы, чтобы избежать конфликтов заимствования (borrow checker)
let handles: Vec<SocketHandle> = socket_set.iter().map(|(h, _)| h).collect();
for handle in handles {
// 🔥 ИСПРАВЛЕНИЕ: get_mut возвращает &mut Socket, а не Option<&mut Socket>
let socket = socket_set.get_mut(handle);
match socket {
Socket::Tcp(s) => self.handle_tcp(handle, s),
Socket::Udp(s) => self.handle_udp(handle, s),
@@ -263,46 +299,26 @@ impl ConnectionManager {
}
}
}
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
self.tracker.update_activity(handle);
let state = socket.state();
// 1. Терминальные состояния (уже закрыты)
if state == tcp::State::Closed || state == tcp::State::TimeWait {
self.tracker.queue_removal(handle);
return;
}
// 🛡️ ИСПРАВЛЕНИЕ: Мы не считаем сокет "мертвым", если он в процессе Handshake
let is_handshaking = state == tcp::State::SynSent || state == tcp::State::SynReceived;
// 🚨 Логика очистки Half-Close (CloseWait/FinWait2)
// Убиваем сокет ТОЛЬКО если он не слушает, не коннектится И при этом не может передавать данные
if !is_handshaking && state != tcp::State::Listen {
if !socket.may_recv() && !socket.may_send() {
trace!(%handle, ?state, "💀 Cleaning up dead half-closed socket");
socket.abort();
self.tracker.queue_removal(handle);
return;
}
}
// Тайм-аут на установку соединения (если SYN висит слишком долго)
if self
.tracker
.check_pending_timeout(handle, TCP_HANDSHAKE_TIMEOUT)
{
socket.abort();
self.tracker.queue_removal(handle);
return;
}
// Инициализация туннеля при успешном коннекте
if state == tcp::State::Established && self.tracker.should_init_tcp(handle) {
if let Some(ep) = socket.local_endpoint() {
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(ep.addr, ep.port);
let (dst_ip, target) = self.resolver.resolve_destination(local.addr, local.port);
let (conn, rx_smol, tx_smol, handshake_tx) = TcpConnection::new(handle);
self.tracker.register_tcp(handle, socket_id, conn, tx_smol);
@@ -310,7 +326,7 @@ impl ConnectionManager {
TcpConnection::spawn(
socket_id,
dst_ip,
ep.port,
local.port,
target,
rx_smol,
handshake_tx,
@@ -319,10 +335,27 @@ 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) {
// Если tick вернул false (ошибка в каналах или Close от туннеля)
socket.abort();
self.tracker.queue_removal(handle);
}
@@ -331,7 +364,6 @@ impl ConnectionManager {
fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) {
self.tracker.update_activity(handle);
if socket.endpoint().port == 53 {
while let Ok((data, meta)) = socket.recv() {
if let Some(res) = self.resolver.process_dns_query(data) {
@@ -340,7 +372,6 @@ impl ConnectionManager {
}
return;
}
if let Some(conn) = self.tracker.get_udp_mut(handle) {
if !conn.tick(socket) {
self.tracker.queue_removal(handle);
@@ -349,7 +380,15 @@ impl ConnectionManager {
}
pub fn cleanup(&mut self, socket_set: &mut SocketSet) {
// 🔥 ФИКС 3: Очистка зависших pending_connects (например, если SYN потерялся)
self.pending_connects
.retain(|_, timestamp| timestamp.elapsed() < TCP_HANDSHAKE_TIMEOUT);
self.tracker.enforce_idle_timeouts(GLOBAL_IDLE_TIMEOUT);
self.tracker.cleanup(socket_set);
}
}
fn is_handshaking(state: tcp::State) -> bool {
matches!(state, tcp::State::SynSent | tcp::State::SynReceived)
}
+23 -26
View File
@@ -8,7 +8,6 @@ use smoltcp::{
iface::{Config, Interface, SocketSet},
phy::DeviceCapabilities,
};
use tokio::sync::mpsc::UnboundedReceiver;
use std::net::Ipv4Addr;
use std::sync::atomic::Ordering;
use std::{
@@ -16,6 +15,7 @@ use std::{
time::Instant as StdInstant,
};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::time::{Duration, sleep};
use tun::{DeviceReader, DeviceWriter};
@@ -40,6 +40,7 @@ pub struct Engine {
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
avail: Arc<AtomicBool>,
rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
factory: Arc<dyn SocketProvider>, // 🔥 Сохраняем фабрику для доступа к логам статистики
}
impl Engine {
@@ -69,6 +70,7 @@ impl Engine {
avail,
manager,
rx_from_tunnel,
factory,
}
}
@@ -76,7 +78,6 @@ impl Engine {
info!("Current routes: {:?}", self.interface.routes());
let (writer, reader) = tun.split().expect("Failed to split TUN");
// 🚨 ПРАВИЛО 1: ОГРАНИЧИВАЕМ КАНАЛ ОТ TUN, чтобы ОС не затопила нас памятью!
let (tun_to_engine_tx, mut tun_to_engine_rx) =
mpsc::channel(NetworkConfig::global().client_tun_capacity);
@@ -84,7 +85,10 @@ impl Engine {
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;
@@ -92,10 +96,15 @@ impl Engine {
self.manager.process_sockets(&mut self.socket_set);
let poll_res = self.poll();
self.manager.cleanup(&mut self.socket_set);
// Если сокеты изменились, крутим еще раз, пока не вытолкнем всё
repeat_poll = matches!(poll_res, PollResult::SocketStateChanged);
}
// 🔥 Логируем состояние буферов раз в 5 секунд
if last_stats_log.elapsed() >= Duration::from_secs(5) {
self.factory.log_stats(&self.socket_set);
last_stats_log = StdInstant::now();
}
// 2. Считаем задержку
let delay = self
.interface
@@ -110,7 +119,6 @@ impl Engine {
msg = self.rx_from_tunnel.recv() => {
if let Some(frame) = msg {
let _ = self.manager.try_inject_inbound(frame);
// Читаем по чуть-чуть (max 32), чтобы чаще делать poll()
let mut count = 0;
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
let _ = self.manager.try_inject_inbound(frame);
@@ -124,15 +132,9 @@ impl Engine {
if let Some(token) = msg {
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
// 🔥 ФИКС 1: Используем .send().await.
// Мы не имеем права терять пакеты внутри системы.
// Если smoltcp занят, TUN Reader просто подождет.
if self.to_smoltcp_tx.send(token).await.is_ok() {
self.device.mark_rx_available();
}
// Пакетную обработку Ok(token) = try_recv лучше убрать или
// тоже заменить на логику, которая не дропает данные.
} else { break; }
}
}
@@ -144,36 +146,34 @@ impl Engine {
self.interface
.poll(now, &mut self.device, &mut self.socket_set)
}
fn spawn_tun_reader(
mut reader: DeviceReader,
to_engine: mpsc::Sender<TokenBuffer>,
to_engine: mpsc::Sender<TokenBuffer>,
is_avail: Arc<AtomicBool>,
) {
tokio::spawn(async move {
debug!("TUN Reader task started");
let mut buf = [0u8; 65536];
while let Ok(n) = reader.read(&mut buf).await {
if n == 0 { break; }
// ... (фильтрация IP) ...
if n == 0 {
break;
}
let mut token = TokenBuffer::with_capacity(n);
token.extend_from_slice(&buf[..n]);
// 🔥 ФИКС 2: Возвращаем .send().await!
// Это заставит ОС притормозить отправку пакетов,
// если туннель перегружен. Пинг упадет, так как пакеты
// будут стоять в очереди ОС, а не дропаться.
if to_engine.send(token).await.is_ok() {
is_avail.store(true, Ordering::Release);
} else { break; }
} else {
break;
}
}
});
}
fn spawn_tun_writer(
mut writer: DeviceWriter,
mut from_smoltcp: UnboundedReceiver<TokenBuffer>, // 🔥 Обновили тип
mut from_smoltcp: UnboundedReceiver<TokenBuffer>,
) {
tokio::spawn(async move {
debug!("TUN Writer task started");
@@ -265,7 +265,6 @@ impl EngineConfig {
pub struct EngineBuilder {
config: EngineConfig,
tun_device: Option<Tun>,
socket_factory: Option<Arc<dyn SocketProvider>>,
}
@@ -321,9 +320,7 @@ impl EngineBuilder {
let factory = self.socket_factory.unwrap_or_else(|| {
let config_owned = (*NetworkConfig::global()).clone();
let config = Arc::new(config_owned);
Arc::new(SmolSocketFactory::new(config))
});
@@ -351,4 +348,4 @@ impl EngineBuilder {
Ok((engine, tun))
}
}
}
+41 -26
View File
@@ -1,4 +1,5 @@
use netrunner_core::net::NetworkConfig;
use netrunner_logger::info;
use smoltcp::{
iface::SocketSet,
socket::{
@@ -34,17 +35,17 @@ impl TrafficProfile {
}
pub trait SocketProvider: Send + Sync {
// Убрали <'static> у tcp::Socket
fn create_tcp(&self, profile: TrafficProfile) -> tcp::Socket;
fn create_tcp(&self, profile: TrafficProfile) -> tcp::Socket;
fn create_udp(&self, profile: TrafficProfile) -> udp::Socket<'static>;
fn create_icmp(&self) -> icmp::Socket<'static>;
// Убрали <'static> у tcp::Socket
fn create_listening_tcp(&self, addr: Option<IpAddress>, port: u16) -> tcp::Socket;
fn create_icmp(&self, profile: TrafficProfile) -> icmp::Socket<'static>;
fn create_listening_tcp(&self, addr: Option<IpAddress>, port: u16) -> tcp::Socket;
fn create_bound_udp(&self, addr: Option<IpAddress>, port: u16) -> udp::Socket<'static>;
fn create_base_set(&self, n_icmp: usize) -> SocketSet<'static>;
fn reconfigure_tcp(&self, socket: &mut tcp::Socket, profile: TrafficProfile);
// 🔥 НОВЫЙ МЕТОД: Логирование статистики всех сокетов в сете
fn log_stats(&self, sockets: &SocketSet);
}
pub struct SmolSocketFactory {
@@ -57,24 +58,25 @@ impl SmolSocketFactory {
}
fn alloc_buf(&self, size: usize) -> Vec<u8> {
// Убрали пул памяти, так как 1MB RX буферы раздуют оперативку (128MB+).
// Выделение памяти через vec! при старте сокета работает достаточно быстро.
vec![0u8; size]
}
}
impl SocketProvider for SmolSocketFactory {
// Убрали <'static>
fn create_tcp(&self, profile: TrafficProfile) -> tcp::Socket {
// 👈 Асимметричные буферы: RX большой (Download), TX маленький (Upload)
let (rx_size, tx_size) = match profile {
TrafficProfile::Bulk => (self.config.tcp_rx_heavy, self.config.tcp_tx_heavy),
TrafficProfile::Interactive => (self.config.tcp_rx_light, self.config.tcp_tx_light),
_ => (self.config.tcp_rx_light * 2, self.config.tcp_tx_light * 2),
};
// 🔥 ИСПОЛЬЗУЕМ НАШИ НОВЫЕ ДИНАМИЧЕСКИЕ БУФЕРЫ 🔥
// Мы больше не аллоцируем векторы снаружи, буфер сам управляет памятью
info!(
"🚀 TCP Socket Created | Profile: {:?} | Initial RX: {} KB, TX: {} KB",
profile,
rx_size / 1024,
tx_size / 1024
);
let rx_buffer = tcp::DynamicSocketBuffer::new(rx_size);
let tx_buffer = tcp::DynamicSocketBuffer::new(tx_size);
let mut socket = tcp::Socket::new(rx_buffer, tx_buffer);
@@ -87,28 +89,41 @@ impl SocketProvider for SmolSocketFactory {
}
fn reconfigure_tcp(&self, socket: &mut tcp::Socket, profile: TrafficProfile) {
// Выключаем Nagle везде, так как внутри Netrunner мы сами батчим данные в чанки (tcp_chunk_size),
// а BBR сам управляет плавностью отправки (Pacing). Nagle здесь будет только мешать.
socket.set_nagle_enabled(false);
match profile {
TrafficProfile::Interactive | TrafficProfile::Dns => {
// Для интерактивного трафика (SSH, RDP, DNS) нам нужна минимальная задержка.
// Отвечаем ACK'ами мгновенно.
socket.set_ack_delay(None);
}
TrafficProfile::Bulk | TrafficProfile::Default => {
// Для тяжелого трафика ставим крошечный delay (1 мс).
// Это снизит нагрузку на CPU (будет меньше syscall-ов на отправку голых ACK),
// но при этом 1 мс не испортит расчеты RTT и RateSample для BBR.
socket.set_ack_delay(Some(Duration::from_millis(1)));
}
}
// Включаем нашу ракету
socket.set_congestion_control(CongestionControl::Bbr);
}
fn log_stats(&self, sockets: &SocketSet) {
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();
let state = tcp_socket.state();
info!(
"📊 [TCP {}] Buffers: RX: {:>4} KB, TX: {:>4} KB | State: {:?}",
handle,
rx_cap / 1024,
tx_cap / 1024,
state
);
}
}
}
}
fn create_udp(&self, profile: TrafficProfile) -> udp::Socket<'static> {
let (buf_size, meta_count) = match profile {
TrafficProfile::Bulk => (self.config.udp_buf_heavy, self.config.udp_meta_heavy),
@@ -131,20 +146,20 @@ impl SocketProvider for SmolSocketFactory {
)
}
fn create_icmp(&self) -> icmp::Socket<'static> {
fn create_icmp(&self, _profile: TrafficProfile) -> icmp::Socket<'static> {
icmp::Socket::new(
icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY; 4], vec![0; 512]),
icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY; 4], vec![0; 512]),
)
}
fn create_listening_tcp(&self, addr: Option<IpAddress>, port: u16) -> tcp::Socket {
fn create_listening_tcp(&self, addr: Option<IpAddress>, port: u16) -> tcp::Socket {
let profile = TrafficProfile::guess_from_port(port, true);
let mut socket = self.create_tcp(profile);
let _ = socket.listen(IpListenEndpoint { addr, port });
socket
}
fn create_bound_udp(&self, addr: Option<IpAddress>, port: u16) -> udp::Socket<'static> {
let profile = TrafficProfile::guess_from_port(port, false);
let mut socket = self.create_udp(profile);
@@ -156,7 +171,7 @@ impl SocketProvider for SmolSocketFactory {
let mut sockets = SocketSet::new(Vec::with_capacity(128));
sockets.add(self.create_bound_udp(None, 53));
for _ in 0..n_icmp {
sockets.add(self.create_icmp());
sockets.add(self.create_icmp(TrafficProfile::Default));
}
sockets
}