关闭

libpcap使用简解

标签: unixlibpcap-网络
952人阅读 评论(0) 收藏 举报
分类:

一,libpcap简介
libpcap是unix/linux平台下的网络数据包捕获函数包,大多数网络监控软件都以它为基础。Libpcap可以在绝大多数类unix平台下工作,本文分析了libpcap在linux 下的源代码实现,其中重点是linux的底层包捕获机制和过滤器设置方式,同时也简要的讨论了 libpcap使用的包过滤机制 BPF。其官方网站是http://winpcap.polito.it/
二,工作原理
Libpcap 主要由两部份组成:网络分接头(Network Tap)和数据过滤器(Packet Filter)。网络分接头从网络设备驱动程序中收集数据拷贝,过滤器决定是否接收该数据包。Libpcap 利用 BSD Packet Filter(BPF)算法对网卡接收到的链路层数据包进行过滤。BPF 算法的基本思想是在有 BPF 监听的网络中,网卡驱动将接收到的数据包复制一份交给 BPF 过滤器,过滤器根据用户定义的规则决定是否接收此数据包以及需要拷贝该数据包的那些内容,然后将过滤后的数据给与过滤器相关联的上层应用程序。BPF 的架构如图所示:这里写图片描述
libpcap的包捕获机制就是在数据链路层加一个旁路处理。当一个数据包到达网络接口时,libpcap首先利用已经创建的Socket从链路层驱动程序中获得该数据包的拷贝,再通过Tap函数将数据包发给BPF过滤器。BPF过滤器根据用户已经定义好的过滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,并传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲区,并传递给用户层缓冲区。捕捉步骤
这里写图片描述
三,libpcap的抓包框架(很简单的一个执行步骤)
pcap_lookupdev()函数用于查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。
pcap_open_live()函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
pcap_lookupnet()函数获得指定网络设备的网络号和掩码。
pcap_compile()函数用于将用户制定的过滤策略编译到过滤程序中。
pcap_setfilter()函数用于设置过滤器。
pcap_loop()函数pcap_dispatch()函数用于捕获数据包,捕获后还可以进行处理,此外pcap_next()和pcap_next_ex()两个函数也可以用来捕获数据包。
pcap_close()函数用于关闭网络设备,释放资源。
其实pcap的应用程序格式很简单,总的来说可以可以分为以下5部分
1.我们从决定用哪一个接口进行嗅探开始。在Linux中,这可能是eth0,而在BSD系统中则可能是xl1等等。我们自己也可以自行定义,或者采用pcap提供的接口名来工作(pcap_lookupdev())。
2.初始化pcap。在这里我们要告诉pcap对什么设备进行嗅探。假如愿意的话,我们还可以嗅探多个设备。怎样区分它们呢?使用 文件句柄。就像打开一个文件进行读写一样,必须命名我们的嗅探“会话”,以此使它们各自区别开来(pcap_open_live(),pcap_lookupnet())。
3.假如我们只想嗅探特定的传输(如TCP/IP包,发往端口23的包等等),我们必须创建一个规则集合,编译并且使用它。这个过程分为三个相互紧密关联的阶段。规则集合被置于一个字符串内,并且被转换成能被pcap读的格式(因此编译它)。编译实际上就是在我们的程序里调用一个不被外部程序使用的函数。接下来我们要告诉 pcap使用它来过滤出我们想要的那一个会话(pcap_compile()和pcap_setfilter())。
4.最后,我们告诉pcap进入它的主体执行循环。在这个阶段内pcap一直工作到它接收了所有我们想要的包为止。每当它收到一个包就调用另一个已经定义好的函数,这个函数可以做我们想要的任何工作,它可以剖析所部获的包并给用户打印出结果,它可以将结果保存为一个文件,或者什么也不作(pcap_loop()或pcap_dispatch())。
5.在嗅探到所需的数据后,我们要关闭会话并结束(pcap_close())。
四,部分函数解析以及实例
1,获取网络接口
函数原型char * pcap_lookupdev(char * errbuf)
上面这个函数返回第一个合适的网络接口的字符串指针,如果出错,则errBuf存放出错信息字符串,errbBuf至少应该是PCAP_ERRBUF_SIZE个字节长度的。注意,很多libpcap函数都有这个参数。
pcap_lookupdev()一般可以在跨平台的,且各个平台上的网络接口名称都不相同的情况下使用。
如果我们手动指定要监听的网络接口,则这一步跳过,我们在第二步中将要监听的网络接口字符串硬编码在pcap_open_live里。PCAP_ERRBUF_SIZE是在头文件中已有定义
案例

