主要功能
- 三层数据包转发: 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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。