学习PF_RING的过程中,发现对libpcap进行重载。所以回到基础再次学习了一下pcap抓包原理。
这篇文章写的就非常清晰《libpcap实现机制及接口函数》
这里实现的包捕获机制是在数据链路层增加一个旁路处理,并不干扰系统自身的网路协议栈的处理,对发送和接收的数据包通过Linux内核做过滤和缓冲处理,最后直接传递给上层应用程序。因此libpcap在捕获到达网卡的数据包后绕开了传统linux协议栈处理,直接使用链路层PF_PACKET协议族原始套接字方式向用户空间传递报文。
打开网络设备 > 设置过滤规则 > 捕获数据 > 用户态处理 > 关闭网络设备
class mod_pcap()
直接以类的风格实现了三板斧函数:setup、dispatch、teardown。
#ifndef _MOD_PCAP_H_
#define _MOD_PCAP_H_
#include
#include
class mod_pcap
{
private:
std::string _dev;
pcap_t *_pcap = NULL;
int _c_snaplen = 64 * SIZE_KB;
int _c_promisc = 0;
int _c_to_ms = 100;
char _errbuf[PCAP_ERRBUF_SIZE] = {0};
public:
~mod_pcap()
{
teardown();
}
int setup(const std::string &dev,
const std::string &filter)
{
int res = -1;
struct bpf_program fp;
_dev = dev;
_pcap = pcap_open_live(dev.c_str(), _c_snaplen, _c_promisc, _c_to_ms, _errbuf);
if (!_pcap) {
printf("pcap_open_live: %s, [%s]\n", dev.c_str(), _errbuf);
return -1;
}
res = pcap_compile(_pcap, &fp, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN);
if (0 != res) {
printf("pcap_compile: %s, [%s]\n", filter.c_str(), _errbuf);
return -1;
}
printf("pcap_compile: %s, %s\n", dev.c_str(), filter.c_str());
res = pcap_setfilter(_pcap, &fp);
if (0 != res) {
printf("pcap_setfilter\n");
return -1;
}
return 0;
}
int dispatch(pcap_handler cb, uint8_t *args)
{
return pcap_loop(_pcap, 0, cb, args);
}
void teardown()
{
if (_pcap) {
(void)pcap_close(_pcap);
_pcap = NULL;
}
}
};
#endif // #ifndef _MOD_PCAP_H_
主函数使用了该接口,在eth0上面抓取UDP报文,打印出抓取长度:
#include "mod_pcap.h"
static void on_handle_pkt(uint8_t *args, const struct pcap_pkthdr *ph, const uint8_t *sp)
{
printf("Capture: %u, len: %u\n", ph->caplen, ph->len);
}
int main(int argc, char *argv[])
{
class mod_pcap pcap;
if (pcap.setup("eth0", "udp") == 0) {
pcap.dispatch(on_handle_pkt, NULL);
pcap.teardown();
}
exit(EXIT_SUCCESS);
}
实验结果如下:
1、使用iperf
工具打1KB的payload,小于MTU:
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
Capture: 1066, len: 1066
2、使用iperf
工具打8KB的payload,大于MTU,libpcap是按数据帧为单位抓取的:
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 834, len: 834
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 834, len: 834
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 1514, len: 1514
Capture: 834, len: 834
本文提供了快速上手测试的一个demo,方便大家理解libpcap抓包的效果。