循序渐进学习使用WINPCAP(三)
现在我门已经知道了如何去获得网卡的信息现在就让我们开始真正的工作:打开网卡并捕获数据流。在这一节
里我们将写一个打印流经网络的每个数据包信息的程序。打开网卡的功能是通过pcap_open_live()来实现的它
有三个参数snaplen promisc to_ms。
snaplen用于指定所捕获包的特定部分,在一些系统上(象xBSD and Win32等)驱动只给出所捕获数据包
的一部分而不是全部,这样就减少了拷贝数据的数量从而提高了包捕获的效率。
promisc指明网卡处于混杂模式,在正常情况下网卡只接受去往它的包而去往其他主机的数据包则被忽略
。相反当网卡处于混杂模式时他将接收所有的流经它的数据包:这就意味着在共享介质的情况下我门可以捕获
到其它主机的数据包。大部分的包捕获程序都将混杂模式设为默认,所有我们在下面的例子里也将网卡设为混杂模式。
to_ms 参数指定读数据的超时控制,超时以毫秒计算。当在超时时间内网卡上没有数据到来时对网卡的读
操作将返回(如pcap_dispatch() or pcap_next_ex()等函数)。还有,如果网卡处于统计模式下(请查看“统计和收集网络数据流一节”)to_ms还定义了统计的时间间隔。如果该参数为0那么意味着没有超时控制,对网卡的读操作在没有数据到来是将永远堵塞。如果为-1那么对网卡的读操作将立即返回不管有没有数据可读。
#include "pcap.h"
/* prototype of the 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(&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"); /* Free the device list */ pcap_freealldevs(alldevs); return -1; } /* 找到要选择的网卡结构 */ for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); /* 打开选择的网卡 */ if ( (adhandle= pcap_open_live(d->name, // 设备名称 65536, // portion of the packet to capture. // 65536 grants that the whole packet will be captured on all the
MACs. 1, // 混杂模式 1000, // 读超时为1秒 errbuf // error buffer ) ) == NULL) { fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n"); /* Free the device list */ pcap_freealldevs(alldevs); return -1; } printf("\nlistening on %s...\n", d->description); /* At this point, we don't need any more the device list. Free it */ 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]; /* 将时间戳转变为易读的标准格式*/ ltime=localtime(&header->ts.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()可以不被阻塞,而pcap_loop()在没有数据流到达时将阻塞。在这个简单的例子里用
pcap_loop()就足够了,而在一些复杂的程序里往往用pcap_dispatch()。 Once the adapter is opened, the capture can be started with pcap_dispatch() or pcap_loop(). These
two functions are very similar, the difference is that pcap_ dispatch() is granted to return when
the expires while pcap_loop() doesn't return until cnt packets have been captured, so it can
block for an arbitrary period on a few utilized network. pcap_loop() is enough for the purpose of
this sample, while pcap_dispatch() is normally used in more complex program. 这两个函数都有返回的参数,一个指向某个函数(该函数用来接受数据如该程序中的packet_handler)的指针 ,libpcap调用该函数对每个从网上到来的数据包进行处理和接收数据包。另一个参数是带有时间戳和包长等信
息的头部,最后一个是含有所有协议头部数据报的实际数据。注意MAC的冗余校验码一般不出现,因为当一个桢
到达并被确认后网卡就把它删除了,同样需要注意的是大多数网卡会丢掉冗余码出错的数据包,所以WinPcap一
般不能够捕获这些出错的数据报。
刚才的例子里从pcap_pkthdr中提取出了每个数据报的时间戳和长度并在显示器上打印出了他们。 |