分享

wimpcap基础知识 good

 昵称7127209 2011-06-12

WinPcap基础知识(第一课:获得设备列表) 

 

 

一个基本的WinPcap应用程序所需的第一件事情是获得合适的网络适配器。Libpcap/ Winpcap提供 pcap_findalldevs() 函数完成这个功能:这个函数返回一个相连的pcap_if结构的列表。列表的每一项包含关于适配器的复杂的信息。特别的,name和description域数据包含设备的名称和可读的描述。如下的代码提取设备列表,然后打印到屏幕上。如果没有发现适配器,则显示一个错误。

 

 

 

 

view plaincopy to clipboardprint?

#include <pcap.h> 

 

void main() 

    pcap_if_t *alldevs; 

    pcap_if_t *d; 

    int i=0; 

    char errbuf[PCAP_ERRBUF_SIZE]; 

     

     

    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL , &alldevs, errbuf) == -1) 

   

        fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf); 

        exit(1); 

   

     

     

    for(d= alldevs; d != NULL; 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; 

   

 

     

    pcap_freealldevs(alldevs); 

 

在第一行 #include <pcap.h>的前面加上#define HAVE_REMOTE,或者你在项目属性里面添加“C语言预处理程序定义”一栏里面加上HAVE_REMOTE(注意用‘,’隔开)。加上HAVE_REMOTE,因为在pcap.h头文件里面对此进行判断,如果定义了符号HAVE_REMOTE,pcap.h头文件会自动包含<remote-ext.h>文件,而该文件中申明了函数pcap_findalldevs_ex()以及宏定义PCAP_SRC_IF_STRING。  另外注意一下,在vs2003下面编译的时候,要把项目属性里面的预处理器定义(宏定义)要加上_MBCS的定义,默认的创建VC++控制台应用程序里面是没有该符号的定义的,如果没有该符号定义,编译上面的程序会提示出错。 

 

 

代码的解释如下(部分):

 

 

 

 

首先,象其他libpcap函数一样, pcap_findalldevs()具有一个errbuf参数。该参数指向一个由libpcap填充的字符串。当有错误发生时,错误的描述被写入到这个字符串。

     其次,记住不是libpcap支持的所有的操作系统提供网络适配器的描述,因此如果我们想要编写一个可移植的应用程序,我们必须考虑description为null的情况。在这种情况下我们打印"No description available" 这个字符串。

     最后注意当我们使用列表完成后我们用pcap_freealldevs() 函数释放了它。

     试着编译和运行第一个例子。为了在Unix或者Cygwin下编译它,输入:

gcc -o testaprog testprog.c -lpcap

在Windows下,你需要建立一个project。按照"Using WinPcap in your programs " 的介绍。不过,我建议你使用WinPcap 开发包(在http://winpcap. 提供下载)。开发包提供很多正确设置的示例程序,并且包含本指南所有代码和编译示例所需的项目、包含文件和库。

编译完成,在我的WinXP工作站上运行程序,结果是

1. {4E273621-5161-46C8-895A-48D0E52A0B83} (Realtek RTL8029(AS) Ethernet Adapter)

2. {5D24AE04-C486-4A96-83FB-8B5EC6C7F430} (3Com EtherLink PCI)

现在你可以看到,Windows下网络适配器的名称(当打开设备时会传递给libpcap)基本上不知所云,也就是说可读性不好,所以旁边的描述对读者将十分有用。

 

 

 

 

 

 

 

本课用到的数据结构简介:

 

 

 

 

①pcap_if 结构:

 

 

 

 

⑴typedef struct pcap_if pcap_if_t  

 

   pcap_if在incs/pcap.h文件的72行有定义,在程序里面用此结构,要#include <pcap.h>

 

⑵pcap_if结构里面的数据域:

 

 

 

 

  pcap_if *  next :如果非空,是指向下一个元素的指针;如果是NULL,表示是最后一个元素了。

 

  char *  name  :一个指向字符串的指针,该字符串表示一个设备的名字,作为一个参数传递给pcap_open_live()

 

 

   char *description;   textual description of interface, or NULL

 

 

 

  pcap_addr * addresses :指向接口的地址列表的第一个元素

 

  u_int flags  : PCAP_IF_INTERFACE 标志。目前唯一可以的标志是PCAP_IF_LOOPBACK,当接口是回送接口(loopback)

 

 

 

 

 

 

②pcap_addr结构:代表一个接口地址,在<pcap.h>中定义

 

 

 

 

数据域:

 

 

 

 

 

 

 

pcap_addr * 

 

 

next

 

 

 

 

 

 

 

if not NULL, a pointer to the next element in the list; NULL for the last element of the list

 

 

 

 

sockaddr * 

 

 

addr

 

 

 

 

 

 

 

a pointer to a struct sockaddr containing an address

 

 

 

 

sockaddr * 

 

 

netmask

 

 

 

 

 

 

 

if not NULL, a pointer to a struct sockaddr that contains the netmask corresponding to the address pointed to by addr.

 

 

 

 

sockaddr * 

 

 

broadaddr

 

 

 

 

 

 

 

if not NULL, a pointer to a struct sockaddr that contains the broadcast address corre- sponding to the address pointed to by addr; may be null if the interface doesn't support broadcasts

 

 

 

 

sockaddr * 

 

 

dstaddr

 

 

 

 

 

 

 

if not NULL, a pointer to a struct sockaddr that contains the destination address corre- sponding to the address pointed to by addr; may be null if the interface isn't a point- to-point interface

 

 

 

 

⑶函数说明

 

 

 

 

int pcap_findalldevs_ex  char *  source,    struct pcap_rmtauth *  auth, 

 

  pcap_if_t **  alldevs,    char *  errbuf  )

 

pcap_findalldevs_ex函数获得网络设备的一个列表,并且这个列表可以被pcap_open()打开。

 

 

 

 

      Pcap_findalldevs_ex()是pcap_findalldevs()的超集,后者是以前的老函数,它只能列出在本地机器的设备。相反,pcap_findalldevs_ex可以列出远程及其上面的设备。除此之外,它还可以列出某个给定的具体目录下面的所有的pcap文件的列表。而且,pcap_findalldevs_ex()还是平台独立的,因为它依靠标准的pcap_findalldevs()来在本地机器上获得地址。和pcap_findalldevs()不同的是,该函数获得接口名称(alldevs->name )已经可以直接作为参数传递给pcap_open()进行调用。但是pcap_findalldevs_ex()不能,它得到的结果必须要先用

 

 

 

 

Pcap_createsrcstr()进行格式化,然后把source 标识符传递给pcap_open()。

 

 

 

 

      如果该函数必须列出远程机器上的接口,它打开一个新的控制连接来连接远程机器,然后获得接口,完成后就释放连接。但是,如果该函数检测到远程机器是处于非活动状态,这个连接不会被释放。

 

 

 

 

   

 

参数说明:

 

 

 

 

Source :该变量告诉函数在哪儿去查找,它和pcap_open()使用通用的语法。它是一个字符串,用来存放源位置(source location),例如:source 可以是”rpcap://”,表示本地适配器;也可以是”rpcap://host:port”,表示远程主机上面的适配器;还可以是pcap文件,例如:source可以是”rpcap://c:/myfolder/”。

 

 

 

 

auth:指向pcap_rmtauth结构的指针,用来保存连接到远程主机上授权信息。在查询本地机器时,此参数没什么意义,可以为NULL。

 

 

 

 

alldevs: 指向pcap_if_t结构的指针,该指针不需要初始化,它会在函数的调用过程中进行初始化。此函数返回时,该指针被设置为所获得的设备接口列表的第一个元素,列表的每一个元素都是

 

 

 

 

Pcap_if_t结构。

 

 

 

 

errbuf :指向用户分配的缓冲区(大小为PCAP_ERRBUF_SIZE),该buf用来存放出错信息。

 

 

 

 

返回值:

 

 

 

 

成功返回0,alldevs返回设备列表,alldevs不会为NULL。否则返回-1,那就是说系统没有任何接口可以列举的。

 

 

 

 

出错的消息在errbuf里面返回,错误可能由下面的原因造成的:

 

 

 

 

①    libpcap/winpcap在本地/远程主机上没有安装。

 

 

 

 

②    用户没有足够的权限来列举设备/文件

 

 

 

 

③    网络问题

 

 

 

 

④    其它的错误(比如没有足够的内存或者其它原因)

 

 

 

 

注意的问题:

 

 

 

 

      接口列表一定要手动释放,通过调用pcap_freealldevs()函数。

 

 

 

 

 

 

Source参数的语法:

 

 

 

 

⑴两个宏定义:

 

 

 

 

   #define  PCAP_SRC_FILE_STRING  “ file://”

 

   #define  PCAP_SRC_IF_STRING  “rpcap://”

 

   此两个宏在remote-ext.h里面定义。

 

 

 

 

(2)详细描述

 

 

 

 

下面列举出了能够被pcap_open()函数打开的格式:

 

 

 

 

file://path_and_filename [打开一个本地文件]

 

rpcap://devicename [打开本地机器上面的可以打开的设备,不使用rpcap协议]

 

rpcap://host/devicename [打开远程机子上可以打开的设备]

 

rpcap://host:port/devicename [打开远程机器上面选择的设备,用一个非标准端口作为rpcap]

 

adaptername [打开一个本地适配器,kept for compability,不推荐]

 

(NULL) [打开第一个本地适配器,kept for compability,不推荐]

 

 

 

Pcap_findalldevs_ex()允许的格式如下:

 

 

 

 

file://folder/ [列出指定目录的所有文件]

 

rpcap:// [列出本地的适配器]

 

rpcap://host:port/ [列出远程机器上的可以列出的设备]

 

关于host和port参数,可以为数字或者字母。由于支持IPV6,它们可以是下面的格式:

 

 

 

 

·         host (literal): e.g. host.foo.bar

?host (numeric IPv4): e.g. 10.11.12.13

?host (numeric IPv4, IPv6 style): e.g. [10.11.12.13]

?host (numeric IPv6): e.g. [1:2:3::4]  

 ?port: can be either numeric (e.g. '80') or literal (e.g. 'http')

 

这里是一些例子:

 

rpcap://host.foo.bar/devicename [everything literal, no port number]

?rpcap://host.foo.bar:1234/devicename [everything literal, with port number]

?rpcap://10.11.12.13/devicename [IPv4 numeric, no port number]

?rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number]

?rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number]

 ?rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number]

?rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number]

?rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number]

 

 

 

转自:http://wotseb./1418997.html

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/19/4457794.aspx

 

 

WinPcap基础知识(第二课:获得已安装设备的高级信息) 

 

 

第一课 (Obtaining the device list ) 展示了如何获得有用适配器的基本信息(如设备名称和描述)。事实上,WinPcap也提供另外的高级信息。特别的,每个pcap_findalldevs() 返回的 pcap_if 结构也包含了一个pcap_addr结构的列表:

?该接口的地址列表a list of addresses for that interface.

?网络掩码的列表a list of netmasks (each of which corresponds to an entry in the addresses list).

 ?广播地址的列表a list of broadcast addresses (each of which corresponds to an entry in the addresses list).

 ?目标地址的列表a list of destination addresses (each of which corresponds to an entry in the addresses list).

 

如下的例子提供一个ifpriont()函数,来打印pcap_if结构的完整内容。它被pcap_findalldevs()返回的每个条目调用。

 

 

 

 

 

 

 

view plaincopy to clipboardprint?

 

 

#include <pcap.h> 

 

#ifndef WIN32 

    #include <sys/socket.h> 

    #include <netinet/in.h> 

#else 

    #include <winsock.h> 

#endif 

 

#pragma comment(lib,"ws2_32.lib") 

 

 

// Function prototypes 

void ifprint(pcap_if_t *d); 

char *iptos(u_long in); 

char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen); 

 

 

int main() 

  pcap_if_t *alldevs; 

  pcap_if_t *d; 

  char errbuf[PCAP_ERRBUF_SIZE+1]; 

  char source[PCAP_ERRBUF_SIZE+1]; 

 

  printf("Enter the device you want to list:\n" 

            "rpcap://              ==> lists interfaces in the local machine\n" 

            "rpcap://hostname:port ==> lists interfaces in a remote machine\n" 

                                    (rpcapd daemon must be up and running\n" 

                                     and it must accept 'null' authentication)\n" 

            "file://foldername     ==> lists all pcap files in the give folder\n\n" 

            "Enter your choice: "); 

 

  fgets(source, PCAP_ERRBUF_SIZE, stdin); 

  source[PCAP_ERRBUF_SIZE] = '\0'; 

 

   

  if (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1) 

 

    fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf); 

    exit(1); 

 

 

   

  for(d=alldevs;d;d=d->next) 

 

    ifprint(d); 

 

 

  pcap_freealldevs(alldevs); 

 

  return 1; 

 

 

