当前位置: 首页 > 工具软件 > PF_RING > 使用案例 >

PF_RING + libpcap 兼容打开网络设备实现

沈鸿光
2023-12-01

本文采用 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

 类似资料: