Whosthere Sweeper 深度解析:如何用 TCP/UDP 探测无感填充 ARP 缓存
【免费下载链接】whosthereLocal Area Network discovery tool with an interactive Terminal User Interface (TUI) written in Go. Discover, explore, and understand your LAN in an intuitive way. Knock Knock.. who's there? 🚪项目地址: https://gitcode.com/gh_mirrors/wh/whosthere
Whosthere 是一款用 Go 编写的局域网发现工具,其核心的 Sweeper 模块通过向子网内所有 IP 发送 TCP/UDP 探测包,"无感"地填充系统 ARP 缓存,从而无需 root 权限就能快速发现内网设备。本文将拆解它的触发端口、并发控制与大子网保护机制,帮你理解这个巧妙的设计。
为什么需要"无感"填充 ARP 缓存?
ARP 缓存是操作系统记录"IP → MAC 地址"对应关系的表。Whosthere 的 ARP 扫描器(arp.go)直接读取这张表,轻量且完全无侵入——但它有个前提:缓存里得有数据。
问题来了:
- 直接发送 ARP 请求需要原始套接字(raw socket),必须 root 权限 ❌
- 而 Whosthere 的设计目标恰恰是纯用户态运行、无需提权✅
Sweeper 的解法写在 sweeper.go 的注释里:向子网内每个 IP 发送普通 UDP/TCP 包,操作系统在发包前必须自己完成一次 ARP 解析来确定目标 MAC 地址——ARP 填充成了网络通信的"副作用"。
┌──────────┐ UDP/TCP 包 ┌──────────┐ OS 自动发起 ┌──────────┐ │ Sweeper │ ────────────►│ 目标 IP │ ◄────────────── │ ARP 解析 │ └──────────┘ └──────────┘ (内核完成) └──────────┘ │ │ │ ▼ │ 写入 ARP 缓存 ▼ │ 连接失败无所谓 ◄─────────────────────────────◄─────────────┘💡 关键细节:这些连接预期都会失败,Whosthere 只关心 ARP 解析被触发,不关心是否建立连接(见 sweeper.go 的注释)。
触发端口选择:UDP 9/33434 与 TCP 80/443
Sweeper 对每个 IP 依次尝试 4 个端口(sweeper.go):
| 协议 | 端口 | 选择理由 |
|---|---|---|
| UDP | 9 | 标准的 Discard 端口,几乎所有设备都会收到并丢弃 |
| UDP | 33434 | 高位随机端口,大概率无人监听,UDP 发包即触发 ARP |
| TCP | 80 | 常见服务端口,即使拒绝也完成了 ARP 解析 |
| TCP | 443 | 同上,且部分设备/网关仅对 443 有响应 |
具体发包逻辑在sendARPTarget函数(sweeper.go):
- UDP 探测:写一个 1 字节的包后立即关闭,不等待任何响应
- TCP 探测:
DialTimeout超时仅 300ms,连上立刻关闭
为什么 UDP 和 TCP 双管齐下?因为部分设备(如某些防火墙后的主机)对入站 TCP SYN 不做 ARP 响应,而对已到达的 UDP 数据报会先解析链路层地址。两条路径互补,最大化 ARP 命中率。
并发与超时控制:200 并发 + 300ms 超时
家用网段常见/24(254 个 IP),串行逐个触发会非常慢。Sweeper 用信号量控制并发(sweeper.go):
maxConcurrentTriggers = 200:最多 200 个 IP 同时触发,避免拥塞网卡和内核 ARP 队列triggerDeadline = 300ms:UDP 写入的整体截止时间tcpDialTimeout = 300ms:TCP 拨号超时
这样一轮/24扫描通常在秒级完成,对网络几乎无感知。
大子网保护:/16 截断策略
如果你的接口落在一个超大子网(如/8,上千万个 IP),盲目全扫会瘫痪网络。generateSubnetIPs默认将扫描范围截断为 /16 等效的 65534 个 IP(sweeper.go):
- 默认行为:大子网只扫前 65534 个 IP,并输出警告日志
- 需要全量扫描时,在配置中设置
scan_large_subnets: true即可解除限制(对应 config.go 的ScanLargeSubnets字段)
默认配置与调优参数
Sweeper 在 Whosthere 中默认开启,配置项定义在 config.go:
sweeper: enabled: true # 默认开启 interval: 5m # 两轮扫描间隔(默认 5 分钟) timeout: 20s # 单轮扫描超时(默认 20 秒)运行节奏由 engine.go 中的常量决定:
| 参数 | 默认值 | 含义 |
|---|---|---|
扫描间隔interval | 5 分钟 | ARP 缓存会老化,定期重新触发保持缓存新鲜 |
单轮超时timeout | 20 秒 | 超时后取消本轮,进入下一轮 |
引擎在Scan时会并行启动 Sweeper 与各类扫描器(engine.go):一边发包填充缓存,一边读缓存,互不阻塞。
与 ARP 扫描器的接力配合
整个发现流程是一条流水线:
- Sweeper 触发:并发 TCP/UDP 探测 → 内核填充 ARP 缓存
- ARP Scanner 轮询:每 250ms 读取一次系统 ARP 表(arp.go),跨 Linux / macOS / Windows 平台
- 数据合并:引擎按 IP 合并来自 ARP、mDNS、SSDP 的数据,并用 OUI 库补全厂商信息(device.go)
组件之间的组装逻辑见 engine_builder.go——只有sweeper.enabled: true时才会创建 Sweeper 并注入引擎。
快速上手:命令行开关
日常使用无需关心细节,直接运行即可(Sweeper 默认参与扫描):
# 交互式 TUI whosthere # 单次 CLI 扫描(5 秒) whosthere scan -t 5 # 显式关闭 Sweeper,只靠 mDNS/SSDP 发现 whosthere scan --sweeper=falseJSON 输出示例:whosthere scan -t 5 --json --pretty > devices.json
总结
Whosthere 的 Sweeper 用三个技巧解决了"无提权扫描内网"这个经典难题:
- 🎯副作用思维:不发 ARP,而是用必然失败的 TCP/UDP 发包让操作系统代劳
- ⚡精准端口组合:UDP 9/33434 + TCP 80/443,双协议互补提升命中率
- 🛡️安全兜底:200 并发限流、300ms 超时、/16 子网截断,保证普通用户"无感"
对于想理解无提权局域网扫描的开发者,pkg/discovery/sweeper/ 目录下的 sweeper.go 和 sweeper_options.go 是绝佳的参考实现。
【免费下载链接】whosthereLocal Area Network discovery tool with an interactive Terminal User Interface (TUI) written in Go. Discover, explore, and understand your LAN in an intuitive way. Knock Knock.. who's there? 🚪项目地址: https://gitcode.com/gh_mirrors/wh/whosthere
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考