加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

获取以太网中IP地址与MAC地址的对应关系(中)

(2010-09-22 11:51:10)
标签:

杂谈

分类: Netwoks

三、 程序框架和流程

1. 程序框架

本程序分为以下多个部分:

·ARP帧的结构定义,然后定义一个接口信息结构,便于对各种接口数据进行处理

程序中对ARP数据包的各种处理都要严格依照ARP帧的结构定义,这里要说明的是,ARP帧的定义不仅要包括图4中所示的各个字段,同时还要包含ARP帧的首部,故首先要定义ARP帧首部的结构,该结构中,只包括有目的地址和源地址以及长度/类型这些通过软件处理的字段。

另外,在这部分所有结构定义的前后分别要加上预处理命令#pragma pack(1)来调整各个结构进入1字节对齐方式。在这里数据包及其头部应该是连续的字节,但在内存里struct里为了让程序跑的快,减少CPU读取数据的指令周期,对结构体的存储进行了优化,在一些成员存储的内存空间之间加入填充使整个struct的成员以其中某一成员字节标准来对齐,但会使数据在内存中的存储不再连续,所以设定这个可以让所有struct内的成员全部1字节对齐,即避免了填充,可在内存里连续存储,符号数据包要求,使WinPcap捕获到的数据包在缓冲区中是连续存放的。具体实现如下:

http://s9/bmiddle/6c7c7fe3490d1259c4958&690

·设置一个缓冲区储存捕获到的数据包

首先为缓冲区数组定义一个保存数据包信息的结构,所包括的成员分别对应pcap_next_ex()的后两个参数,保存数据包的一些基本信息并指向捕获到的网络数据包。其中struct pcap_pkthdr结构是WinPcappcap.h定义的一个数据包首部的结构类型包含一个时间戳,一个捕获数据的端口长度,一个接收的数据包在离线下的长度。

该缓冲区依靠两个下标CaptureHeaderCaptureTail来维护,初始化两下标均指向数组第一个元素,即0下标,每次通过CaptureTail的向后移动来添加新捕获的数据包,当缓冲区满后,将CaptureTail重置为0;当需要读取缓冲区内的数据包时,通过CaptureHeader的向后移动来实现,直到移动到CaptureTail的位置处理完毕,或者移动到缓冲区尾,则将CaptureHeader重置为0。具体实现如下:

http://s6/bmiddle/6c7c7fe3490d1265450f5&690

·初始化对话框,获取设备列表,打开列表上的网络接口,获取相应句柄,启动数据包捕获工作者线程,为模拟的远端主机设置虚假的MAC地址和IP地址,进行ARP请求

OnInitDialog()中,首先要利用pcap_findalldevs_ex()获得本机的设备列表。函数的原型如下:

 

int pcap_findalldevs_ex(

        char *source,

        struct  pcap_rmtauth auth,

        pcap_if_t **alldevs,

        char *errbuf

);

 

·source指定从哪里获取网络接口列表,使用PCAP_SRC_IFSTRING常数,可以获取本机的网络接口列表。

·auth在获取远程设备的网络接口列表时,如果远程设备需要认证,则需要使用该参数。本实验无需使用,设置为NULL

·alldevs该函数成功返回后,该参数指向获取的网络接口列表的第一个元素,为一个pcap_if_t结构。

·errbuf用户定义的存放错误信息的缓冲区,长度不能小于PCAP_ERRBUF_SIZE

·调用发生错误上函数返回-1,具体错误信息存于errbuf参数中;成功则返回0

在获取本机设备列表之后,要将获取的各种列表信息存于之前定义的接口信息中。其中涉及到2个重要的结构类型struct pcap_ifstruct pcap_addr

alldevs指向的网络接口链表中,每一个元素都是一个pcap_if_t结构。其定义如下:

 

typedef struct pcap_if pcap_if_t;

 

struct pcap_if {

        struct pcap_if *next;

        char *name;

        char *description;

        struct pcap_addr *addresses;

        bpf_u_int32 flags;

};

 

struct pcap_addr {

        struct pcap_addr *next;

        struct sockaddr *addr;

        struct sockaddr *netmask;

        struct sockaddr *broadaddr;

        struct sockaddr *dstaddr;

};

 

·next指向链表中的下一个元素,NULL表示链表结束。

·name指向一个字符串,该字符串是WinPcap为本网络接口卡分配的名字。本实验中该名字需要传递给pcap_open()函数来打开这块网卡。

·description指向该网卡的描述字符串。

·addresses指向该网卡拥有的地址链表的第一个元素。该地址链表的每一个元素都是一个pcap_addr结构,其中包括了下一个地址元素的指针(next)、地址(addr)、网络掩码(netmask)、广播地址(broadaddr)和目的地址(dstaddr)。在TCP/IP网络中,address指向的地址链表中包含了这块网卡拥有的所有IP地址。

