- 轉載自:http://blog.csdn.net/tennysonsky/article/details/44811899
- 概述
- libpcap是一個網絡數據包捕獲函數庫,tcpdump就是以libpcap為基礎的。
- 主要作用:
- 捕獲各種數據包,例如:網絡流量統計
- 過濾網絡數據包,例如:過濾掉本地上的一些數據,類似防火牆
- 分析網絡數據包,例如:分析網絡協議,數據的采集
- 存儲網絡數據包,例如:保存捕獲的數據以為將來進行分析
- 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); //釋放網絡接口
- void pcap_close(pcap_t *p);
-
//接收一個數據包 #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 }