admin 管理员组

文章数量: 1086019


2024年4月18日发(作者:sqlsugar官网)

课 程 设 计

课程名称 计算机网络 _

题目名称______________________

学生学院_____计算机学院_______

专业班级____

学 号

学生姓名

指导教师___ _ _ _ _

2013 年 1 月 11 日

计算机网络课程设计

一、 设计题目

使用ARP协议获取局域网内部活动主机物理地址的程序实现(C++)

二、已知技术参数和设计要求

1.已知参数:选择适当的网络适配器,以绑定Winpcap。

2.设计要求:

2.1协议实现需要哪些数据帧?注意要数据帧的定义要符合公共标准。

2.2如何实现实现数据帧的发送?

2.3如何接收及解析目的主机的响应数据帧?

2.4如何显示ARP协议的执行结果?

3. C++环境为Visual C++

三、设计内容与步骤

1.熟悉ARP协议的工作原理;

2.熟悉ARP协议相关数据帧结构;

3.构造ARP请求数据帧;

4.使用Winpcap相关函数实现数据帧的发送;

5.接收及解析目的主机的响应数据帧;

6.获得IP地址与MAC地址的对应关系并显示;

7.课程设计任务说明书。

四、课程设计中涉及的网络基本理论简介:

(1) 在网际协议中定义的是因特网的IP地址,但在实际进行通信时,物理层不能识别IP地址只

能识别物理地址。因此,需在IP地址与物理地址之间建立映射关系,地址之间的这种映射

称为地址解析。

(2) 以太网网络中的物理地址即网卡的序列号。IEEE规定网卡序列号为6个字节(48位),

前三个字节为厂商代号,由于厂商向IEEE注册登记申请,后3个字节为网卡的流水号。

(3) 地址解析包括从IP地址到物理地址的映射和从物理地址到IP地址的映射。TCP/IP协议

组提供了两个映射协议:地址解析协议ARP和逆向地址解析协议RARP。ARP用于从IP地址

到物理地址的映射,RARP用于从物理地址到IP地址的映射。

(4) 地址解析协议的ARP的工作原理:假定在一个物理网络上,A(源主机)要与D(目的

主机)进行通信,但是不知道D的物理地址。 A利用ARP协议工作的过程如下:

A广播一个ARP请求报文,请求IP地址为IPD的主机回答其物理地址。网上所有主机

都能收到该ARP请求,并将本机IP地址与请求的IP地址比较,D主机识别出自己的地址IPD,

并作出回应,通报自己的物理地址。A收到这个ARP回应包后,就可以与D进行通信。

为了提高效率,ARP协议使用了高速缓存技术。在每台使用ARP的主机中,都保留了一个专

用的内存区,一收到ARP应答,主机就将获得的IP地址和物理地址存入缓存。以后每次要

发送报文时,首先到缓存中查找有无相应的项,若找不到,再利用ARP进行地址解析。由

于多数网络通信都要连续发送多个报文,所以高速缓存大大提高ARP的效率。

在ARP请求报文中还放入源主机的“IP地址——物理地址”的地址对,源主机在广播ARP

请求时,网络上所有主机都可以知道该源主机的“IP地址——物理地址”的地址对并将其存

入自己的缓存。

在新主机入网时,令其主动广播其地址映射,以减少其他主机进行ARP请求。

(5) 网卡具有如下的几种工作模式:

广播模式(Broad Cast Model):它的物理地址(目的地址)地址是 0Xfff fff 的帧为广播帧,

工作在广播模式的网卡接收广播帧。

多播传送(MultiCast Model):多播传送地址作为目的物理地址的帧可以被组内的其它主机

同时接收,而组外主机却接收不到。但是,如果将网 卡设置为多播传送模式,它可以接收

所有的多播传送帧,而不论它是不是组内成员。

直接模式(Direct Model):工作在直接模式下的网卡只接收目地址是自己 Mac地址的帧。

混杂模式(Promiscuous Model):工作在混杂模式下的网卡接收所有的流过网卡的帧,信包

捕获程序就是在这种模式下运行的。

(6) ARP帧的数据结构表达方式:

以太网帧头中的前两个字段是以太网的目的地址和源地址。目的地址为全1时为广播地

址。

两个字节长的以太网帧类型表示后面数据的类型。对于ARP请求或应答来说,该字段的

值为0X0806.

硬件类型字段:指明了发送方想知道的硬件地址的类型,以太网的值为1; 应该是

接收方;什么硬件,这和以太网有关系吗

协议类型字段:表示要映射的协议地址类型,IP为0X0800;

硬件地址长度和协议地址长度:指明了硬件地址和高层协议地址的长度,这样ARP帧

