自由行

2007-01-25

ARP协议浅析(1):缘起

Filed under: 生活, 程序员 — thomas @ 18:19

ARP协议浅析(1):缘起

下一章:ARP协议浅析(2):协议分析

(关心技术的朋友可以跳过)

朝花夕拾

一直想写一个局域网IP查看工具,用来查询一个局域网的以使用的IP地址。毕业一年多了,仍然牵挂着这个网络小工具。当时由于水平有限未能完成,8月底正好有这个时间,于是花了一个星期的时间,终于完成了。其间的一些体会写出来和大家分享。此文适合于对TCP/IP协议有所了解和具有一定编程基础的朋友。

IP地址冲突

2002年笔者所在宿舍楼开通校园网,大家无比兴奋,但是很快烦恼就来了。笔者所在宿舍楼共6层,计算机多达400多台(可能还要多一些),而只分配了一个CIP段,254IP地址,IP地址太少了,IP地址冲突非常激烈。

“瓜分之”

最初大家很早起床打开计算机来占住IP地址,那时候晚上是停电的。所以每天IP地址都要在早上重新被“瓜分”。有个哥们每天早上起来推电闸,搞得大家觉都睡得不好。僧多粥少,实属无奈。

宿舍里有好几台电脑,于是先占住IP的同志有义务为后面的同志提供查询义务,随便设置一个IP,可能别人已经使用了。好在Window2000可以动态该IP地址,无需重启。WinXP更强,修复一下可以恢复过来。Win98就没有这么幸运,经常被冲突死机,于是那个时候Win98就被大家无情的抛弃了。问题是:你可以改动IP设置,但是你并不知道那些IP没有被其他机器使用。

那个时候,经常你下网不到一分钟就会被别人占住IP地址。如果是网络游戏,就惨了,装备

捡不到,还可能被别的玩家砍死。不过笔者不玩网游,所以体会不到他们的痛苦。

寻找空闲IP

Ping命令是一个最常见的工具。Ping命令不好用,一个一个的Ping,很浪费时间,没有时效性。另外天网防火墙等软件可以设置不响应Ping,所以很不准确。看着别人上网,自己设置一个IP冲突一个,真是郁闷。如果有个工具可以准确的查看局域网的IP使用情况该多好啊!

当时在网上搜索了很久,发现了一个基于UDP协议的查看IP的程序,还有源代码。效果改观了很多,它使用一个独立的线程发送,利用socket接受,缺点是天网可以屏蔽它。

当时开始思考这个问题,每台机器的唯一不变的是网卡的物理地址即Mac地址,局域网到底是用什么机制来实现IP地址到Mac的映射了,这可是通信的基本的问题!

因为对网络协议感兴趣的原因,后来查阅很多种资料,包括RFC的英文文档,用代理在国外的找的,该文档介绍各种于网络相关的标准,有各种协议的规范。我知道了一些ARP(Address Resolution Protocol,地址解析协议)方面的知识,后面介绍详细这个协议。

不久,真的出现了这么一个小软件。据说是这栋楼的一个VC高手写的,看到这个东西的只需要输入一个查询范围如202.4.151.1-202.4.151.254它就会快速的查询IP地址的使用情况。当时看不到源代码,佩服了半天。不过有一点可以肯定,它使用的是ARP协议,用到了多线程。

当时笔者仅仅知道一些网络协议,没有编过Winsock方面的程序,多线程也很弱。但当时就想写一个工具,苦于知识经验缺乏,以后有机会要写一个类似的小工具。

峰回路转

大概3个月后,学校调查后采取了措施,扩大了IP的范围,几乎每台一个IP。问题不复存在了,想编写一个IP-MAC查询的工具的念头从此沉底。

旧“情”复燃

之后又发生了很多情。考程序员,做毕业设计,工作,再换工作从机械设计回到软件开发。加班,学习,学习,加班日子过得真快。

