一、前言
局域网查看某个IP是否可用(未被其它网卡占用),可使用两种方法:
1、使用ping命令,不通即未被占用
2、使用ARP,如果该IP存在则会返回相应MAC地址
使用ping命令来确认该IP占用与否不够严谨,比如该IP存在,但由于掩码问题,ping不通,故采用ARP协议来确定IP是否被占用。
二、查找资料
1、ARP协议介绍
2、 实现方案
1)使用npcap实现
优势:接口开放性高、自主性强
劣势:需要安装npcap驱动
示例:alandau/arpspoof: A simple ARP spoofer for Windows
2)socket(AF_PACKET)
优势:例子很多,使用普遍
劣势:为linux代码,windows无法直接编译,需交叉编译【未验证】
示例:
ARP的C代码实现_interface "lo" is not arpable-CSDN博客
https://github.com/huih/arp/tree/master
3)sendARP
优势:windows 原生API
劣势:调用不存在的IP地址时,接口超时时间为5秒
示例:QbsuranAlang/arp-scan-windows-: send arp request to whole specific LAN
4)命令行 arp -a
优势:使用方便,只需解析执行结果
劣势:未通讯的IP,需要先ping操作,arp -a 才能找到【未找到其它参数】
示例:
总结:综合考虑,采用SendARP方案实现。
三、实现
1、网段扫描
2 、固定IP扫描
3、代码实现
扫描单个IP
cpp
//扫描固定IP
void ArpScan::sendArpImpl(qint64 id, ulong nIp)
{
IPAddr SrcIp = 0; /* default for src ip */
ULONG MacAddr[2]; /* for 6-byte hardware addresses */
ULONG PhysAddrLen = 6; /* default to length of six bytes */
memset(&MacAddr, 0xff, sizeof(MacAddr));
PhysAddrLen = 6;
DWORD dwRetVal = SendARP(nIp, SrcIp, &MacAddr, &PhysAddrLen);
if (PhysAddrLen)
{
//ip
in_addr ip_addr;
ip_addr.s_addr = nIp;
QString ip = inet_ntoa(ip_addr);
//mac
BYTE* pPhysAddr = (BYTE*)&MacAddr;
QString mac = QString("%1:%2:%3:%4:%5:%6")
.arg(pPhysAddr[0], 2, 16, QLatin1Char('0'))
.arg(pPhysAddr[1], 2, 16, QLatin1Char('0'))
.arg(pPhysAddr[2], 2, 16, QLatin1Char('0'))
.arg(pPhysAddr[3], 2, 16, QLatin1Char('0'))
.arg(pPhysAddr[4], 2, 16, QLatin1Char('0'))
.arg(pPhysAddr[5], 2, 16, QLatin1Char('0'));
emit ArpScan::Instance()->sig_beScanned(id, ip, mac);
}
}
为避免SendARP阻塞太久,造成网段多IP扫描耗时很久的问题,采用多线程来避免这个问题
cpp
//循环搜索
uint loopCount = 1 << (32 - slashMask);
for (uint i = 0; i < loopCount; ++i)
{
//此处使用线程detach,不会阻塞
std::thread thr(sendArpImpl, id, nIp);
thr.detach();
ulong localIp = ntohl(nIp);
nIp = htonl(++localIp);
}