您的位置:首页 > 其它

Libpcap 总结

2015-06-03 13:38 423 查看


Libpcap 总结

1. Libpcap 简介

Libpcap是Packet Capture Libray的英文缩写, 即C语言库。该库提供的C函数接口,提供了用户级别的网络数据包捕获接口。在安装了 libpcap 的平台上,以 libpcap 为接口写的程序、应用,能够自由地跨平台使用。TCPDUMP就是在Libpcap的基础上开发而成的。Libpcap提供的接口函数实现和封装了与数据包截获有关的过程。在Windows平台上,也有一款与其功能类似的开发库:Wincap。

它的工作在上层应用程序与网络接口之间。

   主要功能:

数据包捕获:捕获流经网卡的原始数据包

自定义数据包发送:构造任何格式的原始数据包

流量采集与统计:采集网络中的流量信息

规则过滤:提供自带规则过滤功能,按需要选择过滤规则

  

具体介绍见: http://www.tcpdump.org/

2. Libpcap的工作原理

作为捕捉网络数据包的库,它是一个独立于系统的用户级的API接口,为底层网络检测提供了一个可移植的框架。

Libpcap主要由两部份组成:网络分接头(Network Tap)和数据过滤器(Packet Filter)。网络分接头从网络设备驱动程序中收集数据拷贝,过滤器决定是否接收该数据包。一个包的捕捉分为三个主要部分,包括面向底层包捕获、面向中间层的数据包过滤和面向应用层的用户接口。这与Linux操作系统对数据包的处理流程是相同的(网卡->网卡驱动->数据链路层->IP层->传输层->应用程序)。Libpcap利用BSD
Packet Filter(BPF)算法对网卡接收到的链路层数据包进行过滤。BPF算法的基本思想是在有BPF监听的网络中,网卡驱动将接收到的数据包复制一份交给BPF过滤器,过滤器根据用户定义的规则决定是否接收此数据包以及需要拷贝该数据包的那些内容,然后将过滤后的数据给与过滤器相关联的上层应用程序。

Libpcap的包捕获机制就是在数据链路层加一个旁路处理。当一个数据包到达网络接口时,libpcap首先利用已经创建的Socket从链路层驱动程序中获得该数据包的拷贝,再通过Tap函数将数据包发给BPF过滤器。BPF过滤器根据用户已经定义好的过滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,并传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲区,并传递给用户层缓冲区。



3. Libpcap 安装

在http://www.tcpdump.org/下载libpcap(tcpdump的源码也可以从这个网站下载)

./configure

make

sudo make install

如果需要监听所有流经某网络接口的数据包,则设置该网络接口为混杂模式,在混杂模式下网络接口可以接收所有经过网卡的数据包,包括不是发给它的包。默认情况下网络接口只把发给它的包传递给上层程序,其它的包一律丢弃。打开网卡的混杂模式, 命令:

ifconfig eth1 promisc 设置混杂模式

ifconfig eth1 -promisc 取消混杂模式

4. Libpcap 函数介绍和使用

编译命令: gcc -g -Wall -o test test.c -lpcap

清除命令:rm -rf *.o test

运行: 因为要访问网络接口,要使用sudo

4.1 简单认识

<span style="font-size:14px;">//********************* test.c **********************
#include <stdio.h>
#include <pcap.h>

int main (int argc, char* argv[])
{
/*the error code buf of libpcap*/
char errBuf[PCAP_ERRBUF_SIZE], * device;

device = pcap_lookupdev(errBuf);

if(device)
{
printf("success: device: %s\n", device);
}
else
{
printf("error: %s\n", errBuf);
}
/*the printer running when packet have captured*/
void printer()
{
printf("A packet is captured!\n");
return;
}

/*create capture handler of libpcap*/
pcap_t *pd = pcap_open_live (device, 65535, 0, 1000, errBuf);

/*start the loop of capture, loop 5 times, enter printer when capted*/
pcap_loop (pd, 5, printer, NULL);
pcap_close (pd);
return 0;
}
</span>


