arp-scan 1.9 编译安装与IP冲突排查实践

发布时间:2026/10/27 11:10:17
arp-scan 1.9 编译安装与IP冲突排查实践

简介:arp-scan 1.9 源码包面向 Linux 网络运维、安全测试及协议学习人员,用于在局域网中扫描 ARP 响应,定位 IP 地址冲突对应的 MAC 地址,也可辅助排查异常主机与设备接入情况。压缩包共 57 个文件、约 360KB,以 15 个 C 源文件和头文件为主体,附带 7 个 pcap 报文样本、6 个 dat 请求数据、OUI/IAB 厂商库、man 手册及 README、测试脚本等,兼顾编译构建、抓包对照与协议细节查阅。已有 1408 人学习,说明其在网络排障与 ARP 机制实践中具有一定参考价值。读者可从中获得完整的命令行工具实现、报文构造与解码素材、厂商前缀数据以及自检用例,适合通过阅读源码和样本包理解 ARP 扫描流程、验证冲突检测思路,也可作为二次开发与教学实验的基础材料。

1. 从新机器 ping 不通网关说起:arp-scan 1.9 到底扫的是什么

机房里新上架的一台机器配好地址后 ping 不通网关,网线灯是亮的,交换机端口状态 up,ip addr里的地址掩码网关三项都对着。换到同网段另一台机器上跑一次 arp-scan,网关那个 IP 回出来的 MAC 压根不是网关的,问题一下就落到二层——地址被人占了,或者网关地址配重了。这类“IP 层看着全绿、二层在打架”的故障,用 ping 和常规端口扫描都很容易看走眼,而 arp-scan 这种直接玩 ARP 的小工具,能把 IP 与 MAC 的对应关系一次摊开。arp-scan 1.9 是一个用 C 写的小体积命令行工具,依赖 libpcap,在链路层自己构造 ARP 请求帧,只对直连网段有效,输出 IP、MAC、厂商三列。它最适合 IP 冲突排查、上线前地址占用确认、内网资产粗盘点,不适合跨网段扫端口,也不是漏洞扫描器。

2. 把 tar.gz 编译成可用命令:依赖、configure 参数与安装路径

拿到arp-scan-1.9.tar.gz之后第一件事不是急着解压,而是先确认这台机器有没有让它跑起来的最小环境。它是源码包,不是预编译二进制,装完能不能用,八成取决于 libpcap 开发头文件和编译器是否齐全。我一般会先看系统里有没有pcap.h、gcc和make,再决定是补包还是直接编译,省得 configure 报一屏幕错还要回头猜。

2.1 依赖清单与最小化安装

arp-scan 运行期依赖 libpcap 收发链路层帧,编译期依赖 libpcap 的头文件与静态链接信息,所以只装运行库不够,必须装-dev或-devel包。用 apt 的发行版和用 dnf 的发行版包名不同,命令也不一样,下面两段按你手上的系统选一段执行即可。

# apt 系发行版:装编译器、make 和 libpcap 开发包 sudo apt-get update sudo apt-get install -y build-essential libpcap-dev # dnf/yum 系发行版:包名是 libpcap-devel sudo dnf install -y gcc make libpcap-devel

这段命令里build-essential是一组包的集合,包含 gcc、g++、make 和 libc 头文件;libpcap-dev提供pcap.h、libpcap.so以及链接时需要的符号。装完可以用ls /usr/include/pcap.h和pkg-config --libs libpcap各验证一次,前者确认头文件在,后者确认链接参数能查出来。如果pkg-config报找不到 libpcap,说明开发包没装全,configure 阶段大概率会卡在“checking for pcap_open_live”这一行。

2.2 configure 与 make 的关键选择

解压后进目录先跑 configure 看它认不认环境,再决定要不要加参数。绝大多数场景直接--prefix=/usr/local就够,需要把工具装到非标准路径(比如打包进自己的运维镜像)时再动 prefix,其它选项保持默认。