void ifprint(pcap_if_t *d) 

  pcap_addr_t *a; 

  char ip6str[128]; 

 

   

  printf("%s\n",d->name); 

 

   

  if (d->description) 

    printf("\tDescription: %s\n",d->description); 

 

   

  printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no"); 

 

   

  for(a=d->addresses;a;a=a->next) { 

    printf("\tAddress Family: #%d\n",a->addr->sa_family); 

   

    switch(a->addr->sa_family) 

   

      case AF_INET: 

        printf("\tAddress Family Name: AF_INET\n"); 

        if (a->addr) 

          printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr)); 

        if (a->netmask) 

          printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr)); 

        if (a->broadaddr) 

          printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr)); 

        if (a->dstaddr) 

          printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr)); 

        break; 

 

      case AF_INET6: 

        printf("\tAddress Family Name: AF_INET6\n"); 

        if (a->addr) 

          printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str))); 

       break; 

 

      default: 

        printf("\tAddress Family Name: Unknown\n"); 

        break; 

   

 

  printf("\n"); 

 

 

#define IPTOSBUFFERS    12 

char *iptos(u_long in) 

    static char output[IPTOSBUFFERS][3*4+3+1]; 

    static short which; 

    u_char *p; 

 

    p = (u_char *)&in; 

    which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1); 

    sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 

    return output[which]; 

 

char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen) 

    socklen_t sockaddrlen; 

 

    #ifdef WIN32 

    sockaddrlen = sizeof(struct sockaddr_in6); 

    #else 

    sockaddrlen = sizeof(struct sockaddr_storage); 

    #endif 

 

 

    if(getnameinfo(sockaddr,  

        sockaddrlen,  

        address,  

        addrlen,  

        NULL,  

        0,  

        NI_NUMERICHOST) != 0) address = NULL; 

 

    return address; 

 

 

 

 

 

 

 

 说明:该成员用到getnameinfo()和里面的NI_NUMERICHOST 宏,这些在"WS2tcpip.h" 头文件里面有定义,所以要包含这个头文件(官方网站上面下载的文档里面的pack里面是没有包含这个头文件的,那样编译会出错(vs6)。但是在vs7里面编译没有任何问题。估计是在其它文件里面已经把该头文件包含进去了。

 

 

 

此程序的缺点很明显:显示本机没有问题,但是显示远程主机就一般都不行,它要求双方都要装winpcap包并且对方在后台要运行rpcap,并且接收参数NULL授权参数。笔者试了一下,几乎不能获得远程主机的接口列表。

 

 

 

转自:http://wotseb./1419033.html

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4458008.aspx

 

 

 

 

WinPcap基础知识(第三课:打开一个适配器捕捉数据包)

 

现在我们已经知道了怎样去获取一个适配器并使用它,让我们开始真正的工作-----开始抓取网络数据包吧。在这一课中我们将写一个程序,这个程序将在我们选择的适配器上监听,并抓取通过这个适配器上的每一个数据包,打印其中的一些信息。

 我们主要使用的函数是pcap_open(),这个函数的功能是打开一个抓取设备。在这里有必要对其中的几个参数snaplen, flags and to_ms作一下说明。

 snaplen指定了我们所要抓取的包的长度(译者:也就是我们想抓多长就设置多长)。在一些操作系统(如xBSD和Win32)中,底层驱动可以通过配置只抓取数据包的开始部分:这样就减少了拷贝给应用程序的数据量,因此可以提高抓取效率。在这个例子里我们使用65536这个比我们所能遇到的最大的MTU还大的数字。这样我们就能确保我们的程序可以抓到整个数据包。

 flags:最重要的标志是一个指示适配器是否工作在混杂模式下的。在正常状况下,一个适配器仅仅抓取网络中目的地是它自己的数据包;因此其他主机交换的数据包都被忽略。相反,当适配器处在混杂模式下的时候它就会抓取所有的数据包而不管是不是发给它的。这就意味着在共享媒体(如非交换的以太网)上,WinPcap将能够抓取其他主机的数据包。混杂模式是大部分抓取程序的默认模式,所以在下面的例子中我们就开启它。

 to_ms以豪秒为单位指定了读取操作的超时界限。在适配器上一个读取操作(比如,pcap_dispatch() 或者 pcap_next_ex())将总是在to_ms豪秒后返回,即使网络中没有数据包可供抓取。如果适配器工作在统计模式(如果对此不了解,请看课程9),to_ms还定义了统计报告之间的间隔。把tm_ms设置为0意味着没有时间限制,如果没有数据包到达适配器,读取操作将永远不会返回。反过来,把tm_ms设置为-1将使读取操作总是立即返回。

 

 

 

 

 

view plaincopy to clipboardprint?

#define HAVE_REMOTE 

#include <pcap.h> 

 

#pragma comment(lib,"wpcap.lib") 

 

 

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,          // name of the device 

                              65536,            // portion of the packet to capture 

                                                // 65536 guarantees that the whole packet will be captured on all the link layers 

                              PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode 

                              1000,             // read timeout 

                              NULL,             // authentication on the remote machine 

                              errbuf            // error buffer 

                              ) ) == 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]; 

     

     

    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_open()函数里面的3个重要参数:snaplen设置为65536保证每次都把整个包抓获;flags设置为混杂模式以便抓获所有的数据包。关于第三个参数to_ms,我运行的环境是WindowsXP,设置这个值对程序指向结果没有影响。

 

疑问:会不会是to_ms参数不被Wins平台支持?

 

一旦打开了适配器,就由pcap_dispatch() 或者pcap_loop()开始抓取。这两个函数都非常慢,所不同的是pcap_ dispatch()一旦超时就可以返回(尽管不能保证)而pcap_loop() 会一直等到cnt包被抓到才会返回,所以这个函数在没有数据包的网络中会阻塞任意的时间。在这个例子中pcap_loop()就足够了,而pcap_dispatch() 则可以在一个更复杂的程序中使用。

 这两个函数都有一个回调函数作为参数,packet_handler,这个参数指定的函数将收到数据包。这个函数在每一个新的数据包从网络中到达时都会被libpcap调用,并且会收到一个反映pcap_loop() 和 pcap_dispatch()函数的生成状态,和一个结构体header。这个header中带有一些数据包中的信息,比如时间戳和长度、包括所有协议头的实际数据包。注意结构体中是没有CRC的,因为在数据确认后已经被适配器去掉了。也要注意大部分适配器丢弃了CRC错误的数据包,因此Winpcap不能抓取这些包。

 上面的例子从pcap_pkthdr中提取每个数据包的时间戳和长度并显示它们。

 请注意使用pcap_loop()可能有一个缺点,就是使用这个函数时包处理函数要被包抓取驱动程序来调用;因此应用程序不能直接控制它。另一种方法(并且更容易理解)是使用函数pcap_next_ex(),这个函数我们将在下一个例子中使用。

 

 

 

