利用libpcap抓取数据包

转自:http://blog.youkuaiyun.com/tennysonsky/article/details/44811899
概述
libpcap是一个网络数据包捕获函数库,tcpdump就是以libpcap为基础的。
主要作用:
捕获各种数据包,例如:网络流量统计
过滤网络数据包,例如:过滤掉本地上的一些数据,类似防火墙
分析网络数据包,例如:分析网络协议,数据的采集
存储网络数据包,例如:保存捕获的数据以为将来进行分析

libpcap的抓包框架
pcap_lookupdev():函数用来查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。
pcap_lookupnet():函数获得指定网络设备的网络号和掩码。
pcap_open_live():函数用于打开设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
pcap_compile():函数用于将用户制定的过滤策略编译到过滤程序中
pcap_setfilter():函数用于设置过滤器
pcap_loop():与pcap_next()和pcap_next_ex()两个函数一样用来捕获数据包
pcap_close():函数用于关闭网络设备,释放资源

利用libpcap函数库开发应用程序的步骤:
打开网络设备
设置过滤规则
捕获数据
关闭网络设备

详细步骤:
首先要使用libpcap,需要包含pcap.h头文件。
获取网络设备接口:
char pcap_lookupdev(char errbuf);
功能:自动获取可用的网络设备名指针
参数:errbuf,存放出错信息字符串,有宏定义缓冲区大小,PCAP_ERRBUF_SIZE
返回值:成功返回设备名指针(第一个合适的网络接口的字符串指针),失败则返回NULL,同时,errbuf存放出错信息字符串

1 //自动获取网络接口形式
2 char errBuf[PCAP_ERRBUF_SIZE], *devStr;
3 devStr = pcap_lookupdev(errBuf);
4 
5 //手动获取网络接口形式只需要被devStr赋值即可
6 char errBuf[PCAP_ERRBUF_SIZE], *devStr = “eth0”;

获取网络号(ip地址)和掩码
int pcap_lookupnet(char* device,bpf_u_int32 *netp,bpf_u_int32 *maskp,char *errbuf);
功能:获取指定网卡的ip地址,子网掩码
参数:device:网络设备名,为第一步获取的网络接口字符串,也可以人为指定,如“eth0”;
netp:存放ip地址的指针,buf_u_int32为32位无符号整型
maskp:存放子网掩码的指针
errbuf:存放出错信息
返回值:成功返回0,失败返回1

1 char error_content[PCAP_ERRBUF_SIZE] = {0}; // 出错信息  
 2 char *dev = pcap_lookupdev(error_content);  
 3 if(NULL == dev)  
 4 {  
 5     printf(error_content);  
 6     exit(-1);  
 7 }  
 8   
 9   
10 bpf_u_int32 netp = 0, maskp = 0;  
11 pcap_t * pcap_handle = NULL;  
12 int ret = 0;  
13   
14 //获得网络号和掩码  
15 ret = pcap_lookupnet(dev, &netp, &maskp, error_content);  
16 if(ret == -1)  
17 {  
18     printf(error_content);  
19     exit(-1);  
20 }  

打开网络接口:
pcap_t pcap_open_live(const char device,int snaplen,int promisc,int to_ms,char *errbuf);
功能:打开一个用于捕获数据的网络端口
参数:device:网络接口的名字,为第一步获取的网络接口字符串,也可以人为指定,如:”eth0“
snaplen:捕获数据包的长度,不能大于65535个字节
promise:”1“代表混杂模式,其他值代表非混杂模式
to_ms:指定需要等地啊的毫秒数,超过这个时间后,获得数据包的函数会立即返回,0表示一直等待直到有数据包到来
errbuf:存储错误信息
返回值:返回pcap_t类型指针,后面的所有操作都要使用这个指针。

1 char error_content[PCAP_ERRBUF_SIZE] = {0}; // 出错信息  
 2 char *dev = pcap_lookupdev(error_content);  // 获取网络接口  
 3 if(NULL == dev)  
 4 {  
 5     printf(error_content);  
 6     exit(-1);  
 7 }  
 8   
 9 // 打开网络接口  
10 pcap_t * pcap_handle = pcap_open_live(dev, 65535, 1, 0, error_content);  
11 if(NULL == pcap_handle)  
12 {  
13     printf(error_content);  
14     exit(-1);  
15 }  

获取数据包:
方法一:const u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h);
功能:捕获一个网络数据包,收到一个数据包立即返回
参数:p:pcap_open_live()返回的pcap_t类型的指针
h:数据包头
pcap_pkthdr类型的定义如下:

1 struct pcap_pkthdr  
2 {  
3     struct timeval ts; // 抓到包的时间  
4     bpf_u_int32 caplen; // 表示抓到的数据长度  
5     bpf_u_int32 len; // 表示数据包的实际长度  
6 }  