# 解压并进入源码目录 tar -xzf arp-scan-1.9.tar.gz cd arp-scan-1.9 # 指定安装前缀,其余用默认探测结果 ./configure --prefix=/usr/local # 并行编译,核数按机器实际情况给 make -j"$(nproc)" # 安装需要写 /usr/local,必须提权 sudo make install

./configure的核心工作是探测 libpcap 的位置并生成 Makefile,输出末尾会打印一行“configure: creating ./config.status”,看到它基本就过了。--prefix=/usr/local决定二进制落在/usr/local/sbin/arp-scan,数据文件落在/usr/local/share/arp-scan/,注意是sbin不是bin,因为工具需要 raw socket 权限,按惯例归到系统管理命令。make -j后面跟核数能明显缩短编译时间,但这包本身很小,单核也就几十秒,机器负载高时不必强行并行。

如果你的 libpcap 装在非默认路径(比如自己编译到了/opt/pcap),configure 会找不到,这时加--with-libpcap=/opt/pcap显式指路;只加LDFLAGS和CPPFLAGS有时不够,因为脚本里对库路径有独立判断。编译阶段如果报undefined reference to pcap_...,九成是链接时没带上-lpcap,回头检查 configure 的输出里 libpcap 那一项是不是no。

2.3 安装后的自检与 OUI 数据库位置

装完别直接拿去扫生产网段,先做三件小事:看版本、看帮助里的接口参数、确认 OUI 数据库在不在。厂商列的准确性完全依赖这份 OUI 文件,文件缺失时输出里 vendor 一列全是Unknown,很容易被误判成“工具没扫到东西”。

# 确认版本与安装路径 /usr/local/sbin/arp-scan --version which arp-scan # 查看数据文件是否就位 ls -l /usr/local/share/arp-scan/ # 试跑一次帮助,确认能正常加载 /usr/local/sbin/arp-scan --help | head -20

--version输出里应该能看到 1.9 字样,同时打印编译时用的 libpcap 版本;ls那一步重点看ieee-oui.txt和mac-vendor.txt两个文件是否存在,前者是 IEEE 分配的厂商前缀表,后者是 arp-scan 自带的一些补充记录。如果文件在但 vendor 仍显示 Unknown,多半是路径没对上,可以用--ouifile=/usr/local/share/arp-scan/ieee-oui.txt显式指定再试一次。which arp-scan如果找不到命令,说明/usr/local/sbin不在当前用户的 PATH 里,普通用户本来也没有权限直接跑,这不是安装失败。

注意:安装完成后不要用普通用户直接执行,raw socket 需要 root 或 CAP_NET_RAW,权限不足时程序会给出明确的报错而不是静默失败,看到权限相关提示先去查执行身份,别怀疑编译。

3. 二层探测的原理与参数选型:为什么它比 ping 更靠近真相

理解 arp-scan 为什么好用,得回到 ARP 协议本身。它是 IPv4 在局域网里把 IP 解析成 MAC 的机制,请求帧直接封装在以太网帧里,目的 MAC 是全 F 广播,不经过 IP 路由,也不经过主机防火墙的常规过滤链。arp-scan 就是自己拼这种帧、自己收回应答,所以它能看见的东西,比 ping 更贴近链路上的真实情况。搞不清这个边界,就很容易拿它去扫跨网段地址,然后得出“这工具不好用”的结论。

3.1 ARP 请求与应答在 arp-scan 里怎么走

一次探测的完整过程是:arp-scan 通过 libpcap 拿到指定接口的句柄,构造 ARP request,操作码为 1,发送方 MAC 填本机接口 MAC,发送方 IP 默认填本机地址(可以用--arpspa改),目标 MAC 填全零,目标 IP 填待探测地址,然后从接口发出去。网段内任何认为自己拥有这个 IP 的设备都会回一个 ARP reply,操作码为 2,带上它自己的 MAC。arp-scan 收到 reply 后把 IP、MAC 和 MAC 前缀对应的厂商拼成一行输出。

