基于libpcap的C编程_第1页
基于libpcap的C编程_第2页
基于libpcap的C编程_第3页
基于libpcap的C编程_第4页
基于libpcap的C编程_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Pcap程序设计开始:pcap应用程序的格式我们所要理解的第一件事情是一个基于pcap的嗅探器程序的总体布局。流程如下:1.我们从决定用哪一个接口进行嗅探开始。在Linux中,这可能是eth0,而在BSD系统中则可能是xl1等等。我们也可以用一个字符串来定义这个设备,或者采用pcap提供的接口名来工作。2.初始化pcap。在这里我们要告诉pcap对什么设备进行嗅探。假如愿意的话,我们还可以嗅探多个设备。怎样区分它们呢?使用 文件句柄。就像打开一个文件进行读写一样,必须命名我们的嗅探“会话”,以此使它们各自区别开来。3.如果我们只想嗅探特定的传输(如TCP/IP包,发往端口23的包等等),我们必

2、须创建一个规则集合,编译并且使用它。这个过程分为三个相互紧密关联的阶段。规则集合被置于一个字符串内,并且被转换成能被pcap读的格式(因此编译它)。编译实际上就是在我们的程序里调用一个不被外部程序使用的函数。接下来我们要告诉 pcap使用它来过滤出我们想要的那一个会话。4.最后,我们告诉pcap进入它的主体执行循环。在这个阶段内pcap一直工作到它接收了所有我们想要的包为止。每当它收到一个包就调用另一个已经定义好的函数,这个函数可以做我们想要的任何工作,它可以剖析所部获的包并给用户打印出结果,它可以将结果保存为一个文件,或者什么也不作。5在嗅探到所需的数据后,我们要关闭会话并结束。这是实际上一

3、个很简单的过程。一共五个步骤,其中一个(第3个)是可选的。我们为什么不看一看是怎样实现每一个步骤呢?设置设备这是很简单的。有两种方法设置想要嗅探的设备。第一种,我们可以简单的让用户告诉我们。考察下面的程序:#include <stdio.h>#include <pcap.h>int main(int argc, char *argv)char *dev = argv1;printf("Device: %s", dev);return(0);用户通过传递给程序的第一个参数来指定设备。字符串“dev”以pcap能“理解”的格式保存了我们要嗅探的接口的名字

4、(当然,用户必须给了我们一个真正存在的接口)。另一种也是同样的简单。来看这段程序:#include <stdio.h>#include <pcap.h>int main()char *dev, errbufPCAP_ERRBUF_SIZE;dev = pcap_lookupdev(errbuf);printf("Device: %s", dev);return(0);在这个例子里,pcap就自己设置设备。“但是,等一下,Tim”,你会说,“字符串errbuf是做什么的?”大多数的pcap命令允许我们向它们传递字符串作为参数。这个字符串的目的是什么呢?

5、如果命令失败,它将传给这个字符串关于错误的描述。这样,如果pcap_lookupdev()失败,它将在 errbuf存储错误信息。很好,是不是?这就是我们怎样去设置设备。打开设备进行嗅探创建一个嗅探会话的任务真的非常简单。为此,我们使用pcap_open_live()函数。此函数的原型(根据pcap的手册页)如下:pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)其第一个参数是我们在上一节中指定的设备,snaplen是整形的,它定义了将被pcap捕获的最大字节数。当promi

6、sc设为true时将置指定接口为混杂模式(然而,当它置为false时接口仍处于混杂模式的特殊情况也是有可能的)。to_ms是读取时的超时值,单位是毫秒(如果为0则一直嗅探直到错误发生,为-1则不确定)。最后,ebuf是一个我们可以存入任何错误信息的字符串(就像上面的errbuf)。此函数返回其会话句柄。举个例子,考察以下代码片断:#include <pcap.h>.pcap_t *handle;handle = pcap_open_live(somedev, BUFSIZ, 1, 0, errbuf);这个代码片断打开字符串somedev的设备,告诉它读取被BUFSIZ指定的字节数