len和caplen的区别:因为在某些情况下不能保证捕获的包是完整的,例如一个包长1480,但是你捕获到1000的时候,可能因为某些原因就终止捕获了,所以caplen是记录实际捕获的包长,也就是1000,而len就是1480
返回值:成功则返回捕获数据包的地址,失败返回NULL

1 const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址  
 2 pcap_t *pcap_handle = NULL;  
 3 struct pcap_pkthdr protocol_header;  
 4   
 5 pcap_handle = pcap_open_live("eth0", 1024, 1, 0,NULL);  
 6   
 7 p_packet_content = pcap_next(pcap_handle, &protocol_header);   
 8 //p_packet_content  所捕获数据包的地址  
 9           
10 printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec)); // 时间  
11 printf("Packet Lenght is :%d\n",protocol_header.len);   // 数据包的实际长度

方法二:int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
功能:循环捕获网络数据包,直到遇到错误或者满足退出条件,每次捕获一个数据包就会调用callback指定的回调函数,所以,可以在回调函数中进行数据包的处理操作。
参数:p:pcap_open_live()返回的pcap_t类型的指针
cnt:指定捕获数据包的个数,一旦抓到cnt个数据包,pcap_loop立即返回,如果是-1,就会一直捕获直到出错
callback:回调函数,名字任意,根据需要自行取名
user:向回调函数中传递的参数
callback回调函数的定义:
void callback(u_char *userarg,const struct pcap_pkthdr *pkthdr,const u_char *packet)
userarg:pcap_loop()的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
pkthdr:是收到数据包的pcap_pkthdr类型的指针,和pcap_next()第二个参数是一样的
packet:收到的数据包数据
返回值:成功返回0,失败返回负数
方法三:int pcap_dispatch(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第四个参数)
释放网络接口:
void pcap_close(pcap_t *p);
功能:关闭pcap_open_live()打开的网络接口,并释放相关资源
参数:p:需要关闭的网络接口,pcap_open_live()的返回值

 1 // 打开网络接口  
 2 pcap_t * pcap_handle = pcap_open_live("eth0", 1024, 1, 0, error_content);  
 3 if(NULL == pcap_handle)  
 4 {  
 5     printf(error_content);  
 6     exit(-1);  
 7 }  
 8   
 9 //// ……  
10 //// ……  
11   
12 pcap_close(pcap_handle); //释放网络接口  
//接收一个数据包
#include <stdio.h>  
#include <pcap.h>  
#include <arpa/inet.h>  
#include <time.h>  
#include <stdlib.h>  
struct ether_header  
{  
    unsigned char ether_dhost[6];   //目的mac  
    unsigned char ether_shost[6];   //源mac  
    unsigned short ether_type;      //以太网类型  
};  
#define BUFSIZE 1514  

int main(int argc,char *argv[])  
{  
    pcap_t * pcap_handle = NULL;  
    char error_content[100] = "";   // 出错信息  
    const unsigned char *p_packet_content = NULL;       // 保存接收到的数据包的起始地址  
    unsigned char *p_mac_string = NULL;         // 保存mac的地址,临时变量  
    unsigned short ethernet_type = 0;           // 以太网类型  
    char *p_net_interface_name = NULL;      // 接口名字  
    struct pcap_pkthdr protocol_header;  
    struct ether_header *ethernet_protocol;  

    //获得接口名  
    p_net_interface_name = pcap_lookupdev(error_content);  
    if(NULL == p_net_interface_name)  
    {  
        perror("pcap_lookupdev");  
        exit(-1);  
    }  

    //打开网络接口  
    pcap_handle = pcap_open_live(p_net_interface_name,BUFSIZE,1,0,error_content);  
    p_packet_content = pcap_next(pcap_handle,&protocol_header);  

    printf("------------------------------------------------------------------------\n");  
    printf("capture a Packet from p_net_interface_name :%s\n",p_net_interface_name);  
    printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec));  
    printf("Packet Lenght is :%d\n",protocol_header.len);  

    /* 
    *分析以太网中的 源mac、目的mac 
    */  
    ethernet_protocol = (struct ether_header *)p_packet_content;  
    p_mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac  
    printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+0),*(p_mac_string+1),*(p_mac_string+2),*(p_mac_string+3),*(p_mac_string+4),*(p_mac_string+5));  
    p_mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac  
    printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+0),*(p_mac_string+1),*(p_mac_string+2),*(p_mac_string+3),*(p_mac_string+4),*(p_mac_string+5));  

    /* 
    *获得以太网的数据包的地址,然后分析出上层网络协议的类型 
    */  
    ethernet_type = ntohs(ethernet_protocol->ether_type);  
    printf("Ethernet type is :%04x\t",ethernet_type);  
    switch(ethernet_type)  
    {  
        case 0x0800:printf("The network layer is IP protocol\n");break;//ip  
        case 0x0806:printf("The network layer is ARP protocol\n");break;//arp  
        case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp  
        default:printf("The network layer unknow!\n");break;  
    }  

    pcap_close(pcap_handle);  
    return 0;  
}  
 1//接收多个数据包 2 #include <stdio.h>  
 3 #include <pcap.h>  
 4 #include <arpa/inet.h>  
 5 #include <time.h>  
 6 #include <stdlib.h>  
 7   
 8 #define BUFSIZE 1514  
 9   
