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

 

打开设备的函数是 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         else
35             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

相关文章:

  • 2021-06-16
  • 2022-12-23
  • 2021-04-24
  • 2021-12-27
  • 2021-11-20
  • 2021-11-21
  • 2021-07-11
  • 2021-12-08
猜你喜欢
  • 2021-11-03
  • 2022-12-23
  • 2021-11-15
  • 2022-02-07
  • 2021-05-27
  • 2021-05-15
  • 2022-01-02
相关资源
相似解决方案