diff --git a/src/hostdemo.rs b/src/hostdemo.rs index 3d6949e..56a2844 100644 --- a/src/hostdemo.rs +++ b/src/hostdemo.rs @@ -207,12 +207,15 @@ fn rdm_region(b: &[u8]) -> Option<(bool, &[u8])> { pub fn run(target: &str, bind_ip: &str, secs: u64) { let dst = format!("{}:31772", target); - let sock = open_reuse(&format!("{}:31772", bind_ip)); // 源 123.113:31772 (真机过滤+主机惯例) + // HOSTDEMO_LISTEN: 实际 socket 绑定地址 (默认=bind_ip:31772)。 + // 跨网段经 udprelay 中继时, Ubuntu 侧须绑本地地址 (如 0.0.0.0:31772), 帧内宣告 IP 仍为 bind_ip (123.113) + let listen = std::env::var("HOSTDEMO_LISTEN").unwrap_or_else(|_| format!("{}:31772", bind_ip)); + let sock = open_reuse(&listen); // 源 123.113:31772 (真机过滤+主机惯例) sock.set_read_timeout(Some(Duration::from_millis(5))).unwrap(); let mut rng = Rng::new(); let mut buf = [0u8; 4096]; - println!("=== 主机侧等效示例: 目标 {} (源 {}) ===", dst, bind_ip); + println!("=== 主机侧等效示例: 目标 {} (源 {}, 监听 {}) ===", dst, bind_ip, listen); // ---- 1) 发现 ---- let disc = load_template("discovery_req"); @@ -1886,3 +1889,43 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u } println!("[S] 会话环结束: DMX {} 帧, 发现灯具 {} 台, 控制地址 {:?}", sent, fixtures.len(), addr_list); } + +/// ★UDP 31772 通用中继 (udprelay 子命令): 跨网段主机验证用 +/// 客户端(任意网段) ↔ sockA(listen_addr) | sockB(src_ip:0) ↔ Butler +/// 发往 Butler 的帧源 IP = src_ip (123.113, 绕过 AECH 固件源过滤); +/// Butler 应答(单播到 sockB) 转发回最后一个客户端; Butler 广播(到 sockA) 也转发。 +pub fn udp_relay(listen_addr: &str, butler_ip: &str, src_ip: &str) { + let a = open_reuse(listen_addr); + let b = open_reuse(&format!("{}:0", src_ip)); + 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 mut client: Option = None; + let mut ba = [0u8; 4096]; + let mut bb = [0u8; 4096]; + let (mut n_fwd, mut n_rev) = (0u64, 0u64); + println!("[RELAY] {} <-> Butler {} (源伪装 {}), Ctrl+C 停止", listen_addr, bdst, src_ip); + loop { + if let Ok((n, from)) = a.recv_from(&mut ba) { + if from.ip().to_string() == butler_ip { + // Butler 广播/事件 → 客户端 + 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; + } + } + if (n_fwd + n_rev) % 2000 == 1 && n_fwd > 1 { + println!("[RELAY] 已转发 客户端→Butler {} 帧, Butler→客户端 {} 帧", n_fwd, n_rev); + } + } +} + diff --git a/src/main.rs b/src/main.rs index 4aad074..f0118e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -83,6 +83,14 @@ fn main() { } return; } + "udprelay" => { + // udprelay [listen_addr] [butler_ip] [src_ip] — UDP/31772 通用中继 (跨网段主机验证) + let listen = std::env::args().nth(2).unwrap_or_else(|| "0.0.0.0:31772".into()); + let butler = std::env::args().nth(3).unwrap_or_else(|| "192.168.123.1".into()); + let src = std::env::args().nth(4).unwrap_or_else(|| "192.168.123.113".into()); + hostdemo::udp_relay(&listen, &butler, &src); + return; + } "all" => { results.push(tests::t1_discover()); results.push(tests::tx_observe("T2", "announce", false));