7、(BUFSIZ在pcap.h里定义)。我们告诉它将设备置为混杂模式,一直嗅探到错误发生,如果有了错误,把它存放在字符串errbuf中。混杂模式与非混杂模式的区别:这两种方式区别很大。一般来说,非混杂模式的嗅探器中,主机仅嗅探那些跟它直接有关的通信,如发向它的,从它发出的,或经它路由的等都会被嗅探器捕获。而在混杂模式中则嗅探传输线路上的所有通信。在非交换式网络中,这将是整个网络的通信。这样做最明显的优点就是使更多的包被嗅探到,它们因你嗅探网络的原因或者对你有帮助,或者没有。但是,混杂模式是可被探测到的。一个主机可以通过高强度的测试判定另一台主机是否正在进行混杂模式的嗅探。其次,它仅在非交换式的网

8、络环境中有效工作(如集线器,或者交换中的ARP层面)。再次,在高负荷的网络中,主机的系统资源将消耗的非常严重。过滤通信通常,我们的嗅探器仅对某特定的通信感兴趣。例如,有时我们想嗅探到端口23(telnet)的包以获得密码;或者我们想截获一个正通过端口21 (FTP)传送的文件;可能我们仅想要得到DNS的通信(端口53,UDP)。无论哪种情况,我们都很少盲目的嗅探整个网络的通信。下面讨论pcap_compile()与pcap_setfilter()。这个过程非常简单。当我们已经调用了pcap_open_live()从而建立了一个嗅探会话之后就可以应用我们自己的过滤器了。为什么要用我们自己的过滤器

9、呢?有两个原因。第一,pcap的过滤器太强大了,因为它直接使用 BPF过滤器,我们通过使用BPF驱动直接过滤跳过了很多的关节。第二,这样做要容易的多。在使用我们自己的过滤器前必须编译它。过滤表达式被保存在一个字符串中(字符数组)。其句法在tcpdump的手册页中被证明非常好。我建议你亲自阅读它。但是我们将使用简单的测试表达式,这样你可能很容易理解我的例子。我们调用pcap_compile()来编译它,其原型是这样定义的:int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32

10、 netmask)第 一个参数是会话句柄(pcap_t *handle在前一节的示例中)。接下来的struct bpf_program *是我们存储被编译的过滤器版本的地址的引用。再接下来的char *则是表达式本身,存储在规定的字符串格式里。再下边是一个定义表达式是否被优化的整形量(0为false,1为true,标准规定)。最后,我们必须指定应用此过滤器的网络掩码。函数返回-1为失败,其他的任何值都表明是成功的。表达式被编译之后就可以使用了。现在进入pcap_setfilter()。仿照我们介绍pcap的格式,先来看一看pcap_setfilter()的原型:int pcap_setfilt

11、er(pcap_t *p, struct bpf_program *fp)这非常直观,第一个参数是会话句柄,第二个参数是被编译表达式版本的引用(可推测出它与pcap_compile()的第二个参数相同)。下面的代码示例可能能使你更好的理解:#include <pcap.h>pcap_t *handle; /* 会话的句柄 */char dev = "rl0" /* 执行嗅探的设备 */char errbufPCAP_ERRBUF_SIZE; /* 存储错误 信息的字符串 */struct bpf_program filter; /*已经编译好的过滤表达式*/ch

12、ar filter_app = "port 23" /* 过滤表达式*/bpf_u_int32 mask; /* 执行嗅探的设备的网络掩码 */bpf_u_int32 net; /* 执行嗅探的设备的IP地址 */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, &f

13、ilter);这个程序使嗅探器嗅探经由端口23的所有通信,使用混杂模式,设备是rl0。你可能注意到前面的示例包含一个我们还没提到的函数:pcap_lookupnet(),向这个函数提供设备接口名,它将返回其IP和网络掩码,这是很基本的,因为我们需要知道网络掩码以便应用过滤器。此函数在此文最后的miscellaneous一节里还有描述。据我的经验,这个过滤器在所有的操作系统下都不会工作。在我的测试环境里,我发现OpenBSD 2.9默认内核支持这种过滤器,但FreeBSD 4.3默认内核则不支持。你的情况可能会有变化。实际的嗅探到此为止,我们已经学习了如何定义一个设备,让它准备嗅探,还有应用过滤

14、器使我们嗅谈到什么或者不嗅探到什么。现在到了真正去捕获一些数据包的时候了。有两种手段捕获包。我们可以一次只捕获一个包,也可以进入一个循环,等捕获到多个包再进行处理。我们将先看看怎样去捕获单个包,然后再看看使用循环的方法。为此,我们使用函数pcap_next()。Pcap_next()的原型及其简单:u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)第一个参数是会话句柄,第二个参数是指向一个包括了当前数据包总体信息(被捕获时的时间,包的长度,其被指定的部分长度)的结构体的指针(在这里只有一个片断,只作为一个示例)。Pcap_next()返回一个

15、u_char指针给被这个结构体描述的包。我们将稍后讨论这种实际读取包本身的手段。这里有一个演示怎样使用pcap_next()来嗅探一个包的例子:#include <pcap.h>#include <stdio.h>int main()pcap_t *handle; /* 会话句柄 */char *dev; /* 执行嗅探的设备 */char errbufPCAP_ERRBUF_SIZE; /* 存储错误信息的字符串 */struct bpf_program filter; /* 已经编译好的过滤器 */char filter_app = "port 23&qu

16、ot; /* 过滤表达式 */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,

17、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); /* 关闭会话 */retu

18、rn(0);这个程序嗅探被pcap_lookupdev()返回的设备并将它置为混杂模式。它发现第一个包经过端口23(telnet)并且告诉用户此包的大小(以字 节为单位)。这个程序又包含了一个新的调用pcap_close(),我们将在后面讨论(尽管它的名字就足够证明它自己的作用)。我们可以使用的另一种手段则要复杂的多,并且可能也更为有用。很少有(如果有的话)嗅探器真正的使用pcap_next()。通常,它们使用pcap_loop()或者 pcap_dispatch()(它就是用了pcap_loop())。为了理解这两个函数的用法,你必须理解回调函数的思想。回调函数并不是什么新东西,它在许多AP

19、I里面非常普遍。回调函数的概念极其简单。设想我有一个程序正等待某种排序的事件。为了达到这个例子的目的,让我们假象我的程序想让用户在键盘上按下一个键,每当他们按下了一个键,我就想调用一个作相应处理的函数。我所用的函数就是一个回调函数。用户每按一个键一次,我的程序就调用回调函数一次。回调函数在应用在pcap里,取代当用户按下键时被调用的函数的是当pcap嗅探到一个数据包时所调用的函数。可以定义它们的回调函数的两个函数就是pcap_loop()和pcap_dispatch()。此二者在它们的回调函数的使用上非常的相似。它们都是每当捕获到一个符合我们过滤器的包时调用器回调函数(当然是存在一个过滤器时,

20、如果不存在则所有被嗅探到的包都被送到会调函数处理)。Pcap_loop()的原型如下:int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)第一个参数是会话句柄,接下来是一个整型,它告诉pcap_loop()在返回前应捕获多少个数据包(若为负值则表示应该一直工作直至错误发生)。第三个参数是回调函数的名称(正像其标识符所指,无括号)。最后一个参数在有些应用里有用,但更多时候则置为NULL。假设我们有我们自己的想送往回调函数的参数,另外还有pcap_loop()发送的参数,这就需要用到它。很明显,必须是一个u_c

21、har类型的指针以确保结果正确;正像我们稍后见到的, pcap使用了很有意思的方法以u_char指针的形势传递信息。在我们展示了一个pcap是怎样做的例子之后就很容易去做了。若是还不行就参考你的本地的C引用文本,作为一个指针的解释那就超出了本文的范围。 Pcap_dispatch()的用法几乎相同。唯一不同的是它们如何处理超时(还记得在调用pcap_open_live()时怎样设置超时吗?这就是它起作用的地方)。Pcap_loop()忽略超时而pcap_dispatch()则不。关于它们之间区别的更深入的讨论请参见pcap的手册页。在提供使用pcap_loop()的示例之前,我们必须检查我们的

22、回调函数的格式。我们不能武断的定义回调函数的原型,否则pcap_loop()将会不知道如何去使用它。因此我们使用这样的格式作为我们的回调函数的原型:void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);让我们更细致的考察它。首先,你会注意到该函数返回void类型,这是符合逻辑的,因为pcap_loop()不知道如何去处理一个回调返回值。第一个参数相应于pcap_loop()的最后一个参数。每当回调函数被调用时,无论最后一个参数传给pcap_loop()什么值,这个值都会传给我们

