You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARP协议网络设备监控脚本异常:扫描设备数量不稳定问题排查

网络设备ARP扫描结果波动问题排查与优化

问题背景

我编写了一个基于ARP协议(请求+响应)的本地网络设备监控脚本,代码如下:

import time
import scapy.all as scapy
import argparse
import pandas as pd


def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument('--target', '-t', dest='target', help='Target IP / IP range.')
    option = parser.parse_args()
    return option


def scan(ip):
    arp_request = scapy.ARP(pdst=ip)
    broadcast = scapy.Ether(dst='ff:ff:ff:ff:ff:ff')
    arp_request_broadcast = broadcast / arp_request
    answered_list = scapy.srp(arp_request_broadcast, timeout=3, verbose=False)[0]

    clients_list = []
    for element in answered_list:
        clients_dict = {'ip': element[1].psrc, 'mac': element[1].hwsrc}
        clients_list.append(clients_dict)
    return clients_list


opt = get_arguments()
while True:
    time.sleep(3)
    scan_result = pd.DataFrame(scan(opt.target))
    print(scan_result)

脚本运行基本正常,但存在严重问题:扫描返回的网络设备数量与实际不符——前两次扫描能显示全部6台设备,但后续扫描结果中设备数量随机波动,有时多有时少。需要排查该问题是ARP协议特性导致,还是代码存在错误,并给出优化方案。

原因分析

1. ARP协议特性的影响

  • 设备ARP静默/过滤机制:部分智能设备、打印机或配置了安全策略的主机,会忽略短时间内重复的ARP广播请求,以节省资源或避免恶意扫描。
  • 网络数据包丢失:ARP请求是二层广播包,当网络存在其他广播流量(如DHCP)或拥塞时,可能导致ARP请求/响应包被丢弃。
  • 设备低功耗模式:IoT设备、笔记本等进入低功耗状态时,会减少网络响应频率,不会每次都回复ARP请求。

2. 代码存在的不足

  • 超时时间过短:当前设置timeout=3,部分设备响应延迟较长,无法在超时窗口内返回响应。
  • 无重试机制:单次扫描仅发送一次ARP请求,未收到响应直接放弃,没有补发包逻辑。
  • 扫描频率过高:每3秒扫描一次,高频请求可能被部分设备判定为恶意行为,从而拒绝响应。
  • 结果未去重与状态跟踪:仅单次返回当前扫描结果,未处理重复响应,也没有跟踪设备历史状态,单次波动直接影响显示结果。

优化方案

1. 调整扫描核心参数

  • 延长超时时间:将timeout调整为8-10秒,给设备足够的响应缓冲时间。
  • 添加重试机制:每次扫描重复发送2-3次ARP请求,合并多次响应结果并去重。
  • 降低扫描频率:将扫描间隔从3秒改为15-30秒,避免触发设备的防扫描策略。

2. 优化结果处理逻辑

  • 结果去重:基于IP地址对多次扫描的响应结果去重,确保同一设备仅记录一次。
  • 设备状态跟踪:维护设备状态历史,连续3次未扫描到才标记为离线,避免单次扫描波动导致的误判。

3. 修改后的代码示例

import time
import scapy.all as scapy
import argparse
import pandas as pd
from collections import defaultdict


def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument('--target', '-t', dest='target', help='Target IP / IP range.')
    option = parser.parse_args()
    return option


def scan(ip, retry=3, timeout=8):
    arp_request = scapy.ARP(pdst=ip)
    broadcast = scapy.Ether(dst='ff:ff:ff:ff:ff:ff')
    arp_request_broadcast = broadcast / arp_request
    # 用字典存储结果,自动去重
    clients_dict = {}
    for _ in range(retry):
        answered_list = scapy.srp(arp_request_broadcast, timeout=timeout, verbose=False)[0]
        for element in answered_list:
            clients_dict[element[1].psrc] = element[1].hwsrc
    # 转换为列表格式返回
    return [{'ip': ip, 'mac': mac} for ip, mac in clients_dict.items()]


# 维护设备状态:key为IP,value为连续未出现的次数
device_status = defaultdict(int)
opt = get_arguments()

while True:
    time.sleep(15)  # 降低扫描频率至15秒
    current_scan = scan(opt.target)
    current_ips = {item['ip'] for item in current_scan}

    # 更新已有设备的状态
    for ip in list(device_status.keys()):
        if ip not in current_ips:
            device_status[ip] += 1
        else:
            device_status[ip] = 0  # 在线则重置计数

    # 新增扫描到的设备
    for item in current_scan:
        if item['ip'] not in device_status:
            device_status[item['ip']] = 0

    # 生成状态报告
    report = []
    for ip, count in device_status.items():
        status = '在线' if count < 3 else '离线'
        mac = next((item['mac'] for item in current_scan if item['ip'] == ip), '未知')
        report.append({'ip': ip, 'mac': mac, '状态': status})

    print(pd.DataFrame(report))
    print('-' * 50)

4. 额外建议

  • 使用管理员权限运行脚本:部分系统对二层数据包的发送和捕获有限制,权限不足可能导致扫描结果不准确。
  • 避开网络高峰时段扫描:办公时段网络流量大,ARP包丢失概率更高,非高峰时段扫描结果更稳定。

内容的提问来源于stack exchange,提问作者Alex Rebell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:22:56