《深入浅出MFC》《Windows核心编程》《Windows网络编程技术》看了很长时间了,渐渐的领悟了一些东西,多线程也没有那么惧怕了。特别是有个项目涉及到了UDP多点传输,当时把socket深入的看了几章,调试了几个这个方面的程序,对这个模型熟悉起来。感觉有足够的知识来写这个东西了。

现在终于有了时间,也感觉自己有这个实力了,但还没有想到具体怎么写?到网上搜所(在前人的基础上有所超越),于是找到了几篇关于ARP的文章,下面的两篇是其一。同时也开始接触Row Socket(原始套接字),研究Sniffer(嗅探器)。当时想,Ping的时候,如果不知道相应的IP地址的MAC地址该如何处理,只能还是回到ARP协议,对了上层协议必须建立在下层协议的基础上工作!Row Socket虽然原始,却只能到网络层,而ARP协议在链路层工作,因为在网线或设备识别的只能是MAC地址,同时它不是物理层。

WinPcap利器

武侠世界盛传:“真气所至,草木皆为利器”。

在查找Row Socket资料时,发现了WinPcap库,得知一些网络工具是基于它开发的,于是着手研究WinPcap库。通过阅读帮助文档和练习里面的例子,逐渐对它有了深入认识。它经封装了对设备的操作,可以直接发送链路层的包。其实,只要你了解了各种包格式,你就可以实现ISO/OSI的各个协议层的功能!OKWinPcap能够实现ARP协议的应用,剩下的就是设计编码。

真气是ARP工作原理和流程,草木就是具体程序的代码。当然没有必要从头做起,利用成熟的库可以事半功倍。

实践

WinPcap的参考帮助文档是Web格式的,没有索引(不如MSDN方便)

笔者也没有彻底研究,涉及到的代码,有较为详细的注释。

首先参考了《手把手教你玩转ARP》,作者提供了详细的代码,讲的也很详细,于是没有花多少时间就看明白了。但是作者的工程是用VS2003写的,但没有说在哪个Window版本上通过,估计上Win2000,转换到VC6的工程后,在WinXP上却没有提到的arp缓冲条目增加的情况。

这个问题困扰了两天,最后发现之前下载的文章《详谈调用WinPcap驱动写arp多功能工具》有收发arp包的功能。这个例子离笔者的需要很近。该代码是console工程,必须改造成MFC App。这倒不是很难。简单的设计了界面,具体实现需要的就是时间。

前后用了一个星期,利用别人的间隙时间使用电脑,着实不方便。中间还有几个细节问题,最后还是完成了,感觉不错。

下一章:ARP协议浅析(2):协议分析

ARP协议浅析(2):协议分析

Filed under: 程序员 — thomas @ 18:15

ARP协议浅析(2):协议分析

上一章:ARP协议浅析(1):缘起

下一章:ARP协议浅析(3):付诸实践

ARP协议原理方面的内容,来自于手把手教你玩转ARP。笔者进行了一些整理,想获得更多信息参考RFC文档和《TCP-IP详解卷1:协议ARP章节,卷2021.pdf》。

表目录

下面是ARP帧主要的格式目录

1 物理帧头格式2

2 ARP帧格式2

3 ARP请求包中的DLC帧头3

4 ARP请求包中 ARP3

5 ARP响应包中 DLC Header内容4

6 ARP响应包中ARP帧的内容4

为何有ARP协议

(1) IP-MAC映射

以太网设备比如网卡都有自己全球唯一的MAC地址,它们是以MAC地址来传输以太网数据包的,它们不可能识别IP包中的IP地址,实际上网卡把受到的包提交到协议栈的缓冲,成为链路层包,再分离出IP层的包,再分离出TCPUDP…。就像剥开竹笋,发送包则是相反的过程。

我们在以太网中进行IP通信的时候需要一个协议来建立IP地址与MAC地址的对应关系,以使IP数据包能发到一个确定的地方去。这就是ARP(Address Resolution Protocol,地址解析协议)

讲到此处,我们可以在命令行窗口中,输入

arp -a

来看一下效果,类似于这样的条目

210.118.45.100    00-0b-5f-e6-c5-d7    dynamic

