主要功能

  • 三层数据包转发: l3fwd 应用程序接收数据包,解析 IP 头部,根据目的 IP 地址查找路由表,并将数据包转发到相应的输出端口。
  • 路由表管理: l3fwd 包含一个路由表,用于存储网络前缀和相应的下一跳信息。应用程序在初始化时加载路由表,并在运行时根据路由表进行转发决策。
  • 多核并行处理: l3fwd 可以利用 DPDK 的多核架构,分配多个核心来并行处理数据包,以提高转发性能。
  • 多种转发模式: l3fwd 支持多种转发模式,包括基于最长前缀匹配(LPM)的路由和基于哈希表的转发。

l3fwd 支持多种类型的路由表,主要包括:

  • 最长前缀匹配(LPM)路由表:
    基于前缀长度的查找,适用于需要精确匹配的路由场景。
  • 基于哈希表的路由表:
    使用哈希表进行快速查找,适用于需要快速查找和更新的场景。
  • 基于Trie的数据结构:
    使用Trie数据结构进行高效的前缀匹配,适用于大规模路由表的场景。

struct rte_lpm {
    struct rte_lpm_tbl_entry *tbl;    // 指向 LPM 查找表的指针
    struct rte_lpm_tbl_entry *tbl8;   // 指向 LPM 查找表的辅助表的指针
    uint32_t max_rules;               // 路由表中允许的最大规则数
    uint32_t number_tbl8s;            // 辅助表的数量
    uint32_t used_rules;              // 当前已使用的规则数量
    uint32_t used_tbl8s;              // 当前已使用的辅助表数量
    uint32_t rule_info[RTE_LPM_MAX_RULES]; // 存储路由规则的信息
    uint32_t tbl8_pool[RTE_LPM_TBL8_NUM_GROUPS]; // 辅助表的池
    uint32_t tbl8_avail;              // 可用的辅助表数量
    uint32_t rule_lifetime[RTE_LPM_MAX_RULES]; // 路由规则的生命周期
    struct rte_lpm_rule *rules;       // 指向路由规则的数组
    struct rte_lpm_rule *rule_list;   // 路由规则的链表
};

rte_lpm_tbl_entry 是 LPM 查找表中的条目结构,用于存储前缀和下一跳信息。

struct rte_lpm_tbl_entry {
    uint8_t next_hop;    // 下一跳信息
    uint8_t depth;       // 前缀长度
    uint8_t valid;       // 条目是否有效
    uint8_t valid_group; // 辅助表是否有效
};

int rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth, uint8_t next_hop);

参数说明:
lpm:指向 LPM 路由表的指针。
ip:要添加的网络前缀的 IP 地址。
depth:网络前缀的长度(即子网掩码的位数)。
next_hop:下一跳端口或接口的标识符。


#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_malloc.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_lpm.h>
#include <signal.h>

#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
#define IPV4_ADDR(a, b, c, d) ((uint32_t)(((a) & 0xff) << 24) | (((b) & 0xff) << 16) | (((c) & 0xff) << 8) | ((d) & 0xff))

static volatile bool force_quit;

struct rte_mempool *mbuf_pool;
struct rte_lpm *lpm_table;

// 信号处理函数,用于处理终止信号
static void signal_handler(int signum) {
    if (signum == SIGINT || signum == SIGTERM) {
        printf("\nSignal %d received, preparing to exit...\n", signum);
        force_quit = true;
    }
}

// 初始化 LPM 路由表
static void init_lpm_table(void) {
    struct rte_lpm_config lpm_config = {
        .max_rules = 1024,
        .number_tbl8s = 256,
        .flags = 0,
    };

    lpm_table = rte_lpm_create("L3FWD_LPM_TABLE", rte_socket_id(), &lpm_config);
    if (lpm_table == NULL) {
        rte_exit(EXIT_FAILURE, "Cannot create LPM table\n");
    }

    // 添加路由规则
    rte_lpm_add(lpm_table, IPV4_ADDR(192, 168, 1, 0), 24, 1); // 192.168.1.0/24 -> port 1
    rte_lpm_add(lpm_table, IPV4_ADDR(10, 0, 0, 0), 8, 2);     // 10.0.0.0/8 -> port 2
}

// 主循环函数,处理数据包并实现 L3 转发
static void l3fwd_main_loop(void) {
    struct rte_mbuf *pkts_burst[BURST_SIZE];
    unsigned lcore_id;

    lcore_id = rte_lcore_id();

    printf("Entering main loop on lcore %u\n", lcore_id);
    while (!force_quit) {
        // 从每个端口接收数据包并处理
        for (int port = 0; port < RTE_MAX_ETHPORTS; port++) {
            uint16_t nb_rx = rte_eth_rx_burst(port, 0, pkts_burst, BURST_SIZE);
            if (nb_rx == 0)
                continue;

            // 处理每个接收到的数据包
            for (int i = 0; i < nb_rx; i++) {
                struct rte_mbuf *m = pkts_burst[i];
                struct rte_ipv4_hdr *ipv4_hdr;
                uint32_t dst_ip;
                uint8_t next_hop;

                // 解析 IP 头部
                ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
                dst_ip = rte_be_to_cpu_32(ipv4_hdr->dst_addr);

                // 查找 LPM 路由表
                if (rte_lpm_lookup(lpm_table, dst_ip, &next_hop) == 0) {
                    // 转发数据包到下一跳端口
                    rte_eth_tx_burst(next_hop, 0, &m, 1);
                } else {
                    // 丢弃无法找到路由的包
                    rte_pktmbuf_free(m);
                }
            }
        }
    }
}

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");

    // 配置以太网设备
    struct rte_eth_conf port_conf = {
        .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
    };

    for (int port = 0; port < RTE_MAX_ETHPORTS; port++) {
        // 配置端口
        if (rte_eth_dev_configure(port, 1, 1, &port_conf) < 0)
            rte_exit(EXIT_FAILURE, "Cannot configure eth dev\n");

        // 设置接收队列
        if (rte_eth_rx_queue_setup(port, 0, 128, rte_eth_dev_socket_id(port), NULL, mbuf_pool) < 0)
            rte_exit(EXIT_FAILURE, "Cannot setup rx queue\n");

        // 设置发送队列
        if (rte_eth_tx_queue_setup(port, 0, 128, rte_eth_dev_socket_id(port), NULL) < 0)
            rte_exit(EXIT_FAILURE, "Cannot setup tx queue\n");

        // 启动端口
        if (rte_eth_dev_start(port) < 0)
            rte_exit(EXIT_FAILURE, "Cannot start eth dev\n");
    }

    // 初始化 LPM 路由表
    init_lpm_table();

    // 进入主循环,处理数据包并实现 L3 转发
    l3fwd_main_loop();

    // 清理资源
    for (int port = 0; port < RTE_MAX_ETHPORTS; port++) {
        rte_eth_dev_stop(port);
        rte_eth_dev_close(port);
    }

    rte_eal_cleanup();

    return 0;
}

putao
8 声望1 粉丝

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