本文采用 PF_RING(8.0) 发行版本为例:
libpcap 1.9.1(PF_RING修改支持PF_RING的发行版本,PF_RING(8.0)编译源码包内已经自带,自行编译即可)
以下是C++语言不引入 pfring.h 头文件,建议使用到的一些关键函数,“pcap_get_pfring” 宏可以从 pcap_t 类型之中获取到 pf_ring 的句柄(实为一个内存结构地址标识符)
#ifndef PCAP_OPENFLAG_PROMISCUOUS
#define PCAP_OPENFLAG_PROMISCUOUS 1
#endif
#ifdef HAVE_PF_RING
// #include <pfring.h>
// #include <linux/pf_ring.h>
#ifndef PF_RING_PROMISC
#define PF_RING_PROMISC (1 << 3) /**< pfring_open() flag: The device is open in promiscuous mode. */
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
rx_and_tx_direction = 0,
rx_only_direction,
tx_only_direction
} packet_direction;
typedef enum {
send_and_recv_mode = 0,
send_only_mode,
recv_only_mode
} socket_mode;
void* pfring_open(const char *device_name, u_int32_t caplen, u_int32_t flags);
void pfring_close(void *ring);
void pfring_config(u_short cpu_percentage);
void pfring_breakloop(void *ring);
int pfring_set_direction(void *ring, packet_direction direction);
int pfring_set_socket_mode(void *ring, socket_mode mode);
int pfring_enable_ring(void *ring);
int pfring_disable_ring(void *ring);
int pfring_set_bpf_filter(void *ring, const char *filter_buffer);
int pfring_remove_bpf_filter(void *ring);
int pfring_set_application_name(void *ring, const char *name);
int pfring_send(void *ring, char *pkt, u_int pkt_len, u_int8_t flush_packet);
// pcap-int.h
#define pcap_get_pfring(p) (p ? *(void**)((char*)p + 648u) : NULL) /* bpf_filter */
#define pcap_fld_initialized(p) (*(uint8_t*)((char*)p))
#define pcap_fld_enabled(p) (*(uint8_t*)((char*)p + sizeof(uint8_t)))
#ifdef __cplusplus
}
#endif
#endif
通过 libpcap(pf_ring)版:pcap_live_open 函数打开网络设备,需要兼容 linux 系统上没有运行 pf_ring.ko 内核驱动模块的场景。
以下参数为最佳吞吐性能,调试参数,建议不要修改相关的参数,否则性能下降,自行担待。
if (device_.empty()) {
return NULL;
}
int readTimeout = 0;
int flags = PCAP_OPENFLAG_PROMISCUOUS;
#ifdef HAVE_PF_RING
pfring_config(99);
void* ring = pfring_open(device_.data(), 65536, PF_RING_PROMISC);
if (ring) {
pfring_close(ring);
readTimeout = INT32_MAX;
}
else {
readTimeout = 1;
}
flags |= PCAP_OPENFLAG_MAX_RESPONSIVENESS;
#endif
pcap_t* device = pcap_open_live(
device_.data(), // name of the device
65536, // portion of the packet to capture
// 65536 guarantees that the whole packet
// will be captured on all the link layers
flags, // promiscuous mode
readTimeout, // read timeout
errbuf); // error buffer
配置由 libpcap 打开的 pf_ring 句柄实例。
#ifdef HAVE_PF_RING
ring = pcap_get_pfring(device);
if (ring) {
if (!pcap_fld_initialized(ring)) {
pcap_close(device);
return NULL;
}
// https://www.ntop.org/guides/pf_ring_api/pfring_8h.html#a175b6d8450f04848226a9aac99fefbe5
char sz[1000 + 1];
int dw = readlink("/proc/self/exe", sz, 1000);
sz[dw] = '\x0';
if (pfring_set_application_name(ring, sz)) {
pcap_close(device);
return NULL;
}
if (pfring_set_direction(ring, rx_only_direction)) {
pcap_close(device);
return NULL;
}
if (pfring_set_socket_mode(ring, send_and_recv_mode)) {
pcap_close(device);
return NULL;
}
if (pfring_set_bpf_filter(ring, rules)) {
pcap_close(device);
return NULL;
}
if (!pcap_fld_enabled(ring)) {
if (pfring_enable_ring(ring)) {
pcap_close(device);
return NULL;
}
}
}
#endif
极微弱发包性能提升,蚊子再小也是肉:
int ethernet_output(struct eth_hdr* eth, int len) {
if (!eth || len < sizeof(*eth)) {
return -1;
}
pcap_t* pcap = ETHERNET_NIC;
if (!pcap) {
return -1;
}
#ifdef HAVE_PF_RING
void* ring = pcap_get_pfring(pcap);
if (ring) {
return pfring_send(ring, (char*)eth, len, 1) == -1 ? -1 : 0;
}
#endif
return pcap_inject(pcap, (u_char*)eth, len) == -1 ? -1 : 0;
}
详细可以参考文档:
PF_RING API: /tmp/PF_RING/userland/lib/pfring.h File Reference