10 struct ether_header  
11 {  
12     unsigned char ether_dhost[6];   //目的mac  
13     unsigned char ether_shost[6];   //源mac  
14     unsigned short ether_type;      //以太网类型  
15 };  
16   
17 /*******************************回调函数************************************/  
18 void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)  
19 {  
20     unsigned char *mac_string;              //  
21     struct ether_header *ethernet_protocol;  
22     unsigned short ethernet_type;           //以太网类型  
23     printf("----------------------------------------------------\n");  
24     printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //转换时间  
25     ethernet_protocol = (struct ether_header *)packet_content;  
26       
27     mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac地址  
28     printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));  
29     mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac  
30     printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));  
31       
32     ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网的类型  
33     printf("Ethernet type is :%04x\n",ethernet_type);  
34     switch(ethernet_type)  
35     {  
36         case 0x0800:printf("The network layer is IP protocol\n");break;//ip  
37         case 0x0806:printf("The network layer is ARP protocol\n");break;//arp  
38         case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp  
39         default:break;  
40     }  
41     usleep(800*1000);  
42 }  
43   
44 int main(int argc, char *argv[])  
45 {  
46     char error_content[100];    //出错信息  
47     pcap_t * pcap_handle;  
48     unsigned char *mac_string;                
49     unsigned short ethernet_type;           //以太网类型  
50     char *net_interface = NULL;                 //接口名字  
51     struct pcap_pkthdr protocol_header;  
52     struct ether_header *ethernet_protocol;  
53       
54     //获取网络接口  
55     net_interface = pcap_lookupdev(error_content);  
56     if(NULL == net_interface)  
57     {  
58         perror("pcap_lookupdev");  
59         exit(-1);  
60     }  
61   
62     pcap_handle = pcap_open_live(net_interface,BUFSIZE,1,0,error_content);//打开网络接口  
63           
64     if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)  
65     {  
66         perror("pcap_loop");  
67     }  
68       
69     pcap_close(pcap_handle);  
70     return 0;  
71 }  

过滤数据包:
设置过滤条件:举一些例子:
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个字节)
编译BPF过滤规则:
int pcap_compile(pcap_t *p,struct bpf_program *fp,char *buf,int optimize,bpf_u_int32 mask);
参数:
p:pcap_open_live()返回的pcap_t类型的指针
fp:存放编译后的bpf,应用过来规则时需要使用这个指针
buf:过滤规则
optimize:是否需要优化过滤表达式
mask:指定本地网络的网络掩码,不需要时可写0
返回值:成功返回0,失败返回-1
应用BPF过滤规则:
int pcap_setfilter(pcap_t *p,struct bpf_program *fp);
功能:应用BPF过滤规则
参数:p:pcap_open_live()返回的pcap_t类型的指针
fp:pcap_compile()的第二个参数
返回值:成功返回0,失败返回-1

1 #include <pcap.h>  
 2 #include <time.h>  
 3 #include <stdlib.h>  
 4 #include <stdio.h>  
 5   
 6 void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
 7 {  
 8   int * id = (int *)arg;  
 9     
10   printf("id: %d\n", ++(*id));  
11   printf("Packet length: %d\n", pkthdr->len);  
12   printf("Number of bytes: %d\n", pkthdr->caplen);  
13   printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   
14     
15   int i;  
16   for(i=0; i<pkthdr->len; ++i)  
17   {  
18     printf(" %02x", packet[i]);  
19     if( (i + 1) % 16 == 0 )  
20     {  
21       printf("\n");  
22     }  
23   }  
24     
25   printf("\n\n");  
26 }  
27   
28 int main()  
29 {  
30   char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
31     
32   /* get a device */  
33   devStr = pcap_lookupdev(errBuf);  
34     
35   if(devStr)  
36   {  
37     printf("success: device: %s\n", devStr);  
38   }  
39   else  
40   {  
41     printf("error: %s\n", errBuf);  
42     exit(1);  
43   }  
44     
45   /* open a device, wait until a packet arrives */  
46   pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
47     
48   if(!device)  
49   {  
50     printf("error: pcap_open_live(): %s\n", errBuf);  
51     exit(1);  
52   }  
53     
54   /* construct a filter */  
55   struct bpf_program filter;  
56   pcap_compile(device, &filter, "dst port 80", 1, 0);  
57   pcap_setfilter(device, &filter);  
58     
59   /* wait loop forever */  
60   int id = 0;  
61   pcap_loop(device, -1, getPacket, (u_char*)&id);  
62     
63   pcap_close(device);  
64   
65   return 0;  
66 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值