BPF 程序通常以字节码的形式存在,可以通过编译器生成。以下是一个简单的 BPF 程序,展示了如何定义一个过滤 IPv4 数据包的 BPF 程序。

static const struct rte_bpf_prm bpf_prm = {
    .prog = (struct rte_bpf_insn[]){
        { .code = BPF_LD + BPF_W + BPF_ABS, .dst_reg = 0, .src_reg = 0, .off = 12, .imm = 0 }, // Load Ethernet type
        { .code = BPF_JMP + BPF_JEQ + BPF_K, .dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0x0800 }, // Check if IPv4
        { .code = BPF_RET + BPF_K, .dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0xffffffff }, // Accept packet
        { .code = BPF_RET + BPF_K, .dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0x0 }, // Reject packet
    },
    .nb_ins = 4,
    .stack_sz = 0,
    .arg_sz = 0,
};

dpdk,加载 BPF 程序

static struct rte_bpf *load_bpf_program(void) {
    struct rte_bpf *bpf = rte_bpf_load(&bpf_prm);
    if (!bpf) {
        printf("Failed to load BPF program\n");
        return NULL;
    }
    return bpf;
}

处理数据包

static void process_packets(uint16_t port_id, struct rte_bpf *bpf) {
    struct rte_mbuf *bufs[BURST_SIZE];
    uint16_t nb_rx;
    while (!force_quit) {
        // 从指定端口接收数据包
        nb_rx = rte_eth_rx_burst(port_id, 0, bufs, BURST_SIZE);
        if (nb_rx > 0) {
            for (int i = 0; i < nb_rx; i++) {
                struct rte_mbuf *mbuf = bufs[i];
                uint64_t result;
                // 执行 BPF 程序
                int ret = rte_bpf_exec(bpf, mbuf, &result);
                if (ret == 0 && result != 0) {
                    // 处理通过 BPF 过滤的数据包
                    printf("Packet accepted\n");
                } else {
                    // 丢弃未通过 BPF 过滤的数据包
                    rte_pktmbuf_free(mbuf);
                }
            }
        }
    }
}

主函数

int main(int argc, char **argv) {
    // 初始化 DPDK 环境
    int ret = rte_eal_init(argc, argv);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");

    force_quit = false;
    // 注册信号处理函数
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);

    // 创建内存池,用于存放数据包
    mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
    if (mbuf_pool == NULL)
        rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");

    uint16_t port_id = 0;
    struct rte_eth_conf port_conf = {0};
    // 配置以太网设备
    ret = rte_eth_dev_configure(port_id, 1, 1, &port_conf);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n", ret, port_id);

    // 设置接收队列
    ret = rte_eth_rx_queue_setup(port_id, 0, 128, rte_eth_dev_socket_id(port_id), NULL, mbuf_pool);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n", ret, port_id);

    // 设置发送队列
    ret = rte_eth_tx_queue_setup(port_id, 0, 128, rte_eth_dev_socket_id(port_id), NULL);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n", ret, port_id);

    // 启动以太网设备
    ret = rte_eth_dev_start(port_id);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n", ret, port_id);

    // 加载 BPF 程序
    struct rte_bpf *bpf = load_bpf_program();
    if (!bpf)
        rte_exit(EXIT_FAILURE, "Failed to load BPF program\n");

    // 处理数据包
    process_packets(port_id, bpf);

    // 停止并关闭以太网设备
    rte_eth_dev_stop(port_id);
    rte_eth_dev_close(port_id);
    // 释放内存池
    rte_mempool_free(mbuf_pool);
    // 释放 BPF 程序
    rte_bpf_destroy(bpf);

    return 0;
}

putao
5 声望0 粉丝

推动世界向前发展,改善民生。