就是我们电脑里存储的关于IP地址与MAC地址的对应关系,dynamic表示是临时存储在ARP缓存中的条目,过一段时间就会超时被删除(具体时间不同操作系统不同)

这样一来,比如我们的电脑要和一台机器比如210.118.45.1通信的时候,它会首先去检查arp缓存,查找是否有对应的arp条目,如果没有,它就会给这个以太网络发ARP请求包广播询问210.118.45.1的对应MAC地址,当然,网络中每台电脑都会收到这个请求包,但是它们发现210.118.45.1并非自己,就不会做出相应,而210.118.45.1就会给我们的电脑回复一个ARP应答包,告诉我们它的MAC地址是xx-xx-xx-xx-xx-xx,于是我们电脑的ARP缓存就会相应刷新,多了这么一条:

210.118.45.1   xx-xx-xx-xx-xx-xx   dynamic

为什么要有这么一个ARP缓存呢,试想一下如果没有缓存,我们每发一个IP包都要发个广播查询地址,岂不是又浪费带宽又浪费资源?

(2) 真真假假ARP

我们的网络设备无法识别ARP包的真伪,而且也不能识别,基本的约定都是假的,会带来灾难。如果我们按照ARP的格式来发送数据包,只要信息有效计算机就会根据包中的内容做相应的反应。

这也是以太网可靠通信的基础,但是如果我能操纵网卡设备发送数据和掌握包的格式,我可以做一些欺骗和捣乱。当然,笔者只是理论分析,并不提倡读者这么做。

ARP包的格式

一个ARP包是分为两个部分的,前面一个是物理帧头或EtherHeader,后面一个才是ARP帧或Arp-Frame

首先,物理帧头,它将存在于任何一个协议数据包的前面,我们称之为DLC Header,因为这个帧头是在数据链路层构造的,并且其主要内容为收发双方的物理地址,以便硬件设备识别。

1 物理帧头格式

DLC Header

字段

长度(Byte)

默认值

备注

接收方MAC

6

广播时,为 ff-ff-ff-ff-ff-ff

发送方MAC

6

Ethertype

2

0×0806

0×0806ARP帧的类型值

1是需要我们填充的物理帧头的格式,我们可以看到需要我们填充的仅仅是发送端和接收端的物理地址罢了,是不是很简单呢?

接下来我们看一下ARP帧的格式。

2 ARP帧格式

ARP Frame

字段

长度(Byte)

默认值

备注

硬件类型

2

0×1

以太网类型值

上层协议类型

2

0×0800

上层协议为IP协议

MAC地址长度

1

0×6

以太网MAC地址长度为6

IP地址长度

1

0×4

IP地址长度为4

操作码

2

0×1表示ARP请求包,0×2表示应答包

发送方MAC

6

发送方IP

4

接受方MAC

6

接受方IP

4

填充数据

18

因为物理帧最小长度为64字节,前面的42字节再加上4CRC校验字节,还差18个字节

我们可以看到需要我们填充的同样也只是MACIP,再加上一个12的操作码而已。

ARP包的填充

(1) 请求包的填充

比如我们的电脑MAC地址为 aa-aa-aa-aa-aa-aaIP 192.168.0.1

我们想要查询 192.168.0.99MAC地址,应该怎么来做呢?

首先填充DLC Header,通过前面的学习我们知道,想要知道某个计算机对应的MAC地址是要给全网发送广播的,所以接收方MAC肯定是 ffffffffffff发送方MAC当然是自己啦,于是我们的DLC Header就填充完成了,如图,加粗的是我们要手动输入的值。

3 ARP请求包中的DLC帧头

DLC Header

字段

长度(Byte)

填充值

接收方MAC

6

Ffffffffffff

发送方MAC

6

Aaaaaaaaaaaa

Ethertype

2

0×0806

接下来是ARP帧,请求包的操作码当然是发送方的MAC以及IP当然填入我们自己的。然后要注意一下,这里的接收方IP填入我们要查询的那个IP地址,就是192.168.0.99了,而收方MAC填入任意值就行,不起作用。具体格式如下表。

4 ARP请求包中 ARP