这里有个容易被忽略的细节:arp-scan 只发一次请求、按--retry重发,它不会像完整协议栈那样维护 ARP 缓存表,所以每次扫描都是“冷启动”,不会因为本机缓存里有旧记录而漏掉新设备。反过来说,它也不会替你更新系统 ARP 表,扫完arp -n里看不到这些条目,两者互不影响。默认发送方 IP 用本机地址,意味着目标设备看到的请求来自“本机”,回包也回给本机 MAC,这条路在同一个广播域里是通的。

3.2 超时、重试与发送速率:-t、-r 与 -B 的取舍

默认参数在安静的有线网段够用,但一旦碰上无线、虚拟化或老旧设备,超时和重试就得调。-t控制单个目标的等待毫秒数,默认 100ms;-r控制重试次数,默认 2 次。两个参数不是越大越好,调大意味着单目标耗时线性增长,扫一个 /24 的时间会从几秒涨到几十秒。

参数默认值作用建议调整场景
-t, --timeout100ms单个目标的应答等待时间无线网段或有休眠设备的网段调到 300~500
-r, --retry2每个目标的重发次数丢包明显的环境调到 3~4,再高收益很低
-B, --bandwidth256000发送速率上限,单位 bit/s大网段怕打爆交换机时下调,小网段不用动
-i, --interval未设包间隔,毫秒需要严格控速时用它替代带宽参数

实际调参时我一般先跑默认值,看结尾统计行里的响应率,再决定要不要加大-t和-r。统计行会打印“扫了多少个、用了多少秒、多少个有响应”,响应率明显低于预期时,先怀疑网段边界或接口选错,再去动超时参数,顺序反了会浪费很多时间。

3.3 输出格式与去重:-x、-g 与 --format 的组合

arp-scan 默认输出带一段头部说明和结尾统计,人看方便,喂给脚本就碍事。-x去掉这些装饰只留结果行,-g忽略重复应答,--format则可以把输出重排成你要的字段顺序,三个参数组合起来基本能覆盖所有自动化场景。

# 去掉头尾统计,忽略重复应答,按 IP 制表符 MAC 厂商输出 sudo arp-scan -I eth0 --localnet -x -g \ --format='${ip}\t${mac}\t${vendor}' # 从文件读取目标列表,适合只关心一批已知地址 sudo arp-scan -I eth0 -f targets.txt -x # 随机化发送顺序,降低被上层限速设备识别的概率 sudo arp-scan -I eth0 --localnet --random -x

--format支持的变量里,${ip}、${mac}、${vendor}是最常用的三个,${name}需要配合-D做反向解析才有意义,反解会拖慢速度,内网没有 DNS 时不要开。-g的语义是丢掉重复的应答记录,默认不去重是为了让你看到“同一个 IP 回了两次”这类异常,排查冲突时反而要故意不开-g。-f读文件时一行一个地址或网段,适合把 CMDB 里导出的地址清单直接丢进去核对,比整段扫描更省时间。

提示:-x和-g一起用适合做日常巡检输出,排查 IP 冲突时把-g去掉,重复记录本身就是证据。

4. 定位 IP 冲突:从 IP-MAC 表到锁定冲突主机

IP 冲突的表象是网络时通时断、ping 丢包、连接被重置,但根因在二层,必须拿到 IP 与 MAC 的对应关系才能定位。arp-scan 在这个场景里的价值不是“扫出设备”,而是“把同一个 IP 到底有几个 MAC 在应答这件事摆到台面上”。方法不难,难的是分清哪些是真空闲冲突、哪些是代理 ARP 或虚拟化造成的假象。

4.1 冲突的两种形态:一 IP 多 MAC 与一 MAC 多 IP

第一种形态是同一个 IP 被两个不同 MAC 应答,这是最典型的地址冲突,通常是一台设备手工配了静态地址,和 DHCP 池里分出去的地址撞了,或者两台设备抄了同一份配置模板。第二种形态是同一个 MAC 对应多个 IP,这不一定是冲突,虚拟化环境里一块物理网卡承载多个虚拟接口、路由器做代理 ARP、负载均衡设备用同一 MAC 代答多个地址,都会产生这种结果,看到就判冲突容易误伤。

区分方法很直接:一 IP 多 MAC 要重点查,一 MAC 多 IP 先确认这台设备的角色。交换机下挂的虚拟化宿主、防火墙接口、带 VRRP 的三层设备都可能出现后者,虚拟 MAC 的前缀通常有规律,比如 VRRP 用的虚拟 MAC 前缀是00:00:5e:00:01,看到这类地址基本可以排除是终端在抢地址。

# 第一轮:全量扫描,输出 IP 和 MAC,不做去重 sudo arp-scan -I eth0 --localnet -x \ --format='${ip} ${mac}' > scan1.txt # 按 MAC 排序后找重复,同一 MAC 出现多次即为多 IP 共用 sort -k2 scan1.txt | awk '{print $2}' | uniq -d # 按 IP 排序后找重复,同一 IP 出现多次即为多 MAC 应答 sort -k1 scan1.txt | awk '{print $1}' | uniq -d

这两条管道命令分别从 MAC 和 IP 两个维度找重复,uniq -d只输出出现次数大于一的项。注意第一条只能发现“同一轮扫描里同一 MAC 出现多次”,如果两个冲突设备不是每轮都应答,单轮扫描会漏,所以下面要多轮扫描对比。sort -k1和sort -k2的字段序号对应--format里的输出顺序,改过格式就要同步改这里,否则找出来的重复项毫无意义。

4.2 多轮扫描加抓包:把偶发冲突变成可观察证据

地址冲突最烦人的地方是偶发,扫十次可能只有两次能看到两个 MAC。这时候单靠 arp-scan 的输出不够,要让它和抓包配合:一边用 arp-scan 反复扫同一个可疑 IP,一边用 tcpdump 只抓 ARP reply,把每个应答的源 MAC 和时间戳记下来,冲突就从“感觉有问题”变成“几点几分谁回了包”。

# 终端一:只抓 ARP 应答,打印时间戳、源 MAC 和发送方 IP sudo tcpdump -i eth0 -n -e -tttt 'arp and arp[6:2] = 2' # 终端二:对可疑 IP 连续扫描 20 轮,每轮间隔 1 秒 for i in $(seq 1 20); do sudo arp-scan -I eth0 192.168.10.50 -x --format='${ip} ${mac}' sleep 1 done

tcpdump 的过滤表达式arp[6:2] = 2表示只保留 ARP 操作码为 2 的包,也就是应答;-e打印链路层头部,能直接看到源 MAC;-tttt给每行加完整日期时间。第二个循环里seq 1 20控制轮数,sleep 1拉开间隔,目的是让两台冲突设备都有机会应答,间隔太短容易总是同一台先回。两边对时间戳,如果同一个 IP 在不同时刻回了不同 MAC,冲突就坐实了,接下来拿 MAC 去交换机上查端口,或者去 DHCP 服务器上查租约,就能定位到具体设备。

4.3 用伪造源地址主动验证地址占用

还有一种更主动的验证方式:把 ARP 请求的发送方 IP 设成待验证的地址本身,也就是让请求看起来“来自这个 IP”。正常情况下,如果这个地址真的被某台设备占着,它收到这种请求后往往会有反应,有的设备会回应答,有的会发免费 ARP 宣告自己才是正主。这个手法适合在变更前确认“我要用的地址到底有没有人在用”,比 ping 一遍靠谱得多,因为很多设备禁 ping 但不会禁 ARP。

# 把发送方协议地址伪造成待检查的地址,扫描同网段 sudo arp-scan -I eth0 --arpspa=192.168.10.50 192.168.10.0/24 -x # 配合 arping 做重复地址检测,-D 是重复地址检测模式 sudo arping -D -I eth0 -c 3 192.168.10.50 echo "退出码: $?"

--arpspa只改 ARP 报文里的发送方 IP 字段,不改本机网络配置,所以不会影响本机通信,扫完就恢复原样。arping -D的退出码是有含义的:返回 0 表示没有收到应答、地址可用,返回 1 表示收到了应答、地址已被占用,脚本里可以直接拿它做判断。这两个手段都有边界,如果对方开了 ARP 防护或者处在隔离端口后面,可能一声不吭,所以结论要结合多轮扫描和交换机侧信息一起看,不能只凭一次没回应就断定地址干净。

注意:--arpspa伪造的是协议地址字段,不是让你改网卡配置;扫描期间本机正常通信不受影响,但不要在网关设备上对着大网段长时间跑,容易触发防护策略。

5. 避坑与排查:五条翻车记录

arp-scan 的参数不多,但踩过的坑一点都不少,而且大多不是工具本身的问题,是环境和使用姿势的问题。下面这几条是实际排查里反复出现的,按现象、原因、解决三段写,照着对照能省掉不少来回。

5.1 权限、接口与安装类问题

现象一:普通用户执行直接报错,提示无法打开链路层设备或权限不足。原因很明确,arp-scan 需要构造原始帧,必须拿 raw socket,普通用户没有这个能力,跟安装是否成功无关。解决办法是用 sudo 执行,或者给二进制加上CAP_NET_RAW能力再用普通用户跑;后者适合把它塞进定时任务,但要注意能力授予的粒度,别顺手把整个脚本提权。

# 方式一:临时提权执行 sudo /usr/local/sbin/arp-scan -I eth0 --localnet # 方式二:授予 raw socket 能力,之后普通用户可执行 sudo setcap cap_net_raw,cap_net_admin+eip /usr/local/sbin/arp-scan getcap /usr/local/sbin/arp-scan

setcap那两个能力里,cap_net_raw用于原始套接字,cap_net_admin用于接口相关操作,给这两个就够。getcap用来验证是否生效,输出里能看到能力列表就说明设置成功。要提醒一句,给二进制加能力等于给它开了口子,升级或重新编译后能力会丢失,需要重新设置,别忘了这一步。

现象二:命令能跑,结果一条都没有,统计行显示扫描了 N 个主机、0 个响应。最常见的原因是-I指定的接口不对,比如机器上有eth0和管理口ens192,扫的是没接网的那个。其次是网段不是直连网段,ARP 广播出不去,自然没有应答。解决办法是先ip addr确认接口和地址,再用--localnet让工具自己按接口地址和掩码推导网段,最后才是手写 CIDR。

# 先看清楚接口、地址和掩码 ip -brief addr show # 让工具按接口自动推导本地网段,避免手写网段写错 sudo arp-scan -I ens192 --localnet -x

ip -brief addr show一行一个接口,重点看目标接口有没有 IPv4 地址、掩码多少。--localnet会根据接口地址和掩码算出网段,比手工敲 CIDR 少一个出错点;如果接口没配地址,这个参数会直接报错,这也是一种快速判断接口是否可用的办法。

5.2 结果判定与数据文件类问题

现象三:明明在线的设备扫不到,但同一网段其它设备都能扫到。原因通常有三类:目标在另一个网段、中间隔了路由器;无线网络开了客户端隔离,终端之间二层不通;交换机端口隔离或私有 VLAN 配置,同一广播域被人为切开。解决办法是先确认目标与本机是否在同一广播域,把扫描机接到同一台交换机的同一 VLAN 里再试,无线环境改用有线接入验证。

# 确认本机路由,判断目标是否真的直连 ip route get 192.168.10.50 # 看目标网段是否走了网关,走了就说明不是直连 ip route show

ip route get会打印到达目标地址实际走的路由和出口接口,如果显示via <网关地址>,说明不是直连,ARP 探测必然无效。这一步能快速排掉“网段配错”这类低级问题,比反复调超时参数有效得多。跨网段发现本来就不是 arp-scan 的职责,需要换成三层扫描手段,别在这上面死磕。

现象四:扫描结果里出现同一个 IP 对应两个 MAC,兴冲冲去查,结果是虚拟化或网关代答。原因是代理 ARP 和虚拟化环境会让一台设备替别的地址应答,VRRP 之类的协议还会用虚拟 MAC。解决办法是看 MAC 前缀,00:00:5e:00:01开头的是 VRRP 虚拟 MAC,常见于双机热备;同一台宿主上的多块虚拟网卡会有相近的 MAC 段。判定前先去交换机上查这个 MAC 落在哪个端口,端口对得上设备角色再下结论。

现象常见原因下一步动作
一 IP 多 MAC真冲突、代理 ARP、VRRP 虚拟地址查 MAC 前缀,去交换机查端口
一 MAC 多 IP虚拟化宿主、子接口、负载均衡确认设备角色,排除代答
vendor 全是 UnknownOUI 文件缺失或路径不对用--ouifile显式指定

现象五:输出里 vendor 一列全是 Unknown,或者扫描大网段时中途卡住很久。前者是 OUI 数据库没找到,后者多半是网段太大加上超时重试叠加,单目标耗时乘以地址数量,时间就上去了。解决办法分别是显式指定 OUI 文件路径,以及把大网段拆成多个小段分批扫,或者下调-t和-r用速度换一点准确率,扫完再对可疑地址单独精扫。

# 显式指定厂商数据库路径 sudo arp-scan -I eth0 --localnet \ --ouifile=/usr/local/share/arp-scan/ieee-oui.txt -x # 大网段拆分扫描,降低单次持续时间 for net in 192.168.10 192.168.11 192.168.12; do sudo arp-scan -I eth0 "${net}.0/24" -x -t 150 done

第一条里--ouifile指向的文件必须真实存在且有读权限,路径写错时工具不会报错,只会安静地显示 Unknown。第二条的循环把三个 /24 分开扫,每段独立计时,中间可以插入sleep控制节奏;-t 150比默认 100ms 略宽松,适合设备响应偏慢的网段。拆段扫描还有个好处,某一段出问题时不影响其它段的结果采集。

提示:排查冲突时不要开-g,重复应答恰恰是要找的证据;日常巡检再开去重,两种场景的参数习惯最好分开记。

6. 把 arp-scan 塞进日常巡检:基线、diff 与告警

单次扫描能解决眼前问题,但要防住下一次地址冲突,得让它定时跑、留基线、做对比。我的做法是每天凌晨扫一次,把 IP、MAC、厂商三列按 IP 排序落盘,和前一天的基线做 diff,新增、消失、MAC 变化的条目全部进日志,第二天上班看日志就知道网段里有没有新面孔。

#!/usr/bin/env bash # arp_baseline.sh:采集网段基线并与上一次结果比对 set -euo pipefail IFACE="${1:-eth0}" BASE_DIR="/var/lib/arp-baseline" mkdir -p "$BASE_DIR" CUR="$BASE_DIR/current.txt" LAST="$BASE_DIR/last.txt" # 采集当前网段,去重、按 IP 排序,保证两次输出可比 sudo /usr/local/sbin/arp-scan -I "$IFACE" --localnet -x -g \ --format='${ip}\t${mac}\t${vendor}' | sort -k1,1 > "$CUR" # 首次运行没有基线,只落盘不比对 if [[ -f "$LAST" ]]; then diff -u "$LAST" "$CUR" >> "$BASE_DIR/changes.log" || true fi cp "$CUR" "$LAST"

脚本里set -euo pipefail让任何一步失败都能暴露出来,IFACE用位置参数传入,默认eth0,换机器不用改脚本。sort -k1,1按第一列 IP 排序是整段逻辑的关键,diff 只有在两边顺序一致时才有意义,少了这一步会每天报一堆假差异。diff -u的退出码在发现差异时是 1,|| true把它吞掉,避免脚本被set -e中断。changes.log会一直追加,记得配个 logrotate,不然一年下来日志比基线大几百倍。

这套东西上线前,我吃过一次亏:基线里混进了 DHCP 池的动态地址,每天早上 diff 全是变动,告警疲劳之后真出事反而没人看。从那以后我每次建基线都强制走一遍白名单——把 DHCP 地址段和已知虚拟化宿主的 MAC 前缀先剔掉,只对静态区和新增 MAC 做比对,告警量一下降到每周几条,每条都值得点开看。希望帮到你。

本文还有配套的精品资源,点击获取