转自:http://wotseb./1419050.html

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4458204.aspx

 

 

 

 

WinPcap基础知识(第四课:不用回调函数来捕捉数据包) 

 

 

这节课程中的例子程序完成的功能和上节课的一样,但是使用的是pcap_next_ex()而不是pcap_loop().

 基于回调捕获机制的 pcap_loop()是非常优雅的,在很多情况下都是一个不错的选择。不过,有时候处理一个回调函数显得不太现实 --- 通常这会使程序更加复杂,在使用多线程或者c++类的时候尤其如此。

 在这种情况下,可以直接调用 pcap_next_ex() 来返回一个数据包 -- 这样程序员可以在仅仅想使用它们的时候再处理 pcap_next_ex() 返回的数据包。

 这个函数的参数和回调函数 pcap_loop() 的一样 -- 由一个网络适配器描述符作为入口参数和两个指针作为出口参数,这两个指针将在函数中被初始化,然后再返回给用户(一个指向pcap_pkthdr 结构,另一个指向一个用作数据缓冲区的内存区域)。

 在下面的程序中,我们继续使用上一节课中的例子的数据处理部分的代码,把这些代码拷贝到main()函数中pcap_next_ex()的后面。

 

 

 

 

view plaincopy to clipboardprint?

#define HAVE_REMOTE 

#include <pcap.h> 

 

#pragma comment(lib,"wpcap.lib") 

 

main() 

pcap_if_t *alldevs; 

pcap_if_t *d; 

int inum; 

int i=0; 

pcap_t *adhandle; 

int res; 

char errbuf[PCAP_ERRBUF_SIZE]; 

struct tm *ltime; 

char timestr[16]; 

struct pcap_pkthdr *header; 

u_char *pkt_data; 

     

     

     

    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,          // name of the device 

                              65536,            // portion of the packet to capture.  

                                                // 65536 guarantees that the whole packet will be captured on all the link layers 

                              PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode 

                              1000,             // read timeout 

                              NULL,             // authentication on the remote machine 

                              errbuf            // error buffer 

                              ) ) == 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); 

     

     

    while((res = pcap_next_ex( adhandle, &header, (const u_char**)&pkt_data)) >= 0){ 

         

        if(res == 0) 

             

            continue; 

         

         

        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); 

   

     

    if(res == -1){ 

        printf("Error reading the packets: %s\n", pcap_geterr(adhandle)); 

        return -1; 

   

     

    return 0; 

 

 

疑问:没有完全体现出好控制的特征

 

感觉也只是点点,可以设置一个变量,来控制是否接收下一个数据包,这点还是可以做到得,不过这里是因为数据包太多了,一般不会出现问题。

 

关于pcap_next_ex()方法,文档里面没有介绍是阻塞得还是非阻塞,个人认为是阻塞得,也就是说从开始执行,一直倒接收倒一个包才返回。尽管这里是阻塞得,但是设置一个变量每次检查一下,看看是否是用户要求关闭接收,这个操作还是没什么问题。不会出现虽然用户设置了停止阻塞得标志位,但是由于此方法是阻塞的,因为等不到数据包而一直没有响应用户,因为这里有一个事实大家应该明白,网上发送的数据包时时刻刻都在。

 

 

 

注意:之所以要使用pcap_next_ex()而不使用pcap_next()是因为pcap_next()有些非常讨厌的限制。首先,他不是很有效的方法,因为它隐藏了回调方法,但是还是依靠pcap_dispatch()。第二,它不能探测出EOF,所以在合并文件时它几乎没用。

 

     另外,pcap_next_ex()方法的返回值可以给用户很多的提示,对应的所有可能情况。

 

转自:http://wotseb./1419069.html

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4458383.aspx

 

 

 

WinPcap基础知识(第五课:过滤信息) 

 

 

WinPcap提供的最强大的特性之一就是过滤引擎。它是被集成到了winpcap的捕获机制中的,提供了一种非常高效的方法来获取部分网络数据。被用来过滤数据包的函数是 pcap_compile() 和 pcap_setfilter()。

 pcap_compile() 接受一个包含布尔表达式的字符串,生成可以被捕获包驱动中的过滤引擎解释的代码。布尔表达式的语法在这个文档的Filtering expression syntax 那一节(译者注:其实和tcpdump的一样,如果了解tcpdump,可以直接按照tcpdump的语法来写)。

 pcap_setfilter() 绑定一个过滤器到一个在核心驱动中的捕获进程中。一旦 pcap_setfilter() 被调用,这个过滤器就会对网络来的所有数据包进行过滤,所有符合条件的数据包(按照布尔表达式来计算出结果是真的数据包)都会被拷贝给进行捕获的应用程序。

 下面的代码说明了怎样编译和设置一个过滤器。注意我们必须得到说明适配器的 pcap_if 结构中的子网掩码,因为一些被 pcap_compile() 生成的过滤器需要它。这个过滤器中传递给 pcap_compile() 的字符串是 "ip and tcp",意思是“仅仅把IPv4 and TCP 数据包保存下来并交付给应用程序”。

 

 

 

 

view plaincopy to clipboardprint?

if (d->addresses != NULL) 

         

        netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; 

    else 

         

        netmask=0xffffff;  

 

 

    //compile the filter 

    if (pcap_compile(adhandle, &fcode, "ip and tcp", 1, netmask) < 0) 

   

        fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n"); 

         

        pcap_freealldevs(alldevs); 

        return -1; 

   

     

    //set the filter 

    if (pcap_setfilter(adhandle, &fcode) < 0) 

   

        fprintf(stderr,"\nError setting the filter.\n"); 

         

        pcap_freealldevs(alldevs); 

        return -1; 

   

 

 

如果你想看一些在这节课中讲述的使用过滤功能的代码,请看下节课中的例子,Interpreting the packets.

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4458607.aspx

 

 

 

 

WinPcap基础知识(第六课:翻译数据包) 

 

 

现在我们已经能够捕获并且过滤网络数据包,下面我们就把我们的知识运用到一个简单的“真实的”应用程序中去。

 在这节课中我们将从前面的课程中拷贝代码并用它们来构造出一个更有用途的程序。这个程序主要的目的就是说明怎样分析和解释我们已经捕获的数据包的协议结构。最终的应用程序,叫做UDPdump,会打印出一个在我们的网络中的UDP数据包的概要。

 在开始阶段我们选择分析并显示UDP协议,因为UDP协议比其他的协议比如TCP协议更容易理解,从而非常适合作为初始阶段的例子。还是让我们开始看代码吧:

 

 

 

 

view plaincopy to clipboardprint?

#define HAVE_REMOTE 

#include <pcap.h> 

 

#pragma comment(lib,"wpcap.lib") 

#pragma comment(lib,"Ws2_32.lib") 

 

 

typedef struct ip_address{ 

    u_char byte1; 

    u_char byte2; 

    u_char byte3; 

    u_char byte4; 

}ip_address; 

 

 

typedef struct ip_header{ 

    u_char  ver_ihl;        // Version (4 bits) + Internet header length (4 bits) 

    u_char  tos;            // Type of service  

    u_short tlen;           // Total length  

    u_short identification; // Identification 

    u_short flags_fo;       // Flags (3 bits) + Fragment offset (13 bits) 

    u_char  ttl;            // Time to live 

    u_char  proto;          // Protocol 

    u_short crc;            // Header checksum 

    ip_address  saddr;      // Source address 

    ip_address  daddr;      // Destination address 

    u_int   op_pad;         // Option + Padding 

}ip_header; 

 

 

typedef struct udp_header{ 

    u_short sport;          // Source port 

    u_short dport;          // Destination port 

    u_short len;            // Datagram length 

    u_short crc;            // Checksum 

}udp_header; 

 

 

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]; 