·flags标识该网络接口卡是不是一块回送网卡,本实验中不使用该参数。

具体实现如下:

http://s7/bmiddle/6c7c7fe3490d12775e9e6&690

获取本机网络接口卡之后,需要将其IP地址等信息存入之前定义的接口信息中。其中涉及2个重要的结构类型struct sockaddrstruct sockaddr_instruct in_addr,其定义如下:

 

struct sockaddr {

        u_short sa_family;

        char    sa_data[14];

};

 

struct sockaddr_in {

        short   sin_family;

        u_short sin_port;

        struct  in_addr sin_addr;

        char    sin_zero[8];

};

 

struct in_addr {

        union {

                struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;

                struct { u_short s_w1,s_w2; } S_un_w;

                u_long S_addr;

        } S_un;

#define s_addr  S_un.S_addr

#define s_host  S_un.S_un_b.s_b2

#define s_net   S_un.S_un_b.s_b1

#define s_imp   S_un.S_un_w.s_w2

#define s_impno S_un.S_un_b.s_b4

#define s_lh    S_un.S_un_b.s_b3

};

sockaddr是内核用来储存地址的结构。sa_family指向一个地址族,本实验中用来判断该地址是否为IP地址。该结构用不同的unsigned short数来表示不同的地址协议,AF_INET被定义为2,代表TCP/IP协议族。sa_data数组存储地址,本实验中不使用。

sockaddr_in是一个指向socket地址和网络类型的结构。sin_family指代协议族,在socket编程中只能是AF_INETsin_port存储端口号(使用网络字节顺序),sin_addr存储IP地址,使用in_addr这个数据结构,sin_zero是为了让sockaddrsockaddr_in两个数据结构保持大小相同而保留的空字节。s_addr按照网络字节顺序存储IP地址,sockaddr_insockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockaddr的结构体,并代替它,即可以使用sockaddr_in建立所需要的信息,然后进行类型的强制转换。

in_addr是一个用来储存IP地址的结构,本实验中使用该结构中定义的联合中的一个unsigned long变量s_addr来实际存储IP地址。

实现过程中,需要使用双重链表,即pcap_ifpcap_addr来处理IP地址。

具体实现如下:

http://s12/bmiddle/6c7c7fe3490d128168a9b&690

在打开本机设备列表上的网络接口时,要使用pcap_open()函数。其原型如下:

 

pcap_t* pcap_open(

            const char *source,

            int snaplen,

            int flags,

            int read_timeout,

            struct pcap_rmtauth *auth,

            char *errbuf

);

 

·source指向需要打开的网络接口卡的名字。该名字通常可以从获取网络接口卡设备列表中得到。

·snaplen指向WinPcap获取网络数据包的最大长度。本实验中用户数据报总长度最大为65535字节,因此设置该值为65536,这样可以完整捕获链路层中的数据包。

·flags指定以何种方式打开网络接口设备并获取网络数据包。本实验中将该标志设置为PCAP_OPENFLAG_PROMISCUOUS,它通知系统以混杂模式打开网络接口设备,便于监听流经一块网络接口卡的所有数据包。

·read_timeout指定数据包捕获函数返回的时间。本实验中,无论pcap_next_ex()是否捕获到数据包,read_timeout指定的时间到时之后捕获函数一定返回。

·auth在远程设备中捕获网络数据包时使用,本实验不需要,设置为NULL

·errbuf用户定义的存放错误信息的缓冲区。

该函数调用出错时,返回NULL,通过errbuf获取错误的详细信息。调用成功,则返回一个指向pcap_t的指针,后续函数调用时会使用。

该函数具体实现如下:

http://s11/bmiddle/6c7c7fe3490d128cdbb7a&690

在打开网络接口之后,就要启动数据包捕获工作者线程,然后准备设置一个远端主机,用来发送一个ARP请求报文并启动定时器,该报文请求本机网络接口上绑定的IP地址与MAC地址的对应关系。在组装报文过程中,源MAC地址字段和源IP地址字段需要使用虚假的MAC地址和IP地址,本实验中使用课本中的定义,即以66-66-66-66-66-66作为源MAC地址,以112.112.112.112作为源IP地址。本地主机一旦获取该ARP请求就会做出响应,向虚假主机发送ARP响应信息,虚假主机获取该响应信息之后,便可对该信息进行分析,从而获取本地主机IP地址与MAC地址的对应关系。具体实现如下:

http://s16/bmiddle/6c7c7fe3490d12962cb3f&690

 

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有