ファイルのフォーマットはlibpcapのものです。このフォーマットはとてもシンプルで、キャプチャされたパケット内のデータをバイナリフォー ムで含んでいますし、WinDump、EtherealやSnortといった多くのネットワークツールによって標準的に使用されています。
ダンプファイルにパケットを保存する
まず第一に、libpcapフォーマットにパケットを書き込む方法を見てみましょう。
続くサンプルは、選択されたインターフェイスからパケットをキャプチャして、それらをユーザーが設定したファイル名を持つファイルに書き込む、と いうものです。
#include "pcap.h" /* prototype of the packet handler */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
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 */ 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);
else printf(" (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 */ pcap_freealldevs(alldevs);
return -1;
}
/* Jump to the selected adapter */ for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
/* Open the adapter */ if ( (adhandle = pcap_open_live(d->name, // name of the device 65536, // portion of the packet to capture. // 65536 grants that the whole packet will be captured on all the MACs. 1, // promiscuous mode 1000, // read timeout errbuf // error buffer ) ) == NULL) { fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
/* 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...\n", d->description);
/* At this point, we don't need any more 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);
}
ご覧の通り、プログラムの構造は以前のレッスンに出てきたものと非常に似ています。違いは以下の通りです:
ダンプライフからのパケットの読み込み
さてダンプファイルが利用可能になったところで、その内容の読み込み方を見て行きましょう。続くコードはlibpcapダンプをオープンし、その 中の全てのパケットを表示するものです。ファイルは pcap_open_offline() 二よって開かれ、そして通常のpcap_loop() はパケット間の循環のために使用されます。見ての通り、オフラインキャプチャからパケットを読み込むことは、物理インターフェイスからそれらを受け取るの にほぼ一致しています。
#include <stdio.h> #include <pcap.h> #define LINE_LEN 16 void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *);
main(int argc, char **argv) {
pcap_t *fp;
char errbuf[PCAP_ERRBUF_SIZE];
if(argc != 2){
printf("usage: %s filename", argv[0]);
return -1;
}
/* Open a capture file */ if ( (fp = pcap_open_offline(argv[1], errbuf) ) == NULL)
{
fprintf(stderr,"\nError opening dump file\n");
return -1;
}
// read and dispatch packets until EOF is reached pcap_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;
/* 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");
}
続くサンプルは前回のものと同じ目的を持っていますが、このサンプルではpcap_loop() のコールバックメソッドを使用する代わりに pcap_next_ex() が使用されています。
#include <stdio.h> #include <pcap.h> #define LINE_LEN 16 main(int argc, char **argv) {
pcap_t *fp;
char errbuf[PCAP_ERRBUF_SIZE];
struct pcap_pkthdr *header;
u_char *pkt_data;
u_int i=0;
int res;
if(argc != 2){
printf("usage: %s filename", argv[0]);
return -1;
}
/* Open a capture file */ if ( (fp = pcap_open_offline(argv[1], errbuf) ) == NULL)
{
fprintf(stderr,"\nError opening dump file\n");
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;
}
pcap_live_dumpでダンプファイルにパケットを書き込む
最近のWinPcap バージョンでは、更なるネットワークトラフィックのディスク保存方法として pcap_live_dump() 関数が用意されています。pcap_live_dump() は三つのパラメータを取ります。ファイル名、そのファイルの差大サイズ(バイト)、そしてファイルに収容可能なパケットの最大量です。0であることは、両 方の値(最大ファイルサイズと最大パケット量)に制限がないということ意味します。このプログラムが、保存されるトラフィックのサブセットを規定するpcap_live_dump() が呼び出される前に、フィルタ(pcap_setfilter() による。トラフィックのフィルタリング を参照) をセットできることに注目してください。
pcap_live_dump() はノンブロッキングなので、ダンプを始めてすぐに値を返します。ダンプのプロセスは、ファイルサイズかパケット量が最大に達してしまうまで、非同期的に続 きます。
アプリケーションは、pcap_live_dump_ended() によってダンプの終了を待機かどうかチェックすることができます。両方の最大値がどちらもゼロの場合には、syncパラメータが非0の値では、この関数はあなたのアプリケーションを 無限にブロック(アプリケーションからの指示を受け付けない)してしまいますので注意してください。
pcap_live_dump() と pcap_dump() の違いとして、制限の設定は別として、パフォーマンスが挙げられます。pcap_live_dump() はコンテキストスイッチとメモリーコピーの数を最小限にして、カーネルレベルからダンプを書き込むという、WinPcap NPFドライバ(NPF ドライバの内部マニュアル 参照)の能力を利用します。
明らかに、この機能は今のところ他のOSには対応していないので、pcap_live_dump() はWInPcap固有の機能であり、Win32環境下でだけ存在するものです。
documentation. Copyright (c)2002-2003 Politecnico di Torino.
2005 translated by Telebusiness,Inc.
All rights reserved.