u_int netmask; 

char packet_filter[] = "ip and udp"; 

struct bpf_program fcode; 

 

     

    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,  // name of the device 

                             65536,     // portion of the packet to capture.  

                                        // 65536 grants that the whole packet will be captured on all the MACs. 

                             PCAP_OPENFLAG_PROMISCUOUS,         // promiscuous mode 

                             1000,      // read timeout 

                             NULL,      // remote authentication 

                             errbuf     // error buffer 

                             ) ) == NULL) 

   

        fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n"); 

         

        pcap_freealldevs(alldevs); 

        return -1; 

   

     

     

    if(pcap_datalink(adhandle) != DLT_EN10MB) 

   

        fprintf(stderr,"\nThis program works only on Ethernet networks.\n"); 

         

        pcap_freealldevs(alldevs); 

        return -1; 

   

     

    if(d->addresses != NULL) 

         

        netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; 

    else 

         

        netmask=0xffffff;  

 

 

    //compile the filter 

    if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 ) 

   

        fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n"); 

         

        pcap_freealldevs(alldevs); 

        return -1; 

   

     

    //set the filter 

    if (pcap_setfilter(adhandle, &fcode)<0) 

   

        fprintf(stderr,"\nError setting the filter.\n"); 

         

        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]; 

    ip_header *ih; 

    udp_header *uh; 

    u_int ip_len; 

    u_short sport,dport; 

 

     

    ltime=localtime(&header->ts.tv_sec); 

    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime); 

 

     

    printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len); 

 

     

    ih = (ip_header *) (pkt_data + 

        14); //length of ethernet header 

 

     

    ip_len = (ih->ver_ihl & 0xf) * 4; 

    uh = (udp_header *) ((u_char*)ih + ip_len); 

 

     

    sport = ntohs( uh->sport ); 

    dport = ntohs( uh->dport ); 

 

     

    printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n", 

        ih->saddr.byte1, 

        ih->saddr.byte2, 

        ih->saddr.byte3, 

        ih->saddr.byte4, 

        sport, 

        ih->daddr.byte1, 

        ih->daddr.byte2, 

        ih->daddr.byte3, 

        ih->daddr.byte4, 

        dport); 

 

 

首先,我们设置过滤器为”ip and udp”。这样,我们保证packet_handler()将只接收基于IPv4的udp数据包;这样可以简单化分解(parsing)过程和提高程序的执行效率。

 

 

 

 

     我们还创建了一对用来描述IP和UDP头信息的结构体。这两个结构被packet_handler()调用来定位各种各样的信息头域。

 

 

 

 

     Packet_handler(),尽管限制为只为一个协议解剖(UDP OVER IPV4),向我们展示了象tcpdump/windump这种复杂的探测是怎么样对网络信息进行解码的。首先,因为我们对MAC头部不感兴趣,所以我们忽略它。为了简单起见,在开始启动捕获前,我们用pcap_datalink()在MAC层做一个检查,这样UDPdump就会仅仅在以太网上进行工作。这样我们能保证MAC头部是14个字节。

 

 

 

 

     IP头在MAC头之后,我们从IP头中获得源IP地址和目的IP地址。

 

 

 

 

     到了UDP头部就有点复杂,因为IP头没有一个固定的长度。因此,我们使用接口首部长度域来得到大小。一旦我们知道了UDP首部的位置,我们就可以从中提取出源端口和目的端口号。

 

 

 

 

     提取的信息打印在屏幕上,结果象下面所示:

 

 

 

 

1.     {A7FD048A-5D4B-478E-B3C1-34401AC3B72F} (Xircom t 10/100 Adapter)

Enter the interface number (1-2):1

 

 

 

listening on Xircom CardBus Ethernet 10/100 Adapter...

16:13:15.312784 len:87 130.192.31.67.2682 -> 130.192.3.21.53

16:13:15.314796 len:137 130.192.3.21.53 -> 130.192.31.67.2682

 

 

16:13:15.322101 len:78 130.192.31.67.2683 -> 130.192.3.21.53

 

 

每一行代表一个数据包。

 

转自:http://wotseb./1419081.html

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4458618.aspx

 

 

 

 

 

WinPcap基础知识(第七课:处理离线dump文件) 


dump文件的格式和libpcap的是一样的。在这个格式里捕获的数据包是用二进制的形式来保存的,现在已经作为标准被许多网络工具使用,其中就包括WinDump, Ethereal 和 Snort。
保存数据包到一个dump文件:
首先,让我们看看怎样用libpcap格式来写数据包。下面的例子从选择的网络接口中捕获数据并保存到一个由用户提供名字的文件中。

view plaincopy to clipboardprint?

1.    #define HAVE_REMOTE

2.    #include <pcap.h>

3.     

4.    #pragma comment(lib,"wpcap.lib")

5.     

6.   

7.    void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

8.     

9.    main(int argc, char **argv)

10. {

11. pcap_if_t *alldevs;

12. pcap_if_t *d;

13. int inum;

14. int i=0;

15. pcap_t *adhandle;

16. char errbuf[PCAP_ERRBUF_SIZE];

17. pcap_dumper_t *dumpfile;

18.  

19.  

20.  

21.

22. if(argc != 2)

23. {

24. printf("usage: %s filename", argv[0]);

25. return -1;

26. }

27.  

28.

29. if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)

30. {

31. fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);

32. exit(1);

33. }

34.  

35.

