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
相关产品推荐
相关产品推荐

