WinPcap笔记(4):打开适配器并捕获数据包
前面已經(jīng)能夠得到是設(shè)備的信息了,現(xiàn)在開始做真正有意義的事情:打開適配器并捕獲數(shù)據(jù)包。這里將用到函數(shù)pcap_open(),下面是函數(shù)的具體格式:
pcap_t* pcap_open ( const char * source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth * auth, char * errbuf );各個參數(shù)含義:
source:設(shè)備名稱;
snaplen:制定要捕獲數(shù)據(jù)包中的哪些部分。 在一些操作系統(tǒng)中 (比如 xBSD 和 Win32), 驅(qū)動可以被配置成只捕獲數(shù)據(jù)包的初始化部分: 這樣可以減少應(yīng)用程序間復(fù)制數(shù)據(jù)的量,從而提高捕獲效率。本例中,我們將值定為65535,它比我們能遇到的最大的MTU還要大。因此,我們確信我們總能收到完整的數(shù)據(jù)包;
flags:最最重要的flag是用來指示適配器是否要被設(shè)置成混雜模式。 一般情況下,適配器只接收發(fā)給它自己的數(shù)據(jù)包, 而那些在其他機器之間通訊的數(shù)據(jù)包,將會被丟棄。 相反,如果適配器是混雜模式,那么不管這個數(shù)據(jù)包是不是發(fā)給我的,我都會去捕獲。也就是說,我會去捕獲所有的數(shù)據(jù)包。 這意味著在一個共享媒介(比如總線型以太網(wǎng)),WinPcap能捕獲其他主機的所有的數(shù)據(jù)包。 大多數(shù)用于數(shù)據(jù)捕獲的應(yīng)用程序都會將適配器設(shè)置成混雜模式,所以,我們也會在下面的范例中,使用混雜模式。還有下面幾種模式:
- PCAP_OPENFLAG_DATATX_UDP?? 2?:定義遠(yuǎn)程模式下數(shù)據(jù)轉(zhuǎn)化是否使用UDP協(xié)議;
- PCAP_OPENFLAG_NOCAPTURE_RPCAP?? 4 :定義遠(yuǎn)程機器捕獲自己產(chǎn)生的數(shù)據(jù);
- PCAP_OPENFLAG_NOCAPTURE_LOCAL?? 8 :定義本地機器捕獲自己產(chǎn)生的數(shù)據(jù);
- PCAP_OPENFLAG_MAX_RESPONSIVENESS?? 16 :標(biāo)識配置適配器最大相應(yīng)能力;
auth:遠(yuǎn)程機器驗證;
errbuf:錯誤緩沖池,存儲錯誤信息;
函數(shù)返回:
返回一個可以作為調(diào)用參數(shù)的指向pcap_t結(jié)構(gòu)的指針,并且指定一個WinPcap會話。如果出現(xiàn)錯誤,返回NULL,errbuf中存儲錯誤信息。
其中pcap_t結(jié)構(gòu)定義如下:
typedef struct pcap pcap_t 這個是一個已打開的捕捉實例的描述符。這個結(jié)構(gòu)體對用戶來說是不透明的,它通過wpcap.dll提供的函數(shù),維護(hù)了它的內(nèi)容。
當(dāng)適配器被打開,捕獲工作就可以用 pcap_dispatch() 或 pcap_loop()進(jìn)行。 這兩個函數(shù)非常的相似,區(qū)別就是 pcap_ dispatch() 當(dāng)超時時間到了(timeout expires)就返回 (盡管不能保證) ,而 pcap_loop() 不會因此而返回,只有當(dāng) cnt 數(shù)據(jù)包被捕獲,所以,pcap_loop()會在一小段時間內(nèi),阻塞網(wǎng)絡(luò)的利用。pcap_loop()對于我們這個簡單的范例來說,可以滿足需求,不過, pcap_dispatch() 函數(shù)一般用于比較復(fù)雜的程序中。
下面給出函數(shù)pcap_loop()的定義:
int pcap_loop ( pcap_t * p, int cnt, pcap_handler callback, u_char * user );各個參數(shù)含義:
p:pcap的句柄;
cnt:捕獲包的數(shù)量,如果是負(fù)數(shù)表示永不停止直到出現(xiàn)錯誤;
callback:回調(diào)函數(shù),當(dāng)捕獲到數(shù)據(jù)包時調(diào)用此函數(shù);
user:留給用戶使用的;
其中,回調(diào)函數(shù)定義如下:
void(*) pcap_handler(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data );各個參數(shù)含義:
user:函數(shù)pcap_loop()傳遞過來的,就是pcap_loop()函數(shù)中的user參數(shù);
header:表示捕獲到的數(shù)據(jù)包的基本信息;
pkt_data:表示捕獲到的數(shù)據(jù)包的內(nèi)容;
其中結(jié)構(gòu)體pcap_pkthdr保存捕獲到的包的基本信息,定義如下:
struct pcap_pkthdr {struct timeval ts; /* 時間戳 */ bpf_u_int32 caplen; /* 已捕獲部分的長度 */ bpf_u_int32 len; /* 該包的脫機長度 */ };這個結(jié)構(gòu)體由pcap_loop函數(shù)自動填充,caplen表示已捕獲部分的長度而len表示該包的總長度,因為在某些情況下你不能保證捕獲的包是完整的,例如一個包長1480,但是你捕獲到1000的時候,可能因為某些原因就中止捕獲了,所以caplen是記錄實際捕獲的包長,也就是1000,而len就是1480。len可以根據(jù)ip頭部的u_short total_len域計算出來。
下面的代碼將每一個數(shù)據(jù)包的時間戳和長度從 pcap_pkthdr 的首部解析出來,并打印在屏幕上。代碼如下:
#include "pcap.h"/* packet handler 函數(shù)原型 */ 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];/* 獲取本機設(shè)備列表 */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);elseprintf(" (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");/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}/* 跳轉(zhuǎn)到選中的適配器 */for (d = alldevs, i = 0; i< inum - 1;d = d->next, i++);/* 打開設(shè)備 */if ((adhandle = pcap_open(d->name, // 設(shè)備名65536, // 65535保證能捕獲到不同數(shù)據(jù)鏈路層上的每個數(shù)據(jù)包的全部內(nèi)容PCAP_OPENFLAG_PROMISCUOUS, // 混雜模式1000, // 讀取超時時間NULL, // 遠(yuǎn)程機器驗證errbuf // 錯誤緩沖池)) == NULL){fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}printf("\nlistening on %s...\n", d->description);/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);/* 開始捕獲 */pcap_loop(adhandle, 10, packet_handler, NULL);return 0; }/* 每次捕獲到數(shù)據(jù)包時,libpcap都會自動調(diào)用這個回調(diào)函數(shù) */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) {struct tm *ltime;char timestr[16];time_t local_tv_sec;/* 將時間戳轉(zhuǎn)換成可識別的格式 */local_tv_sec = header->ts.tv_sec;ltime = localtime(&local_tv_sec);strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);}結(jié)果如下:
結(jié)果顯示,捕獲到10個數(shù)據(jù)包后停止。
使用 pcap_loop() 函數(shù)可能會遇到障礙,主要因為它直接由數(shù)據(jù)包捕獲驅(qū)動所調(diào)用。因此,用戶程序是不能直接控制它的。另一個實現(xiàn)方法(也是提高可讀性的方法),是使用 pcap_next_ex() 函數(shù)。
總結(jié)
以上是生活随笔為你收集整理的WinPcap笔记(4):打开适配器并捕获数据包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WinPcap笔记(3):获取已安装设备
- 下一篇: Spring gradle 构建 ecl