基于libcapwinpcap的网络编程.docx

上传人:b****6 文档编号:7865274 上传时间:2023-01-26 格式:DOCX 页数:16 大小:154.82KB
下载 相关 举报
基于libcapwinpcap的网络编程.docx_第1页
第1页 / 共16页
基于libcapwinpcap的网络编程.docx_第2页
第2页 / 共16页
基于libcapwinpcap的网络编程.docx_第3页
第3页 / 共16页
基于libcapwinpcap的网络编程.docx_第4页
第4页 / 共16页
基于libcapwinpcap的网络编程.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

基于libcapwinpcap的网络编程.docx

《基于libcapwinpcap的网络编程.docx》由会员分享,可在线阅读,更多相关《基于libcapwinpcap的网络编程.docx(16页珍藏版)》请在冰豆网上搜索。

基于libcapwinpcap的网络编程.docx

基于libcapwinpcap的网络编程

第五章基于libpcap的网络编程技术

5.1常见的包捕获机制简介

包捕获就是利用以太网的介质共享的特性,通过将网络适配器设置为混杂模式的方法,接收到所有网络上的以太网帧。

包捕获的机制大致可以分为两类:

一类是由操作系统内核提供的捕获机制。

另一类是由应用软件或系统开发包捕获驱动程序提供的捕获机制。

常见的包捕获机制如表5-1所示。

其中最主要的是下列4种:

✓BPF(BerkeleyPacketFilter)

✓DLPI(DataLinkProviderInterface)

✓NIT(NetworkInterfaceTap)

✓SOCK-PACKET类型套接口。

BPF由基于BSD的Unix系统内核所实现。

DLPI是Solaris(和其他SystemVUNIX)系统的内嵌子系统。

NIT是SUNOS4系统的一部分,但在Solaris/SUNOS5中被DLPI所取代。

Linux核心则实现了SOCK-PACKET的包捕获机制。

从性能上看,BPF比DLPI和NIT好得多,SOCK-PACKET最弱。

表5-1常用的包捕获机制

由于现在很多局域网为NT网,其网络传输方式大多采用以太网标准,所以涉及的编程也是在Windows环境下实现的。

Windows操作系统没有提供包捕获机制,只提供了数量很少并且功能有限的API调用。

在Windows环境下由于其自身的封装性,很难对其底层进行编程。

本章将对BSD系列的libpcap进行深入地介绍。

 

5.2Libpcap与BPF

(1)libpcap概述

libpcap(PacketCapturelibrary),即数据包捕获函数库。

该库提供的C函数接口可用于捕获经过网络接口(只要经过该接口,目标地址不一定为本机)的数据包。

它是由洛仑兹伯克利试验室的研究人员StevenMcCanne和VanJacobson于1993年在Usenix'93会议上正式提出的一种用于Unix内核数据包过滤体制。

该函数库支持Linux、Solaris和BSD系统平台。

采用libpcap可以捕获本地网络数据链路层上的数据。

libpcap库是基于BPF(BerkeleyPacketFilter:

BSD包过滤器)系统的。

BPF是BSD系统在的TCP/IP软件在实现的时候所提供的一个接口,通过这个接口,外部程序可以得到到达本机的数据链路层网络数据,同时也可以设置过滤器,嵌入到网络软件中,获得过滤后的数据包。

(2)BPF结构及工作原理

BPF或者BSDPacketFilter是一种由stevenMcCanne和VanJacobson提出的内核包捕获的体系结构,它使得UNIX下的应用程序通过一个高度优化的方法读取流经网

 

图5-1BPF工作原理

络适配器的数据包。

winpcap的核心网络驱动程序也是采用这种结构的。

它主要由两部分组成:

Networktap和PacketFilter。

Networktap是一个回调函数(callbackfunction),

并不是直接由BPF执行,当一个新的数据包到达时,由网络适配器的设备驱动程序激活,它从网络拷贝每个数据包,并且将它们分发到对应的应用程序。

Packetfilter来决定一个数据包是否需要进行接收和拷贝到相应的应用程序,同时还提供一种很复杂的功能,可以确定需要进行拷贝的数据的长度。

BPF的工作原理如图5-1所示。