36. for(d=alldevs; d; d=d->next)

37. {

38. printf("%d. %s", ++i, d->name);

39. if (d->description)

40. printf(" (%s)\n", d->description);

41. else

42. printf(" (No description available)\n");

43. }

44.  

45. if(i==0)

46. {

47. printf("\nNo interfaces found! Make sure WinPcap is installed.\n");

48. return -1;

49. }

50.  

51. printf("Enter the interface number (1-%d):",i);

52. scanf("%d", &inum);

53.  

54. if(inum < 1 || inum > i)

55. {

56. printf("\nInterface number out of range.\n");

57.

58. pcap_freealldevs(alldevs);

59. return -1;

60. }

61.  

62.

63. for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

64.  

65.  

66.

67. if ( (adhandle= pcap_open(d->name, // name of the device

68. 65536, // portion of the packet to capture

69. // 65536 guarantees that the whole packet will be captured on all the link layers

70. PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode

71. 1000, // read timeout

72. NULL, // authentication on the remote machine

73. errbuf // error buffer

74. ) ) == NULL)

75. {

76. fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);

77.

78. pcap_freealldevs(alldevs);

79. return -1;

80. }

81.  

82.

83. dumpfile = pcap_dump_open(adhandle, argv[1]);

84.  

85. if(dumpfile==NULL)

86. {

87. fprintf(stderr,"\nError opening output file\n");

88. return -1;

89. }

90.  

91. printf("\nlistening on %s... Press Ctrl+C to stop...\n", d->description);

92.  

93.

94. pcap_freealldevs(alldevs);

95.  

96.

97. pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);

98.  

99. return 0;

100.  }

101.   

102. 

103.  void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)

104.  {

105. 

106.  pcap_dump(dumpfile, header, pkt_data);

107.  }

可以看出:这个程序的结构和前面我们学过的课程里面的结构非常相似,不同在于:

⑴接口一打开,就马上调用pcap_dump_open()打开一个dump文件并且把它联系到此接口⑵在packet_handler()回调函数执行中,数据包被pcap_dump()写入到文件中。参数dumpfile被pcap_handler()函数接收,所以保存一个数据包是非常简单的事情。大家要控制好时间,因为网络数据包太多了,时间久了dump的文件就会特别大,到时候你直接打开都要等一会儿.......

从dump文件中读取数据包

既然现在我们有一个dump文件,我们就来看看怎么读取它的内容。下面的代码向我们展示了怎么样打开一个winpcap/libpcap dump文件,然后显示它里面的每一个数据包。这个文件是用pcap_open_offline()打开的,然后在数据包中循环使用pcap_loop()。你可以看到,其实从一个离线的捕获中读取数据包和从一个物理接口上接收他们几乎一样。

下面的例子也介绍了另外一个函数:pcap_createsrcsrc()。这个函数用来创建一个源字符串,这个字符串的开始部分是用来告诉WinPcap要使用的类型,例如:如果我们要打开一个适配器就用”rpcap://”;如果我们要打开一个文件就用file://。在你使用pcap_findalldevs_ex()时这个步骤不是必须的(因为返回值已经包含了这些字符串)。但是,在此例中必需,因为我们要从用户的输入中读取到文件的名字。

转自:http://wotseb./1419199.html

 

 

 

 

 

WinPcap基础知识(第八课:发送数据包)

 

 

尽管WinPcap这个名字已经很清晰的表明这个库是用来捕获数据包的,但是另外一个非常有用的功能是提供了原始网络操作。这里,用户可以找到一系列函数来发送数据包,这节课我们就会向大家一一展示。注意到原始的libcap库不提供任何方法来发送数据库:这里讲的所有的函数是WinPcap扩展,它们不能在Unix系统下面工作。用pcap_sendpacket()发送单个数据包函数原型: int pcap_sendpacket ( pcap_t * p, u_char * buf, int size ) 说明:该函数可以发送一个原始数据包到网络上。Buf包含要发送到网络上的数据包的数据(包括协议头)。注意,MAC CRC不用包含,因为它是网卡驱动计算然后添加的。返回值为0说明数据包已经成功的发送了,否则返回-1。

 

用pcap_sendpacket()发送单个包

 

最简单发送一个数据包的方法如下面代码所示。打开一个适配器后,调用pcap_sendpacket()来发送一个手工数据包。Pcap_sendpacket()的参数如下:一个包含将要发送的数据缓冲区;缓冲区的长度;将要在它上面发送数据的适配器。注意到缓冲区的数据是不经过处理就被发送到网络上的,这就意味着应用程序必须要创建正确的协议头以保证发送一些有用的信息。

 

 

 

 

view plaincopy to clipboardprint?

01.#include <stdlib.h> 

02.#include <stdio.h> 

03.

04.#define HAVE_REMOTE 

05.#include <pcap.h> 

06.

07.#pragma comment(lib,"wpcap") 

08. 

09. 

10.void main(int argc, char **argv) 

11.{ 

12.pcap_t *fp; 

13.char errbuf[PCAP_ERRBUF_SIZE]; 

14.u_char packet[100]; 

15.int i; 

16. 

17.     

18.    if (argc != 2) 

19.   

20.        printf("usage: %s interface (e.g. 'rpcap://eth0')", argv[0]); 

21.        return; 

22.   

23.     

24.     

25.    if ( (fp= pcap_open(argv[1],            // name of the device 

26.                        100,                // portion of the packet to capture (only the first 100 bytes) 

27.                        PCAP_OPENFLAG_PROMISCUOUS,  // promiscuous mode 

28.                        1000,               // read timeout 

29.                        NULL,               // authentication on the remote machine 

30.                        errbuf              // error buffer 

31.                        ) ) == NULL) 

32.   

33.        fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", argv[1]); 

34.        return; 

35.   

36. 

37.     

38.    packet[0]=1; 

39.    packet[1]=1; 

40.    packet[2]=1; 

41.    packet[3]=1; 

42.    packet[4]=1; 

43.    packet[5]=1; 

44.     

45.     

46.    packet[6]=2; 

47.    packet[7]=2; 

48.    packet[8]=2; 

49.    packet[9]=2; 

50.    packet[10]=2; 

51.    packet[11]=2; 

52.     

53.     

54.    for(i=12;i<100;i++) 

55.   

56.        packet[i]=(u_char)i; 

57.   

58. 

59.     

60.    if (pcap_sendpacket(fp, packet, 100 ) != 0) 

61.   

62.        fprintf(stderr,"\nError sending the packet: %s\n", pcap_geterr(fp)); 

63.        return; 

64.   

65. 

66.    return; 

67.} 

 

 

发送队列

 

