WinPcap编程4——捕获数据包(转)

http://www.cnblogs.com/TianFang/archive/2007/11/27/973707.html



winpcap程序中,捕获数据包主要分为如下几步:



  1. 获取网络适配器列表
  2. 打开网络适配器
  3. 捕获数据

获取网络适配器列表在前文中已经讲述,这里就不再累述了。


打开设备的函数是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,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。

打开适配器后,捕获工作就可以用 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_loop()函数可能会遇到障碍,主要因为它直接由数据包捕获驱动所调用。因此,用户程序是不能直接控制它的。另一个实现方法(也是提高可读性的方法),是使用
pcap_next_ex() 函数,从而可以以同步的方式实现数据捕获。


#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;
}
/*
每次捕获到数据包时,会自动调用这个回调函数*/
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);
}



posted @ 2011-05-20 00:06  董雨  阅读(420)  评论(0编辑  收藏  举报