Winpcap打开适配器并捕获数据包

来源:互联网 发布:国内互联网网络股票 编辑:程序博客网 时间:2024/06/08 08:42

打开设备的函数是pcap_open()。下面是参数snaplen,flagsto_ms的解释说明

snaplen:制定要捕获数据包中的哪些部分。在一些操作系统中 (比如 xBSD Win32)驱动可以被配置成只捕获数据包的初始化部分:这样可以减少应用程序间复制数据的量,从而提高捕获效率。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。

flags:最最重要的flag是用来指示适配器是否要被设置成混杂模式。一般情况下,适配器只接收发给它自己的数据包,而那些在其他机器之间通讯的数据包,将会被丢弃。相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。这意味着在一个共享媒介(比如总线型以太网)WinPcap能捕获其他主机的所有的数据包。大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,我们也会在下面的范例中,使用混杂模式。

to_ms指定读取数据的超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用pcap_dispatch() pcap_next_ex()) 都会在to_ms毫秒时间内响应,即使在网络上没有可用的数据包。在统计模式下,to_ms还可以用来定义统计的时间间隔。to_ms设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。如果设置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回

 

#include "pcap.h"/* packet handler 函数原型 */void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);main(){pcap_if_t *alldevs;pcap_if_t *d;int inum;int i=0;pcap_t *adhandle;char errbuf[PCAP_ERRBUF_SIZE];        /* 获取本机设备列表 */    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)    {        fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);        exit(1);    }        /* 打印列表 */    for(d=alldevs; d; d=d->next)    {        printf("%d. %s", ++i, d->name);        if (d->description)            printf(" (%s)\n", d->description);        else            printf(" (No description available)\n");    }        if(i==0)    {        printf("\nNo interfaces found! Make sure WinPcap is installed.\n");        return -1;    }        printf("Enter the interface number (1-%d):",i);    scanf("%d", &inum);        if(inum < 1 || inum > i)    {        printf("\nInterface number out of range.\n");        /* 释放设备列表 */        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,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);        /* 释放设备列表 */        pcap_freealldevs(alldevs);        return -1;    }        printf("\nlistening on %s...\n", d->description);        /* 释放设备列表 */    pcap_freealldevs(alldevs);     /* 开始捕获 */    pcap_loop(adhandle, 0, packet_handler, NULL);        return 0;}/* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data){    struct tm *ltime;    char timestr[16];    time_t local_tv_sec;        /* 将时间戳转换成可识别的格式 */    local_tv_sec = header->ts.tv_sec;    ltime=localtime(&local_tv_sec);    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);        printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);    }

当适配器被打开,捕获工作就可以用pcap_dispatch() pcap_loop()进行。这两个函数非常的相似,区别就是 pcap_ dispatch()当超时时间到了(timeout expires)就返回 (尽管不能保证),而pcap_loop() 不会因此而返回,只有当cnt 数据包被捕获,所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_loop()对于我们这个简单的范例来说,可以满足需求,不过,pcap_dispatch() 函数一般用于比较复杂的程序中。

这两个函数都有一个回调参数,packet_handler指向一个可以接收数据包的函数。这个函数会在收到每个新的数据包并收到一个通用状态时被libpcap所调用 ( 与函数pcap_loop() pcap_dispatch() 中的user 参数相似),数据包的首部一般有一些诸如时间戳,数据包长度的信息,还有包含了协议首部的实际数据。注意:冗余校验码CRC不再支持,因为帧到达适配器,并经过校验确认以后,适配器就会将CRC删除,与此同时,大部分适配器会直接丢弃CRC错误的数据包,所以,WinPcap没法捕获到它们。

上面的程序将每一个数据包的时间戳和长度从pcap_pkthdr的首部解析出来,并打印在屏幕上。

请注意,使用 pcap_loop() 函数可能会遇到障碍,主要因为它直接由数据包捕获驱动所调用。因此,用户程序是不能直接控制它的
原创粉丝点击