简单分析:

pcap_lookupdev(errBuf) 这个函数返回第一个合适的网络接口的字符串指针,如果出错,则errbuf存放出错信息字符串,errbuf至少应该是PCAP_ERRBUF_SIZE个字节长度的。网络接口可以硬编码,但是不同操作系统名称不同,需要跨平台必须这样做,linux一般是"eth0"

pcap_open_live(device, 65535, 0, 1000, errBuf) 获取网络接口句柄

void pcap_close(pcap_t * p) 释放接口

4.2 使用一般流程

基于pcap的嗅探器程序的总体架构,其流程如下:

4.2.1 选择接口

首先要决定用哪一个接口进行嗅探开始。在Linux中,这可能是eth0,而在BSD系统中则可能是xl1等等。我们也可以用一个字符串来定义这个设备,或者采用pcap提供的接口名来工作。

4.2.2 初始化pcap

在这里需要告诉pcap对什么设备进行嗅探。假如愿意的话,我们还可以嗅探多个设备。怎样区分它们呢?使用 文件句柄。就像打开一个文件进行读写一样,必须命名我们的嗅探“会话”,以此使它们各自区别开来。

4.2.3 过滤

如果只想嗅探特定的传输(如TCP/IP包,发往端口23的包等等),我们必须创建一个规则集合,编译并且使用它。这个过程分为三个相互紧密关联的阶段。 规则集合被置于一个字符串内,并且被转换成能被pcap读的格式(因此编译它)。编译实际上就是在我们的程序里调用一个不被外部程序使用的函数。接下来我
们要告诉 pcap使用它来过滤出我们想要的那一个会话。(此步骤可选)

4.2.4 执行循环抓包

在这个阶段内pcap一直工作到它接收了所有我们想要的包为止。每当它收到一个包就调用另一个已经定义好的 函数,这个函数可以做我们想要的任何工作,它可以剖析所部获的包并给用户打印出结果,它可以将结果保存为一个文件,或者什么也不作。

4.2.5 关闭会话并结束

4.3 Libpcap库主要函数说明

函数名称:pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)

函数功能:获得用于捕获网络数据包的数据包捕获描述字。

参数说明:device 参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定是否将网络接口置于混杂模式。to_ms参数指*定超时时 间(毫秒)。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。

函数名称:pcap_t *pcap_open_offline(char *fname, char *ebuf)

函数功能:打开以前保存捕获数据包的文件,用于读取。

参数说明:fname参数指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。”-“为标准输入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。

函数名称:pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)

函数功能:打开用于保存捕获数据包的文件,用于写入。

参数说明:fname 参数为”-“时表示标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或pcap_open_live()函数后返回的 pcap结构指针。fname参数指定打开的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消 息。

函数名称:char *pcap_lookupdev(char *errbuf)

函数功能:用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针。参数说明:如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

函数名称:int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)

函数功能:获得指定网络设备的网络号和掩码。

参数说明:netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

函数名称:int pcap_dispatch(pcap_t *p, int cnt,pcap_handler callback, u_char *user)

函数功能:捕获并处理数据包。

参数说明:cnt 参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取 到EOF;超时读取。callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从pcap_dispatch()函数传递过来的 u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的字节数。读取到EOF时则返回零 值。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。

函数名称:int pcap_loop(pcap_t *p, int cnt,pcap_handler callback, u_char *user)

函数功能: 功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。而如果为 pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时 pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。

函数名称:void pcap_dump(u_char *user, struct pcap_pkthdr *h,u_char *sp)

函数功能:向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可作为pcap_dispatch()函数的回调函数。

函数名称:int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask)

函数功能:将str参数指定的字符串编译到过滤程序中。

参数说明:fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码。

函数名称:int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

函数功能:指定一个过滤程序。

参数说明:fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1;成功时返回0。

函数名称:u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

函数功能:返回指向下一个数据包的u_char指针。

函数名称:int pcap_datalink(pcap_t *p)

函数功能:返回数据链路层类型,例如DLT_EN10MB。

函数名称:int pcap_snapshot(pcap_t *p)

函数功能:返回pcap_open_live被调用后的snapshot参数值。

函数名称:int pcap_is_swapped(pcap_t *p)

函数功能:返回当前系统主机字节与被打开文件的字节顺序是否不同。

函数名称:int pcap_major_version(pcap_t *p)

函数功能:返回写入被打开文件所使用的pcap函数的主版本号。

函数名称:int pcap_minor_version(pcap_t *p)

函数功能:返回写入被打开文件所使用的pcap函数的辅版本号。

函数名称:int pcap_stats(pcap_t *p, struct pcap_stat *ps)

函数功能:向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。如果出错或不支持数据包统计,则返回-1,且可调用pcap_perror()或pcap_geterr()函数来获取错误消息。

函数名称:FILE *pcap_file(pcap_t *p)

函数功能:返回被打开文件的文件名。

函数名称:int pcap_fileno(pcap_t *p)

函数功能:返回被打开文件的文件描述字号码。

函数名称:void pcap_perror(pcap_t *p, char *prefix)

函数功能:在标准输出设备上显示最后一个pcap库错误消息。以prefix参数指定的字符串为消息头。

函数名称:char *pcap_geterr(pcap_t *p)

函数功能:返回最后一个pcap库错误消息。

函数名称:char *pcap_strerror(int error)

函数功能:如果strerror()函数不可用,则可调用pcap_strerror函数替代。

函数名称:void pcap_close(pcap_t *p)

函数功能:关闭p参数相应的文件,并释放资源。

函数名称:void pcap_dump_close(pcap_dumper_t *p)

函数功能:关闭相应的被打开文件。

4.4 简单的例子

<span style="font-size:14px;">#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * devStr;

/* get a device */
devStr = pcap_lookupdev(errBuf);

if(devStr)
{
printf("success: device: %s\n", devStr);
}
else
{
printf("error: %s\n", errBuf);
exit(1);
}

/* open a device, wait until a packet arrives */
pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);

if(!device)
{
printf("error: pcap_open_live(): %s\n", errBuf);
exit(1);
}

/* wait a packet to arrive */
struct pcap_pkthdr packet;
const u_char * pktStr = pcap_next(device, &packet);

if(!pktStr)
{
printf("did not capture a packet!\n");
exit(1);
}

printf("Packet length: %d\n", packet.len);
printf("Number of bytes: %d\n", packet.caplen);
printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec));

pcap_close(device);

return 0;
}</span>


4.5 抓包

有两种手段捕捉包。我们可以一次只捕捉一个包,也可以进入一个循环,等捕捉到多个包再进行处理。我们将先看看怎样去捕捉单个包,然后再看看使用循环的方法。为此,我们使用函数pcap_next()。

pcap_next()的原型及其简单:

   u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