Pcap_sendpacket()提供了一个简单而又直接的方法来发送一个数据包,发送队列提供了一个高级的有效的最优化机制来发送一组数据包。一个发送队列是一个用来存放即将要发送到网络上的多个数据包的容器。它有大小,大小表明了它能够存放的数据包的最大数量。发送队列是通过调用pcap_sendqueue_alloc()函数来创建的,创建时要指定所需创建的队列的大小。一旦队列创建完毕,就可以使用pcap_sendqueue_queue()来存放一个数据包在队列里面。此函数获得一个带有时间戳,长度,数据包缓冲区的pcap_pkthdr。这些参数同样被pcap_next_ex()和函数pcap_handler()接收。因此,把一个刚捕获的或者从文件中读取出来的数据包放到队列中去就是把数据包作为参数传递给pcap_sendqueue_queue()。

 

 

 

看看几个函数

 

 

 

int pcap_sendqueue_queue  pcap_send_queue *  queue, 

 

 

 

  const struct pcap_pkthdr *  pkt_header, 

 

 

 

  const u_char *  pkt_data 

 

 

 

功能:向发送队列中添加一个数据包。Pcap_sendqueue_queue()方法向队列得末尾添加一个数据包。Pkt_header参数指向一个pcap_pkthdr结构体(结构体含有数据包得时间戳,长度,以及一个pkt_data指针,它指向包数据的指针)。Pcap_ptkhdr结构在Winpcap和libcap里面都是用来存放包,因此发送一个捕获文件时直接发送的。“原始包”意味着发送应用程序将必须包含协议头。

 

 

 

CRC头就不用了,那是网卡驱动程序进行计算然后加上的。

 

 

 

u_int pcap_sendqueue_transmit  pcap_t *  p,  

 

  pcap_send_queue *  queue, 

 

  int  sync  

 

 

 

功能:把一个装有原始包的队列发送到网络上。注意一下参数sync,它决定发送操作是否必须同步。如果是非0,数据包发送要注意时间戳(the packets are sent respecting the timestamps);否则,数据包尽可能快的发送出去。

 

 

 

为了发送一个队列,WinPcap提供了pcap_sendqueue_transmit()函数。注意一下第三个参数:如果是非空,发送就是同步的。例如:数据包的相关的时间戳也会被重视。这就需要占用大量cpu资源,因为同步方式代替在内核。。。。。(翻译不出来)

 

 

 

 注意到用pcap_sendqueue_transmit()来发送一个队列比执行一序列的pcap_sendpacket()每次发送一个数据包来说效率高的多。因为一个发送队列是一个缓冲区,在内核级大量减少了上下文切换的次数。

 

 

 

当不再需要一个队列,就可以用pcap_sendqueue_destroy()函数来释放,它会释放与之关联的所有的缓冲区。

 

 

 

下面的代码显示了怎么样使用发送队列。首先它用pcap_open_offline()函数打开一个捕获文件,然后把数据包从文件中拷贝到合适分配的队列里面。这时,开始传送队列。 

 

 

 

注意到dump文件的链路层是与使用pcap_datalink()方法来发送数据包所在的其中一个接口一致的。如果他们不同的话就会打印出一个警告:sending on a link-layer the packets captured from a different one is quite pointless.

 

 

 

 

 

 

view plaincopy to clipboardprint?

 

 

#include <stdlib.h> 

#include <stdio.h> 

 

#define WPCAP 

#define HAVE_REMOTE 

#include <pcap.h> 

 

#pragma comment(lib,"wpcap") 

 

void usage(); 

 

void main(int argc, char **argv) 

    pcap_t *indesc,*outdesc; 

    char errbuf[PCAP_ERRBUF_SIZE]; 

    char source[PCAP_BUF_SIZE]; 

    FILE *capfile; 

    int caplen, sync; 

    u_int res; 

    pcap_send_queue *squeue; 

    struct pcap_pkthdr *pktheader; 

    u_char *pktdata; 

    float cpu_time; 

    u_int npacks = 0; 

    errno_t fopen_error; 

 

     

    if (argc <= 2 || argc >= 5) 

   

        usage(); 

        return; 

   

         

     

    fopen_error = fopen_s(&capfile, argv[1],"rb"); 

    if(fopen_error != 0){ 

        printf("Error opening the file, errno %d.\n", fopen_error); 

        return; 

   

     

    fseek(capfile , 0, SEEK_END); 

    caplen= ftell(capfile)- sizeof(struct pcap_file_header); 

    fclose(capfile); 

             

     

    if(argc == 4 && argv[3][0] == 's') 

        sync = TRUE; 

    else 

        sync = FALSE; 

 

     

     

    if ( pcap_createsrcstr( source,         // variable that will keep the source string 

                            PCAP_SRC_FILE,  // we want to open a file 

                            NULL,           // remote host 

                            NULL,           // port on the remote host 

                            argv[1],        // name of the file we want to open 

                            errbuf          // error buffer 

                            ) != 0) 

   

        fprintf(stderr,"\nError creating a source string\n"); 

        return; 

   

     

     

    if ( (indesc= pcap_open(source, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf) ) == NULL) 

   

        fprintf(stderr,"\nUnable to open the file %s.\n", source); 

        return; 

   

 

     

    if ( (outdesc= pcap_open(argv[2], 100, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf) ) == NULL) 

   

        fprintf(stderr,"\nUnable to open adapter %s.\n", source); 

        return; 

   

 

     

    if (pcap_datalink(indesc) != pcap_datalink(outdesc)) 

   

        printf("Warning: the datalink of the capture differs from the one of the selected interface.\n"); 

        printf("Press a key to continue, or CTRL+C to stop.\n"); 

        getchar(); 

   

 

     

    squeue = pcap_sendqueue_alloc(caplen); 

 

     

    while ((res = pcap_next_ex( indesc, &pktheader, (const unsigned char **)&pktdata)) == 1) 

   

        if (pcap_sendqueue_queue(squeue, pktheader, pktdata) == -1) 

       

            printf("Warning: packet buffer too small, not all the packets will be sent.\n"); 

            break; 

       

 

        npacks++; 

   

 

    if (res == -1) 

   

        printf("Corrupted input file.\n"); 

        pcap_sendqueue_destroy(squeue); 

        return; 

   

 

     

     

    cpu_time = (float)clock (); 

 

    if ((res = pcap_sendqueue_transmit(outdesc, squeue, sync)) < squeue->len) 

   

        printf("An error occurred sending the packets: %s. Only %d bytes were sent\n", pcap_geterr(outdesc), res); 

   

     

    cpu_time = (clock() - cpu_time)/CLK_TCK; 

     

    printf ("\n\nElapsed time: %5.3f\n", cpu_time); 

    printf ("\nTotal packets generated = %d", npacks); 

    printf ("\nAverage packets per second = %d", (int)((double)npacks/cpu_time)); 

    printf ("\n"); 

 

     

    pcap_sendqueue_destroy(squeue); 

 

     

    pcap_close(indesc); 

 

     

    pcap_close(outdesc); 

 

 

    return; 

 

 

void usage() 

     

    printf("\nSendcap, sends a libpcap/tcpdump capture file to the net. Copyright (C) 2002 Loris Degioanni.\n"); 

    printf("\nUsage:\n"); 

    printf("\t sendcap file_name adapter [s]\n"); 

    printf("\nParameters:\n"); 

    printf("\nfile_name: the name of the dump file that will be sent to the network\n"); 

    printf("\nadapter: the device to use. Use \"WinDump -D\" for a list of valid devices\n"); 

    printf("\ns: if present, forces the packets to be sent synchronously, i.e. respecting the timestamps in the dump file. This option will work only under Windows NTx.\n\n"); 

 

    exit(0); 

 

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4459058.aspx

 

 

 

 

WinPcap基础知识(第九课:统计网络的通信量)

 

 

这节课我们将要学习WinPcap的另外一个高级特性:对网络通信量进行统计。统计引擎使得内核级的包过滤器能有效的把收集到的包分类。你可以查阅NPF 驱动内部手册看到更多的细节。

 

 

 

 

 

 

为了用这个功能来监视网络,程序员必须要打开一个适配器,并且把它设为统计模式。可以用函数pcap_setmode()来完成这个功能。特别的,在这个函数里面MODE_STAT必须作为模式参数。

 

 

 

 

 

 

用统计模式,我们写一个监视TCP通信负载的应用程序,只需要很少行代码。下面是例子代码:

 

 

 

 

 

 

view plaincopy to clipboardprint?

 

 

#include <stdlib.h> 

#include <stdio.h> 

 

#include <pcap.h> 

 

void usage(); 

 

void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *); 

 

 