ARP Frame

字段

长度(Byte)

填充值

硬件类型

2

1

上层协议类型

2

0×0800

MAC地址长度

1

6

IP地址长度

1

4

操作码

2

1

发送方MAC

6

Aaaaaaaaaaaa

发送方IP

4

192.168.0.1

接收方MAC

6

任意值 xxxxxxxxxxxx

接收方IP

4

192.168.0.99

填充数据

18

0

如果我们构造一个这样的包发送出去,如果 192.168.0.99存在且是活动的,我们马上就会收到一个192.168.0.99发来的一个响应包,我们可以查看一下我们的ARP缓存列表,是不是多了一项类似这样的条目:

192.168.0.99   bb-bb-bb-bb-bb-bb

是不是很神奇呢?我们再来看一下ARP响应包的构造:

(2) 响应包的填充

有了前面详细的解说,你肯定就能自己说出响应包的填充方法来了吧,所以我就不细说了,列两个表就好了

比如说给 192.168.0.99MAC bb-bb-bb-bb-bb-bb)发一个ARP响应包,告诉它我们的MAC地址为 aa-aa-aa-aa-aa-aa,就是如此来填充各个字段

5 ARP响应包中 DLC Header内容

DLC Header

字段

长度(Byte)

填充值

接收方MAC

6

Bbbbbbbbbbbb

发送方MAC

6

Aaaaaaaaaaaa

Ethertype

2

0×0806

6 ARP响应包中ARP帧的内容

ARP Frame

字段

长度(Byte)

填充值

硬件类型

2

1

上层协议类型

2

0800

MAC地址长度

1

6

IP地址长度

1

4

操作码

2

2

发送方MAC

6

Aaaaaaaaaaaa

发送方IP

4

192.168.0.1

接收方MAC

6

Bbbbbbbbbbbb

接收方IP

4

192.168.0.99

填充数据

18

0

这样192.168.0.99ARP缓存中就会多了一条关于我们192.168.0.1的地址映射。

ARP协议相关的代码中,大部分的工具是发送请求包(Request)和接受请求包,分析后填充正确格式的响应包(Response)。这个协议就是这么工作的,下一章将分析一下具体的应用和重要的模块与函数。

ARP协议浅析(3):付诸实践

Filed under: 程序员 — thomas @ 18:03

ARP协议浅析(3):付诸实践

上一章:ARP协议浅析(2):协议分析

下一章:ARP协议浅析(4):回顾

这次研究本意是编写一个用于局域网检测IP地址与MAC对应关系的Application,以获知局域网里IP地址的分配情况,在局域网的IP冲突中找到可用的IP地址。

设计与构思

当确定Winpcap库可以满足操纵ARP后就大胆的展开程序了,首先是对界面进行简单的构思,其中必须把界面和功能函数的关系分离好。

程序架构

1 程序基本结构表

模块

说明

UI Class

对话框类class CIPFluxDlg : public CDialog显示信息,接受用户命令

CWinPcap Class

自定义的WinPcap封装类,包括了所有功能函数

WinPcap Library

实现ARP分析功能底层库

主要的调用关系是从上到下,不过WinPcap中使用了回调机制,CWinPcap类的Sniffer函数也使用一个曲折的获得CIPFluxDlg指针的特殊技巧,当然这并非唯一的途径,也许读者会有更好的方法。

界面构思

界面采用对话框结构,包括

本地信息,包括IP地址,MAC编码;

IP地址搜索范围,当然起始IP地址、终止IP地址,都在内部计算过了,而且可以编辑;

搜索按钮;

搜索结果列表,显示搜索到IP-MAC信息。

点击搜索按钮后,后台线程开始搜索并把结果按先后顺序显示在列表中。程序界面如:1 程序界面所示。

1 程序界面

该程序定位为简易的搜索工具,所以界面很简洁,主要的工作还是在于IP搜索功能的实现。

监听模式

一个监听者线程,多个发送者线程。搜索时针对每个IP地址查询创建独立的线程,发完ARP包就完事,其余的工作就让监听者线程完成。通常我们只需要一个监听者线程就可以,这个线程建立后不用退出,直到程序退出。