23、回调函数的第一个参数。第二个参数是pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息。结构体pcap_pkhdr在pcap.h中定义如下:struct pcap_pkthdr struct timeval ts; /* 时间戳 */bpf_u_int32 caplen; /* 已捕获部分的长度 */bpf_u_int32 len; /* 该包的脱机长度 */;这些量都相当明了。最后一个参数在它们中是最有意思的,也最让pcap程序新手感到迷惑。这又是一个u_char指针,它包含了被pcap_loop()嗅探到的所有包。但是你怎样使用这个我们在原型里称为packet的变量呢?一个数据包包含

24、许多属性,因此你可以想象它不只是一个字符串,而实质上是一个结构体的集合(比如,一个TCP/IP包会有一个以太网的头部,一个IP头部,一个TCP头部,还有此包的有效载荷)。这个u_char就是这些结构体的串联版本。为了使用它,我们必须作一些有趣的匹配工作。首先,在匹配它们之前必须定义这些实际的结构体。下面就是我用来描述一个通过以太网的TCP/IP包的结构体的定义。我使用的所有这些定义都是直接从POSIX库中提取的。通常,我只简单的使用那些库中的定义即可,但据我的经验不同平台的库之间有轻微的差别,这使得它实现起来变得混乱。因此,为达到示例的目的,我就避免那些混乱而简单的复制这些有关的结构体。所有这

25、些都能在你的本地unix系统中的include/netinet中找到。下面就是这些结构体:/* 以太网帧头部 */struct sniff_ethernet u_char ether_dhostETHER_ADDR_LEN; /* 目的主机的地址 */u_char ether_shostETHER_ADDR_LEN; /* 源主机的地址 */u_short ether_type; /* IP? ARP? RARP? etc */;/* IP数据包的头部 */struct sniff_ip #if BYTE_ORDER = LITTLE_ENDIANu_int ip_hl:4, /* 头部长度

26、*/ip_v:4; /* 版本号 */#if BYTE_ORDER = BIG_ENDIANu_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 */#d

27、efine 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; /* 包序号 */t

28、cp_seq th_ack; /* 确认序号 */#if BYTE_ORDER = LITTLE_ENDIANu_int th_x2:4, /* 还没有用到 */th_off:4; /* 数据偏移 */#endif#if BYTE_ORDER = BIG_ENDIANu_int th_off:4, /* 数据偏移*/th_x2:4; /*还没有用到 */#endifu_char th_flags;#define TH_FIN 0x01#define TH_SYN 0x02#define TH_RST 0x04#define TH_PUSH 0x08#define TH_ACK 0x10#def

29、ine TH_URG 0x20#define TH_ECE 0x40#define TH_CWR 0x80#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)u_short th_win; /* TCP滑动窗口 */u_short th_sum; /* 头部校验和 */u_short th_urp; /* 紧急服务位 */;注:在Slackware Linux 8(内核版本2.2.19)上我发现使用以上结构体的代码将不能通过编译。后来证明问题在于include/fearures.h,它只实现了一个 POSIX接口

30、,除非定义BSD_SOURCE。如果它没有被定义,我就只能使用一个不同的结构体去定义TCP头部。使它们工作在FreeBSD或 OpenBSD系统上的更为通用的解决方法如下:#define _BSD_SOURCE 1事先要包含你自己的所有头文件。这将确保正常使用BSD风格的API。如果不想这样做,那你可以改变TCP头结构(点此链接即可,内含注释)。那么所有这些与pcap还有神秘的u_char是怎么关联的呢?看,幸运的是pcap嗅探数据包时正是使用的这些结构。接下来,它简单的创建一个 u_char字符串并且将这些结构体填入。那么我们怎样才能区分它们呢?准备好见证指针最实用的好处之一吧(在此,我可要

31、刺激刺激那些坚持说指针无用的C 程序新手了)。我们再一次假定要对以太网上的TCP/IP包进行处理。同样的手段可以应用于任何数据包,唯一的区别是你实际所使用的结构体的类型。让我们从声明分解u_char包的变量开始:const struct sniff_ethernet *ethernet; /* 以太网帧头部*/const struct sniff_ip *ip; /* IP包头部 */const struct sniff_tcp *tcp; /* TCP包头部 */const char *payload; /* 数据包的有效载荷*/*为了让它的可读性好,我们计算每个结构体中的变量大小*/int

32、 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);paylo