由Networktap不断地从网络适配器获得数据包,通过packetfilter的判断后,拷贝数据到内核storebuffer,当storebuffer填满后,将和holdbuffer进行数据交换,再由holdbuffer将数据拷贝到处于用户层的userbuffer中。

由于采用这种内核过滤、数据包双缓冲、数据包批拷贝的方式,减少了用户态进程调用内核的次数,极大地提高了数据包的处理能力。

在系统实现的过程中可以充分利用BPF过滤模型的这种特性,通过根据不同的网络侦听的需求,如针对特定的协议、特定的机器等,设置内核过滤器,减少捕获的数据的大小,同时增加内核缓冲区的大小,使系统能够获得一个很好的性能。

BPF的结构如图5-2所示。

libpcap在BPF的基础上提供了实用的接口函数供其它程序调用,既可以捕获网络上的数据,也可以打开tcpdump和tcpslice格式的数据,进行分析。

同时也可以根据需要,设置过滤器,获得感兴趣的数据包。

可以编辑简单的BPF过滤器对数据进行过滤,获得指定的IP包、TCP包等。

如下所示:

可以用tcpdump的过滤表达式进行过滤:

tcpdumptcp21

表示获得FTP端口(21)的所有的TCP数据,即通过21端口进出的数据,其它的数据全部丢弃。

 

图5-2BPF的结构

Libpcap软件包可从http:

//www.tcpdump.org/下载,然后依此执行下列三条命令即可安装,但如果希望libpcap能在linux上正常工作,则必须使内核支持"packet"协议,也即在编译内核时打开配置选项CONFIG_PACKET(选项缺省为打开)。

./configure

./make

./makeinstall

5.3libpcap库函数与数据结构

(1)libpcap库函数

libpcap所提供的主要函数如下:

1)pcap_t*pcap_open_live();用于获取一个包捕获描述符

2)char*pcap_lookupdev();返回一个适于pcap_open_live()和pcap_lookupnet()函数使用的指向网络设备的指针

3)intpcap_lookupnet();用于判断与网络设备相关的网络号和掩码

4)intpcap_dispatch()或intpcap_loop();收集和处理数据包

5)voidpcap_dump();将一个包输出到由pcap_dump_open()打开的保存文件中

6)intpcap_compile();用于将过滤规则字符串编译成一个内核过滤程序

7)intpcap_setfilter();设定一个过滤程序

8)intpcap_datalink();返回数据链路层类型,如10M以太网,SLIP,PPP,FDDI,ATM,IEEE802.3等

9)voidpcap_close();关闭关联文件并回收资源

10)intpcap_stats(pcap_t*,structpcap_stat*);

11)intpcap_read(pcap_t*,intcnt,pcap_handler,u_char*);

除了上面列出的主要函数外,libpcap还提供一些其他的函数,将在后面介绍。

dump文件格式:

文件头:

structpcap_file_header{

bpf_u_int32magic;

//0xa1b2c3d4

u_shortversion_major;

u_shortversion_minor;

bpf_int32thiszone;

bpf_u_int32sigfigs;

bpf_u_int32snaplen;

bpf_u_int32linktype;

};

每一个包的包头和数据

structpcap_pkthdr{

structtimevalts;bpf_u_int32caplen;bpf_u_int32len;

};

其中数据部分的长度为caplen

(2)libpcap数据结构

相关的数据结构:

typedefvoid(*pcap_handler)(u_char*,conststructpcap_pkthdr*,constu_char*);

typedefstructpcappcap_t;

structbpf_program{u_intbf_len;structbpf_insn*bf_insns;};

(3)利用libpcap函数库开发应用程序的基本步骤

图5-3说明了libpcap的应用步骤。

 

图5-3libpcap的调用流程

下面列出libpcap应用过程中的几个关键函数。

获取设备名

char*pcap_lookupdev(char*errbuf)

该函数用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名(一个字符串指针)。

如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

获取网络号和掩码

intpcap_lookupnet(char*device,bpf_u_int32*netp,bpf_u_int32*maskp,char*errbuf)

获得指定网络设备的网络号和掩码。

netp参数和maskp参数都是bpf_u_int32指针。

如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

打开设备

pcap_t*pcap_open_live(char*device,intsnaplen,intpromisc,

                         intto_ms,char*errbuf);