#include<stdio.h>
#include<pcap.h>
//#define PACP_ERRBUF_SIZE 256
int main(void)
{
    char *device;//用来保存打开的设备
    char errBuf[PCAP_ERRBUF_SIZE];//保存错误信息
    device = pcap_lookupdev(errBuf);
    if(device)
        {
        printf("lookup is ok %s\n",device);
        }
    else
        {
            printf("lookup is error\n");
        }
        return 0;
}

结果如图
这里写图片描述
如上面所说,在linux中一般是eth0

2,打开网络接口
函数原型pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)
第一个参数是第一步获取的网络接口字符串,可以直接使用硬编码。
第二个参数是对于每个数据包,从开头要抓多少个字节,我们可以设置这个值来只抓每个数据包的头部,而不关心具体的内容。典型的以太网帧长度是1518字节,但其他的某些协议的数据包会更长一点,但任何一个协议的一个数据包长度都必然小于65535个字节。
第三个参数指定是否打开混杂模式(Promiscuous Mode),0表示非混杂模式,任何其他值表示混合模式。如果要打开混杂模式,那么网卡必须也要打开混杂模式,可以使用如下的命令打开eth0混杂模式:
ifconfig eth0 promisc
第四个参数指定需要等待的毫秒数,超过这个数值后,第3步获取数据包的这几个函数就会立即返回。0表示一直等待直到有数据包到来。
第五个参数是存放出错信息的数组

混杂模式与非混杂模式的区别:这两种方式区别很大。一般来说,非混杂模式的嗅探器中,主机仅嗅探那些跟它直接有关的通信,如发向它的,从它发出的,或经它路由的等都会被嗅探器捕捉。而在混杂模式中则嗅探传输线路上的所有通信。在非交换式网络中,这将是整个网络的通信。这样做最明显的优点就是使更多的包被嗅探到,它们因你嗅探网络的原因或者对你有帮助,或者没有。但是,混杂模式是可被探测到的。一个主机可以通过高强度的测试判定另一台主机是否正在进行混杂模式的嗅探。其次,它仅在非交换式的网络环境中有效工作(如集线器,或者交换中的ARP层面)。再次,在高负荷的网络中,主机的系统资源将消耗的非常严重。

案例

#include<stdio.h>
#include<pcap.h>
//pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
int main(void)
{
    char *device;
    char errBuf[PCAP_ERRBUF_SIZE];
    pcap_t *head;
    device = pcap_lookupdev(errBuf);
    if(device)
        {
        printf("lookup is ok %s\n",device);
        }
        else
        {
       printf("lookup is error %s\n",errBuf);
            return 0;
        }
        head = pcap_open_live(device,65535,0,0,errBuf);
        if(head)
         {
            printf("open is ok\n");
         }
        else
        {
            printf("open is error %s\n",errBuf);
            return 0;
        }
        //void pcap_close(pcap_t * p)
        pcap_close(head);
        return 0;
}

结果如图
这里写图片描述
注意如果是打开eth0 必须以root权限进行执行
3,获得指定网络设备的网络号和掩码
函数原型int pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf);
第一个参数是指定的设备
第二个参数和第三个参数分别是保存设备的ip和掩码
第四个参数是保存错误信息
returns 0 on success and -1 on failure
案例

int main(void)
{
    char *device=NULL;
    char *errBuf[PCAP_ERRBUF_SIZE]={0};
    pcap_t *head;
    bpf_u_int32 netp;     
    bpf_u_int32 maskp;    
   int ret;  
    char * net;     
   char * mask;    
   struct in_addr addr;

    device = pcap_lookupdev(errBuf);
    if(device)
    {
        printf("lookup is ok %s\n",device);
    }
    else
    {
        printf("lookup is error %s\n",errBuf);
        return 0;
    }
    head = pcap_open_live(device,65535,1,0,errBuf);
    if(head)
    {
        printf("open is ok\n");
    }
    else
    {
        printf("open is error %s\n",errBuf);
        return 0;
    }

  ret = pcap_lookupnet(device, &netp, &maskp, errBuf);  
  if(ret == -1)  
  {  
    printf("pcap_lookupnet() error: %s\n", errBuf);  
    exit(1);  
  }  
  addr.s_addr = netp;  
  net = inet_ntoa(addr);  
  if(!net)  
  {  
    perror("inet_ntoa() ip error: ");  
    exit(1);  
  }  
  printf("ip: %s\n", net);  
  addr.s_addr = maskp;  
  mask = inet_ntoa(addr);   
  if(!mask)  
  {  
    perror("inet_ntoa() sub mask error: ");  
    exit(1);  
  }    
  printf("sub mask: %s\n", mask);  
 pcap_close(head);
 return 0;      
}