第一个参数是会话句柄,第二个参数是指向一个包括了当前数据包总体信息(被捕捉时的时间,包的长度,其被指定的部分长度)的结构体的指针(在这里只有一个片断,只作为一个示例)。pcap_next()返回一个u_char指针给被这个结构体描述的包。这里有一个演示怎样使用pcap_next()来嗅探一个包的例子:

  
<span style="font-size:14px;">#include <pcap.h>
   #include <stdio.h>
   int main()
   {
   pcap_t *handle; /* 会话句柄 */
   char *dev; /* 执行嗅探的设备 */
   char errbuf[PCAP_ERRBUF_SIZE]; /* 存储错误信息的字符串 */
  
   struct bpf_program filter; /* 已经编译好的过滤器 */
   char filter_app[] = "port 23"; /* 过滤表达式 */
   bpf_u_int32 mask; /* 所在网络的掩码 */
   bpf_u_int32 net; /* 主机的IP地址 */
   struct pcap_pkthdr header; /* 由pcap.h定义 */
   const u_char *packet; /* 实际的包 */
   /* Define the device */
   dev = pcap_lookupdev(errbuf);
   /* 探查设备属性 */
   pcap_lookupnet(dev, &net, &mask, errbuf);
   /* 以混杂模式打开会话 */
   handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
   /* 编译并应用过滤器 */
   pcap_compile(handle, &filter, filter_app, 0, net);
   pcap_setfilter(handle, &filter);
   /* 截获一个包 */
   packet = pcap_next(handle, &header);
   /* 打印它的长度 */
   printf("Jacked a packet with length of [%d]
   ", header.len);
   /* 关闭会话 */
   pcap_close(handle);
   return(0);
   }</span>


这个程序嗅探被pcap_lookupdev()返回的设备并将它置为混杂模式。它发现第一个包经过端口23(telnet)并且告诉用户此包的大小(以字 节为单位)。这个程序又包含了一个新的调用pcap_close(),我们将在后面讨论(尽管它的名字就足够证实它自己的作用)。

实际上很少有嗅探程序会真正的使用pcap_next()。通常,它们使用pcap_loop()或者 pcap_dispatch()(它就是用了pcap_loop())。pcap_loop()的原型如下:

   int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

第一个参数是会话句柄,接下来是一个整型,它告诉pcap_loop()在返回前应捕捉多少个数据包(若为负值则表示应该一直工作直至错误发生)。第三个参数是回调函数的名称(正像其标识符所指,无括号)。最后一个参数在有些应用里有用,但更多时候则置为NULL。假设我们有我们自己的想送往回调函数的参数,另外还有pcap_loop()发送的参数,这就需要用到它。很明显,必须是一个u_char类型的指针以确保结果正确;正像我们稍后见到的,pcap使用了很有意思的方法以u_char指针的形势传递信息。pcap_dispatch()的用法几乎相同。唯一不同的是它们如何处理超时(还记得在调用pcap_open_live()时怎样设置超时吗?这就是它起作用的地方)。Pcap_loop()忽略超时而pcap_dispatch()则不。关于它们之间区别的更深入的讨论请参见pcap的手册页。回调函数的原型:

   void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

让我们更细致的考察它。首先,你会注重到该函数返回void类型,这是符合逻辑的,因为pcap_loop()不知道如何去处理一个回调返回值。第一个参数相应于pcap_loop()的最后一个参数。每当回调函数被调用时,无论最后一个参数传给pcap_loop()什么值,这个值都会传给我们回调函数的第一个参数。第二个参数是pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息。结构体pcap_pkhdr在pcap.h中定义如下:

<span style="font-size:14px;">   struct pcap_pkthdr {
   	struct timeval ts; /* 时间戳 */
   	bpf_u_int32 caplen; /* 已捕捉部分的长度 */
   	bpf_u_int32 len; /* 该包的脱机长度 */
   };</span>


这些量都相当明了。最后一个参数在它们中是最有意思的,也最让pcap程序新手感到迷惑。这又是一个u_char指针,它包含了被pcap_loop()嗅探到的所有包。

但是你怎样使用这个我们在原型里称为packet的变量呢?一个数据包包含许多属性,因此你可以想象它不只是一个字符串,而实质上是一个结构体的集合(比如,一个TCP/IP包会有一个以太网的头部,一个IP头部,一个TCP头部,还有此包的有效载荷)。这个u_char就是这些结构体的串联版本。为了使用它,我们必须作一些有趣的匹配工作。

下面这些是一些数据包的结构体:

<span style="font-size:14px;">   /* 以太网帧头部 */
   struct sniff_ethernet {
   	u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
   	u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
   	u_short ether_type; /* IP? ARP? RARP? etc */
   };
   /* IP数据包的头部 */
   struct sniff_ip {
   	#if BYTE_ORDER == LITTLE_ENDIAN
   	u_int ip_hl:4, /* 头部长度 */
   	ip_v:4; /* 版本号 */
   	#if BYTE_ORDER == BIG_ENDIAN
   	u_int ip_v:4, /* 版本号 */
   	ip_hl:4; /* 头部长度 */
   	#endif
  	 	#endif /* not _IP_VHL */
   	u_char ip_tos; /* 服务的类型 */
   	u_short ip_len; /* 总长度 */
   	u_short ip_id; /*包标志号 */
   	u_short ip_off; /* 碎片偏移 */
   	#define IP_RF 0x8000 /* 保留的碎片标志 */
   	#define IP_DF 0x4000 /* dont fragment flag */
   	#define IP_MF 0x2000 /* 多碎片标志*/
   	#define IP_OFFMASK 0x1fff /*分段位 */
   	u_char ip_ttl; /* 数据包的生存时间 */
   	u_char ip_p; /* 所使用的协议 */
   	u_short ip_sum; /* 校验和 */
   	struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
   };
   /* TCP 数据包的头部 */
   struct sniff_tcp {
   	u_short th_sport; /* 源端口 */
   	u_short th_dport; /* 目的端口 */
   	tcp_seq th_seq; /* 包序号 */
   	tcp_seq th_ack; /* 确认序号 */
   	#if BYTE_ORDER == LITTLE_ENDIAN
   	u_int th_x2:4, /* 还没有用到 */
   	th_off:4; /* 数据偏移 */
   	#endif
   	#if BYTE_ORDER == BIG_ENDIAN
   	u_int th_off:4, /* 数据偏移*/
   	th_x2:4; /*还没有用到 */
   	#endif
   	u_char th_flags;
   	#define TH_FIN 0x01
   	#define TH_SYN 0x02
   	#define TH_RST 0x04
   	#define TH_PUSH 0x08
   	#define TH_ACK 0x10
   	#define TH_URG 0x20
   	#define TH_ECE 0x40
   	#define TH_CWR 0x80
   	#define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
   	u_short th_win; /* TCP滑动窗口 */
   	u_short th_sum; /* 头部校验和 */
   	u_short th_urp; /* 紧急服务位 */
   };</span>


pcap嗅探数据包时正是使用的这些结构。接下来,它简单的创建一个u_char字符串并且将这些结构体填入。那么我们怎样才能区分它们呢?预备好见证指针最实用的好处之一吧。假定要对以太网上的TCP/IP包进行处理,同样的手段可以应用于任何数据包,唯一的区别是你实际所使用的结构体的类型。让我们从声明分解u_char包的变量开始:

  
<span style="font-size:14px;">const struct sniff_ethernet *ethernet; /* 以太网帧头部*/
   const struct sniff_ip *ip; /* IP包头部 */
   const struct sniff_tcp *tcp; /* TCP包头部 */
   const char *payload; /* 数据包的有效载荷*/
   /*为了让它的可读性好,我们计算每个结构体中的变量大小*/
   int size_ethernet = sizeof(struct sniff_ethernet);
   int size_ip = sizeof(struct sniff_ip);
   int size_tcp = sizeof(struct sniff_tcp);
   现在我们开始让人感到有些神秘的匹配:
   ethernet = (struct sniff_ethernet*)(packet);
   ip = (struct sniff_ip*)(packet + size_ethernet);
   tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
   payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);</span>


