ARP 协议与局域网主机存活查询
ARP 是 Ethernet Address Resolution Protocol 的缩写,翻译为地址解析协议,这个协议,
在平时的编程中很少用到,尤其在编写普通的通信程序。但是这个协议,确实非常重要。它的主
要功能是提供 IP 地址到以太网地址的映射关系。我们知道,在以太局域网通信中,主要有两种
网络环境,一种是共享型,一种是交换型。但这两种通信方式,都是需要预先知道目标主机的
MAC 地址,也就是其以太网的物理地址,才能发送数据。只不过,共享型网络中的主机,通过
识别收到数据桢中 MAC 地址,来判断是不是自己的数据。而交换型的网络,虽然也有这个过程,
只不过有交换机事先进行了判断。
IP 地址的设计是为了广域网,也就是更大范围的网络,不受地域和时间的限制。而 MAC 地
址是为小范围网络设计的,收到地域的限制。IP 地址是 32 位,而 MAC 地址是 48 位的。从 IP
地址到 MAC 地址通过 ARP 协议来转换,从 MAC 地址到 IP 地址的转换是通过 RARP(逆向地址
解析协议)协议来进行的。RFC 826 是 ARP 规范的描述文档。感兴趣的可以去下载看看。
|
|
|
ARP 分组的格式如下:
+-------------------------------------------------+
| 以太网目的地址 (6 个字节)
|
+-------------------------------------------------+
| 以太网源地址 (6 个字节)
|
+-------------------------------------------------+
| 桢类型 (2 个字节)
+-------------------------------------------------+
| 硬件类型 (2 个字节)
+-------------------------------------------------+
| 协议类型 (2 个字节)
+-------------------------------------------------+
| 硬件地址长度 (1 个字节)
|
+-------------------------------------------------+
| 协议地址长度 (1 个字节)
|
+-------------------------------------------------+
| op (2 个字节)
+-------------------------------------------------+
| 发送端以太网地址 (6 个字节)
|
+-------------------------------------------------+
| 发送端的 IP 地址 (4 个字节)
|
+-------------------------------------------------+
| 目的以太网地址 (6 个字节)
|
+-------------------------------------------------+
| 目的 IP 地址 (4 个字节)
|
+-------------------------------------------------+
|
分组格式中,需要说明的是,以太网桢头中的目的地址为全是 1 的特殊地址,这是一个广播
地址,目的是为了所有以太网口都能收到该数据桢。也就是说 ARP 分组数据是一个广播包。
桢类型字段是固定值,为 0x0806。硬件类型表示硬件地址的类型,它的值为 1 表示以太网
地址。协议类型表示要映射的协议的地址类型,它的值 0x0800 表示 IP 地址。接下来两字定义
域,都是一个字节,分别表示两种协议地址的长度,因此可知,应该分别为 6 和 4。
op 字段为操作类型字段,用来表示这个数据包的操作类型。一个有四种类型,分别是 ARP 请
求包是 1,ARP 应答包是 2,RARP 请求包是 3,RARP 应答包是 4。因为 ARP 应答和请求的
分组格式是相同的,为了区分是应答还是请求,这个字段必须正确设置。
在 Window 环境下,用下面函数来实现这个功能。分别是:
DWORD
WINAPI
SendARP(
IPAddr DestIP,
IPAddr SrcIP,
PULONG pMacAddr,
PULONG PhyAddrLen
);
因为在以太网上的主机要想能够与其他主机通信,必须支持 ARP 协议,也就是说必须对 ARP
请求进行响应。因此,可以通过,这种手段来发现局域网中哪些 IP 地址已经被用了。下面就是
代码:
//
// Link with ws2_32.lib and iphlpapi.lib
//
#include
#include
#include
#include
#include "iphlpapi.h"
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "IPHlpApi.Lib")
#pragma comment(lib, "netapi32.Lib")
BOOL GetArp(char *ip_name,char *mac_name);
unsigned long GetLocalIP(char *ip_name);
//------------------------------------------------------------
//
// Main
//
// Do it all.
//
//------------------------------------------------------------
int main(int argc,char **argv)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
unsigned long hostip = 0;
struct in_addr ip;
char mac[20];
char hostname[256];
//
// Print banner
//
printf("\n\tSendArp v1.0 - scan IP and MAC\n");
printf("\tNsfocus - www.nsfocus.com\n");
printf("\tby David Zhou\n");
printf("\tDate : 2005/11/25\n\n");
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return -1;
}
if(argc == 1)
{
hostip = GetLocalIP(NULL);
ip.S_un.S_addr = hostip;
printf("local ip: %s\n\n",inet_ntoa(ip));
for(int n=1;n<255;n++)
{
hostip &= 0x00FFFFFF;
hostip |= (n<<24);
ip.S_un.S_addr = hostip;
memset(mac,0,20);
if(NetbiosInfo(inet_ntoa(ip),hostname))
printf("%s == ",hostname);
if(GetArp(inet_ntoa(ip),mac))
{
printf("%s == > %s\n",inet_ntoa(ip),mac);
}
else
{
printf("%s == > No Active\n",inet_ntoa(ip));
}
}
return 1;
}
//
//
//
if(argc == 2)
{
//GetArp("192.168.5.212");
}
else if(argc == 3)
{
}
else
{
}
return 0;
}
BOOL GetArp(char *ip_name,char *mac_name)
{
ipAddr;
HRESULT hr;
IPAddr
ULONG pulMac[2];
ULONG ulLen;
ipAddr = inet_addr (ip_name);
memset (pulMac, 0xff, sizeof (pulMac));
ulLen = 6;
hr = SendARP (ipAddr, 0, pulMac, &ulLen);
if(ulLen <6)
{
//printf("No Active\n");
return FALSE;
}
size_t i, j;
char * szMac = new char[ulLen*3];
PBYTE pbHexMac = (PBYTE) pulMac;
//
// Convert the binary MAC address into human-readable
//
for (i = 0, j = 0; i < ulLen - 1; ++i)
{
}
j += sprintf (szMac + j, "%02X:", pbHexMac[i]);
sprintf (szMac + j, "%02X", pbHexMac[i]);
sprintf(mac_name,"%s",szMac);
delete [] szMac;
return TRUE;
}
unsigned long GetLocalIP(char *ip_name)
{
BYTE *p;
char temp[100];
struct hostent *hp;
char ip[16];
if(gethostname(temp, sizeof(temp)) == 0)
{
if((hp =gethostbyname(temp))!=0)
{
p =(BYTE *)hp->h_addr;
wsprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
}
}
else
{
return 0l;
}
return ((p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]);
}