就可以在任意硬件和任意协议的网络中使用。对于以太网上IP地址的ARP请求或应答来说,

它们的值分别为6和4;

操作字段:用来表示这个报文的类型,ARP请求为1,ARP响应为2,RARP请求为3,

RARP响应为4;

发送端的以太网地址:源主机硬件地址,6个字节;

发送端IP地址:发送端的协议地址(IP地址),4个字节;

目的以太网地址:目的端硬件地址,6个字节;

目的IP地址:目的端的协议地址(IP地址),4个字节

六、运行结果:

打开页面:

选择网卡1:

获取到的结果:

七、代码分析:

#include

#include

#include "pcap.h"

#include "Packet32.h"

#pragma pack(1) //按一个字节内存对齐

#define IPTOSBUFFERS 12

#define ETH_ARP 0x0806 //以太网帧类型表示后面数据的类型,对于ARP请求或应答来说,该字

段的值为x0806

#define ARP_HARDWARE 1 //硬件类型字段值为表示以太网地址

#define ETH_IP 0x0800 //协议类型字段表示要映射的协议地址类型值为x0800表示IP地址

#define ARP_REQUEST 1

#define ARP_REPLY 2

#define HOSTNUM 255

/* packet handler 函数原型*/

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

// 函数原型

void ifget(pcap_if_t *d,char *ip_addr,char *ip_netmask);

char *iptos(u_long in);

char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen);

int SendArp(pcap_t *adhandle,char *ip,unsigned char *mac);

int GetSelfMac(pcap_t *adhandle,const char *ip_addr,unsigned char *ip_mac);

DWORD WINAPI SendArpPacket(LPVOID lpParameter);

DWORD WINAPI GetLivePC(LPVOID lpParameter);

//28字节ARP帧结构

struct arp_head

{

unsigned short hardware_type; //硬件类型

unsigned short protocol_type; //协议类型

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

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

unsigned short operation_field; //操作字段

unsigned char source_mac_add[6]; //源mac地址

unsigned long source_ip_add; //源ip地址

unsigned char dest_mac_add[6]; //目的mac地址

unsigned long dest_ip_add; //目的ip地址

};

//14字节以太网帧结构

struct ethernet_head

{

unsigned char dest_mac_add[6]; //目的mac地址

unsigned char source_mac_add[6]; //源mac地址

unsigned short type; //帧类型

};

//arp最终包结构

struct arp_packet

{

struct ethernet_head ed;

struct arp_head ah;

};

struct sparam

{

pcap_t *adhandle;

char *ip;

unsigned char *mac;

char *netmask;

};

struct gparam

{

pcap_t *adhandle;

};

bool flag;

struct sparam sp;

struct gparam gp;

int main()

{

pcap_if_t *alldevs;

pcap_if_t *d;

int inum;

int i=0;

pcap_t *adhandle;

char errbuf[PCAP_ERRBUF_SIZE];

char *ip_addr;

char *ip_netmask;

unsigned char *ip_mac;

HANDLE sendthread;

HANDLE recvthread;

ip_addr=(char *)malloc(sizeof(char)*16);//申请内存存放IP地址

if(ip_addr==NULL)

{

printf("申请内存存放IP地址失败!n");

return -1;

}

ip_netmask=(char *)malloc(sizeof(char)*16);//申请内存存放NETMASK地址

if(ip_netmask==NULL)

{

printf("申请内存存放NETMASK地址失败!n");

return -1;

}

ip_mac=(unsigned char *)malloc(sizeof(unsigned char)*6);//申请内存存放MAC地址

if(ip_mac==NULL)

{

printf("申请内存存放MAC地址失败!n");

return -1;

}

/* 获取本机设备列表*/

if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)

{

fprintf(stderr,"Error in pcap_findalldevs: %sn", errbuf);

exit(1);

}

/* 打印列表*/

printf("[本机网卡列表:]n");

for(d=alldevs; d; d=d->next)

{

printf("%d) %sn", ++i, d->name);

if (d->description)

printf(" (%s)n", d->description);

else

printf(" (No description available)n");

}

if(i==0)

{

printf("n找不到网卡!请确认是否已安装WinPcap.n");

return -1;

}

printf("n");

printf("请选择要打开的网卡号(1-%d):",i);

scanf("%d", &inum);

if(inum < 1 || inum > i)

{

printf("n该网卡号超过现有网卡数!请按任意键退出…n");

getchar();

getchar();

/* 释放设备列表*/

pcap_freealldevs(alldevs);

return -1;

}

/* 跳转到选中的适配器*/

for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

/* 打开设备*/