获得用于捕获网络数据包的数据包捕获句柄,后续很多libpcap函数将使用该句柄,类似于文件操作函数频繁使用文件句柄。

device参数为指定打开的网络设备名,比如"eth0。

snaplen参数定义捕获数据的最大字节数。

promisc指定是否将网络接口置于混杂模式。

to_ms参数指定超时时间(毫秒),测试下来的结论,0可能代表永不超时。

errbuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。

 /usr/include/pcap.h

typedefstructpcappcap_t;

pcap-int.h里定义了structpcap{}

structpcap

{

   int               fd;

   int               snapshot;

   int               linktype;

   int               tzoff;   /*timezoneoffset   */

   int               offset;  /*offsetforproperalignment  */

   structpcap_sf    sf;

   structpcap_md    md;

   int               bufsize; /*Readbuffer  */

   u_char*        buffer;

   u_char*        bp;

   int               cc;

   u_char*        pkt;     /*Placeholderforpcap_next()   */

   structbpf_programfcode;  

/*Placeholderforfiltercodeifbpfnotinkernel.*/

   char              errbuf[PCAP_ERRBUF_SIZE];

};

编译、优化、调试过滤规则

intpcap_compile(pcap_t*p,structbpf_program*fp,char*str,

                  intoptimize,bpf_u_int32netmask);

该函数用于解析过滤规则串,填写bpf_program结构。

str指向过滤规则串,格式参看tcpdump的man手册,比如:

tcpdump-x-vv-n-tipproto\\tcpanddst192.168.8.90andtcp[13]\&2=2

这条过滤规则将捕捉所有携带SYN标志的到192.168.8.90的TCP报文。

过滤规则串可以是空串(""),表示抓取所有过路的报文。

optimize为1表示对过滤规则进行优化处理。

netmask指定子网掩码,一般从pcap_lookupnet()调用中获取。

返回值小于零表示调用失败。

这个函数可能比较难于理解,涉及的概念源自BPF,Linux系统没有这种概念,但是libpcap采用pcap_compile()和pcap_setfilter()结合的办法屏蔽了各种链路层支持的不同,无论是SOCK_PACKET,还是DLPI。

intpcap_setfilter(pcap_t*p,structbpf_program*fp);

该函数用于设置pcap_compile()解析完毕的过滤规则。

fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。

出错时返回-1;成功时返回0。

抓取下一个数据包

抓取数据包

intpcap_dispatch(pcap_t*p,intcnt,pcap_handlercallback,u_char*user);

捕获并处理数据包,即该函数用于捕捉报文、分发报文到预先指定好的处理函数(回调函数)。

其中,cnt参数指定函数返回前所处理数据包的最大值,pcap_dispatch()接收够cnt个报文便返回。

Cnt为-1表示在一个缓冲区中处理所有的数据包。

如果cnt为0,仅当发生错误、读取到EOF或者读超时到了(pcap_open_live中指定)才停止捕捉报文并返回。

callback参数指定一个带有三个参数的回调函数用于处理pcap_dispatch()所捕获的报文。

回调函数的三个参数为:

typedefvoid(*pcap_handler)(u_char*,conststructpcap_pkthdr*,constu_char*);

一个从pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。

如果成功则pcap_dispatch()返回捕捉到的报文个数,如果在读取静态文件(以前包捕捉过程中存储下来的)时碰到EOF则返回0。

返回-1表示发生错误,此时可以用pcap_perror()、pcap_geterr()显示错误信息。

--------------------------------------------------------------------------

/usr/include/pcap.h

/*

*Eachpacketinthedumpfileisprependedwiththisgenericheader.

*Thisgetsaroundtheproblemofdifferentheadersfordifferent

*packetinterfaces.

*/

structpcap_pkthdr

{

   structtimevalts;     /*timestamp                   */

   bpf_u_int32   caplen; /*lengthofportionpresent    */

   bpf_u_int32   len;    /*lengththispacket(offwire)*/

};

/usr/include/net/bpf.h

/*

*Structureprependedtoeachpacket.

*/

structbpf_hdr

{

   structtimevalbh_tstamp;  /*timestamp                */

   bpf_u_int32   bh_caplen;  /*lengthofcapturedportion*/

   bpf_u_int32   bh_datalen; /*originallengthofpacket */

   u_short       bh_hdrlen;  /*lengthofbpfheader(thisstruct

                                  plusalignmentpadding)   */

};

/*

*Becausethestructureaboveisnotamultipleof4bytes,somecompilers

*willinsistoninsertingpadding;hence,sizeof(structbpf_hdr)won'twork.

*Onlythekernelneedstoknowaboutit;applicationsusebh_hdrlen.

*/

#ifdefKERNEL

#defineSIZEOF_BPF_HDR18

#endif

--------------------------------------------------------------------------

intpcap_loop(pcap_t*p,intcnt,pcap_handlercallback,u_char*user)

功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。

而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。

cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。

例如:

pcap_loop(pd,10,eth_printer,NULL);主循环,开始抓包,共抓10个(由第二个参数指定),抓到包后就进入函数eth_printer􀂄u_char*pcap_next(pcap_t*p,structpcap_pkthdr*h)返回指向下一个数据包的u_char指针。

关闭

voidpcap_close(pcap_t*p)关闭p参数相应的文件,并释放资源。

出错处理

象其它库一样,libpcap也有自己的错误处理机制。

基本上每个函数都有返回值,出错时返回值小于零。

voidpcap_perror(pcap_t*,char*);

char*pcap_strerror(int);

char*pcap_geterr(pcap_t*);

在pcap_t中有一个成员存了错误字串

structpcap{

...

charerrbuf[PCAP_ERRBUF_SIZE];

};

其他的辅助函数

􀂄pcap_read()打开设备

pcap_next()循环包捕获

脱机方式监听部分:

Libpcap支持脱机监听。

即先将网络上的数据截获下来,存到磁盘上,再以后方便时从磁盘上获取数据来做分析。

主要函数为

pcap_open_offline()

pcap_offline_read()

FILE*pcap_file(pcap_t*p)返回被打开文件的文件名。

􀂄intpcap_fileno(pcap_t*p)返回被打开文件的文件描述字号码。

intpcap_snapshot(pcap_t*);

返回最长抓多少字节,就是在pcap_open_live中第二个参数设置

intpcap_stats(pcap_t*,structpcap_stat*);

计数,共抓了多少过滤掉了多少

structpcap_stat{

u_intps_recv;/*numberofpacketsreceived*/

u_intps_drop;/*numberofpacketsdropped*/

u_intps_ifdrop;/*dropsbyinterfaceXXXnot

yetsupported*/

};

intpcap_datalink(pcap_t*);返回网络类型,如DLT_EN10MB就是10M以太网

intpcap_major_version(pcap_t*);返回libpcap主版本号

intpcap_minor_version(pcap_t*);返回libpcap次版本号

5.4Libpcap的应用实例

上面详细说明了libpcap的调用过程和步骤,下面通过两个具体的例子来说明libpcap库的应用。

例5-1一个简单的Libpcap测试程序

(1)test.CXX

#ifdef__cplusplus

extern"C"{

#endif

#include

#ifdef__cplusplus

}

#endif

voidprinter(u_char*user,conststructpcap_pkthdr*h,constu_char*p)

{

printf("Igetonepacket!

");

}

#defineDEFAULT_SNAPLEN68

intmain()

{

charebuf[PCAP_ERRBUF_SIZE];

char*device=pcap_lookupdev(ebuf);

bpf_u_int32localnet,netmask;

pcap_lookupnet(device,&localnet,&netmask,ebuf);

printf("%u.%u.%u.%u",localnet&0xff,localnet>>8&0xff,

localnet>>16&0xff,localnet>>24&0xff);

printf(":

%d.%d.%d.%d",netmask&0xff,netmask>>8&0xff,

netmask>>16&0xff,netmask>>24&0xff);

structpcap_t*pd=pcap_open_live(device,DEFAULT_SNAPLEN,0,1000,ebuf);

if(pcap_datalink(pd)==DLT_EN10MB)

printf("10Mb以太网");

structbpf_programfcode;

pcap_compile(pd,&fcode,NULL,1,0);

pcap_setfilter(pd,&fcode);

pcap_loop(pd,10,printer,NULL);

structpcap_statstat;

p

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 家庭教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1