结果如图
这里写图片描述
4,获取数据包
打开网络接口后就已经开始监听了,那如何知道收到了数据包呢?有下面3种方法:
4.1
u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)
如果返回值为NULL,表示没有抓到包
第一个参数是第2步返回的pcap_t类型的指针
第二个参数是保存收到的第一个数据包的pcap_pkthdr类型的指针
pcap_pkthdr类型的定义如下:
[cpp] view plaincopy
struct pcap_pkthdr
{
struct timeval ts;
bpf_u_int32 caplen;
bpf_u_int32 len;
};
注意这个函数只要收到一个数据包后就会立即返回
案例:

#include<stdio.h>
#include<time.h>
#include<pcap.h>

int main(void)
{
    char *device;
    char errBuf[PCAP_ERRBUF_SIZE];
    pcap_t * head;
    device = pcap_lookupdev(errBuf);
    if(device)
    {
        printf("lookup is ok %s\n",device);
    }
    else
    {
        printf("lookup is error %s\n",errBuf);
        return 0;
    }

    head = pcap_open_live(device,65535,1,0,errBuf);
    if(head)
    {
        printf("open is ok\n");
    }
    else
    {
        printf("open is error %s\n",errBuf);
        return 0;
    }

//u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)
    /*  struct pcap_pkthdr  
        {  
          struct timeval ts;      
          bpf_u_int32 caplen;     
          bpf_u_int32 len;        
        };*/  
    struct pcap_pkthdr pdata;//保存数据的结构体

    const u_char * pktStr = pcap_next(head,&pdata);
    //一旦抓到数据就会立刻返回
    if(pktStr)
        {
        printf(" Packet length: %d\n", pdata.len);  
        printf("Number of bytes: %d\n", pdata.caplen);  
        printf("Recived time: %s\n", ctime((const time_t *)&pdata.ts.tv_sec));   
      }
  else
     {
            printf("is NULL\n");
            return 0;
    }
    pcap_close(head);
    return 0;
}

结果如图
这里写图片描述
4.2
int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
第一个参数是第2步返回的pcap_t类型的指针
第二个参数是需要抓的数据包的个数,一旦抓到了cnt个数据包,pcap_loop立即返回。负数的cnt表示pcap_loop永远循环抓包,直到出现错误。
第三个参数是一个回调函数指针,它必须是如下的形式:
void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
第一个参数是pcap_loop的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
第二个参数是收到的数据包的pcap_pkthdr类型的指针
第三个参数是收到的数据包数据
案例