if ( (adhandle= pcap_open(d->name, // 设备名

65536, // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容

PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式

1000, // 读取超时时间

NULL, // 远程机器验证

errbuf // 错误缓冲池

) ) == NULL)

{

fprintf(stderr,"n无法读取该适配器. 适配器%s 不被WinPcap支持n", d->name);

/* 释放设备列表*/

pcap_freealldevs(alldevs);

return -1;

}

ifget(d,ip_addr,ip_netmask);//获取所选网卡的基本信息--掩码--IP地址

GetSelfMac(adhandle,ip_addr,ip_mac);//输入网卡设备句柄网卡设备ip地址获取该设备的MAC地址

le=adhandle;

=ip_addr;

=ip_mac;

k=ip_netmask;

le=adhandle;

sendthread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)SendArpPacket,&sp,0,NULL);

recvthread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetLivePC,&gp,0,NULL);

printf("nlistening on 网卡%d ...n",inum);

/* 释放设备列表*/

pcap_freealldevs(alldevs);

getchar();

getchar();

return 0;

}

/* 获取可用信息*/

void ifget(pcap_if_t *d,char *ip_addr,char *ip_netmask)

{

pcap_addr_t *a;

char ip6str[128];

/* IP addresses */

for(a=d->addresses;a;a=a->next)

{

switch(a->addr->sa_family)

{

case AF_INET:

if (a->addr)

{

char *ipstr;

ipstr=iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr);//*ip_addr

memcpy(ip_addr,ipstr,16);

}

if (a->netmask)

{

char *netmaskstr;

netmaskstr=iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr);

memcpy(ip_netmask,netmaskstr,16);

}

case AF_INET6:

break;

}

}

}

/* 将数字类型的IP地址转换成字符串类型的*/

char *iptos(u_long in)

{

static char output[IPTOSBUFFERS][3*4+3+1];

static short which;

u_char *p;

p = (u_char *)∈

which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);

sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

return output[which];

}

char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen)

{

socklen_t sockaddrlen;

#ifdef WIN32

sockaddrlen = sizeof(struct sockaddr_in6);

#else

sockaddrlen = sizeof(struct sockaddr_storage);

#endif

if(getnameinfo(sockaddr,

sockaddrlen,

address,

addrlen,

NULL,

0,

NI_NUMERICHOST) != 0) address = NULL;

return address;

}

/* 获取自己主机的MAC地址 */

int GetSelfMac(pcap_t *adhandle,const char *ip_addr,unsigned char *ip_mac)

{

unsigned char sendbuf[42];//arp包结构大小

int i = -1;

int res;

struct ethernet_head eh;

struct arp_head ah;

struct pcap_pkthdr * pkt_header;

const u_char * pkt_data;

memset(_mac_add,0xff,6);//目的地址为全为广播地址

memset(_mac_add,0x0f,6);

memset(_mac_add,0x0f,6);

memset(_mac_add,0x00,6);

= htons(ETH_ARP);

re_type = htons(ARP_HARDWARE);

ol_type = htons(ETH_IP);

re_add_len = 6;

ol_add_len = 4;

_ip_add = inet_addr("100.100.100.100"); //随便设的请求方ip

ion_field = htons(ARP_REQUEST);

_ip_add=inet_addr(ip_addr);

memset(sendbuf,0,sizeof(sendbuf));

memcpy(sendbuf,&eh,sizeof(eh));

memcpy(sendbuf+sizeof(eh),&ah,sizeof(ah));

if(pcap_sendpacket(adhandle,sendbuf,42)==0)

{

printf("nPacketSend succeedn");

}

else

{

printf("PacketSendPacket in getmine Error: %dn",GetLastError());

return 0;

}

while((res = pcap_next_ex(adhandle,&pkt_header,&pkt_data)) >= 0)

{

if(*(unsigned short *)(pkt_data+12) == htons(ETH_ARP)&&

*(unsigned short*)(pkt_data+20) == htons(ARP_REPLY)&&

*(unsigned long*)(pkt_data+38) == inet_addr("100.100.100.100"))

{

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

{

ip_mac[i]=*(unsigned char *)(pkt_data+22+i);

}

printf("获取自己主机的MAC地址成功!n");

break;

}

}

if(i==6)

{

return 1;

}

else

{

return 0;

}

}

/* 向局域网内所有可能的IP地址发送ARP请求包线程 */

DWORD WINAPI SendArpPacket(LPVOID lpParameter)//(pcap_t *adhandle,char *ip,unsigned char

*mac,char *netmask)