void main(int argc, char **argv) 

pcap_t *fp; 

char errbuf[PCAP_ERRBUF_SIZE]; 

struct timeval st_ts; 

u_int netmask; 

struct bpf_program fcode; 

   

     

    if (argc != 2) 

   

        usage(); 

        return; 

   

         

     

    if ( (fp= pcap_open(argv[1], 100, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf) ) == NULL) 

   

        fprintf(stderr,"\nUnable to open adapter %s.\n", errbuf); 

        return; 

   

 

     

    netmask=0xffffff;  

 

    //compile the filter 

    if (pcap_compile(fp, &fcode, "tcp", 1, netmask) <0 ) 

   

        fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n"); 

         

        return; 

   

     

    //set the filter 

    if (pcap_setfilter(fp, &fcode)<0) 

   

        fprintf(stderr,"\nError setting the filter.\n"); 

        pcap_close(fp); 

         

        return; 

   

 

     

    if (pcap_setmode(fp, MODE_STAT)<0) 

   

        fprintf(stderr,"\nError setting the mode.\n"); 

        pcap_close(fp); 

         

        return; 

   

 

 

    printf("TCP traffic summary:\n"); 

 

     

    pcap_loop(fp, 0, dispatcher_handler, (PUCHAR)&st_ts); 

 

    pcap_close(fp); 

    return; 

 

void dispatcher_handler(u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data) 

    struct timeval *old_ts = (struct timeval *)state; 

    u_int delay; 

    LARGE_INTEGER Bps,Pps; 

    struct tm ltime; 

    char timestr[16]; 

    time_t local_tv_sec; 

 

     

     

    delay=(header->ts.tv_sec - old_ts->tv_sec) * 1000000 - old_ts->tv_usec + header->ts.tv_usec; 

     

    Bps.QuadPart=(((*(LONGLONG*)(pkt_data + 8)) * 8 * 1000000) / (delay)); 

     

 

     

    Pps.QuadPart=(((*(LONGLONG*)(pkt_data)) * 1000000) / (delay)); 

 

     

    local_tv_sec = header->ts.tv_sec; 

    localtime_s(<ime, &local_tv_sec); 

    strftime( timestr, sizeof timestr, "%H:%M:%S", <ime); 

 

     

    printf("%s ", timestr); 

 

     

    printf("BPS=%I64u ", Bps.QuadPart); 

    printf("PPS=%I64u\n", Pps.QuadPart); 

 

    //store current timestamp 

    old_ts->tv_sec=header->ts.tv_sec; 

    old_ts->tv_usec=header->ts.tv_usec; 

 

 

void usage() 

     

    printf("\nShows the TCP traffic load, in bits per second and packets per second.\nCopyright (C) 2002 Loris Degioanni.\n"); 

    printf("\nUsage:\n"); 

    printf("\t tcptop adapter\n"); 

    printf("\t You can use \"WinDump -D\" if you don't know the name of your adapters.\n"); 

 

    exit(0); 

 

 

 

 

 

 

Before enabling statistical mode, the user has the option to set a filter that defines the subset of network traffic that will be monitored. See the paragraph on the Filtering expression syntax for details. If no filter has been set, all of the traffic will be monitored.

 

Once

 

 

 ?the filter is set

?pcap_setmode() is called

?callback invocation is enabled with pcap_loop()

 

the interface descriptor starts to work in statistical mode. Notice the fourth parameter (to_ms) of pcap_open(): it defines the interval among the statistical samples. The callback function receives the samples calculated by the driver every to_ms milliseconds. These samples are encapsulated in the second and third parameters of the callback function, as shown in the following figure:

 

 

 

 

 

 

Two 64-bit counters are provided: the number of packets and the amount of bytes received during the last interval.

 

In the example, the adapter is opened with a timeout of 1000 ms. This means that dispatcher_handler() is called once per second. At this point a filter that keeps only tcp packets is compiled and set. Then pcap_setmode() and pcap_loop() are called. Note that a struct timeval pointer is passed to pcap_loop() as the user parameter. This structure will be used to store a timestamp in order to calculate the interval between two samples. dispatcher_handler()uses this interval to obtain the bits per second and the packets per second and then prints these values on the screen.

 

Note finally that this example is by far more efficient than a program that captures the packets in the traditional way and calculates statistics at user-level. Statistical mode requires the minumum amount of data copies and context switches and therefore the CPU is optimized. Moreover, a very small amount of memory is required.

 

 

 

摘自:http://www./docs/docs_41b5/html/group__wpcap__tut9.html

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4459314.aspx

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多