#include<stdio.h>
#include<pcap.h>
#include<time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
//int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
//void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
void callback(u_char *userarg,const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
     int * id = (int *)userarg;  

     printf("id: %d\n", ++(*id));  
     printf("length: %d\n", pkthdr->len);  
     printf("Number of bytes: %d\n", pkthdr->caplen);  
     printf("time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   

    int i;  
    for(i=0; i<pkthdr->len; ++i)  
        {  
            printf(" %0x", packet[i]);  
            if( (i + 1) % 16 == 0 )  
            {  
                    printf("\n");  
            }  
                    //sleep(1);
        }  

        printf("\n\n");  
}
int main(void)
{
    char *device=NULL;
    char *errBuf[PCAP_ERRBUF_SIZE]={0};
    pcap_t *head;

    device = pcap_lookupdev(errBuf);
    if(device)
        {
            printf("lookup is ok %s\n",device);
        }
        else
        {
            printf("lookup is error %s\n",errBuf);
            return 0;
        }
        head = pcap_open_live(device,65535,1,0,errBuf);
        if(head)
        {
            printf("open is ok\n");
        }
        else
        {
            printf("open is error %s\n",errBuf);
            return 0;
        }

    int id = 0;
    pcap_loop(head, -1,  callback, (u_char *)&id);
    pcap_close(head);
        return 0;       
}

结果如图
这里写图片描述

4.3
int pcap_dispatch(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第4个参数)

5,过滤数据
我们抓到的数据包往往很多,如何过滤掉我们不感兴趣的数据包呢?
几乎所有的操作系统(BSD, AIX, Mac OS, Linux等)都会在内核中提供过滤数据包的方法,主要都是基于BSD Packet Filter(BPF)结构的。libpcap利用BPF来过滤数据包。
过滤数据包三步骤
1, 构造一个过滤表达式
2, 编译这个表达式
3, 应用这个过滤器

1
BPF使用一种类似于汇编语言的语法书写过滤表达式,不过libpcap和tcpdump都把它封装成更高级且更容易的语法了,具体可以man tcpdump,以下是一些例子:
src host 192.168.1.177
只接收源ip地址是192.168.1.177的数据包
dst port 80
只接收tcp/udp的目的端口是80的数据包

not tcp
只接收不使用tcp协议的数据包

tcp[13] == 0x02 and (dst port 22 or dst port 23)
只接收SYN标志位置位且目标端口是22或23的数据包(tcp首部开始的第13个字节)

icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo
只接收icmp的ping请求和ping响应的数据包

ehter dst 00:e0:09:c1:0e:82
只接收以太网mac地址是00:e0:09:c1:0e:82的数据包

ip[8] == 5
只接收ip的ttl=5的数据包(ip首部开始的第8个字节)

2
构造完过滤表达式后,我们需要编译它,使用如下函数:
int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)
fp:这是一个传出参数,存放编译后的bpf
str:过滤表达式
optimize:是否需要优化过滤表达式
metmask:简单设置为0即可

3
最后我们需要应用这个过滤表达式:
int pcap_setfilter(pcap_t * p, struct bpf_program * fp)
第二个参数fp就是前一步pcap_compile()的第二个参数

应用完过滤表达式之后我们便可以使用pcap_loop()或pcap_next()等抓包函数来抓包了。
案例

#include<pcap.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include <sys/types.h>

int main(void)
{
        char *device;
        pcap_t *handle;
        char errBuf[PCAP_ERRBUF_SIZE];
        struct bpf_program filter; /*已经编译好的过滤表达式*/
       char filter_app[] = "port 8000";/* 过滤表达式*/
        bpf_u_int32 net;/* 执行嗅探的设备的IP地址 */
        bpf_u_int32 mask;/* 执行嗅探的设备的网络掩码 */

      struct pcap_pkthdr header; /* 由pcap.h定义 用于保存获取到的包*/
      const u_char *packet; /* 实际的包 */
        device = pcap_lookupdev(errBuf);
        if(device)
        {
            printf("lookup is ok %s\n",device);
        }
        else
        {
            printf("lookup is error %s\n",errBuf);
            return 0;
        }
         /* 探查设备属性 */
            pcap_lookupnet(device,&net,&mask,errBuf);
     /* 以混杂模式打开会话 */
     handle = pcap_open_live(device, BUFSIZ, 1, 0, errBuf);
        /* 编译并应用过滤器 */
     pcap_compile(handle, &filter, filter_app, 0, net);
     pcap_setfilter(handle, &filter);
             /* 截获一个包 */
        packet = pcap_next(handle,&header);
      /*打印长度*/
        printf("packet length is %d\n",header.len);
        /*关闭*/
    pcap_close(handle);
return 0;
}

