librte_fib
1.1 librte_fib 简介
librte_fib
是 Data Plane Development Kit (DPDK) 的一部分,用于实现前缀匹配和路由表查找功能。它提供了一种高效的方式来管理和查询前缀路由表,对于需要快速查找 IP 路由的应用特别有用,如网络交换机和路由器
1.2 librte_fib 原理
librte_fib
基于前缀树(也称为 Trie)或基于 Hash 表的算法来实现前缀匹配。其核心思想是将路由前缀组织成一种树形结构,使得查找过程可以通过逐层匹配前缀位来快速定位目标路由。常见的实现方式包括:
Trie:使用前缀树
(如 Patricia Trie 或 LC-Trie)来存储路由前缀。每个节点代表一个前缀,查找时从根节点开始逐级匹配。
Hash 表:
使用带前缀长度的 Hash 表来存储和查找路由条目,通过哈希函数快速定位路由项。
1.3 rte_fib.h
#ifndef _RTE_FIB_H_
#define _RTE_FIB_H_
/**
* @file
*
* RTE FIB 库。
*
* 用于IPv4最长前缀匹配的FIB(转发信息库)实现
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct rte_fib;
struct rte_rib;
/** IPv4 FIB的最大深度值。 */
#define RTE_FIB_MAXDEPTH 32
/** FIB结构类型 */
enum rte_fib_type {
RTE_FIB_DUMMY, /**< 基于RIB树的FIB */
RTE_FIB_DIR24_8 /**< 基于DIR24_8的FIB */
};
/** 修改FIB函数 */
typedef int (*rte_fib_modify_fn_t)(struct rte_fib *fib, uint32_t ip,
uint8_t depth, uint64_t next_hop, int op);
/** FIB批量查找函数 */
typedef void (*rte_fib_lookup_fn_t)(void *fib, const uint32_t *ips,
uint64_t *next_hops, const unsigned int n);
enum rte_fib_op {
RTE_FIB_ADD,
RTE_FIB_DEL,
};
/** DIR24_8 基于FIB的下一跳(1 << nh_sz)位的大小 */
enum rte_fib_dir24_8_nh_sz {
RTE_FIB_DIR24_8_1B,
RTE_FIB_DIR24_8_2B,
RTE_FIB_DIR24_8_4B,
RTE_FIB_DIR24_8_8B
};
/** 查找函数实现类型 */
enum rte_fib_lookup_type {
RTE_FIB_LOOKUP_DEFAULT,
/**< 基于最大SIMD位宽选择最佳实现 */
RTE_FIB_LOOKUP_DIR24_8_SCALAR_MACRO,
/**< 基于宏的查找函数 */
RTE_FIB_LOOKUP_DIR24_8_SCALAR_INLINE,
/**< 使用内联函数的查找实现,用于不同的下一跳大小 */
RTE_FIB_LOOKUP_DIR24_8_SCALAR_UNI,
/**< 用于所有下一跳大小的统一查找函数 */
RTE_FIB_LOOKUP_DIR24_8_VECTOR_AVX512
/**< 使用AVX512的向量实现 */
};
/** FIB配置结构 */
struct rte_fib_conf {
enum rte_fib_type type; /**< FIB结构类型 */
/** 查找时如果没有路由返回的默认值 */
uint64_t default_nh;
int max_routes;
/** 内部RIB结构中节点扩展的大小 */
unsigned int rib_ext_sz;
union {
struct {
enum rte_fib_dir24_8_nh_sz nh_sz;
uint32_t num_tbl8;
} dir24_8;
};
};
/**
* 创建FIB
*
* @param name
* FIB名称
* @param socket_id
* 用于FIB表内存分配的NUMA节点ID
* @param conf
* 包含配置信息的结构
* @return
* 成功时返回FIB对象的句柄
* 否则返回NULL,并设置rte_errno为适当的值。
*/
struct rte_fib *
rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf);
/**
* 查找现有的FIB对象并返回指向它的指针。
*
* @param name
* 传递给rte_fib_create()的FIB对象名称
* @return
* 成功时返回指向FIB对象的指针,否则返回NULL,并适当设置rte_errno。
* 可能的rte_errno值包括:
* - ENOENT - 所需条目不可用。
*/
struct rte_fib *
rte_fib_find_existing(const char *name);
/**
* 释放FIB对象。
*
* @param fib
* 由rte_fib_create()创建的FIB对象句柄。
* 如果fib为NULL,则不执行任何操作。
*/
void
rte_fib_free(struct rte_fib *fib);
/**
* 向FIB添加路由。
*
* @param fib
* FIB对象句柄
* @param ip
* 要添加到FIB的IPv4前缀地址
* @param depth
* 前缀长度
* @param next_hop
* 要添加到FIB的下一跳
* @return
* 成功时返回0,否则返回负值
*/
int
rte_fib_add(struct rte_fib *fib, uint32_t ip, uint8_t depth, uint64_t next_hop);
/**
* 从FIB中删除规则。
*
* @param fib
* FIB对象句柄
* @param ip
* 要从FIB中删除的IPv4前缀地址
* @param depth
* 前缀长度
* @return
* 成功时返回0,否则返回负值
*/
int
rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth);
/**
* 批量查找FIB中的多个IP地址。
*
* @param fib
* FIB对象句柄
* @param ips
* 要在FIB中查找的IP数组
* @param next_hops
* 为IP找到的最具体规则的下一跳。
* 这是一个八字节值的数组。
* 如果给定IP的查找失败,则对应的元素将包含为FIB配置的默认下一跳值。
* @param n
* 要查找的ips(和next_hops)数组中的元素数量。
* @return
* 对于不正确的参数返回-EINVAL,否则返回0
*/
int
rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
uint64_t *next_hops, int n);
/**
* 获取指向特定数据平面结构的指针
*
* @param fib
* FIB对象句柄
* @return
* 成功时返回数据平面结构的指针
* 否则返回NULL
*/
void *
rte_fib_get_dp(struct rte_fib *fib);
/**
* 获取指向RIB的指针
*
* @param fib
* FIB对象句柄
* @return
* 成功时返回RIB的指针
* 否则返回NULL
*/
struct rte_rib *
rte_fib_get_rib(struct rte_fib *fib);
/**
* 根据类型设置查找函数
*
* @param fib
* FIB对象句柄
* @param type
* 查找函数的类型
*
* @return
* 成功时返回0
* 失败时返回-EINVAL
*/
int
rte_fib_select_lookup(struct rte_fib *fib, enum rte_fib_lookup_type type);
#ifdef __cplusplus
}
#endif
#endif /* _RTE_FIB_H_ */
1.4 rte_fib_create
1.4.1 使用场景
rte_fib_create
用于在网络设备或应用中创建一个前缀匹配的数据结构,以实现高效的IP路由查找。这对于路由器、交换机、防火墙等需要快速转发数据包的设备尤为重要。通过创建FIB(Forwarding Information Base),可以根据最长前缀匹配原则快速找到下一跳地址,从而提高数据包转发的效率。
1.4.2 函数原型和参数
struct rte_fib *rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf);
name:
FIB对象的名称,字符串格式。
socket_id:
用于内存分配的NUMA节点ID。
conf:
FIB配置结构,包含FIB的类型、默认下一跳、最大路由数等配置信息。
1.4.3 示例代码
#include <stdio.h>
#include <stdint.h>
#include <rte_fib.h>
#include <rte_malloc.h>
#include <rte_eal.h>
#define SOCKET_ID 0
#define MAX_ROUTES 1024
#define DEFAULT_NH 0
void print_next_hops(uint64_t *next_hops, int n) {
for (int i = 0; i < n; i++) {
printf("Next hop for IP %d: %lu\n", i, next_hops[i]);
}
}
void forward_packet(uint32_t ip, uint64_t next_hop) {
// 模拟数据包转发,根据下一跳选择输出接口
printf("Forwarding packet with destination IP %u to next hop %lu\n", ip, next_hop);
}
int main(int argc, char **argv) {
// 初始化DPDK环境
rte_eal_init(argc, argv);
// 配置FIB
struct rte_fib_conf fib_conf = {
.type = RTE_FIB_DIR24_8,
.default_nh = DEFAULT_NH,
.max_routes = MAX_ROUTES,
.rib_ext_sz = 0,
.dir24_8 = {
.nh_sz = RTE_FIB_DIR24_8_4B,
.num_tbl8 = 256
}
};
// 创建FIB
struct rte_fib *fib = rte_fib_create("example_fib", SOCKET_ID, &fib_conf);
if (fib == NULL) {
printf("Failed to create FIB\n");
return -1;
}
// 添加路由
uint32_t ip1 = RTE_IPV4(192, 168, 1, 0);
uint32_t ip2 = RTE_IPV4(10, 0, 0, 0);
uint8_t depth1 = 24;
uint8_t depth2 = 8;
uint64_t next_hop1 = 1;
uint64_t next_hop2 = 2;
rte_fib_add(fib, ip1, depth1, next_hop1);
rte_fib_add(fib, ip2, depth2, next_hop2);
// 查找路由
uint32_t ips_to_lookup[2] = {RTE_IPV4(192, 168, 1, 100), RTE_IPV4(10, 0, 1, 1)};
uint64_t next_hops[2];
rte_fib_lookup_bulk(fib, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 模拟数据包转发
for (int i = 0; i < 2; i++) {
forward_packet(ips_to_lookup[i], next_hops[i]);
}
// 删除路由
rte_fib_delete(fib, ip1, depth1);
rte_fib_delete(fib, ip2, depth2);
// 查找路由,查看默认下一跳
rte_fib_lookup_bulk(fib, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 模拟数据包转发
for (int i = 0; i < 2; i++) {
forward_packet(ips_to_lookup[i], next_hops[i]);
}
// 释放FIB
rte_fib_free(fib);
// 关闭DPDK环境
rte_eal_cleanup();
return 0;
}
1.5 rte_fib_find_existing
rte_fib_find_existing
函数用于查找已经存在的 FIB(Forwarding Information Base)对象,便于在应用程序的不同部分之间共享 FIB 对象,从而避免重复创建。
1.5.1 函数原型及参数
struct rte_fib *rte_fib_find_existing(const char *name);
参数
- name:字符串类型,FIB 对象的名称。此名称是在使用 rte_fib_create 函数创建 FIB 时指定的。
返回值
- 成功:返回一个指向 struct rte_fib 的指针,即找到的 FIB 对象。
- 失败:返回 NULL,并设置 rte_errno 以指示错误。
1.5.2 使用场景
假设有一个需要在多个模块之间共享 FIB 的应用程序。首先在一个模块中创建 FIB 对象,然后在其他模块中查找并使用该 FIB 对象。
1.5.3 代码示例
#include <stdio.h>
#include <stdint.h>
#include <rte_fib.h>
#include <rte_eal.h>
#define SOCKET_ID 0
void print_next_hops(uint64_t *next_hops, int n) {
for (int i = 0; i < n; i++) {
printf("Next hop for IP %d: %lu\n", i, next_hops[i]);
}
}
int main(int argc, char **argv) {
// 初始化DPDK环境
if (rte_eal_init(argc, argv) < 0) {
fprintf(stderr, "Failed to initialize EAL\n");
return -1;
}
// 查找现有FIB
struct rte_fib *fib = rte_fib_find_existing("shared_fib");
if (fib == NULL) {
printf("Failed to find existing FIB\n");
return -1;
}
// 查找路由
uint32_t ips_to_lookup[2] = {RTE_IPV4(192, 168, 1, 100), RTE_IPV4(10, 0, 1, 1)};
uint64_t next_hops[2];
rte_fib_lookup_bulk(fib, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 关闭DPDK环境
rte_eal_cleanup();
return 0;
}
1.6 rte_fib_select_lookup
rte_fib_select_lookup
是 DPDK(Data Plane Development Kit)中用于选择 FIB(Forwarding Information Base)对象的查找函数。它允许根据不同的查找策略选择适当的查找函数,从而优化性能。
1.6.1 函数原型及参数返回值
int rte_fib_select_lookup(struct rte_fib *fib, enum rte_fib_lookup_type type);
参数
fib:
指向 struct rte_fib 的指针,该结构体表示一个 FIB 对象。
type:
enum rte_fib_lookup_type 类型的查找类型,指示使用哪种查找策略。常见查找类型包括 RTE_FIB_LOOKUP_TRIE 和 RTE_FIB_LOOKUP_AVX512。
返回值
返回值为 0 表示成功,负值表示失败
1.6.2 使用场景
rte_fib_select_lookup
用于在创建 FIB 对象后,根据需要选择适当的查找策略。这在不同的硬件架构或特定的性能优化需求下非常有用。
代码示例
主要是查找之前,切换不同的查找策略,不切换则为默认或者根据创建参数指定
#include <stdio.h>
#include <stdint.h>
#include <rte_fib.h>
#include <rte_fib4.h>
#include <rte_eal.h>
#define MAX_ROUTES 1024
int main(int argc, char **argv) {
// Initialize the Environment Abstraction Layer (EAL)
rte_eal_init(argc, argv);
struct rte_fib *fib;
struct rte_fib_conf fib_conf;
int socket_id = rte_socket_id();
uint64_t next_hop = 0x1234;
// Configuration for FIB
fib_conf.type = RTE_FIB_TRIE;
fib_conf.default_nh = 0;
fib_conf.max_routes = MAX_ROUTES;
fib_conf.trie.nh_sz = RTE_FIB4_TRIE_2B;
// Create FIB
fib = rte_fib_create("example_fib", socket_id, &fib_conf);
if (fib == NULL) {
fprintf(stderr, "Failed to create FIB\n");
return -1;
}
// Add routes to FIB
rte_fib4_add(fib, 0xC0A80000, 24, next_hop); // 192.168.0.0/24
rte_fib4_add(fib, 0xC0A80100, 24, next_hop + 1); // 192.168.1.0/24
// Select lookup function
if (rte_fib_select_lookup(fib, RTE_FIB_LOOKUP_TRIE) < 0) {
fprintf(stderr, "Failed to select FIB lookup function\n");
return -1;
}
// Perform a route lookup
uint64_t nh;
int ret;
ret = rte_fib4_lookup(fib, 0xC0A80001, &nh); // 192.168.0.1
if (ret == 0) {
printf("Lookup successful using TRIE, next hop: 0x%lx\n", nh);
} else {
printf("Lookup failed using TRIE\n");
}
// Switch to another lookup type if supported
if (rte_fib_select_lookup(fib, RTE_FIB_LOOKUP_AVX512) < 0) {
fprintf(stderr, "Failed to select AVX512 lookup function or not supported\n");
} else {
ret = rte_fib4_lookup(fib, 0xC0A80101, &nh); // 192.168.1.1
if (ret == 0) {
printf("Lookup successful using AVX512, next hop: 0x%lx\n", nh);
} else {
printf("Lookup failed using AVX512\n");
}
}
// Clean up
rte_fib_free(fib);
return 0;
}
2 librte_fib ipv6
2.1 rte_fib6_create
2.1.1 rte_fib6_create
rte_fib6_create
是 DPDK(Data Plane Development Kit)中用于创建 IPv6 FIB(Forwarding Information Base)对象的函数。这个函数会根据指定的配置参数创建一个 FIB 实例,用于高性能网络应用中的路由查找。
2.1.2 函数原型
struct rte_fib6 *rte_fib6_create(const char *name, int socket_id, const struct rte_fib6_conf *conf);
参数
- name: 创建的 FIB 对象的名称。
- socket_id: 分配 FIB 对象的 NUMA 节点。
- conf: 指向 struct rte_fib6_conf 结构的指针,包含 FIB 的配置参数。
返回值
- 成功时返回指向新创建的 FIB 对象的指针,失败时返回 NULL。
2.1.3 struct rte_fib6_conf 结构体
struct rte_fib6_conf {
enum rte_fib6_type type; // FIB 类型,例如 RTE_FIB6_TRIE。
uint64_t default_nh; // 默认下一跳。
uint32_t max_routes; // 最大路由数量。
union {
struct {
uint32_t nh_sz; // 下一跳大小。
uint32_t num_tbl8s; // 8位段表数量。
} trie;
// 其他可能的配置
};
};
2.1.4 使用场景
rte_fib6_create
用于在需要处理 IPv6 路由的高性能网络应用中创建 FIB 实例。例如,构建一个支持快速 IPv6 路由查找的高性能路由器或防火墙。
2.1.5 代码示例
#include <stdio.h>
#include <stdint.h>
#include <rte_fib.h>
#include <rte_fib6.h>
#include <rte_eal.h>
#define MAX_ROUTES 1024
int main(int argc, char **argv) {
// Initialize the Environment Abstraction Layer (EAL)
if (rte_eal_init(argc, argv) < 0) {
fprintf(stderr, "Failed to initialize EAL\n");
return -1;
}
struct rte_fib6 *fib;
struct rte_fib6_conf fib_conf;
int socket_id = rte_socket_id();
uint64_t next_hop1 = 0x1234;
uint64_t next_hop2 = 0x5678;
// Configuration for FIB
fib_conf.type = RTE_FIB6_TRIE;
fib_conf.default_nh = 0;
fib_conf.max_routes = MAX_ROUTES;
fib_conf.trie.nh_sz = RTE_FIB6_TRIE_2B;
fib_conf.trie.num_tbl8s = 256; // Example value, adjust as needed
// Create FIB
fib = rte_fib6_create("example_fib6", socket_id, &fib_conf);
if (fib == NULL) {
fprintf(stderr, "Failed to create FIB6\n");
return -1;
}
// Add routes to FIB
rte_fib6_add(fib, (uint8_t *)"\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 64, next_hop1);
rte_fib6_add(fib, (uint8_t *)"\x20\x01\x0d\xb8\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 64, next_hop2);
// Perform a route lookup
uint64_t nh;
int ret;
ret = rte_fib6_lookup(fib, (uint8_t *)"\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", &nh);
if (ret == 0) {
printf("Lookup successful for 2001:db8:85a3::1, next hop: 0x%lx\n", nh);
} else {
printf("Lookup failed for 2001:db8:85a3::1\n");
}
ret = rte_fib6_lookup(fib, (uint8_t *)"\x20\x01\x0d\xb8\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", &nh);
if (ret == 0) {
printf("Lookup successful for 2001:db8:10::1, next hop: 0x%lx\n", nh);
} else {
printf("Lookup failed for 2001:db8:10::1\n");
}
// Clean up
rte_fib6_free(fib);
return 0;
}
2.2 rte_fib6_select_lookup
rte_fib6_select_lookup
是 DPDK 中用于选择 IPv6 FIB(Forwarding Information Base)对象的查找函数。这个函数允许在不同的查找算法之间切换,从而优化性能。
2.2.1 函数原型
int rte_fib6_select_lookup(struct rte_fib6 *fib, enum rte_fib6_lookup_type type);
参数
- fib: 指向 struct rte_fib6 的指针,该结构体表示一个 FIB 对象。
- type: enum rte_fib6_lookup_type 类型的查找类型,指示使用哪种查找策略。常见查找类型包括 RTE_FIB6_LOOKUP_TRIE 和 RTE_FIB6_LOOKUP_AVX512.
返回值
返回值为 0 表示成功,负值表示失败。
2.2.1 使用场景
rte_fib6_select_lookup 用于在创建 FIB 对象后,根据需要选择适当的查找策略。这在不同的硬件架构或特定的性能优化需求下非常有用。
2.2.1 代码示例
#include <stdio.h>
#include <stdint.h>
#include <rte_fib.h>
#include <rte_fib6.h>
#include <rte_eal.h>
#define MAX_ROUTES 1024
int main(int argc, char **argv) {
// Initialize the Environment Abstraction Layer (EAL)
if (rte_eal_init(argc, argv) < 0) {
fprintf(stderr, "Failed to initialize EAL\n");
return -1;
}
struct rte_fib6 *fib;
struct rte_fib6_conf fib_conf;
int socket_id = rte_socket_id();
uint64_t next_hop1 = 0x1234;
uint64_t next_hop2 = 0x5678;
// Configuration for FIB
fib_conf.type = RTE_FIB6_TRIE;
fib_conf.default_nh = 0;
fib_conf.max_routes = MAX_ROUTES;
fib_conf.trie.nh_sz = RTE_FIB6_TRIE_2B;
fib_conf.trie.num_tbl8s = 256; // Example value, adjust as needed
// Create FIB
fib = rte_fib6_create("example_fib6", socket_id, &fib_conf);
if (fib == NULL) {
fprintf(stderr, "Failed to create FIB6\n");
return -1;
}
// Add routes to FIB
rte_fib6_add(fib, (uint8_t *)"\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 64, next_hop1);
rte_fib6_add(fib, (uint8_t *)"\x20\x01\x0d\xb8\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 64, next_hop2);
// Select lookup function - use TRIE
if (rte_fib6_select_lookup(fib, RTE_FIB6_LOOKUP_TRIE) < 0) {
fprintf(stderr, "Failed to select FIB lookup function\n");
return -1;
}
// Perform a route lookup using TRIE
uint64_t nh;
int ret;
ret = rte_fib6_lookup(fib, (uint8_t *)"\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", &nh);
if (ret == 0) {
printf("Lookup successful using TRIE for 2001:db8:85a3::1, next hop: 0x%lx\n", nh);
} else {
printf("Lookup failed using TRIE for 2001:db8:85a3::1\n");
}
// Switch to another lookup type if supported
if (rte_fib6_select_lookup(fib, RTE_FIB6_LOOKUP_AVX512) < 0) {
fprintf(stderr, "Failed to select AVX512 lookup function or not supported\n");
} else {
ret = rte_fib6_lookup(fib, (uint8_t *)"\x20\x01\x0d\xb8\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", &nh);
if (ret == 0) {
printf("Lookup successful using AVX512 for 2001:db8:10::1, next hop: 0x%lx\n", nh);
} else {
printf("Lookup failed using AVX512 for 2001:db8:10::1\n");
}
}
// Clean up
rte_fib6_free(fib);
return 0;
}
该示例展示了如何创建和配置 IPv6 FIB,对其添加路由,选择查找函数,并进行路由查找。通过 rte_fib6_select_lookup,可以根据不同的查找策略优化路由查找的性能。
与lpm的区别
LPM(Longest Prefix Match)
和FIB(Forwarding Information Base)
在网络处理中有相似的功能,但它们在实现细节和应用场景上有所不同。下面是对LPM和FIB的详细介绍,以及它们之间的区别和联系。
3.1 LPM(Longest Prefix Match)
LPM是一种用于查找路由前缀的算法,其核心思想是找到与目标IP地址匹配的最长前缀。LPM通常用于路由表查找,确定数据包应该转发到哪个下一跳。LPM查找需要高效的数据结构和算法,因为它在处理每个数据包时都需要执行查找操作。
3.2 FIB(Forwarding Information Base)
FIB是路由器用来存储路由信息的表,它包含了目的地IP地址前缀和对应的下一跳信息。FIB是根据路由表(RIB,Routing Information Base)生成的,RIB包含更全面的路由信息,包括未被选为最佳路径的路由。FIB只包含最佳路径,并用于快速查找。
3.3区别和联系
-
用途:
LPM:
用于实现最长前缀匹配算法,通常是FIB查找的一部分。
FIB:
用于存储和查找实际的路由信息,包含多个前缀和对应的下一跳信息。 -
数据结构:
LPM:
通常使用特定的数据结构如Trie(前缀树)或哈希表来实现高效的前缀匹配。
FIB:
可以使用LPM算法进行查找,也可以使用其他高效的数据结构,如哈希表、压缩前缀树等。 -
性能:
LPM:
关注查找算法的效率,以保证在大规模路由表中能快速找到最长匹配前缀。
FIB:
关注整体路由查找的效率和内存占用,因为它直接影响数据包的转发性能。
3.4 实例:LPM与FIB的使用
- 场景
假设我们有一台路由器,需要配置和查询路由信息以决定数据包的转发路径。我们有以下路由条目:
目的网络:192.168.1.0/24,下一跳:1
目的网络:10.0.0.0/8,下一跳:2
- 我们需要进行以下操作:
添加上述路由条目。
查询以下IP地址的下一跳:192.168.1.100 和 10.0.1.1。
删除上述路由条目。
再次查询上述IP地址,查看默认下一跳
3.5 使用LPM进行操作
#include <stdio.h>
#include <stdint.h>
#include <rte_lpm.h>
#include <rte_malloc.h>
#include <rte_eal.h>
#define SOCKET_ID 0
#define MAX_ROUTES 1024
#define DEFAULT_NH 0
void print_next_hops(uint32_t *next_hops, int n) {
for (int i = 0; i < n; i++) {
printf("Next hop for IP %d: %u\n", i, next_hops[i]);
}
}
int main(int argc, char **argv) {
// 初始化DPDK环境
rte_eal_init(argc, argv);
// 配置LPM
struct rte_lpm_config lpm_conf = {
.max_rules = MAX_ROUTES,
.number_tbl8s = (1 << 8),
.flags = 0
};
// 创建LPM
struct rte_lpm *lpm = rte_lpm_create("example_lpm", SOCKET_ID, &lpm_conf);
if (lpm == NULL) {
printf("Failed to create LPM\n");
return -1;
}
// 添加路由
uint32_t ip1 = RTE_IPV4(192, 168, 1, 0);
uint32_t ip2 = RTE_IPV4(10, 0, 0, 0);
uint8_t depth1 = 24;
uint8_t depth2 = 8;
uint32_t next_hop1 = 1;
uint32_t next_hop2 = 2;
rte_lpm_add(lpm, ip1, depth1, next_hop1);
rte_lpm_add(lpm, ip2, depth2, next_hop2);
// 查找路由
uint32_t ips_to_lookup[2] = {RTE_IPV4(192, 168, 1, 100), RTE_IPV4(10, 0, 1, 1)};
uint32_t next_hops[2];
rte_lpm_lookup_bulk(lpm, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 删除路由
rte_lpm_delete(lpm, ip1, depth1);
rte_lpm_delete(lpm, ip2, depth2);
// 查找路由,查看默认下一跳
rte_lpm_lookup_bulk(lpm, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 释放LPM
rte_lpm_free(lpm);
// 关闭DPDK环境
rte_eal_cleanup();
return 0;
}
输出结果
Next hop for IP 0: 1
Next hop for IP 1: 2
Next hop for IP 0: 0
Next hop for IP 1: 0
3.6 使用FIB进行操作
#include <stdio.h>
#include <stdint.h>
#include <rte_fib.h>
#include <rte_malloc.h>
#include <rte_eal.h>
#define SOCKET_ID 0
#define MAX_ROUTES 1024
#define DEFAULT_NH 0
void print_next_hops(uint64_t *next_hops, int n) {
for (int i = 0; i < n; i++) {
printf("Next hop for IP %d: %lu\n", i, next_hops[i]);
}
}
int main(int argc, char **argv) {
// 初始化DPDK环境
rte_eal_init(argc, argv);
// 配置FIB
struct rte_fib_conf fib_conf = {
.type = RTE_FIB_DIR24_8,
.default_nh = DEFAULT_NH,
.max_routes = MAX_ROUTES,
.rib_ext_sz = 0,
.dir24_8 = {
.nh_sz = RTE_FIB_DIR24_8_4B,
.num_tbl8 = 256
}
};
// 创建FIB
struct rte_fib *fib = rte_fib_create("example_fib", SOCKET_ID, &fib_conf);
if (fib == NULL) {
printf("Failed to create FIB\n");
return -1;
}
// 添加路由
uint32_t ip1 = RTE_IPV4(192, 168, 1, 0);
uint32_t ip2 = RTE_IPV4(10, 0, 0, 0);
uint8_t depth1 = 24;
uint8_t depth2 = 8;
uint64_t next_hop1 = 1;
uint64_t next_hop2 = 2;
rte_fib_add(fib, ip1, depth1, next_hop1);
rte_fib_add(fib, ip2, depth2, next_hop2);
// 查找路由
uint32_t ips_to_lookup[2] = {RTE_IPV4(192, 168, 1, 100), RTE_IPV4(10, 0, 1, 1)};
uint64_t next_hops[2];
rte_fib_lookup_bulk(fib, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 删除路由
rte_fib_delete(fib, ip1, depth1);
rte_fib_delete(fib, ip2, depth2);
// 查找路由,查看默认下一跳
rte_fib_lookup_bulk(fib, ips_to_lookup, next_hops, 2);
print_next_hops(next_hops, 2);
// 释放FIB
rte_fib_free(fib);
// 关闭DPDK环境
rte_eal_cleanup();
return 0;
}
输出结果
Next hop for IP 0: 1
Next hop for IP 1: 2
Next hop for IP 0: 0
Next hop for IP 1: 0
3.7 详细分析
LPM
- 创建和配置:使用rte_lpm_create创建LPM表,并配置最大规则数和表大小。
- 添加路由:使用rte_lpm_add添加路由条目。
- 查找路由:使用rte_lpm_lookup_bulk进行批量查找。
- 删除路由:使用rte_lpm_delete删除路由条目。
FIB
- 创建和配置:使用rte_fib_create创建FIB,并配置FIB类型、默认下一跳和最大路由数。
- 添加路由:使用rte_fib_add添加路由条目。
- 查找路由:使用rte_fib_lookup_bulk进行批量查找。
- 删除路由:使用rte_fib_delete删除路由条目。
使用场景和性能
- LPM:适用于需要高效前缀匹配的场景,通常用于快速查找和路由决策。
- FIB:适用于需要存储和查找完整路由信息的场景,提供更多的灵活性和功能。
LPM和FIB在很多方面功能重叠,但它们的实现细节和性能特性决定了它们的最佳应用场景。LPM专注于高效的前缀匹配,而FIB则提供了更完整的路由信息存储和查找能力。根据具体需求选择合适的技术,可以在性能和功能之间取得平衡。