此处如何工作?考虑u_char在内存中的层次。基本的,当pcap将这些结构体填入u_char的时候是将这些数据存入一个字符串中,那个字符串将被送入我们的回调函数中。反向转换是这样的,不考虑这些结构体制中的值,它们的大小将是一致的。例如在我的平台上,一个sniff_ethernet结构体的大小是14字节。一个sniff_ip结构体是20字节,一个sniff_tcp结构体也是20字节。
u_char指针正是包含了内存地址的一个变量,这也是指针的实质,它指向内存的一个区域。简单而言,我们说指针指向的地址为x,假如三个结构体恰好线性排列,第一个(sniff_ethernet)被装载到内存地址的x处则我们很轻易的发现其他结构体的地址,让我们以表格显示之:

   Variable Location (in bytes)

   sniff_ethernet X

   sniff_ip X + 14

   sniff_tcp X + 14 + 20

   payload X + 14 + 20 + 20

结构体sniff_ethernet正好在x处,紧接着它的sniff_ip则位于x加上它本身占用的空间(此例为14字节),依此类推可得全部地址。

注重:你没有假定你的变量也是同样大小是很重要的。你应该总是使用sizeof()来确保尺寸的正确。这是因为这些结构体中的每个成员在不同平台下可以有不同的尺寸。