WinPcap的封装

WinPcap这个库非常专业,有很多网络方面的工具,特别是Sniffer类型工具,如:SnifferProEffectHttpSniffer。它是一个跨平台的库,功能接口比较多,IPFlux用不了那么多,如果直接在对话框类中直接调用的话,会使得代码交错,不便于阅读和修改,所以最好进行封装,这样可以与界面无关,如果以后在别的应用用到的话,将会非常方便。

通过分析,决定划分一些具体的子功能来实现的:

* 1 网卡的枚举,设置和缓冲区分配;

* 2 填充和发送一个ARP包;

* 3 监听线程(Sniffer)

* 4 解析ARP包;

* 5 获得本机IP MAC地址;

* 6 发送线程和接受线程的同步与协调;

这些子功能主要使用WinPcap库函数,笔者封装了一个CWinPcap类。以下分别对子功能的代码作详细说明,其它请参考所附源代码。关于封装类的函数请看2 CWinPcap的主要函数表

2 CWinPcap的主要函数表

函数

描述

int CWinPcap::OpenPcap()

打开Pcap

void CWinPcap::ClosePcap()

关闭Pcap

void CWinPcap::GetData(LPPACKET lp)

分析ARP

void CWinPcap::GetNetInfo(sockaddr_in &sin, CString &strMAC, BYTE mac[])

获取本机信息

int CWinPcap::Sender(ULONG ipCur, ULONG ipMine, BYTE mac[])

发送者函数

int CWinPcap::Sniff()

监听者函数

1 网卡的枚举,设置和缓冲区分配

// success return 0

int CWinPcap::OpenPcap()

{

WCHAR buf[1024];

ULONG bufsize;

int res, i = 0;

memset ((void*)adapterlist, 0, sizeof(adapterlist));

res = PacketGetAdapterNames ((char*)buf, &bufsize); // 枚举网卡

if (res == 0)

{

return -1;

}

WCHAR *p1, *p2;

p1 = p2 = buf;

while ((*p1 != ‘\0′) || (*(p1 – 1) != ‘\0′))

{

if (*p1 == ‘\0′)

{

memcpy (adapterlist[i], p2, 2 * (p1 – p2));

p2 = p1 + 1;

i++;

}

p1++;

}

m_iAdapterNum = i;

m_iSelAdapter = i – 1;

// 打开最后一个网卡,PC机一般是一个网卡

m_lpAdapter = PacketOpenAdapter( ((char *)adapterlist + m_iSelAdapter * 1024) );

if (m_lpAdapter == NULL || (m_lpAdapter->hFile == INVALID_HANDLE_VALUE))

{

return -1;

}

// 分配sender包空间

m_lpPacketSender = PacketAllocatePacket();

if (m_lpPacketSender == NULL)

return -1;

return 0;

}

2 如何填充和发送一个ARP

2.1 包的分组格式定义

//////////////////////////////////////////////////////////////////////////

// arp.h 定义以太网arp帧结构,封装了winpcap开发包的重要函数

#include <packet32.h>

#include <ntddndis.h>

#include <stdio.h>

#include <conio.h>

#include “afx.h”

#pragma comment(lib,”ws2_32″)

#pragma comment(lib,”packet”)

#pragma once

#pragma pack(push,1)

// Ether(DLC) header

typedef struct ethdr

{

unsigned char eh_dst[6]; // 以太网目的MAC地址

unsigned char eh_src[6]; // 以太网源MAC地址

unsigned short eh_type; // 帧类型

}ETHDR,*PETHDR;

// ARP分组格式

typedef struct arphdr

{

unsigned short arp_hdr; // 硬件类型

unsigned short arp_pro; // 协议类型

unsigned char arp_hln; // 硬件地址长度

unsigned char arp_pln; // 协议地址长度

unsigned short arp_opt; // ARP/RARP

unsigned char arp_sha[6]; // 发送者地址

unsigned long arp_spa; // 发送者IP地址

unsigned char arp_tha[6]; // 接受者地址

unsigned long arp_tpa; // 接受者IP地址

}ARPHDR,*PARPHDR;

