12.4 数据包文件读取的幕后
数据包文件读取的主要函数调用关系如图12-9所示。
图 12-9 数据包文件读取的主要函数调用关系
在pcap_open函数中,如果打开的源是文件,那么就会调用pcap_open_offline函数来打开该文件,该函数的具体实现代码如下:
pcap_tpcap_open(const charsource,int snaplen,int fags,int read_timeout,
struct pcap_rmtauthauth,charerrbuf)
{
……
/分析源的种类,是文件、本地主机接口还是远程主机接口类型/
if(pcap_parsesrcstr(source,&type,host,
port,name,errbuf)==-1)
{
return NULL;
}
/依据不同的源类型做不同的处理/
switch(type)
{
//文件类型,直接调用pcap_open_offline函数处理
case PCAP_SRC_FILE:
fp=pcap_open_offine(name,errbuf);
break;
……
}
……
}
上述代码中,pcap_open_offline函数用来打开一个savefiles文件,从而读取数据包,其原型如下:
pcap_tpcap_open_offine(const charfname,char*errbuf)
上述函数中,参数fname描述需要打开的文件,-表示stdin文件。注意,在Windows平台上,该文件应该以二进制模式打开。
参数errbuf用来返回错误信息,仅在pcap_open_offline或pcap_fopen_offline执行失败并返回NULL时,才会设置错误信息。
pcap_open_offline函数的具体实现代码如下:
pcap_tpcap_open_offine(const charfname,char*errbuf)
{
FILE*fp;
pcap_t*p;
if(fname[0]=='-'&&fname[1]=='\0'){
/从标准输入读取,因此把它设置为二进制模式/fp=stdin;
SET_BINMODE(fp);
}
else
{
/savefiles也是二进制模式/
fp=fopen(fname,"rb");//打开文件
if(fp==NULL){
snprintf(errbuf,PCAP_ERRBUF_SIZE,"%s:%s",fname,
pcap_strerror(errno));
return(NULL);
}
}
/打开并读取一个已存在的文件流/
p=pcap_fopen_offine(fp,errbuf);
if(p==NULL){
if(fp!=stdin)
fclose(fp);
}
return(p);
}
1.pcap_fopen_offline函数
pcap_fopen_offline函数用于打开并读取一个已存在的文件流,其原型如下:
static pcap_tpcap_fopen_offine(FILEfp,char*errbuf)
{
register pcap_t*p;
struct pcap_fle_header hdr;
size_t amt_read;
bpf_u_int32 magic;
int linklen;
p=(pcap_t)malloc(sizeof(p));
if(p==NULL){
strlcpy(errbuf,"out of swap",PCAP_ERRBUF_SIZE);
return(NULL);
}
memset((char)p,0,sizeof(p));
/读取文件头信息/
amt_read=fread((char*)&hdr,1,sizeof(hdr),fp);
if(amt_read!=sizeof(hdr))
{//出错处理
……
goto bad;
}
/判断文件格式/
magic=hdr.magic;
if(magic!=TCPDUMP_MAGIC&&
magic!=KUZNETZOV_TCPDUMP_MAGIC)
{
magic=SWAPLONG(magic);//交换字节顺序,再判断一次
if(magic!=TCPDUMP_MAGIC&&
magic!=KUZNETZOV_TCPDUMP_MAGIC){
snprintf(errbuf,PCAP_ERRBUF_SIZE,
"bad dump fle format");
goto bad;
}
p->sf.swapped=1;
swap_hdr(&hdr);
}
/此处只分析magic为TCPDUMP_MAGIC的情况/
p->sf.hdrsize=sizeof(struct pcap_sf_pkthdr);
if(hdr.version_major<PCAP_VERSION_MAJOR){
snprintf(errbuf,PCAP_ERRBUF_SIZE,"archaic fle format");
goto bad;
}
p->tzoff=hdr.thiszone;
p->snapshot=hdr.snaplen;
p->linktype=linktype_to_dlt(LT_LINKTYPE(hdr.linktype));
p->linktype_ext=LT_LINKTYPE_EXT(hdr.linktype);
p->sf.rfle=fp;
/为结构体pcap_pkthdr分配空间,其将被pcap_read_ex函数使用/
p->bufsize=hdr.snaplen+sizeof(struct pcap_pkthdr);
/对齐数据链路层头位置/
switch(p->linktype){
case DLT_EN10MB:
linklen=14;
break;
case……
}
if(p->bufsize<0)
p->bufsize=BPF_MAXBUFSIZE;
p->sf.base=(u_char*)malloc(p->bufsize+BPF_ALIGNMENT);
if(p->sf.base==NULL){
strlcpy(errbuf,"out of swap",PCAP_ERRBUF_SIZE);
goto bad;
}
p->buffer=p->sf.base+BPF_ALIGNMENT-(linklen%BPF_ALIGNMENT);
p->sf.version_major=hdr.version_major;
p->sf.version_minor=hdr.version_minor;
/*
*处理不同版本之间存在的差异,
*version 2.3中数据包头的caplen与len成员相互交换的情况与其他版本不一样,
*此外DG/UX tcpdump把版本字段写为543.0
*/
switch(hdr.version_major){
case 2:
if(hdr.version_minor<3)
p->sf.lengths_swapped=SWAPPED;
else if(hdr.version_minor==3)
p->sf.lengths_swapped=MAYBE_SWAPPED;
else
p->sf.lengths_swapped=NOT_SWAPPED;
break;
case 543:
p->sf.lengths_swapped=SWAPPED;
break;
default:
p->sf.lengths_swapped=NOT_SWAPPED;
break;
}
/设置具体的操作函数,不过大部分都是无效的/
p->read_op=pcap_offine_read;
p->inject_op=sf_inject;
p->setflter_op=install_bpf_program;
p->setdirection_op=sf_setdirection;
p->set_datalink_op=NULL;
p->getnonblock_op=sf_getnonblock;
p->setnonblock_op=sf_setnonblock;
p->stats_op=sf_stats;
ifdef WIN32
p->setbuff_op=sf_setbuff;
p->setmode_op=sf_setmode;
p->setmintocopy_op=sf_setmintocopy;
endif
p->cleanup_op=sf_cleanup;
p->activated=1;
return(p);
bad:
free(p);
return(NULL);
}
pcap_fopen_offline函数中所设置的具体操作函数大部分都是无效的,如sf_stats函数的实现代码如下:
static int sf_stats(pcap_tp,struct pcap_statps)
{
snprintf(p->errbuf,PCAP_ERRBUF_SIZE,
"Statistics aren't available from savefles");
return(-1);
}
数据包的实际读取操作是在pcap_loop函数中调用pcap_offline_read函数实现的,具体代码如下:
int pcap_loop(pcap_tp,int cnt,pcap_handler callback,u_charuser)
{
……
for(;;)//循环读取
{
if(p->sf.rfle!=NULL)//读取文件
{
/如果返回0,则表明到达EOF(文件结尾)处,将不会再循环/
n=pcap_offine_read(p,cnt,callback,user);
}
……
}
}
2.pcap_offline_read函数
pcap_offline_read函数用于读取转储文件的数据包,其具体的实现代码如下:
int pcap_offine_read(pcap_tp,int cnt,pcap_handler callback,u_charuser)
{
struct bpf_insn*fcode;
int status=0;
int n=0;
while(status==0){
struct pcap_pkthdr h;
/是否调用了pcap_breakloop函数?/
if(p->break_loop){
……
}
status=sf_next_packet(p,&h,p->buffer,p->bufsize);
if(status){
……
}
/过滤数据包/
if((fcode=p->fcode.bf_insns)==NULL||
bpf_flter(fcode,p->buffer,h.len,h.caplen))
{
(*callback)(user,&h,p->buffer);
if(++n>=cnt&&cnt>0)
break;
}
}
return(n);
}
上述代码中,sf_next_packet函数用于读取转储文件并返回下一个数据包,具体实现代码如下:
static int sf_next_packet(pcap_tp,struct pcap_pkthdrhdr,
u_char*buf,u_int bufen)
在上面的代码中,参数hdr返回数据包头,参数buf返回数据包内容。
sf_next_packet函数执行成功则返回0,若返回SFERR_EOF则说明到达文件尾部,而返回SFERR_TRUNC则表示遇到一个不完整的包。