网络流量在线分析系统的设计与实现.docx
《网络流量在线分析系统的设计与实现.docx》由会员分享,可在线阅读,更多相关《网络流量在线分析系统的设计与实现.docx(45页珍藏版)》请在冰豆网上搜索。
网络流量在线分析系统的设计与实现
综合实训报告
题目:
网络流量在线分析系统的设计与实现
信息学院计算机科学系
一、实训目的………………………………………………3
二、实训内容………………………………………………3
三、主要设备及环境………………………………………3
四、设计与步骤……………………………………………4
五、过程与调试……………………………………………22
六、整理与小结……………………………………………23
七、参考文献………………………………………………24
八、附录……………………………………………………25
一、实训目的
设计并实现一个网络流量的分析系统。
该系统具有以下功能:
(1)实时抓取网络数据。
(2)网络协议分析与显示。
(3)将网络数据包聚合成数据流,以源IP、目的IP、源端口、目的端口及协议等五元组的形式存储。
(4)计算并显示固定时间间隔内网络连接(双向流)的统计量(如上行与下行的数据包数目,上行与下行的数据量大小等)。
在这些统计数据的基础上分析不同网络应用的流量特征。
二、实训内容
(1)能够实时抓取网络中的数据包。
并实时显示在程序界面上。
用户可自定义过滤条件以抓取所需要的数据包。
(2)分析各个网络协议格式,能够显示各协议字段的实际意义。
例如,能够通过该程序反映TCP三次握手的实现过程。
(3)采用Hash链表的形式将网络数据以连接(双向流)的形式存储。
(4)计算并显示固定时间间隔内网络连接(双向流)的统计量(如上行与下行的数据包数目,上行与下行的数据量大小等)。
例如,抓取一段时间(如30分钟)的网络流量,将该段时间以固定时长(如1分钟)为单位分成若干个时间片,计算网络连接在每一个时间片内的相关统计量。
并在上述统计数据的基础上分析不同应用如WEB、DNS、在线视频等服务的流量特征。
注意,可根据实际的流量分析需要自己定义相关的统计量。
三、主要设备及环境
硬件设备:
(1)台式计算机或笔记本计算机(含网络适配器)
软件设备:
(2)Windows操作系统
(3)网络数据包捕获函数包,Windows平台为winpcap
(4)编程语言选用C/C++。
(5)编程环境为codeblocks
4、设计与步骤
(1)设计代码检索机器所连接的所有网络适配器,并在屏幕中显示适配器的名称和详细信息,用户可以输入适配器编号选择指定的适配器用来捕获包,如果没有找到适配器,提示用户检查WinPcap是否安装,代码与结果显示如下:
/*setthesource*/
if(pcap_createsrcstr(source,PCAP_SRC_IFLOCAL,NULL,NULL,NULL,errbuf)==-1){
printf("%s\n",errbuf);
exit(-1);
}
printf("source:
%s",source);
/*findalldevices*/
if(pcap_findalldevs_ex(source,NULL,&alldevs,errbuf)==-1){
printf("%s\n",errbuf);
exit(-1);
}
/*chooseonedevices*/
d=alldevs;
while(d!
=NULL){
printf("%s,%s\n",d->name,d->description);
d=d->next;
}
printf("chooseadevice[numberbetween1to4]:
");
scanf("%d",&i);
d=alldevs;
while(--i)
d=d->next;
printf("\n----------------------------------------------\n");
printf("selecteddevice:
%s\n",d->name);
实验结果显示如下:
(2)选择指定适配器后,调用ifprint();函数计算本机的IP地址、掩码、广播地址、目标地址等信息,并用声明staticcharb;用来记录本机IP地址,为接下来查找Hash表判断流量包的流向做准备:
voidifprint(pcap_if_t*d)
{
pcap_addr_t*a;
/*名称*/
//printf("%s\n",d->name);
/*描述*/
if(d->description)
printf("\tDescription:
%s\n",d->description);
/*回环地址*/
printf("\tLoopback:
%s\n",(d->flags&PCAP_IF_LOOPBACK)?
"yes":
"no");
/*IP地址*/
for(a=d->addresses;a;a=a->next)
{
printf("\tAddressFamily:
#%d\n",a->addr->sa_family);
switch(a->addr->sa_family)
{
caseAF_INET:
printf("\tAddressFamilyName:
AF_INET\n");
if(a->addr)
/*Y-IP地址*/
{
printf("\tAddress:
%s\n",iptos(((structsockaddr_in*)a->addr)->sin_addr.s_addr));
b=iptos(((structsockaddr_in*)a->addr)->sin_addr.s_addr);
}
if(a->netmask)
/*Y-掩码*/
printf("\tNetmask:
%s\n",iptos(((structsockaddr_in*)a->netmask)->sin_addr.s_addr));
if(a->broadaddr)
/*Y-广播地址*/
printf("\tBroadcastAddress:
%s\n",iptos(((structsockaddr_in*)a->broadaddr)->sin_addr.s_addr));
if(a->dstaddr)
/*Y-目标地址*/
printf("\tDestinationAddress:
%s\n",iptos(((structsockaddr_in*)a->dstaddr)->sin_addr.s_addr));
break;
default:
/*未知*/
printf("\tAddressFamilyName:
Unknown\n");
break;
}
}
printf("\n");
}
/*来自tcptracert,把数字IP地址转换为点格式*/
#defineIPTOSBUFFERS12
char*iptos(u_longin)
{
staticcharoutput[IPTOSBUFFERS][3*4+3+1];
staticshortwhich;
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]);
returnoutput[which];
}
结果显示如下:
(3)接收到用户输入的适配器编号,打开指定适配器:
/*openonedevice*/
cap_ins_des=pcap_open(d->name,65536,PCAP_OPENFLAG_PROMISCUOUS,1000,NULL,errbuf);
if(cap_ins_des==NULL){
printf("%s\n",errbuf);
pcap_freealldevs(alldevs);
exit(-1);
}
(4)打开指定文件存储捕获的数据包:
/*openafiletodumpdata*/
dumpfp=pcap_dump_open(cap_ins_des,"traffic1");
if(dumpfp==NULL){
printf("Erroronopeningoutputfile\n");
exit(-1);
}
(5)在main()函数开始做一个声明,方便用户自由选择过滤规则,声明如下:
intswitchnum;
chart1[]="ip";//ip过滤规则
chart2[]="ipandtcp";//tcp过滤规则
chart3[]="ipandudp";//udp过滤规则
chart4[]="";//mac帧过滤
charpacket_filter[100];//thefilter
设置过滤规则时使用swich()语句判断用户输入的编号,是对应的编号与对应的过滤规则相一致:
/*openafiletodumpdata*/
dumpfp=pcap_dump_open(cap_ins_des,"traffic1");
if(dumpfp==NULL){
printf("Erroronopeningoutputfile\n");
exit(-1);
}
/*getthenetmask,usedatcompilingthefilter*/
if(d->addresses!
=NULL)
netmask=((structsockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;/*@#$%^&*!
*/
else
netmask=0xffffff;/*255.25.255.0*/
//netmask=0;
/*选择过滤规则*/
printf("\n----------------------------------------------\n");
printf("%d:
%s\n",1,"IP协议");
printf("%d:
%s\n",2,"IP和TCP协议");
printf("%d:
%s\n",3,"IP和UDP协议");
printf("%d:
%s\n",4,"MAC帧");
printf("请选择要获取的协议类型):
");
scanf("%d",&switchnum);
switch(switchnum){
case1:
strcpy(packet_filter,t1);
break;
case2:
strcpy(packet_filter,t2);
break;
case3:
strcpy(packet_filter,t3);
break;
case4:
strcpy(packet_filter,t4);
break;
default:
printf("error\n");
}
/*compilethefilter*/
if(pcap_compile(cap_ins_des,&fcode,packet_filter,1,netmask)<0){
printf("Error\n");
pcap_freealldevs(alldevs);
exit(-1);
}
结果显示如下:
(6)用户可以根据提示设置抓包的时间长短,该功能的实现依靠创建一个线程:
pthread_tptClock;
argumentargs;
args.handle=cap_ins_des;
intargv_time=atoi(argv[1]);
inttimeLen;
printf("\n设置抓包时长:
");
scanf("%d",&timeLen);
printf("设置抓包时长为%ds",timeLen);
args.timeLen=(argv_time>0)?
argv_time:
timeLen;
//intargv_time=2;
//args.timeLen=argv_time;
//printf("抓取时长:
%ds\n",args.timeLen);
if(pthread_create(&ptClock,NULL,thread_clock,&args))
{
printf("pthread_create():
Error!
\n");
return-1;
}
void*thread_clock(void*argv)
{
pcap_t*handle=((argument*)argv)->handle;
inttimeLen=((argument*)argv)->timeLen;//settime
//printf("%d",timeLen);
Sleep(timeLen*1000);
pcap_breakloop(handle);
}
结果显示如下:
(7)抓包时调用函数pcap_loop()函数调用cb_getPacket()函数,实现在线程内的抓包,Sleep函数一旦结束,通过pcap_breakloop()退出抓包:
pcap_loop(cap_ins_des,-1,cb_getPacket,(u_char*)dumpfp);
voidcb_getPacket(u_char*dumpfile,conststructpcap_pkthdr*pkthdr,constu_char*packet)
{
//ip_header*seg_ip=(ip_header*)(package+ETHER_LEN);
pcap_dump(dumpfile,pkthdr,packet);
ethernet_protocol_packet_callback(dumpfile,pkthdr,packet);
}
(8)设置完成抓包时长后,系统开始进行抓包,一旦抓包结束,调用pcap_close()关闭会话并释放适配器列表:
pcap_close(cap_ins_des);
pcap_freealldevs(allAdapters);//释放适配器列表
(9)捕获结束后将捕获的数据包存入traffic1.data文件中,再将文件打开进行分析,打开文件之前使用pcap_createsrcstr函数指明文件位置为本机文件,文件名为“traffic1.data”,在调用pcap_open()打开捕获文件:
pcap_t*fp;//文件指针
//pcap_createsrcstr指明打开文件的地方:
本地文件
if(pcap_createsrcstr(source,/*源字符串*/
PCAP_SRC_FILE,/*本机文件*/
NULL,/*远程主机*/
NULL,/*远程主机端口*/
"traffic1",/*文件名*/
errbuf/*错误缓冲区*/)!
=0)
{
fprintf(stderr,"\nErrorincreatesourcestring:
%s\n",errbuf);
return-1;
}
//打开捕获文件
if((fp=pcap_open(source,/*设备名*/65536,/*要捕捉的数据包的部分,65535保证能捕获到不同数据链路层上的每个数据包的全部内容*/
PCAP_OPENFLAG_PROMISCUOUS,//混杂模式
1000,//读取超时时间
NULL,//远程机器验证
errbuf//错误缓冲池
))==NULL)
{
fprintf(stderr,"\nCannotopenthefile%s.\n",source);
return-1;
}
(10)打开文件开始对数据包进行分析,通过timeval记录当前时间和上一次采样时间,通过计算可以求出延迟时间,根据数据包的大小,进行字节转换,求出采样时每秒的比特数以及每秒的数据包数量:
structtimeval*old_ts=(structtimeval*)argument;
u_intdelay;
LARGE_INTEGERBps,Pps;
structtm*ltime;
chartimestr[16];
time_tlocal_tv_sec;
//以毫秒计算上一次采样的延迟时间
//这个值通过采样到的时间戳获得
delay=(packet_header->ts.tv_sec-old_ts->tv_sec)*1000000-old_ts->tv_usec+packet_header->ts.tv_usec;
//获取每秒的比特数b/s
//Bps.QuadPart=(((*(LONGLONG*)(packet_content+8))*8*1000000)/(delay));
/*^^
||
||
||
将字节转换成比特--||
延时是以毫秒表示的--|
*/
u_intm=(*(LONGLONG*)(packet_content+8))*8*1000000;
u_intn=((*(LONGLONG*)(packet_content))*1000000);
Bps.QuadPart=m/delay;
Pps.QuadPart=n/delay;
//printf("%I64u\n",m);
//printf("%I64u\n",delay);
//得到每秒的数据包数量
//Pps.QuadPart=(((*(LONGLONG*)(packet_content))*1000000)/(delay));
//将时间戳转化为可识别的格式
/*local_tv_sec=packet_header->ts.tv_sec;
ltime=localtime(&local_tv_sec);
strftime(timestr,sizeoftimestr,"%H:
%M:
%S",ltime);*/
//打印时间戳
//printf("%s",timestr);
//打印采样结果
//printf("%I64u\n",delay);
printf("\n**************************************************\n每秒的比特数:
");
printf("BPS=%I64u\n",Bps.QuadPart);
printf("每秒的数据包数量:
");
printf("PPS=%I64u\n",Pps.QuadPart);
//存储当前的时间戳
old_ts->tv_sec=packet_header->ts.tv_sec;
old_ts->tv_usec=packet_header->ts.tv_usec;
结果显示如下:
(11)通过捕获的文件,对抓取的每个数据包的各层的首部进行解析,并将解析结果进行显示
①首先对以太网协议进行解析:
printf("捕获第%d个网络数据包\n",packet_number);
printf("捕获时间:
");
printf("%s",ctime((consttime_t*)&packet_header->ts.tv_sec));
printf("数据包长度:
");
printf("%d\n",packet_header->len);
printf("\n--------------以太网协议--------------\n");
ethernet_protocol=(structether_header*)packet_content;//获得数据包内容
printf("以太网类型:
");
ethernet_type=ntohs(ethernet_protocol->ether_type);//获得以太网类型
printf("%04x\n",ethernet_type);
switch(ethernet_type)
{
case0x0800:
printf("上层协议是IP协议\n");break;
case0x0806:
printf("上层协议是ARP协议\n");break;
case0x8035:
printf("上层协议是RARP协议\n");break;
case0x814C:
printf("上层协议是简单网络管理协议SNMP\n");break;
case0x8137:
printf("上层协议是因特网包交换(IPX:
InternetPacketExchange)\n");break;
case0x86DD:
printf("上层协议是IPv6协议\n");break;
case0x880B:
printf("上层协议是点对点协议(PPP:
Point-to-PointProtocol)\n");break;
default:
break;
}
printf("MAC帧源地址:
");
mac_string=ethernet_protocol->ether_shost;
printf("%02x:
%02x:
%02x:
%02x:
%02x:
%02x\n",*mac_string,*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
printf("MAC帧目的地址:
");
mac_string=ethernet_protocol->ether_dhost;
printf("%02x:
%02x:
%02x:
%02x:
%02x:
%02x\n",*mac_string,*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
if(ethernet_type==0x0800)//继续分析IP协议
{
ip_protool_packet_callback(argument,pa