Linux下Libpcap源码分析及包过滤机制.docx
- 文档编号:5427177
- 上传时间:2022-12-16
- 格式:DOCX
- 页数:7
- 大小:17.79KB
Linux下Libpcap源码分析及包过滤机制.docx
《Linux下Libpcap源码分析及包过滤机制.docx》由会员分享,可在线阅读,更多相关《Linux下Libpcap源码分析及包过滤机制.docx(7页珍藏版)》请在冰豆网上搜索。
Linux下Libpcap源码分析及包过滤机制
过滤代码的安装
前面我们曾经提到,在内核空间过滤数据包对整个捕获机制的效率是至关重要的。
早期使用SOCK_PACKET方式的Linux不支持内核过滤,因此过滤操作只能在用户空间执行(请参阅函数pcap_read_packet()代码),在《UNIX网络编程(第一卷)》(参考资料B)的第26章中对此有明确的描述。
不过现在看起来情况已经发生改变,Linux在PF_PACKET类型的socket上支持内核过滤。
Linux内核允许我们把一个名为LPF(LinuxPacketFilter)的过滤器直接放到PF_PACKET类型socket的处理过程中,过滤器在网卡接收中断执行后立即执行。
LSF基于BPF机制,但两者在实现上有略微的不同。
实际代码如下:
/*在包捕获设备上附加BPF代码[pcap-Linux.c]*/
staticint
pcap_setfilter_Linux(pcap_t*handle,structbpf_program*filter)
{
#ifdefSO_ATTACH_FILTER
structsock_fprogfcode;
intcan_filter_in_kernel;
interr=0;
#endif
/*检查句柄和过滤器结构的正确性*/
if(!
handle)
return-1;
if(!
filter){
strncpy(handle->errbuf,"setfilter:
Nofilterspecified",
sizeof(handle->errbuf));
return-1;
}
/*具体描述如下*/
if(install_bpf_program(handle,filter)<0)
return-1;
/*缺省情况下在用户空间运行过滤器,但如果
在内核安装成功,则值为1*/
handle->md.use_bpf=0;
/*尝试在内核安装过滤器*/
#ifdefSO_ATTACH_FILTER
#ifdefUSHRT_MAX
if(handle->fcode.bf_len>USHRT_MAX){
/*过滤器代码太长,内核不支持*/
fprintf(stderr,"Warning:
Filtertoocomplexforkerneln");
fcode.filter=NULL;
can_filter_in_kernel=0;
}else
#endif/*USHRT_MAX*/
{
/*Linux内核设置过滤器时使用的数据结构是sock_fprog,
而不是BPF的结构bpf_program,因此应做结构之间的转换*/
switch(fix_program(handle,&fcode)){
/*严重错误,直接退出*/
case-1:
default:
return-1;
/*通过检查,但不能工作在内核中*/
case0:
can_filter_in_kernel=0;
break;
/*BPF可以在内核中工作*/
case1:
can_filter_in_kernel=1;
break;
}
}
/*如果可以在内核中过滤,则安装过滤器到内核中*/
if(can_filter_in_kernel){
if((err=set_kernel_filter(handle,&fcode))==0)
{
/*安装成功!
!
!
*/
handle->md.use_bpf=1;
}
elseif(err==-1)/*出现非致命性错误*/
{
if(errno!
=ENOPROTOOPT&&errno!
=EOPNOTSUPP){
fprintf(stderr,"Warning:
Kernelfilterfailed:
%sn",pcap_strerror(errno));
}
}
}
/*如果不能在内核中使用过滤器,则去掉曾经可能在此socket
上安装的内核过滤器。
主要目的是为了避免存在的过滤器对数据包过滤的干扰*/
if(!
handle->md.use_bpf)
reset_kernel_filter(handle);[pcap-Linux.c]
#endif
}
/*把BPF代码拷贝到pcap_t数据结构的fcode上*/
intinstall_bpf_program(pcap_t*p,structbpf_program*fp)
{
size_tprog_size;
/*首先释放可能已存在的BPF代码*/
pcap_freecode(&p->fcode);
/*计算过滤代码的长度,分配内存空间*/
prog_size=sizeof(*fp->bf_insns)*fp->bf_len;
p->fcode.bf_len=fp->bf_len;
p->fcode.bf_insns=(structbpf_insn*)malloc(prog_size);
if(p->fcode.bf_insns==NULL){
snprintf(p->errbuf,sizeof(p->errbuf),
"malloc:
%s",pcap_strerror(errno));
return(-1);
}
/*把过滤代码保存在捕获句柄中*/
memcpy(p->fcode.bf_insns,fp->bf_insns,prog_size);
return(0);
}
/*在内核中安装过滤器*/
staticintset_kernel_filter(pcap_t*handle,structsock_fprog*fcode)
{
inttotal_filter_on=0;
intsave_mode;
intret;
intsave_errno;
/*在设置过滤器前,socket的数据包接收队列中可能已存在若干数据包。
当设置过滤器后,
这些数据包极有可能不满足过滤条件,但它们不被过滤器丢弃。
这意味着,传递到用户空间的头几个数据包不满足过滤条件。
注意到在用户空间过滤这不是问题,因为用户空间的过滤器是在包进入队列后执行的。
libpcap解决这个问题的方法是在设置过滤器之前,
首先读完接收队列中所有的数据包。
具体步骤如下。
*/
/*为了避免无限循环的情况发生(反复的读数据包并丢弃,
但新的数据包不停的到达),首先设置一个过滤器,阻止所有的包进入*/
setsockopt(handle->fd,SOL_SOCKET,SO_ATTACH_FILTER,
&total_fcode,sizeof(total_fcode);
/*保存socket当前的属性*/
save_mode=fcntl(handle->fd,F_GETFL,0);
/*设置socket它为非阻塞模式*/
fcntl(handle->fd,F_SETFL,save_mode|O_NONBLOCK);
/*反复读队列中的数据包,直到没有数据包可读。
这意味着接收队列已被清空*/
while(recv(handle->fd,&drain,sizeofdrain,MSG_TRUNC)>=0);
/*恢复曾保存的socket属性*/
fcntl(handle->fd,F_SETFL,save_mode);
/*现在安装新的过滤器*/
setsockopt(handle->fd,SOL_SOCKET,SO_ATTACH_FILTER,
fcode,sizeof(*fcode));
}
/*释放socket上可能有的内核过滤器*/
staticintreset_kernel_filter(pcap_t*handle)
{
intdummy;
returnsetsockopt(handle->fd,SOL_SOCKET,SO_DETACH_FILTER,
&dummy,sizeof(dummy));
}
Linux在安装和卸载过滤器时都使用了函数setsockopt(),其中标志SOL_SOCKET代表了对socket进行设置,而SO_ATTACH_FILTER和SO_DETACH_FILTER则分别对应了安装和卸载。
下面是Linux2.4.29版本中的相关代码:
[net/core/sock.c]
#ifdefCONFIG_FILTER
caseSO_ATTACH_FILTER:
……
/*把过滤条件结构从用户空间拷贝到内核空间*/
if(copy_from_user(&fprog,optval,sizeof(fprog)))
break;
/*在socket上安装过滤器*/
ret=sk_attach_filter(&fprog,sk);
……
caseSO_DETACH_FILTER:
/*使用自旋锁锁住socket*/
spin_lock_bh(&sk->lock.slock);
filter=sk->filter;
/*如果在socket上有过滤器,则简单设置为空,并释放过滤器内存*/
if(filter){
sk->filter=NULL;
spin_unlock_bh(&sk->lock.slock);
sk_filter_release(sk,filter);
break;
}
spin_unlock_bh(&sk->lock.slock);
ret=-ENONET;
break;
#endif
上面出现的sk_attach_filter()定义在net/core/filter.c,它把结构sock_fprog转换为结构sk_filter,最后把此结构设置为socket的过滤器:
sk->filter=fp。
其他代码
libpcap还提供了其它若干函数,但基本上是提供辅助或扩展功能,重要性相对弱一点。
我个人认为,函数pcap_dump_open()和pcap_open_offline()可能比较有用,使用它们能把在线的数据包写入文件并事后进行分析处理。
总结
1994年libpcap的第一个版本被发布,到现在已有11年的历史,如今libpcap被广泛的应用在各种网络监控软件中。
libpcap最主要的优点在于平台无关性,用户程序几乎不需做任何改动就可移植到其它unix平台上;其次,libpcap也能适应各种过滤机制,特别对BPF的支持最好。
分析它的源代码,可以学习开发者优秀的设计思想和实现技巧,也能了解到(Linux)操作系统的网络内核实现,对个人能力的提高有很大帮助。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux Libpcap 源码 分析 过滤 机制
![提示](https://static.bdocx.com/images/bang_tan.gif)