首先,该文章不限定OS不单针对Winpcap或者libpcap,对于Winpcap的特殊扩展使用了PS标示。
我以前使用过Winpcap,但并没有对pcap开发进行过深入解析。希望这篇文章可以深入浅出的讲清楚这个问题。Pcap是什么?它是个监视器,或者发生器,它可以监视,也可以发生,但它不能拦截。它可以在网络上增加一些消息,但不能阻止已经存在的消息。
获取设备列表:
int pcap_findalldevs (pcap_if_t **alldevsp, char *errbuf)
构造一个可打开的网络设备的列表
-1表示错误,buf中会有错误消息;0表示成功。能被查找到的设备是能被打开的设备。
PS:下面这个函数是Windows平台下的扩展,不是非常建议使用这个函数。
int pcap_findalldevs_ex (char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf)
创建一个网络设备列表,它们可以由 pcap_open()打开。
对比上面的函数,这个扩展表现在参数上,从上面可以看出来,前面两个参数是增加的,那么这个增加的参数就使打开查找远端网卡成为可能。这两个参数的规则,请查看Winpcap相关文档。
打开设备
pcap_t *pcap_open_live (const char *device, int snaplen, int promisc, int to_ms, char *ebuf)
在网络中打开一个活动的捕获
第一个参数是从查找查到的设备名,snaplen为最大抓包长度,65535可以满足大多数的应用,promisc代表打开设备的模式,当选择混杂模式的情况下,可以抓下所有的包。To_ms,抓包的超时时间。
PS:Winpcap扩展接口
pcap_t * pcap_open (const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf)
打开一个用来捕获或发送流量(仅WinPcap)的通用源。
打开一个文件
pcap_t * pcap_open_offline (const char *fname, char *errbuf)
打开一个 tcpdump/libpcap 格式的存储文件,来读取数据包。
过滤器的使用
int pcap_compile (pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
编译数据包过滤器,将程序中高级的过滤表达式,转换成能被内核级的过滤引擎所处理的东西。 (参见 过滤表达式语法)第一个参数代表设备句柄,第二个参数为输出参数,为编译完成的过滤条件,第三个参数代表过滤条件的表达式,第四个参数表示是否优化,1是个不错的选择,第五个参数为网卡的掩码。
int pcap_setfilter (pcap_t *p, struct bpf_program *fp)
在捕获过程中绑定一个过滤器
回调抓包
int pcap_dispatch (pcap_t *p, int cnt, pcap_handler callback, u_char *user)
收集一组数据包
int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user)
收集一组数据包
typedef void(*) pcap_handler (u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)
接受数据包的回调函数的原型
当适配器被打开,捕获工作就可以用 pcap_dispatch() 或 pcap_loop()进行。 这两个函数非常的相似,区别就是 pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回,只有当 cnt 数据包被捕获,所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_loop()对于我们这个简单的范例来说,可以满足需求,不过, pcap_dispatch() 函数一般用于比较复杂的程序中。
这两个函数都有一个 回调 参数, packet_handler指向一个可以接收数据包的函数。 这个函数会在收到每个新的数据包并收到一个通用状态时被libpcap所调用 ( 与函数 pcap_loop() 和 pcap_dispatch() 中的 user 参数相似),数据包的首部一般有一些诸如时间戳,数据包长度的信息,还有包含了协议首部的实际数据。 注意:冗余校验码CRC不再支持,因为帧到达适配器,并经过校验确认以后,适配器就会将CRC删除,与此同时,大部分适配器会直接丢弃CRC错误的数据包,所以,Pcap没法捕获到它们。
非回调抓包
u_char * pcap_next (pcap_t *p, struct pcap_pkthdr *h)
返回下一个可用的数据包
int pcap_next_ex (pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data)
从一个设备接口,或从一个脱机文件中,读取一个数据包
第一个参数代表设备句柄,第二个参数代表包头,第三个参数代表数据。从我对参数的理解来看,无论是回调或者非回调,内存都是pcap管理的,且这些内存是复用的,没有任何证据表明pcap_next_ex这类的接口是线程安全的,至少参数不满足线程安全的要求。所以我理解的抓包都需要在单线程下进行。如果需要延迟分析的话,需要把所有的数据另行缓存,pcap是不会做这些工作的。
对比之下,为什么我们要用 pcap_next_ex() 代替以前的 pcap_next()? 因为 pcap_next() 有一些不好的地方。首先,它效率低下,尽管它隐藏了回调的方式,但它依然依赖于函数 pcap_dispatch()。第二,它不能检测到文件末尾这个状态(EOF),因此,如果数据包是从文件读取来的,那么它就不那么有用了。
发送数据包
发送单个数据包
int pcap_sendpacket (pcap_t *p, u_char *buf, int size)
发送一个原始数据包
第一个参数为设备句柄,第二个数据为缓存,第三个数据为buffer的大小。
统计
int pcap_stats (pcap_t *p, struct pcap_stat *ps)
返回当前捕获的统计信息
PS:对Winpcap,对于统计来说,pcap使用pcap_setmode直接将模式设置到驱动层,通过驱动层实现对统计的优化。
关闭设备
pcap_close
关闭查询的设备
pcap_freealldevs
pcap的底层实现
pcap在我们看来非常底层,其实并不是这样。Pcap对网卡的调用,以来Cace technologies的packet.dll。而packet.dll的实现使用的是windows底层API——DeviceIOControl。
相关资源:
http://www.coffeecat.net.cn/winpcap/html/index.html
http://blog.csdn.net/bhw98/archive/2003/05/27/19660.aspx DeviceIOControl使用详解