WatchYourLAN 高级 ARP 扫描配置指南:IFACES、ARP_ARGS 与 ARP_STRS 实战详解
【免费下载链接】WatchYourLANLightweight network IP scanner written in Go. With notifications, history, export to Grafana项目地址: https://gitcode.com/GitHub_Trending/wa/WatchYourLAN
WatchYourLAN 是一款用 Go 编写的轻量级局域网 IP 扫描器,核心扫描引擎是系统自带的arp-scan命令行工具。本指南围绕 docs/VLAN_ARP_SCAN.md 展开,系统讲解 WatchYourLAN 向arp-scan传递参数的三种途径——IFACES、ARP_ARGS、ARP_STRS(以及用于环境变量的ARP_STRS_JOINED),并结合 arpscan.go、conf/read.go 等源码,深入说明参数解析、扫描执行与结果归属的底层原理。读完本文,你将掌握通过 Web GUI、配置文件与环境变量三种方式配置复杂 VLAN、docker0 网桥等多网段扫描的完整实战方案。
一、参数体系总览:三类参数如何拼装 arp-scan 命令
WatchYourLAN 本身不实现 ARP 探测,而是封装系统命令arp-scan。所有扫描参数最终都会拼装成arp-scan的 CLI 命令并在后台执行。根据 docs/VLAN_ARP_SCAN.md 的说明,参数分为三类,作用域完全不同:
| 参数 | 是否必需 | 作用范围 | 默认命令形态 |
|---|---|---|---|
IFACES | 必需 | 指定参与扫描的网络接口列表 | arp-scan -glNx -I $ONE_IFACE |
ARP_ARGS | 可选 | 追加到每一个接口的扫描命令中 | arp-scan -glNx <ARP_ARGS> -I $ONE_IFACE |
ARP_STRS | 可选 | 完全独立于IFACES的额外扫描串 | arp-scan $ONE_STRING |
ARP_STRS_JOINED | 可选 | 仅限环境变量方式写入ARP_STRS | 同ARP_STRS |
其中-g表示生成随机源 MAC 地址(与-l冲突时自动切换)、-l使用本机 MAC、-N不做 DNS 反向解析、-x以制表符分隔输出、-I指定接口。扫描命令的实际拼装逻辑见 arpscan.go:当ARP_ARGS非空时执行arp-scan -glNx <args> -I <iface>,否则执行arp-scan -glNx -I <iface>。
这三类参数都可以通过 GUI、配置文件或环境变量设置,但存在一条关键限制(详见后文):ARP_STRS只能通过 GUI 或配置文件设置,环境变量(如 docker-compose)必须使用ARP_STRS_JOINED。
二、IFACES:必需的基础接口列表
2.1 参数格式与来源
IFACES是 WatchYourLAN 正常运行必需的变量,可来自 Web GUI、配置文件或环境变量。它是一个用空格分隔的网络接口列表:
IFACES: "enp4s0 wlxf4ec3892dd51"上例中enp4s0是有线网卡,wlxf4ec3892dd51是无线网卡,WatchYourLAN 会依次对列表中的每个接口发起一次独立扫描。
获取本机接口列表的常用命令:
ip link show # 或 netstat -i在 docker-compose.yml 中,该参数以环境变量形式注入,并明确标注为required:
environment: TZ: Asia/Novosibirsk # required: needs your TZ for correct time IFACES: "enp4s0 wlxf4ec3892dd51" # required: 1 or more interface2.2 源码实现:逐接口循环扫描
从 arpscan.go 的Scan函数可以看出,IFACES字符串会先按空格切分为切片,然后逐接口调用scanIface:
if ifaces != "" { p = strings.Split(ifaces, " ") for _, iface := range p { slog.Debug("Scanning interface " + iface) text = scanIface(iface) foundHosts = append(foundHosts, parseOutput(text, iface)...) } }注意 conf/read.go 中IFACES的默认值为空字符串,这意味着不配置该变量时 WatchYourLAN 不会扫描任何接口——这正是它在 docker-compose 中标记为 required 的原因。
2.3 默认扫描命令
对于每个接口,默认生成的扫描命令为:
arp-scan -glNx -I $ONE_IFACE$ONE_IFACE即IFACES列表中的单个接口名。
三、ARP_ARGS:附加参数作用于每个接口
3.1 参数格式
ARP_ARGS是可选变量,同样可通过 GUI、配置文件或环境变量设置。它是arp-scan的附加参数,会被应用到IFACES中的每一个接口。例如:
ARP_ARGS: "-r 1"实际生成的命令为:
arp-scan -glNx -r 1 -I $ONE_IFACE其中-r 1表示只重试一次。arp-scan的全部可用参数请查阅man arp-scan。
3.2 源码实现:附加参数全局生效
scanIface 中的逻辑非常直白:
var arpArgs string func scanIface(iface string) string { var cmd *exec.Cmd if arpArgs != "" { cmd = exec.Command("arp-scan", "-glNx", arpArgs, "-I", iface) } else { cmd = exec.Command("arp-scan", "-glNx", "-I", iface) } out, err := cmd.Output() ... }arpArgs是包级变量,在Scan入口处由参数args赋值,因此一次扫描周期内对所有接口生效。一个使用技巧:将日志级别设为debug,WatchYourLAN 会通过slog.Debug(cmd.String())输出实际拼装好的完整命令(见 arpscan.go),便于你核对参数是否按预期生效。日志级别本身由 restart-scan.go 的setLogLevel在每次重启扫描协程时根据LOG_LEVEL重新设定。
四、ARP_STRS:VLAN、docker0 与复杂网段的独立扫描
4.1 设计动机:解决 IFACES 的局限
仅靠IFACES只能覆盖本机直连的接口。对于 VLAN 子网(如10.0.107.0/24)、Docker 默认网桥(docker0)、虚拟网桥(virbr0)等需要额外指定网段甚至 VLAN ID 的场景,IFACES无能为力。为此 WatchYourLAN 引入了ARP_STRS。
ARP_STRS是一组完整的arp-scan命令字符串。只要设置了它,WatchYourLAN 就会发起一组与IFACES完全独立的扫描,对列表中的每个字符串各执行一次:
arp-scan $ONE_STRING每个字符串必须包含你要传递给arp-scan的全部信息。例如扫描 VLAN 107 对应的子网:
arp-scan -gNx 10.0.107.0/24 -Q 107 -I eth0其中-Q 107即 VLAN ID。
[!WARNING] 字符串的最后一个元素(上例中的
eth0)会被 WatchYourLAN 设置为该批发现主机的Interface字段。因此强烈建议把接口名放在字符串末尾,否则数据库中主机的接口归属会错乱。
4.2 通过配置文件设置
在配置文件(config_v2.yaml)中,ARP_STRS以 YAML 列表形式书写:
arp_strs: - -gNx 172.17.0.1/24 -I docker0 - -glNx -I virbr0第一个字符串扫描 Docker 网桥网段172.17.0.1/24,第二个字符串扫描虚拟网桥virbr0。注意两处-I都位于字符串末尾,符合上一节的接口归属约定。
4.3 通过 Web GUI 设置
在 Web 界面的 Scan settings 面板中,找到 "Arp Strings" 输入框,一次填入一个字符串,点击Save保存后,页面会自动追加一个空输入框,供你继续填写下一个字符串。前端实现见 frontend/src/components/Config/Scan.tsx:使用 SolidJS 的<For>循环渲染已有的每个arpstrs输入框,并在末尾固定渲染一个空输入框用于新增。
点击 Save 会 POST 到/api/config_settings/,后端 api/config.go 通过c.PostFormArray("arpstrs")收集全部同名表单值,过滤掉空字符串后写入conf.AppConfig.ArpStrs,随后调用conf.Write落盘并触发routines.ScanRestart()立即重启扫描。
4.4 源码实现:独立执行与接口归属提取
ARP_STRS的执行走的是与IFACES完全不同的分支。scanStr 将整个字符串按空格拆分成参数数组后直接传给arp-scan:
func scanStr(str string) string { args := strings.Split(str, " ") cmd := exec.Command("arp-scan", args...) ... }在 Scan 中,扫描完IFACES后紧接着遍历strs:
for _, s := range strs { slog.Debug("Scanning string " + s) text = scanStr(s) p = strings.Split(s, " ") foundHosts = append(foundHosts, parseOutput(text, p[len(p)-1])...) }注意p[len(p)-1]——这正是"最后一个元素被用作接口名"这一约定的源码出处。parseOutput(arpscan.go)按制表符切分arp-scan -x的输出,将 IP、MAC、厂商信息(Hw)与接口名一起封装进models.Host结构体(定义见 models/models.go)。
五、ARP_STRS_JOINED:在 docker-compose 中注入 ARP_STRS
5.1 为什么需要它
ARP_STRS是列表类型,而环境变量是扁平字符串,无法直接表达"字符串列表"。因此 WatchYourLAN 提供了ARP_STRS_JOINED作为从 ENV 注入ARP_STRS的唯一途径。
5.2 格式要求
ARP_STRS_JOINED是用逗号分隔的字符串列表,逗号前后不能有空格:
ARP_STRS_JOINED: "-gNx 172.17.0.1/24 -I docker0,-gNx 10.0.107.0/24 -Q 107 -I eth0"解析逻辑见 conf/read.go:读取ARP_STRS_JOINED后直接按逗号切分,覆盖config.ArpStrs:
joined := viper.Get("ARP_STRS_JOINED").(string) if joined != "" { config.ArpStrs = strings.Split(joined, ",") }因此如果逗号后误留空格,切分结果会带有前导空格,导致arp-scan收到一个非法的参数(如" -I"),最终扫描失败。务必保证逗号紧贴前后字符串,不加空格。
5.3 配置优先级与回写保护
注意 conf/write.go 中的一行特殊处理:
viper.Set("ARP_STRS_JOINED", "") // Can be set only with ENV每当配置被写回文件时,ARP_STRS_JOINED都会被置空——因为该变量只在环境变量中有意义。如果配置文件中同时存在arp_strs和ARP_STRS_JOINED环境变量,则环境变量的优先级更高,会覆盖文件中的arp_strs列表。
5.4 docker-compose 完整示例
version: "3" services: wyl: image: aceberg/watchyourlan network_mode: "host" restart: unless-stopped volumes: - ~/.dockerdata/wyl:/data/WatchYourLAN environment: TZ: Asia/Novosibirsk # required IFACES: "enp4s0" # required ARP_STRS_JOINED: "-gNx 172.17.0.1/24 -I docker0,-gNx 10.0.107.0/24 -Q 107 -I eth0"使用network_mode: "host"可确保容器能看到宿主机全部网络接口与路由表,这是扫描 VLAN 与 docker0 的前提条件。配置文件config_v2.yaml存放在挂载卷/data/WatchYourLAN下,由 conf/start.go 在启动时读取。
六、实战示例汇总
以下示例均来自 docs/VLAN_ARP_SCAN.md,可直接用于ARP_STRS_JOINED或ARP_STRS。
6.1 扫描 VLAN 107
ARP_STRS_JOINED: "-gNx 10.0.107.0/24 -Q 107 -I eth0"含义:扫描10.0.107.0/24网段,-Q 107指定 VLAN ID 为 107,接口为eth0(置于末尾)。
6.2 扫描 docker0 网桥
ARP_STRS_JOINED: "-gNx 172.17.0.1/24 -I docker0"含义:扫描 Docker 默认网桥docker0所在的172.17.0.1/24网段,接口docker0位于末尾。
6.3 组合使用(逗号分隔,无空格)
ARP_STRS_JOINED: "-gNx 172.17.0.1/24 -I docker0,-gNx 10.0.107.0/24 -Q 107 -I eth0"七、扫描结果如何进入数据库与告警链路
理解参数配置后,再看一下扫描结果的处理闭环(见 routines/scan-routine.go):
startScan每TIMEOUT秒(默认 120 秒,见 conf/read.go)调用一次arp.Scan,将三类参数一并传入;- 扫描结果以MAC 地址为键构建映射(
foundHostsMap[fHost.Mac] = fHost); compareHosts与数据库中"当前在线"的主机逐条比对:仍在线的更新 IP、接口与时间戳;已掉线的标记Now = 0;新出现的未知主机会触发notify.Unknown(fHost)通过 Shoutrrr 发送上线通知;- 每条记录同时写入 history 表,供历史曲线展示;若启用了 InfluxDB 或 Prometheus 输出,还会同步导出(对应 backend/internal/influx/influx.go 与 backend/internal/prometheus/prometheus.go)。
这意味着:ARP_STRS配置的 VLAN 或 docker0 网段一旦发现新设备,同样会进入完整的在线状态跟踪、历史记录与告警流程,与IFACES扫描的设备无差别对待。
八、配置建议与排查清单
- 先确认接口存在:配置
IFACES前,用ip link show或netstat -i核对接口名拼写,避免手误导致整轮扫描为空。 - 接口放末尾:所有
ARP_STRS/ARP_STRS_JOINED字符串都把接口名放在最后一个参数位置,否则主机记录会被错误归属到其他接口。 - 逗号后不要有空格:环境变量注入
ARP_STRS_JOINED时严格遵守"逗号分隔、无空格"格式。 - 开启 debug 日志验证:将
LOG_LEVEL设为debug,在日志中核对每条实际执行的arp-scan命令是否与预期一致;日志级别改动后需触发一次扫描重启(GUI 中保存任意扫描设置即可,见 Scan.tsx 的 "Pressing Save button will trigger rescan" 提示)。 - 权限要求:
arp-scan发送原始 ARP 报文通常需要 root 或相应能力(CAP_NET_RAW),请确保运行 WatchYourLAN 的进程具备该权限,否则扫描输出为空(arp-scan报错会被 check.IfError 捕获并返回空结果)。 - 依赖安装:WatchYourLAN 依赖系统提供
arp-scan与tzdata两个外部程序,缺失任一都会导致扫描异常。
【免费下载链接】WatchYourLANLightweight network IP scanner written in Go. With notifications, history, export to Grafana项目地址: https://gitcode.com/GitHub_Trending/wa/WatchYourLAN
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考