{

sparam *spara=(sparam *)lpParameter;

pcap_t *adhandle=spara->adhandle;

char *ip=spara->ip;

unsigned char *mac=spara->mac;

char *netmask=spara->netmask;

printf("ip_mac:%02x-%02x-%02x-%02x-%02x-%02xn",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);

printf("自身的IP地址为:%sn",ip);

printf("地址掩码NETMASK为:%sn",netmask);

printf("n");

unsigned char sendbuf[42];//arp包结构大小

struct ethernet_head eh;

struct arp_head ah;

memset(_mac_add,0xff,6);//目的地址为全为广播地址

memcpy(_mac_add,mac,6);

memcpy(_mac_add,mac,6);

memset(_mac_add,0x00,6);

= htons(ETH_ARP);

re_type = htons(ARP_HARDWARE);

ol_type = htons(ETH_IP);

re_add_len = 6;

ol_add_len = 4;

_ip_add = inet_addr(ip); //请求方的IP地址为自身的IP地址

ion_field = htons(ARP_REQUEST);

//向局域网内广播发送arp包

unsigned long myip=inet_addr(ip);

unsigned long mynetmask=inet_addr(netmask);

unsigned long hisip=htonl((myip&mynetmask));

for(int i=0;i

{

_ip_add=htonl(hisip+i);

memset(sendbuf,0,sizeof(sendbuf));

memcpy(sendbuf,&eh,sizeof(eh));

memcpy(sendbuf+sizeof(eh),&ah,sizeof(ah));

if(pcap_sendpacket(adhandle,sendbuf,42)==0)

{

//printf("nPacketSend succeedn");

}

else

{

printf("PacketSendPacket in getmine Error: %dn",GetLastError());

}

Sleep(50);

}

Sleep(1000);

flag=TRUE;

return 0;

}

/* 分析截留的数据包获取活动的主机IP地址 */

DWORD WINAPI GetLivePC(LPVOID lpParameter)//(pcap_t *adhandle)

{

gparam *gpara=(gparam *)lpParameter;

pcap_t *adhandle=gpara->adhandle;

int res;

unsigned char Mac[6];

struct pcap_pkthdr * pkt_header;

const u_char * pkt_data;

while(true)

{

if(flag)

{

printf("扫描完毕,按任意键退出!n");

break;

}

if((res=pcap_next_ex(adhandle,&pkt_header,&pkt_data))>=0)

{

if(*(unsigned short *)(pkt_data+12)==htons(ETH_ARP))

{

struct arp_packet *recv=(arp_packet *)pkt_data;

if(*(unsigned short *)(pkt_data+20)==htons(ARP_REPLY))

{

printf("-------------------------------------------n");

printf("IP地址:%d.%d.%d.%d MAC地

址:",recv->_ip_add&255,recv->_ip_add>>8&255,recv->_ip_add>>16&255

,recv->_ip_add>>24&255);

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

{

Mac[i]=*(unsigned char *)(pkt_data+22+i);

printf("%02x",Mac[i]);

}

printf("n");

}

}

}

Sleep(10);

}

return 0;

}

八、心得总结:

要做好这个课程设计,得先对计算机网络相关有更多的了解,首先对之前学习的课程进

行了回顾和扩展,找了很多相关资料。通过阅读相关资料,我大致掌握了ARP协议的详细

过程。在网际协议中定义的是因特网的IP地址,但在实际进行通信时,物理层不能识别IP地址

只能识别物理地址。因此,需在IP地址与物理地址之间建立映射关系,地址之间的这种映

射称为地址解析。ARP地址解析协议就是实现地址之间的这种映射关系的。ARP地址解析协

议的整个运作过程我简单的理解为:源主机广播一个ARP请求报文,请求目的主机回答其

物理地址。网上所有主机都能收到该ARP请求,并将本机IP地址与请求的IP地址比较,目

的主机识别出自己的地址IP,并作出回应,通报自己的物理地址。源主机收到这个ARP回

应包后,就可以与目的主机进行通信。了解APR协议的过程,是编写程序的基本要求。

通过本次课程设计,使我们对网络方面的知识有了更深入的认识。深刻体会了ARP协

议的帧结构及运作过程,让我们把网络课上学到得书面的知识在实践中加以运用,深入理解。

课程设计过程中,遇到过很多问题,通过从网上查找解决问题的方法,不断的思考和调试运

行,最后终于运行成功了,提升了自己解决问题的能力。另外,在课程设计过程中,自己的

编程能力也得到了巩固和提高。

九、参考文献:

《计算机网络(第五版)》 谢希任 编著 电子工业出版社;

《C++ Primer Plus(第五版)中文版》 人民邮电出版社;

百度文库;


本文标签: 地址 协议 IP地址 网卡 物理地址