c++ winpcap开发(7)
處理離線轉(zhuǎn)儲(chǔ)文件
在這個(gè)課程中,我們將學(xué)習(xí)如何處理數(shù)據(jù)包捕獲到一個(gè)文件(轉(zhuǎn)儲(chǔ)到文件)。WinPcap提供廣泛的功能來(lái)將文件的網(wǎng)絡(luò)流量保存到文件并讀取轉(zhuǎn)儲(chǔ)的內(nèi)容 - 本課將介紹如何使用所有這些功能。我們還將了解如何使用WinPcap的內(nèi)核轉(zhuǎn)儲(chǔ)功能來(lái)獲取高性能轉(zhuǎn)儲(chǔ)(注意:由于新內(nèi)核緩沖區(qū)存在一些問(wèn)題,此功能已禁用)。
轉(zhuǎn)儲(chǔ)文件的格式是libpcap格式。該格式包含二進(jìn)制形式的捕獲數(shù)據(jù)包的數(shù)據(jù),是許多網(wǎng)絡(luò)工具(包括WinDump,Ethereal和Snort)使用的標(biāo)準(zhǔn)。
將數(shù)據(jù)包保存到轉(zhuǎn)儲(chǔ)文件
首先,我們來(lái)看看如何用libpcap格式寫數(shù)據(jù)包。
以下示例捕獲所選接口的數(shù)據(jù)包,并將其保存在名稱由用戶提供的文件中。
#include "pcap.h"/* prototype of the packet handler */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);int main(int argc, char **argv) { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; pcap_dumper_t *dumpfile;/* Check command line */if(argc != 2){printf("usage: %s filename", argv[0]);return -1;}/* Retrieve the device list on the local machine */if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1){fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);exit(1);}/* Print the list */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_s("%d", &inum);if(inum < 1 || inum > i){printf("\nInterface number out of range.\n");/* Free the device list */pcap_freealldevs(alldevs);return -1;}/* Jump to the selected adapter */for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);/* Open the device */if ( (adhandle= pcap_open(d->name, // name of the device65536, // portion of the packet to capture// 65536 guarantees that the whole packet will be captured on all the link layersPCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode1000, // read timeoutNULL, // authentication on the remote machineerrbuf // error buffer) ) == NULL){fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);/* Free the device list */pcap_freealldevs(alldevs);return -1;}/* Open the dump file */dumpfile = pcap_dump_open(adhandle, argv[1]);if(dumpfile==NULL){fprintf(stderr,"\nError opening output file\n");return -1;}printf("\nlistening on %s... Press Ctrl+C to stop...\n", d->description);/* At this point, we no longer need the device list. Free it */pcap_freealldevs(alldevs);/* start the capture */pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);return 0; }/* Callback function invoked by libpcap for every incoming packet */ void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data) {/* save the packet on the dump file */pcap_dump(dumpfile, header, pkt_data); }正如你所看到的,程序的結(jié)構(gòu)與以前的課程中看到的結(jié)構(gòu)非常相似。差異在于:
- 一旦打開接口,就會(huì)發(fā)出一個(gè)對(duì)pcap_dump_open()的調(diào)用。此調(diào)用打開一個(gè)轉(zhuǎn)儲(chǔ)文件并將其與該接口相關(guān)聯(lián)。
- 數(shù)據(jù)包將從packet_handler()回調(diào)中使用pcap_dump()寫入此文件。的參數(shù)pcap_dump()是在1-1對(duì)應(yīng)于參數(shù)pcap_handler()?。
從轉(zhuǎn)儲(chǔ)文件讀取數(shù)據(jù)包
現(xiàn)在我們有一個(gè)轉(zhuǎn)儲(chǔ)文件可用,我們可以嘗試閱讀它的內(nèi)容。以下代碼打開一個(gè)WinPcap / libpcap轉(zhuǎn)儲(chǔ)文件,并顯示文件中包含的每個(gè)數(shù)據(jù)包。該文件使用pcap_open_offline()打開,那么通常使用pcap_loop()來(lái)對(duì)數(shù)據(jù)包進(jìn)行排序。如您所見,從離線捕獲讀取數(shù)據(jù)包與從物理接口接收數(shù)據(jù)包幾乎相同。
此示例介紹另一個(gè)功能:pcap_createsrcsrc()。創(chuàng)建一個(gè)源字符串需要?jiǎng)?chuàng)建一個(gè)源字符串,它以一個(gè)標(biāo)記開頭,用于告訴WinPcap的源類型,例如“rpcap://”,如果我們要打開一個(gè)適配器,或者“file://”,如果我們打開文件。當(dāng)使用pcap_findalldevs_ex()(返回的值已經(jīng)包含這些字符串)時(shí),不需要此步驟。但是,在此示例中是必需的,因?yàn)閺挠脩糨斎胱x取文件的名稱。
#include <stdio.h> #include <pcap.h>#define LINE_LEN 16void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *);int main(int argc, char **argv) { pcap_t *fp; char errbuf[PCAP_ERRBUF_SIZE]; char source[PCAP_BUF_SIZE];if(argc != 2){printf("usage: %s filename", argv[0]);return -1;}/* Create the source string according to the new WinPcap syntax */if ( pcap_createsrcstr( source, // variable that will keep the source stringPCAP_SRC_FILE, // we want to open a fileNULL, // remote hostNULL, // port on the remote hostargv[1], // name of the file we want to openerrbuf // error buffer) != 0){fprintf(stderr,"\nError creating a source string\n");return -1;}/* Open the capture file */if ( (fp= pcap_open(source, // name of the device65536, // portion of the packet to capture// 65536 guarantees that the whole packet will be captured on all the link layersPCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode1000, // read timeoutNULL, // authentication on the remote machineerrbuf // error buffer) ) == NULL){fprintf(stderr,"\nUnable to open the file %s.\n", source);return -1;}// read and dispatch packets until EOF is reachedpcap_loop(fp, 0, dispatcher_handler, NULL);return 0; }void dispatcher_handler(u_char *temp1, const struct pcap_pkthdr *header, const u_char *pkt_data) {u_int i=0;/** Unused variable*/(VOID)temp1;/* print pkt timestamp and pkt len */printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len); /* Print the packet */for (i=1; (i < header->caplen + 1 ) ; i++){printf("%.2x ", pkt_data[i-1]);if ( (i % LINE_LEN) == 0) printf("\n");}printf("\n\n"); } 以下示例與最后一個(gè)示例具有相同的用途,但是使用pcap_next_ex()而不是pcap_loop()回調(diào)方法。 #include <stdio.h> #include <pcap.h>#define LINE_LEN 16int main(int argc, char **argv) { pcap_t *fp; char errbuf[PCAP_ERRBUF_SIZE]; char source[PCAP_BUF_SIZE]; struct pcap_pkthdr *header; const u_char *pkt_data; u_int i=0; int res;if(argc != 2){printf("usage: %s filename", argv[0]);return -1;}/* Create the source string according to the new WinPcap syntax */if ( pcap_createsrcstr( source, // variable that will keep the source stringPCAP_SRC_FILE, // we want to open a fileNULL, // remote hostNULL, // port on the remote hostargv[1], // name of the file we want to openerrbuf // error buffer) != 0){fprintf(stderr,"\nError creating a source string\n");return -1;}/* Open the capture file */if ( (fp= pcap_open(source, // name of the device65536, // portion of the packet to capture// 65536 guarantees that the whole packet will be captured on all the link layersPCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode1000, // read timeoutNULL, // authentication on the remote machineerrbuf // error buffer) ) == NULL){fprintf(stderr,"\nUnable to open the file %s.\n", source);return -1;}/* Retrieve the packets from the file */while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0){/* print pkt timestamp and pkt len */printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len); /* Print the packet */for (i=1; (i < header->caplen + 1 ) ; i++){printf("%.2x ", pkt_data[i-1]);if ( (i % LINE_LEN) == 0) printf("\n");}printf("\n\n"); }if (res == -1){printf("Error reading the packets: %s\n", pcap_geterr(fp));}return 0; }Writing packets to a dump file with pcap_live_dump
注意:由于新內(nèi)核緩沖區(qū)存在一些問(wèn)題,此功能已被禁用。
WinPcap的最新版本提供了一種將網(wǎng)絡(luò)流量節(jié)省到pcap_live_dump()函數(shù)的另一種方法。pcap_live_dump()具有三個(gè)參數(shù):一個(gè)文件名,允許該文件達(dá)到的最大大小(以字節(jié)為單位),以及該文件允許包含的最大數(shù)據(jù)包數(shù)。零表示對(duì)這兩個(gè)值沒有限制。請(qǐng)注意,在調(diào)用pcap_live_dump()之前,該程序可以設(shè)置一個(gè)過(guò)濾器(使用pcap_setfilter(),請(qǐng)參閱過(guò)濾流量),以定義要保存的流量的子集。
pcap_live_dump()是非阻塞的,因此它將啟動(dòng)轉(zhuǎn)儲(chǔ)并立即返回:轉(zhuǎn)儲(chǔ)進(jìn)程異步進(jìn)行,直到達(dá)到最大文件大小或最大數(shù)據(jù)包數(shù)。
應(yīng)用程序可以使用pcap_live_dump_ended()等待或檢查轉(zhuǎn)儲(chǔ)結(jié)束。請(qǐng)注意,如果同步參數(shù)非零,如果極限均為0,此功能將永久阻止應(yīng)用程序。
#include <stdlib.h> #include <stdio.h>#include <pcap.h>#error At the moment the kernel dump feature is not supported in the drivermain(int argc, char **argv) {pcap_if_t *alldevs, *d;pcap_t *fp;u_int inum, i=0;char errbuf[PCAP_ERRBUF_SIZE];printf("kdump: saves the network traffic to file using WinPcap kernel-level dump faeature.\n");printf("\t Usage: %s [adapter] | dump_file_name max_size max_packs\n", argv[0]);printf("\t Where: max_size is the maximum size that the dump file will reach (0 means no limit)\n");printf("\t Where: max_packs is the maximum number of packets that will be saved (0 means no limit)\n\n");if(argc < 5){/* The user didn't provide a packet source: Retrieve the device list */if (pcap_findalldevs(&alldevs, errbuf) == -1){fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);exit(1);}/* Print the list */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");/* Free the device list */return -1;}/* Jump to the selected adapter */for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);/* Open the device */if ( (fp = pcap_open_live(d->name, 100, 1, 20, errbuf) ) == NULL){fprintf(stderr,"\nError opening adapter\n");return -1;}/* Free the device list */pcap_freealldevs(alldevs);/* Start the dump */if(pcap_live_dump(fp, argv[1], atoi(argv[2]), atoi(argv[3]))==-1){printf("Unable to start the dump, %s\n", pcap_geterr(fp));return -1;}}else{/* Open the device */if ( (fp= pcap_open_live(argv[1], 100, 1, 20, errbuf) ) == NULL){fprintf(stderr,"\nError opening adapter\n");return -1;}/* Start the dump */if(pcap_live_dump(fp, argv[0], atoi(argv[1]), atoi(argv[2]))==-1){printf("Unable to start the dump, %s\n", pcap_geterr(fp));return -1;}}/* Wait until the dump finishes, i.e. when max_size or max_packs is reached*/pcap_live_dump_ended(fp, TRUE);/* Close the adapter, so that the file is correctly flushed */pcap_close(fp);return 0; }除了設(shè)置限制的可能性之外,pcap_live_dump()和pcap_dump()之間的區(qū)別是性能。pcap_live_dump()利用了WinPcap NPF驅(qū)動(dòng)程序的功能(參見NPF驅(qū)動(dòng)程序內(nèi)部手冊(cè))從內(nèi)核級(jí)寫入轉(zhuǎn)儲(chǔ),最小化上下文切換和內(nèi)存副本的數(shù)量。
顯然,由于此功能目前在其他操作系統(tǒng)上不可用,因此pcap_live_dump()是WinPcap特有的,僅在Win32下存在。
總結(jié)
以上是生活随笔為你收集整理的c++ winpcap开发(7)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: libevent编程疑难解答
- 下一篇: Linux_Environment_Re