6,数据解析
部分数据包的格式
   /* 以太网帧头部 */
   struct sniff_ethernet {
   u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
   u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
   u_short ether_type; /* IP? ARP? RARP? etc */
   };
   /* IP数据包的头部 */
   struct sniff_ip {
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int ip_hl:4, /* 头部长度 */
   ip_v:4; /* 版本号 */
   #if BYTE_ORDER == BIG_ENDIAN
   u_int ip_v:4, /* 版本号 */
   ip_hl:4; /* 头部长度 */
   #endif
   #endif /* not _IP_VHL */
   u_char ip_tos; /* 服务的类型 */
   u_short ip_len; /* 总长度 */
   u_short ip_id; /包标志号 /
   u_short ip_off; /* 碎片偏移 */
   #define IP_RF 0x8000 /* 保留的碎片标志 */
   #define IP_DF 0x4000 /* dont fragment flag */
   #define IP_MF 0x2000 /* 多碎片标志*/
   #define IP_OFFMASK 0x1fff /分段位 /
   u_char ip_ttl; /* 数据包的生存时间 */
   u_char ip_p; /* 所使用的协议 */
   u_short ip_sum; /* 校验和 */
   struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
   };
   /* TCP 数据包的头部 */
   struct sniff_tcp {
   u_short th_sport; /* 源端口 */
   u_short th_dport; /* 目的端口 */
   tcp_seq th_seq; /* 包序号 */
   tcp_seq th_ack; /* 确认序号 */
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int th_x2:4, /* 还没有用到 */
   th_off:4; /* 数据偏移 */
   #endif
   #if BYTE_ORDER == BIG_ENDIAN
   u_int th_off:4, /* 数据偏移*/
   th_x2:4; /还没有用到 /
   #endif
   u_char th_flags;
   #define TH_FIN 0x01
   #define TH_SYN 0x02
   #define TH_RST 0x04
   #define TH_PUSH 0x08
   #define TH_ACK 0x10
   #define TH_URG 0x20
   #define TH_ECE 0x40
   #define TH_CWR 0x80
   #define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
   u_short th_win; /* TCP滑动窗口 */
   u_short th_sum; /* 头部校验和 */
   u_short th_urp; /* 紧急服务位 */
   };
案例

#include<stdio.h>
#include<pcap.h>
#include<time.h>

#include<unistd.h>


#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>

 void  callback(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *packet)
 {

                     int * id = (int *)user;

                    struct in_addr addr;
                struct iphdr *ipptr;
                struct tcphdr *tcpptr;//太次片,,ip,tcp数据结构
                struct ether_header *eptr;//以太网字头
                u_char *ptr;
                char *data;
                int i;
              printf("id: %d\n", ++(*id));  
                  printf("Packet length: %d\n", pkthdr->len);  
                  printf("Number of bytes: %d\n", pkthdr->caplen);  
                  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   



          eptr = (struct ether_header*)packet;//得到以太网字头
                if (ntohs(eptr->ether_type) == ETHERTYPE_IP)
                {
                    printf ("Ethernet type hex:%x dec:%d is an IP packet/n",
                                ntohs(eptr->ether_type), ntohs(eptr->ether_type));
                }
                else 
                {
                    if (ntohs(eptr->ether_type) == ETHERTYPE_ARP)
                    {
                        printf ("Ethernet type hex:%x dec:%d is an ARP packet/n",
                                    ntohs(eptr->ether_type), ntohs(eptr->ether_type));
                    }
                    else
                    {
                        printf ("Ethernet type %x not IP/n", ntohs(eptr->ether_type));
                        exit (1);
                    }
                }

    ptr = eptr->ether_dhost;
    i = ETHER_ADDR_LEN;
    printf ("i=%d/n", i);
    printf ("Destination Address: ");
    do
    {
        printf ("%s%x", (i == ETHER_ADDR_LEN)?"":":", *ptr++);
    }while(--i>0);
    printf ("/n");
    //printf ("%x/n",ptr);

    ptr = eptr->ether_shost;
    i = ETHER_ADDR_LEN;
    printf ("Source Address: ");
    do
    {
        printf ("%s%x", (i == ETHER_ADDR_LEN)?"":":", *ptr++);
    }while(--i>0);
    printf ("/n");
    printf ("Now decoding the IP packet.\n");
    ipptr = (struct iphdr*)(packet+sizeof(struct ether_header));//得到ip包头

    printf ("the IP packets total_length is :%d\n", ipptr->tot_len);
    printf ("the IP protocol is %d\n", ipptr->protocol);
     printf("\n\n");  
    addr.s_addr = ipptr->daddr;
    printf ("Destination IP: %s\n", inet_ntoa(addr));    
    addr.s_addr = ipptr->saddr;
    printf ("Source IP: %s\n", inet_ntoa(addr));

    printf ("Now decoding the TCP packet.\n");
    tcpptr = (struct iphdr*)(packet+sizeof(struct ether_header)
                                    +sizeof(struct iphdr));//得到tcp包头
    printf ("Destination port : %d\n", tcpptr->dest);
    printf ("Source port : %d\n", tcpptr->source);
    printf ("the seq of packet is %d\n", tcpptr->seq);
//以上关于ip、tcp的结构信息请查询/usr/include/linux/ip.h | tcp.h

    data = (char*)(packet+sizeof(struct ether_header)+sizeof(struct iphdr)
                                    +sizeof(struct tcphdr));//得到数据包里内容,不过一般为乱码。
     printf("\n\n");  
    printf ("the content of packets is /n%s/n",data);



 }