33、ad = (u_char *)(packet + size_ethernet + size_ip + size_tcp);此处如何工作?考虑u_char在内存中的层次。基本的,当pcap将这些结构体填入u_char的时候是将这些数据存入一个字符串中,那个字符串将被送入我们的会调函数中。反向转换是这样的,不考虑这些结构体制中的值,它们的大小将是一致的。例如在我的平台上,一个sniff_ethernet结构体的大小是14字节。一个sniff_ip结构体是20字节,一个sniff_tcp结构体也是20字节。 u_char指针正是包含了内存地址的一个变量,这也是指针的实质,它指向内存的一个区域。简单而

34、言,我们说指针指向的地址为x,如果三个结构体恰好线性排列,第一个(sniff_ethernet)被装载到内存地址的x处则我们很容易的发现其他结构体的地址,让我们以表格显示之:Variable Location (in bytes)sniff_ethernet Xsniff_ip X + 14sniff_tcp X + 14 + 20payload X + 14 + 20 + 20结构体sniff_ethernet正好在x处,紧接着它的sniff_ip则位于x加上它本身占用的空间(此例为14字节),依此类推可得全部地址。注意:你没有假定你的变量也是同样大小是很重要的。你应该总是使用sizeof(

35、)来确保尺寸的正确。这是因为这些结构体中的每个成员在不同平台下可以有不同的尺寸。到现在,我们已经知道了怎样设置回调函数,调用它,弄清被嗅探到的数据包的属性。你可能正期待着写出一个可用的包嗅探器。因为代码的长度关系,我不想列在这篇文章里。你可以点击这里下载并测试它。结束语到此为止,你应该可以写出一个基于pcap的包嗅探器了。你已经学习了基本的概念:打开一个pcap会话,有关它的全体属性,嗅探数据包,使用过滤器,使用回调函数,等等。现在是进行数据包嗅探的时候了。libpcap使用总结!libpcap 是一个开发sniffer的工具包。pcap:packet capture!libpcap的数据类型

36、定义:struct pcap_addr:网卡地址描述pcap_addr * next;sockaddr * addr;sockaddr * netmask;sockaddr *broadaddr;sockaddr *dstaddr;pcap_addr * next;如果非空,指向链表中一个元素的指针;空表示链表中的最后一个元素。sockaddr * addr;指向包含一个地址的sockaddr的结构的指针。sockaddr * netmask;如果非空,指向包含相对于addr指向的地址的一个网络掩码的结构。sockaddr * broadaddr;如果非空,指向包含相对于addr指向的地址的一

37、个广播地址,如果网络不支持广播可能为空。sockaddr * dstaddr;如果非空,指向一个相对于addr指向的源地址的目的地址,如果网络不支持点对点通讯,则为空。struct pcap_file_header bpf_u_int32 magic;u_short version_major;u_short version_minor;bpf_int32 thiszone; /* gmt to local correction */bpf_u_int32 sigfigs; /* accuracy of timestamps */bpf_u_int32 snaplen; /* max leng

38、th saved portion of each pkt */bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */;bpf_u_int32 magic;/*magic是32位数值,代表了存储类型*/u_short version_major;/* Libpcap的主版本号*/u_shart version_minor;/* Libpcap的从版本号*/bpf_u_int32 sigfigs;/* 时间戳描述*/bpf_u_int32 snaplen;/* 保存的每个pkt的分片号的最大值*/bpf_u_int32 linktype;/*

39、 数据链的类型*/细节说明:libpcap dump文件头;libpcap dump文件中的第一个记录包含了一些标志的保存值,这些标志在打印阶段用到。这儿的很多域都是32位的int,所以compilers不用进行转化;这些文件需要具有跨层次的可交换性。无论如何不要改变结构的层次(包括仅仅改变这个结构中域的长度);struct pcap_if /*网卡数据链的一个元素*/struct pcap_if *next;char *name; /* name to hand to "pcap_open_live()" */char *description; /* textual d

40、escription of interface, or NULL */struct pcap_addr *addresses;u_int flags; /* PCAP_IF_ interface flags */;pcap_if *next; 如果非空,指向链的下一个元素。如果为空是链的最后一个元素。char * name; 指向一个字符串,该字符串是传给pcap_open_live()函数的设备名;char * description; 如果非空,指向一个对设备的人性化的描述字符串。pcap_addr *addresses; 指向网卡地址链中的第一个元素。u_int flags; PCAP_

41、IF_ 网卡的标志。现在唯一可用的标识是PCAP_IF_LOOKBACK,它被用来标识网卡是不是lookback网卡。struct pcap_pkthdr /*dump 文件中的数据包头*/struct timeval ts; /* time stamp */bpf_u_int32 caplen; /* length of portion present */bpf_u_int32 len; /* length this packet (off wire) */;timeval ts; 数据报时间戳;bpf_u_int32 caplen; 当前分片的长度;dpf_u_int32 len; 这个

42、数据报的长度;细节描述:在dump文件中的每个数据报都有这样一个报头。它用来处理不同数据报网卡的不同报头问题。struct pcap_stat /*用来保存网卡静态变量的结构*/u_int ps_recv; /* number of packets received */u_int ps_drop; /* number of packets dropped */u_int ps_ifdrop; /* drops by interface XXX not yet supported */;u_int ps_recv; 接受数据报的数目;u_int ps_drop; 被驱动程序丢弃的数据报的数目;

43、u_int ps_ifdrop; 被网卡丢弃的数据报的数目;lipcap的声明:#define PCAP_VERSION_MAJOR 2libpcap dump文件的主版本号;#define PCAP_VERSION_MINOR 4libpcap dump文件的从版本号;#define PCAP_ERRBUF_SIZE 256 用来存放libpcap出错信息的缓冲区的大小;#define PCAP_IF_LOOPBACK 0x00000001 网卡是回环网卡;#define MODE_CAPT 0 抓报模式,在调用pcap_setmode()时使用;#define MODE_STAT 1 静态

44、模式,在调用pcap_setmode()时使用;libpcap的类型定义:typedef int bpf_int32 32bit 的整形;typedef u_int bpf_u_int32 32bit 的无类型整形;typedef pcap pcap_t Descriptor of an open capture instance(一个打开的捕获实例的描述符?)这个结构对用户是不透明的。typedef pcap_dumper pcap_dumper_t libpcap保存文件的描述符。typedef pcap_if pcap_if_t 网卡链表的一个元素;typedef pcap_addr p

45、cap_addr_t 网卡地址的表示;libpcap函数描述:char *pcap_lookupdev(char * errbuf);描述:这个函数用于获取一个合适的网卡描述,以供pcap_open_liver函数和pcap_lookupnet函数使用。如果找不到网卡或者所有网卡为 off,则返回null。如果一个系统中有多个网卡,那么该函数返回找到的第一个on的网卡。最后才是回环接口。回环网卡一直被忽略;参数:char * errbuf 存放pcap_lookupdev函数的出错信息,只有在pcap_lookup失败是才有值。返回值: 如果函数执行成功,则返回一个用于描述系统上的一个网卡的描

46、述符的指针。如果失败,返回null,errbuf中存放出错信息。int pcap_lookupnet(char * device, bpf_u_int32 * netp, bpf_u_int32 * maskp,char * errbuf);描述:该函数用于监测网卡所在网络的网络地址和子网掩码。参数:char *devic:网卡的描述符指针,由pcap_looupdev函数获取;bpf_u_int32 *netp:存放网络地址;bpf_u_int32 *maskp:存放子网掩码;char * errbuf: 存放出错信息;返回值:如果函数执行成功,则返回值为0,否则返回值为-1,并在errbu

47、f中存放出错信息。pcap_t *pcap_open_live(char * device, int snaplen,int promisc, int to_ms, char * ebuf);描述:该函数用于打开网卡用于捕获数据报。单词live的意思就是表示一个运行的网卡(相对于offline而言)被打开了,如同一个保存有被抓数据报的文件被打开一样。在捕获数据报之前这个函数必须被执行。所有的其他的用于处理数据报捕获的函数用到的捕获数据报的描述符由该函数产生。查看 pcap_open_offlin()函数的定义,了解如何打开一个预先保存的包含数据报的文件的细节。参数:char *device:网

48、卡的描述符指针,由pcap_looupdev函数获取;int snaplen:规定捕获的每个数据报的最大字节数;int promisc:1为混杂模式;0为非混杂模式;int to_ms:规定读超时的微秒(milliseconds)数;char *ebuf:存放错误信息,只有在pcap_open_live失败时才被设置;返回值:如果函数成功执行,则返回一个指向数据报捕获的指针;如果错误,返回null,ebuf存放出错信息;int pcap_compile(pcap_t * p, struct bpf_ program *fp, char * str,int optimize, bpf_u_int

49、32 netmask);描述:该函数用于将str指定的规则整合到fp过滤程序中去,并生成过滤程序入口地址,用于过滤选择期望的数据报;参数:pcap_t *p:pcap_open_live返回的数据报捕获的指针;struct bpf_program *fp:指向一个子函数用于过滤,在pcap_compile()函数中被赋值;char *str:该字符串规定过滤规则;int optimize:规定了在结果代码上的选择是否被执行;bpf_u_int32 netmask:该网卡的子网掩码,可以通过pcap_lookupnet()获取;返回值:如果成功执行,返回0,否则返回-1;int pcap_loo

50、p(pcap_t * p, int cnt, pcap_handler callback,u_char * user);描述:该函数用于读取和处理数据报。既可以用来处理事先捕获的保存在文件中的数据报,也可以用来处理实时捕获的数据报;这个函数类似于pcap_dispatch函数,除了它继续读取数据报直至完成cnt个报的处理,或者文件处理完(在offline情况下),或者有错误发生为止。它不会在实时读超时时返回(而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。)注意第三个参数

51、,callback是pcap_handler类型的变量。这是一个用户提供的有着三个参数的子函数。定义为:void user_routine(u_char *user, struct pcap_pkthdr *phrd, u_char *pdata)这三个参数中,user,是传递给pcap_dispatch()的那个参数;phdr,是个pcap_pkthdr类型的指针,是savefile中的数据报的头指针,pdata,指向数据报数据;这个函数允许用户定义子集的数据报过滤程序;参数:pcap_t * pcap_open_live返回的数据报捕获的指针;int cnt:规定了函数返回前应处理的数据报

52、数目;pcap_handler callback:指向一个用户自定义的函数,在处理每个报后自动调用该函数进行再处理;u_char *user:该指针用于传递给callback.(不知道有什么用?)返回值:如果函数成功执行(包括读文件时读到EOF),则返回0.否则返回-1,那么错误信息将由函数pcap_geterr或pcap_perror给出;libpcap使用举例(1)我们曾经提供过<<libnet使用举例(1-12)>>,比较详细地介绍了报文发送编程。始终没有介绍libpcap报文捕捉编程的原因很多,tcpdump、snort等著名软件包都是基于libpcap,加上W

53、.Richard.Stevens的<<Unix Network Programming Vol I>>第26章推波助澜,实在觉得没有必要继续介绍libpcap编程。更真实的原因可能是BPF、DLPI、SOCK_PACKET三种接口编程已经被演练得太多太滥。今天讨论的不是效率,而是可能的移植性要求。没办法,第一次使用libpcap库,举例能深入到什么地步,不知道。如果你也是第一次用这个库,跟我来,第N次使用?那还是忙你的去吧,别来看这篇无聊的灌水,:-P。我无聊是因为有程序要广泛可移植,你无聊是为什么。char * pcap_lookupdev ( char * errb

54、uf );该函数返回一个网络设备接口名,类似libnet_select_device(),对于Linux就是"eth0"一类的名字。pcap_open_live()、pcap_lookupnet()等函数将用到这个网络设备接口名。失败时返回NULL,errbuf包含了失败原因。errbuf一般定义如下:/usr/include/pcap.h#define PCAP_ERRBUF_SIZE 256char errbuf PCAP_ERRBUF_SIZE ;pcap_t * pcap_open_live ( char * device, int snaplen, int pro

55、misc, int to_ms, char * errbuf ); 该函数用于获取一个抽象的包捕捉句柄,后续很多libpcap函数将使用该句柄,类似文件操作函数频繁使用文件句柄。device指定网络接口设备名,比如"eth0。snaplen指定单包最大捕捉字节数,为了保证包捕捉不至于太低效率,snaplen尽量适中,以恰能获得所需协议层数据为准。promisc指定网络接口是否进入混杂模式,注意即使该参数为false(0),网络接口仍然有可能因为其他原因处在混杂模式。to_ms指定毫秒级读超时,man手册上并没有指明什么值意味着永不超时,测试下来的结论,0可能代表永不超时。如果调用失败返回NULL,errbuf包含失败原因。-/usr/include/pcap.htypedef struct pcap pcap_t;pcap-int.h里定义了struct pcap struct pcap int fd; int snapshot; int linktype; int t

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论