#pragma pack(push)

#define ETH_IP 0×0800

#define ETH_ARP 0×0806

#define ARP_REQUEST 0×0001

#define ARP_REPLY 0×0002

#define ARP_HARDWARE 0×0001

#define max_num_adapter 10

class CWinPcap

{

public:

/* 分析arp包内容*/

void GetData(LPPACKET lp);

/* 发送arp请求包*/

int Sender(ULONG ipCur, ULONG ipMine, BYTE mac[]);

/* 监听网卡收到的包*/

int Sniff();

/* 获得网卡信息,IP地址和MAC地址*/

void GetNetInfo(sockaddr_in &sin, CString &strMAC, BYTE mac[]);

CWinPcap()

{

OpenPcap();

};

~CWinPcap()

{

ClosePcap();

};

/* 打开网卡并初始化*/

int OpenPcap();

/* 关闭网卡*/

void ClosePcap();

private:

char adapterlist[max_num_adapter][1024];

/* 网卡*/

LPADAPTER m_lpAdapter;

/* 帧缓冲*/

LPPACKET m_lpPacketReceiver,

m_lpPacketSender;

int m_iAdapterNum;

int m_iSelAdapter;

npf_if_addr m_netinfo;

};

2.2 填充和发送一个ARP请求包

/********************************************************************

功能 供发送线程调用,发送一个arp请求包

参数

ipCur 目标IP地址

ipMine 本机IP地址

mac[] 网卡MAC地址

********************************************************************/

int CWinPcap::Sender(ULONG ipCur, ULONG ipMine, BYTE mac[])

{

char pBufSend[1024];

ETHDR eth;

ARPHDR arp;

// Fill the ARP request packets.

int i;

for (i = 0; i < 6; i++)

{

eth.eh_dst[i] = 0xff;

arp.arp_tha[i] = 0×00;

}

// {填充DLC头和ARP

eth.eh_type     = htons(ETH_ARP);

memcpy(eth.eh_src, mac, 6);

arp.arp_hdr     = htons(ARP_HARDWARE);

arp.arp_pro     = htons(ETH_IP);

arp.arp_hln     = 6;

arp.arp_pln     = 4;

arp.arp_opt     = htons(ARP_REQUEST);

memcpy(arp.arp_sha, mac, 6);

arp.arp_spa     = htonl(ipMine);

arp.arp_tpa = htonl(ipCur);

// }

memset(pBufSend, 0, sizeof(pBufSend));

memcpy(pBufSend, &eth, sizeof(eth));

// 装配完整arp

memcpy(pBufSend + sizeof(eth), &arp, sizeof(arp));

PacketInitPacket(m_lpPacketSender, pBufSend, sizeof(eth) + sizeof(arp));

if(PacketSendPacket(m_lpAdapter, m_lpPacketSender,TRUE)==FALSE)

{

return -1;

}

return 0;

}

3 监听线程(Sniffer)

线程函数体实际执行的函数:

int CWinPcap::Sniff()

{

static CIPFluxDlg *pdlg = (CIPFluxDlg *)AfxGetMainWnd();

char recvbuf[1024*250];

DWORD res = 0;

// {初始化网卡,设置为混合模式NDIS_PACKET_TYPE_PROMISCUOUS

if(PacketSetHwFilter(m_lpAdapter, NDIS_PACKET_TYPE_PROMISCUOUS)==FALSE)

{

//printf(“Warning: Unable to set the adapter to promiscuous mode\n”);

}

if(PacketSetBuff(m_lpAdapter, 500*1024)==FALSE)

{

//printf(“PacketSetBuff Error: %d\n”,GetLastError());

return -1;

}

if(PacketSetReadTimeout(m_lpAdapter, 1)==FALSE)

{

//printf(“Warning: Unable to set the timeout\n”);

}

if((m_lpPacketReceiver=PacketAllocatePacket())==FALSE)

{

//printf(“PacketAllocatePacket receive Error: %d\n”,GetLastError());

return -1;

}

PacketInitPacket(m_lpPacketReceiver, (char *)recvbuf, sizeof(recvbuf));

// }

// {接受包

do

{

if(PacketReceivePacket(m_lpAdapter, m_lpPacketReceiver, TRUE) == FALSE)

{

if(GetLastError() == 6)

return 0;

return -1;

}

GetData (m_lpPacketReceiver); // 解析包的内容

if (pdlg->m_bStop == TRUE)

break;

}while (1);

// }

ResetEvent(pdlg->m_hEvent); // 辅助函数

return 0;

}

4 解析ARP响应包

/********************************************************************

功能 从接受到的包中提取和解析出ARP包个字段

参数

lp 网卡接受到的包的缓冲区指针

********************************************************************/

void CWinPcap::GetData(LPPACKET lp)

{

ULONG ulOffset = 0, ulBytesReceived;

char *buf = NULL;

char *pChar, *pBase;

struct bpf_hdr *phdr = NULL;

struct sockaddr_in sin;

ETHDR *pEther;

ARPHDR *pArp;

//IPHDR  *pIphdr;

CString strIP, strMAC;

static CIPFluxDlg *pdlg = (CIPFluxDlg *)AfxGetMainWnd();

buf = (char*)lp->Buffer;

ulBytesReceived = lp->ulBytesReceived;

while (ulOffset < ulBytesReceived)

{

phdr = (struct bpf_hdr *)(buf + ulOffset);

ulOffset += phdr->bh_hdrlen;

pChar = (char *)(buf + ulOffset);

pBase = pChar;

ulOffset = Packet_WORDALIGN(ulOffset + phdr->bh_caplen);

pEther = (PETHDR)pChar;

pArp   = (PARPHDR)(pChar + sizeof(ETHDR));

// receive ARP reply packets which contain IP address and relative MAC address

// 受到arp响应包,包含IP地址和相关的MAC地址

if (pEther->eh_type == htons(ETH_ARP) && pArp->arp_opt == htons(ARP_REPLY))

{

sin.sin_addr.s_addr = pArp->arp_spa;

strIP.Format(“%-16s”, inet_ntoa(sin.sin_addr));

CString str;

str.Format(“%02X”, pEther->eh_src[0]);

strMAC = str;

for (int i = 1; i < 6; i++)

{

str.Format (“-%02X”, pEther->eh_src[i]);

strMAC += str;

}

pdlg->ShowSearch(strIP, strMAC);

SetEvent(pdlg->m_hEvent);

}

}

}

5 获得本机IP地址和MAC地址

工程中没有采取Winpcap函数来获得MAC地址,而是采用非常有用的iphelp库函数。

DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen);

/************************************************************************

功能 获得本机网卡的IP地址,物理地址,涉及到<ntddndis.h>

参数

sin          IP地址

strMAC   MAC地址16进制表达

mac[]      MAC地址字节表达

************************************************************************/

void CWinPcap::GetNetInfo(sockaddr_in &sin, CString &strMAC, BYTE mac[])

{

PIP_ADAPTER_INFO pAdapterInfo = NULL;

char ch;

long sizeinfo;

ULONG size = 0;

int res = 0;

sizeinfo = sizeof(m_netinfo);

if (m_lpAdapter)

{

// 先获得ip地址

res = PacketGetNetInfoEx (adapterlist[m_iSelAdapter], &m_netinfo, &sizeinfo);

if (res)

{

sin = *(struct sockaddr_in *)&m_netinfo.IPAddress;

}

else

{

strMAC = “FF-FF-FF-FF-FF-FF”;

}

// 试图获得AdapterInfosize返回需要的缓冲区的大小

res = GetAdaptersInfo ((PIP_ADAPTER_INFO)&ch, &size);

if (res == ERROR_BUFFER_OVERFLOW)

{

pAdapterInfo = (PIP_ADAPTER_INFO)malloc (sizeof(IP_ADAPTER_INFO));

res = GetAdaptersInfo(pAdapterInfo, &size);

if (res == 0)

{

CString str;

BYTE *pch = pAdapterInfo->Address; // 导出mac地址16进制表达

memcpy (mac, pch, 6);

for (int i = 0; i < 6; i++)

{

if (i)

strMAC += “-”;

str.Format(“%02X”, *(pch + i));

strMAC += str;

}

}

free (pAdapterInfo);

}

}

}