int main()
{
    char *device;
    char errBuf[PCAP_ERRBUF_SIZE];
    pcap_t *head;

    device = pcap_lookupdev(errBuf);
    if(device)
        {
            printf("lookup is ok %s\n",device);
        }
        else
        {
            printf("lookup is error %s\n",errBuf);
            return 0;
        }
        head = pcap_open_live(device,65535,1,0,errBuf);
        if(head)
            {
                printf("open is ok\n");
            }
            else
            {
                    printf("open is error %s\n",errBuf);
                    return 0;
            }

            // typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);

      // int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
      int i = 0;
       pcap_dispatch(head, 0,callback, (u_char *)&i);

       pcap_close(head);
       return 0;
}

结果这里写图片描述如图

7,一些其他的主要接口

pcap_t *pcap_open_offline(char *fname, char *ebuf)
打开以前保存捕获数据包的文件,用于读取。fname参数指定打开的文
件名。该文件中的数据格式与tcpdump和tcpslice兼容。”-“为标准输
入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传
递错误消息。
pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
打开用于保存捕获数据包的文件,用于写入。fname参数为”-“时表示
标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或
pcap_open_live()函数后返回的pcap结构指针。fname参数指定打开
的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消
息。

int pcap_dispatch(pcap_t *p, int cnt,
pcap_handler callback, u_char *user)
捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。
cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有
数据包,直到产生以下错误之一:读取到EOF;超时读取。callback
参数指定一个带有三个参数的回调函数,这三个参数为:一个从
pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构
的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的
字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用
pcap_perror()或pcap_geterr()函数获取错误消息。

void pcap_dump(u_char *user, struct pcap_pkthdr *h,
u_char *sp)
向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可
作为pcap_dispatch()函数的回调函数。
int pcap_compile(pcap_t *p, struct bpf_program *fp,
char *str, int optimize, bpf_u_int32 netmask)
将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结
构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果
代码的优化。netmask参数指定本地网络的网络掩码。
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
指定一个过滤程序。fp参数是bpf_program结构指针,通常取自
pcap_compile()函数调用。出错时返回-1;成功时返回0。

int pcap_datalink(pcap_t *p)
返回数据链路层类型,例如DLT_EN10MB。
int pcap_snapshot(pcap_t *p)
返回pcap_open_live被调用后的snapshot参数值。
int pcap_is_swapped(pcap_t *p)
返回当前系统主机字节与被打开文件的字节顺序是否不同。
int pcap_major_version(pcap_t *p)
返回写入被打开文件所使用的pcap函数的主版本号。
int pcap_minor_version(pcap_t *p)
返回写入被打开文件所使用的pcap函数的辅版本号。
int pcap_stats(pcap_t *p, struct pcap_stat *ps)
向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始
捕获数据以来至今共捕获到的数据包统计。如果出错或不支持
数据包统计,则返回-1,且可调用pcap_perror()或
pcap_geterr()函数来获取错误消息。
FILE *pcap_file(pcap_t *p)
返回被打开文件的文件名。
int pcap_fileno(pcap_t *p)
返回被打开文件的文件描述字号码。
void pcap_perror(pcap_t *p, char *prefix)
在标准输出设备上显示最后一个pcap库错误消息。以prefix参
数指定的字符串为消息头。
char *pcap_geterr(pcap_t *p)
返回最后一个pcap库错误消息。
char *pcap_strerror(int error)
如果strerror()函数不可用,则可调用pcap_strerror函数替代。
void pcap_close(pcap_t *p)
关闭p参数相应的文件,并释放资源。

参考的一些资料
http://blog.chinaunix.net/uid-21556133-id-120228.html
http://blog.sina.com.cn/s/blog_6e4728e50102uxjx.html
https://www.ibm.com/developerworks/cn/linux/l-libpcap/
http://www.paper.edu.cn/uploads/releasepaper/2012/06/26/A201205-758_1340677716.pdf

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:27888次
    • 积分:782
    • 等级:
    • 排名:千里之外
    • 原创:51篇
    • 转载:2篇
    • 译文:0篇
    • 评论:7条