4.6 过滤数据包

几乎所有的操作系统(BSD, AIX, Mac OS, Linux等)都会在内核中提供过滤数据包的方法,主要都是基于BSD Packet
Filter(BPF)结构的。libpcap利用BPF来过滤数据包。

过滤数据包需要完成3件事:

1) 构造一个过滤表达式

2) 编译这个表达式

3) 应用这个过滤器

4.6.1构造一个过滤表达式

BPF使用一种类似于汇编语言的语法书写过滤表达式,不过libpcap和tcpdump都把它封装成更高级且更容易的语法了,具体可以man tcpdump,以下是一些例子:

src host 192.168.1.177 只接收源ip地址是192.168.1.177的数据包

dst port 80 只接收tcp/udp的目的端口是80的数据包

not tcp 只接收不使用tcp协议的数据包

tcp[13] == 0x02 and (dst port 22 or dst port 23) 只接收SYN标志位置位且目标端口是22或23的数据包(tcp首部开始的第13个字节)

icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo 只接收icmp的ping请求和ping响应的数据包

ehter dst 00:e0:09:c1:0e:82 只接收以太网mac地址是00:e0:09:c1:0e:82的数据包

ip[8] == 5 只接收ip的ttl=5的数据包(ip首部开始的第8个字节)
详细的过滤,可见 : http://man.he.net/man7/pcap-filter

4.6.2编译这个表达式

构造完过滤表达式后,我们需要编译它,使用如下函数:

int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)

p:这是一个传出参数,存放编译后的bpf

str:过滤表达式

optimize:是否需要优化过滤表达式

metmask:简单设置为0即可

4.6.3 应用这个过滤器

最后我们需要应用这个过滤表达式:

nt pcap_setfilter(pcap_t * p, struct bpf_program * fp)

第二个参数fp就是前一步pcap_compile()的第二个参数

例子:

<span style="font-size:14px;">#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
int * id = (int *)arg;

printf("id: %d\n", ++(*id));
printf("Packet length: %d\n", pkthdr->len);
printf("Number of bytes: %d\n", pkthdr->caplen);
printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

int i;
for(i=0; i<pkthdr->len; ++i)
{
printf(" %02x", packet[i]);
if( (i + 1) % 16 == 0 )
{
printf("\n");
}
}

printf("\n\n");
}

int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * devStr;

/* get a device */
devStr = pcap_lookupdev(errBuf);

if(devStr)
{
printf("success: device: %s\n", devStr);
}
else
{
printf("error: %s\n", errBuf);
exit(1);
}

/* open a device, wait until a packet arrives */
pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);

if(!device)
{
printf("error: pcap_open_live(): %s\n", errBuf);
exit(1);
}

/* construct a filter */
struct bpf_program filter;
pcap_compile(device, &filter, "dst port 80", 1, 0);
pcap_setfilter(device, &filter);

/* wait loop forever */
int id = 0;
pcap_loop(device, -1, getPacket, (u_char*)&id);

pcap_close(device);

return 0;
}</span>


更多详细内容,见 http://www.tcpdump.org/pcap.html
参考:
http://www.tcpdump.org/ http://man.he.net/man7/pcap-filter http://blog.csdn.net/htttw/article/details/7521053 http://www.cnblogs.com/coder2012/archive/2013/04/13/3012390.html http://blog.csdn.net/hanyu1980/article/details/2225558 http://blog.chinaunix.net/uid-9950859-id-98958.html http://blog.chinaunix.net/uid-21556133-id-120228.html http://www.tcpdump.org/pcap.html http://blog.neu.edu.cn/elm/archives/44
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: