首页 > 代码库 > winPcap_5_打开适配器并捕获数据包

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

 

  知道如何获取适配器的信息了,那我们就开始一项更具意义的工作,打开适配器并捕获数据包。编写一个程序,将每一个通过适配器的数据包打印出来。

 

打开设备的函数是 pcap_open()。

(Open a generic source in order to capture / send (WinPcap only) traffic.)

pcap_t* pcap_open  ( const char *  source,                     int  snaplen,                     int  flags,                     int  read_timeout,                     struct pcap_rmtauth *  auth,                     char *  errbuf                   )

 

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

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

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

Returns:
A pointer to a ‘pcap_t‘ which can be used as a parameter to the following calls (pcap_compile() and so on) and that specifies an opened WinPcap session. In case of problems, it returns NULL and the ‘errbuf‘ variable keeps the error message.
Warning:
The source cannot be larger than PCAP_BUF_SIZE.

The following formats are not allowed as ‘source‘ strings:

  • rpcap:// [to open the first local adapter]
  • rpcap://hostname/ [to open the first remote adapter] 

 

 

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

 

Collect a group of packets. 

 

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

 

Collect a group of packets.

 

pcap_dispatch()与pcap_loop()的区别:

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

 

struct  pcap_pkthdr{    timeval    ts    //time stamp    bpf_u_int32    caplen    //length of portion present    bpf_u_int32    len    //length this packet (off wire)};

Detailed Description

Header of a packet in the dump file.

Each packet in the dump file is prepended with this generic header. This gets around the problem of different headers for different packet interfaces. 

 

 1 #include "pcap.h" 2 #pragma comment(lib, "wpcap.lib") 3 #pragma comment(lib, "Packet.lib") 4 #pragma comment(lib, "wsock32.lib") 5  6  7 #include "pcap.h" 8  9 /* packet handler 函数原型 */10 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);11 12 main()13 {14     pcap_if_t *alldevs;15     pcap_if_t *d;16     int inum;17     int i=0;18     pcap_t *adhandle;19     char errbuf[PCAP_ERRBUF_SIZE];20     21     /* 获取本机设备列表 */22     if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)23     {24         fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);25         exit(1);26     }27     28     /* 打印列表 */29     for(d=alldevs; d; d=d->next)30     {31         printf("%d. %s", ++i, d->name);32         if (d->description)33             printf(" (%s)\n", d->description);34         else35             printf(" (No description available)\n");36     }37     38     if(i==0)39     {40         printf("\nNo interfaces found! Make sure WinPcap is installed.\n");41         return -1;42     }43     44     printf("Enter the interface number (1-%d):",i);45     scanf("%d", &inum);46     47     if(inum < 1 || inum > i)48     {49         printf("\nInterface number out of range.\n");50         /* 释放设备列表 */51         pcap_freealldevs(alldevs);52         return -1;53     }54     55     /* 跳转到选中的适配器 */56     for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);57     58     /* 打开设备 */59     if ( (adhandle= pcap_open(d->name,          // 设备名60         65536,            // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容61         PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式62         1000,             // 读取超时时间63         NULL,             // 远程机器验证64         errbuf            // 错误缓冲池65         ) ) == NULL)66     {67         fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);68         /* 释放设备列表 */69         pcap_freealldevs(alldevs);70         return -1;71     }72     73     printf("\nlistening on %s...\n", d->description);74     75     /* 释放设备列表 */76     pcap_freealldevs(alldevs);77     78     /* 开始捕获 */79     pcap_loop(adhandle, 0, packet_handler, NULL);80     81     return 0;82 }83 84 85 /* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */86 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)87 {88     struct tm *ltime;89     char timestr[16];90     time_t local_tv_sec;91     92     /* 将时间戳转换成可识别的格式 */93     local_tv_sec = header->ts.tv_sec;94     ltime=localtime(&local_tv_sec);95     strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);96     97     printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len); 98 }
打开适配器并捕获数据包.c

  *结果:

时间戳(精确到微妙),包长度;

 

timeval

The timeval structure is used to specify time values. It is associated with the Berkeley Software Distribution (BSD) file Time.h.

struct timeval {  long    tv_sec;         // seconds   long    tv_usec;        // and microseconds };

Members

tv_sec
Time value, in seconds.
tv_usec
Time value, in microseconds. 

 

localtime

Converts a time value and corrects for the local time zone.

struct tm *localtime( const time_t *timer );

 

 

struct tm {        int tm_sec;     /* seconds after the minute - [0,59] */        int tm_min;     /* minutes after the hour - [0,59] */        int tm_hour;    /* hours since midnight - [0,23] */        int tm_mday;    /* day of the month - [1,31] */        int tm_mon;     /* months since January - [0,11] */        int tm_year;    /* years since 1900 */        int tm_wday;    /* days since Sunday - [0,6] */        int tm_yday;    /* days since January 1 - [0,365] */        int tm_isdst;   /* daylight savings time flag */        };

 

 

 

asctime, _wasctime

Converts a tm time structure to a character string.

char *asctime( const struct tm *timeptr );wchar_t *_wasctime( const struct tm *timeptr );

 

Return Value

asctime returns a pointer to the character string result; _wasctime returns a pointer to the wide-character string result. There is no error return value.

 

 

time

Gets the system time.

time_t time( time_t *timer );

 

 

 

strftime, wcsftime

Format a time string.

size_t strftime( char *strDest, size_t maxsize, const char *format, const struct tm *timeptr );size_t wcsftime( wchar_t *strDest, size_t maxsize, const wchar_t *format, const struct tm *timeptr );

 

Return Value

strftime returns the number of characters placed in strDest if the total number of resulting characters, including the terminating null, is not more than maxsize.

wcsftime returns the corresponding number of wide characters. Otherwise, the functions return 0, and the contents of strDest is indeterminate.

 

 

void pcap_freealldevs  ( pcap_if_t *  alldevsp   )  

Free an interface list returned by pcap_findalldevs().  

 

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