udprelay: per-client upstream sockets (SL 30fps flood mis-routed scan replies to the wrong socket)

This commit is contained in:
Tony Cao 2026-09-14 00:49:02 +08:00
parent ab8090be79
commit 03d0e79268

View File

@ -1877,40 +1877,78 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
} }
/// ★UDP 31772 通用中继 (udprelay 子命令): 跨网段主机验证用 /// ★UDP 31772 通用中继 (udprelay 子命令): 跨网段主机验证用
/// 客户端(任意网段) ↔ sockA(listen_addr) | sockB(src_ip:0) ↔ Butler /// 客户端(任意网段) ↔ sockA(listen_addr) | 每客户端独立 sockB(src_ip:0) ↔ Butler
/// 发往 Butler 的帧源 IP = src_ip (123.113, 绕过 AECH 固件源过滤); /// 发往 Butler 的帧源 IP = src_ip (123.113, 绕过 AECH 固件源过滤);
/// Butler 应答(单播到 sockB) 转发回最后一个客户端; Butler 广播(到 sockA) 也转发。 /// Butler 应答(单播到该客户端的 sockB) 原路转发回该客户端; Butler 广播(到 sockA) 广播给所有客户端。
///
/// 2026-09-13 重构: 旧版用单个 `client` 变量记录"最后发包者", 任何客户端在
/// 请求与应答之间只要有另一个客户端发了包(如 SL 30fps 灌流 vs 低频扫描套接字),
/// 应答就会被投递给错误的套接字 —— SL 扫描因此永远收不到 DUB 上行。
/// 现在每个客户端持有独立的上行 socket, Butler 按目的端口(=该 socket)天然
/// 区分客户端, 应答无歧义回原客户端; 上限 8 客户端, 淘汰最旧。
pub fn udp_relay(listen_addr: &str, butler_ip: &str, src_ip: &str) { pub fn udp_relay(listen_addr: &str, butler_ip: &str, src_ip: &str) {
let a = open_reuse(listen_addr); let a = open_reuse(listen_addr);
let b = open_reuse(&format!("{}:0", src_ip)); a.set_read_timeout(Some(Duration::from_millis(1))).unwrap();
a.set_read_timeout(Some(Duration::from_millis(2))).unwrap();
b.set_read_timeout(Some(Duration::from_millis(2))).unwrap();
let bdst = format!("{}:31772", butler_ip); let bdst = format!("{}:31772", butler_ip);
let mut client: Option<std::net::SocketAddr> = None; let butler_sa: std::net::SocketAddr = bdst.parse().unwrap();
struct Up {
addr: std::net::SocketAddr,
sock: UdpSocket,
}
let mut ups: Vec<Up> = Vec::new();
let mut ba = [0u8; 4096]; let mut ba = [0u8; 4096];
let mut bb = [0u8; 4096]; let mut bb = [0u8; 4096];
let (mut n_fwd, mut n_rev) = (0u64, 0u64); let (mut n_fwd, mut n_rev) = (0u64, 0u64);
println!("[RELAY] {} <-> Butler {} (源伪装 {}), Ctrl+C 停止", listen_addr, bdst, src_ip); println!("[RELAY] {} <-> Butler {} (源伪装 {}, 每客户端独立上行), Ctrl+C 停止", listen_addr, bdst, src_ip);
loop { loop {
if let Ok((n, from)) = a.recv_from(&mut ba) { // Butler → 各客户端: 逐上行 socket 收(非阻塞, 无歧义归属)
for up in ups.iter_mut() {
loop {
match up.sock.recv_from(&mut bb) {
Ok((n, from)) => {
if from.ip().to_string() == butler_ip { if from.ip().to_string() == butler_ip {
// Butler 广播/事件 → 客户端 let _ = a.send_to(&bb[..n], up.addr);
if let Some(c) = client { let _ = a.send_to(&ba[..n], c); n_rev += 1; }
} else {
client = Some(from);
let _ = b.send_to(&ba[..n], &bdst);
n_fwd += 1;
if n_fwd == 1 { println!("[RELAY] 客户端 = {}", from); }
}
}
if let Ok((n, _from)) = b.recv_from(&mut bb) {
if let Some(c) = client {
let _ = a.send_to(&bb[..n], c);
n_rev += 1; n_rev += 1;
} }
} }
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
Err(_) => break,
}
}
}
// 客户端(或 Butler 侧广播) → listen socket
if let Ok((n, from)) = a.recv_from(&mut ba) {
// 2026-09-13: Butler 判定改为完整 socket (IP:31772)。旧逻辑只比
// 对 IP —— DUT 侧内部发现 walker 从 .68.200:*临时端口* 发出的查询
// 被误判为 Butler 事件转发给 SL,永远到不了总线。
if from == butler_sa {
// Butler 广播/事件 → 所有客户端
for up in ups.iter() {
let _ = a.send_to(&ba[..n], up.addr);
n_rev += 1;
}
} else {
match ups.iter_mut().find(|up| up.addr == from) {
Some(up) => {
let _ = up.sock.send_to(&ba[..n], &bdst);
n_fwd += 1;
}
None => {
let sock = open_reuse(&format!("{}:0", src_ip));
let _ = sock.set_nonblocking(true);
let _ = sock.send_to(&ba[..n], &bdst);
ups.push(Up { addr: from, sock });
if ups.len() > 8 {
ups.remove(0);
}
n_fwd += 1;
println!("[RELAY] 客户端 #{} = {}", ups.len(), from);
}
}
}
}
if (n_fwd + n_rev) % 2000 == 1 && n_fwd > 1 { if (n_fwd + n_rev) % 2000 == 1 && n_fwd > 1 {
println!("[RELAY] 已转发 客户端→Butler {} 帧, Butler→客户端 {} 帧", n_fwd, n_rev); println!("[RELAY] 已转发 客户端→Butler {} 帧, Butler→客户端 {} 帧 ({} 客户端)", n_fwd, n_rev, ups.len());
} }
} }
} }