6 发送线程和接受线程的协调

只是简单的多个发送线程,一个监听线程,没有涉及同步问题。

void CIPFluxDlg::OnBtnStart()

{

int nTotal = 0;

ULONG ipFirst, ipLast;

ULONG ulStartIP = 0;

m_bStop = FALSE;

static BOOL bInit = FALSE;

m_IPFirst.GetAddress(ipFirst);

m_IPLast.GetAddress(ipLast);

nTotal = ipLast – ipFirst + 1;

m_lstIP.DeleteAllItems();

GetDlgItem (IDC_BTN_START)->EnableWindow (FALSE);

// 生成一次监听线程

if (bInit == FALSE)

{

m_hSniffer = AfxBeginThread(_tFuncReceiver, (void*)this);

bInit = TRUE;

}

Sleep (300);

m_IPFirst.GetAddress(ulStartIP);

for (int i = 0; i < nTotal; i++)

{

m_ipCur = ulStartIP + i;

m_hSender = AfxBeginThread(_tFuncSender, (void*)this);

// 等待上一个线程生成

WaitForSingleObject (m_hSender, INFINITE);

}

m_bStop = TRUE;

GetDlgItem (IDC_BTN_START)->EnableWindow (TRUE);

}

上一章:ARP协议浅析(2):协议分析

下一章:ARP协议浅析(4):回顾

2007-01-24

ARP协议浅析(4):回顾

Filed under: 程序员 — thomas @ 17:54

ARP协议浅析(4):回顾

上一章:ARP协议浅析(3):付诸实践

一些bug

l 不能严格的发送一个请求ARP包收到后立刻解析显示出来,再发送下一个,而是差不多全部发送完毕后,才显示出来;

l Release模式有未处理异常,Debug模式却没有;

l 与多线程有关的异常处理还不完善。

重要经验

1,网络方面的应用重在对网络协议的原理的了解;

2,程序的框架设计得比较好,在编写代码前分析比较充分,所以思路清晰;

3,对WinPcap的封装,简化了结构,便于划分模块和理清调用关系,CWinPcap类比较好的封装,减轻了工程量。

Row SocketWinPcapIPFlux只要读者理解了其中的原理,很容易基于WinPcap库实现类似的实用工具。

一是自给自足,因为以前需要这样的网络工具,所以编写这个工具,二是练手,同时检验自己的编程水平。当然在这后来找工作也起了一点作用。

若干问题

为了阅读的方便,ARP协议原理部分引用了《手把手教你玩转ARP包》的一部分内容本文与另外两篇文章侧重不同,读者可自行比较。关于ARP的技术问题欢迎读者参与讨论。

关于WinPcap库的使用和相关的编译问题请仔细查看WinPcap开发包帮助,和引用的两篇文章。

工程代码在WinXP/VC6.0编译通过,WinXPWin2000下正常运行;

本程序需要WinPcap运行库的支持,笔者没有测试所有版本,3.0v是可以的,其它请读者自行测试;

下载:

电子书

源代码

上述链接失效,需要源代码的读者请email给笔者:liuxk99@gmail.com

[有其它问题在本页留言]

参考

1.《手把手教你玩转ARP包》

http://blog.csdn.net/PiggyXP/- PiggyXP- 5/31/2004 11:40:00 AM系列文章

2.TCP-IP详解卷1:协议ARP章节,卷2021.pdf

3.《详谈调用WinPcap驱动写arp多功能工具》

{

Author: TOo2y[原创]

E-mail: TOo2y@safechina.net

Homepage: http://www.safechina.net/

Date: 11-9-2002

}

上一章:ARP协议浅析(3):付诸实践

Powered by WordPress