dpdk-lib
librte_lpm
1.1 librte_lpm 简介
librte_lpm是DPDK(Data Plane Development Kit)中的一个库,用于实现长网址前缀匹配(Longest Prefix Match)算法,通常用于高性能网络应用中的路由表查找。
功能:
实现了高效的长网址前缀匹配算法,用于快速查找路由表中最长匹配的前缀。支持IPv4和IPv6地址。
数据结构:
路由表(Routing Table):librte_lpm使用路由表来存储网络地址前缀及其对应的下一跳信息。
节点表(Node Table):librte_lpm内部使用一个节点表来组织路由表,以便快速查找最长匹配的前缀。
主要函数:
rte_lpm_create():创建一个新的长网址前缀匹配实例,并初始化相关数据结构。
rte_lpm_add():向已创建的长网址前缀匹配实例中添加一个前缀及其对应的下一跳信息。
rte_lpm_lookup():在已创建的长网址前缀匹配实例中查找一个目标地址的最长匹配前缀,并返回其对应的下一跳信息。
rte_lpm_delete():从已创建的长网址前缀匹配实例中删除一个前缀。
rte_lpm_free():释放一个长网址前缀匹配实例所占用的内存空间。
性能优化:
librte_lpm通过使用树状结构以及优化的查找算法来实现高效的路由表查找。
在内部实现中,通常使用二叉树或者Trie树等数据结构来组织路由表,以便快速查找最长匹配的前缀。
适用场景:
librte_lpm适用于需要高性能路由表查找的网络应用,例如路由器、防火墙、负载均衡器等。
由于其高效的实现和优化,特别适用于需要处理大量数据包的高速网络环境
1.2 rte_lpm.h
#ifndef _RTE_LPM_H_
#define _RTE_LPM_H_
/**
* @file
* RTE Longest Prefix Match (LPM)
*/
#include <errno.h>
#include <stdint.h>
#include <rte_branch_prediction.h>
#include <rte_byteorder.h>
#include <rte_common.h>
#include <rte_vect.h>
#include <rte_rcu_qsbr.h>
#ifdef __cplusplus
extern "C" {
#endif
/** LPM名称中的最大字符数 */
#define RTE_LPM_NAMESIZE 32
/** IPv4 LPM的最大深度值 */
#define RTE_LPM_MAX_DEPTH 32
/** @internal tbl24表中的总条目数 */
#define RTE_LPM_TBL24_NUM_ENTRIES (1 << 24)
/** @internal tbl8组中的条目数 */
#define RTE_LPM_TBL8_GROUP_NUM_ENTRIES 256
/** @internal tbl8中的最大tbl8组数 */
#define RTE_LPM_MAX_TBL8_NUM_GROUPS (1 << 24)
/** @internal tbl8中的总tbl8组数 */
#define RTE_LPM_TBL8_NUM_GROUPS 256
/** @internal tbl8表中的总条目数 */
#define RTE_LPM_TBL8_NUM_ENTRIES (RTE_LPM_TBL8_NUM_GROUPS * \
RTE_LPM_TBL8_GROUP_NUM_ENTRIES)
/** @internal 启用/禁用运行时检查的宏 */
#if defined(RTE_LIBRTE_LPM_DEBUG)
#define RTE_LPM_RETURN_IF_TRUE(cond, retval) do { \
if (cond) return (retval); \
} while (0)
#else
#define RTE_LPM_RETURN_IF_TRUE(cond, retval)
#endif
/** @internal 具有有效和valid_group字段的位掩码 */
#define RTE_LPM_VALID_EXT_ENTRY_BITMASK 0x03000000
/** 用于指示成功查找的位掩码 */
#define RTE_LPM_LOOKUP_SUCCESS 0x01000000
/** @internal 默认的RCU延迟队列中的条目数量 */
#define RTE_LPM_RCU_DQ_RECLAIM_MAX 16
/** RCU回收模式 */
enum rte_lpm_qsbr_mode {
/** 创建延迟队列以进行回收 */
RTE_LPM_QSBR_MODE_DQ = 0,
/** 使用阻塞模式回收,不创建延迟队列 */
RTE_LPM_QSBR_MODE_SYNC
};
#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
/** @internal tbl24条目结构 */
__extension__
struct rte_lpm_tbl_entry {
/**
* 存储下一个跳点(当valid_group未设置时为tbl8或tbl24,或当valid_group设置时为tbl24)
* 或指向tbl8结构的group_index(仅tbl24,当valid_group设置时)
*/
uint32_t next_hop :24;
/* 使用单个uint8_t存储3个值 */
uint32_t valid :1; /**< 验证标志 */
/**
* 对于tbl24:
* - valid_group == 0:条目存储下一个跳点
* - valid_group == 1:条目存储指向tbl8的group_index
* 对于tbl8:
* - valid_group指示当前tbl8是否正在使用中
*/
uint32_t valid_group :1;
uint32_t depth :6; /**< 规则深度 */
};
#else
__extension__
struct rte_lpm_tbl_entry {
uint32_t depth :6;
uint32_t valid_group :1;
uint32_t valid :1;
uint32_t next_hop :24;
};
#endif
/** LPM配置结构 */
struct rte_lpm_config {
uint32_t max_rules; /**< 最大规则数量 */
uint32_t number_tbl8s; /**< 要分配的tbl8数量 */
int flags; /**< 此字段当前未使用 */
};
/** @internal LPM结构 */
struct rte_lpm {
/* LPM表 */
struct rte_lpm_tbl_entry tbl24[RTE_LPM_TBL24_NUM_ENTRIES]
__rte_cache_aligned; /**< LPM tbl24表 */
struct rte_lpm_tbl_entry *tbl8; /**< LPM tbl8表 */
};
/** LPM RCU QSBR配置结构 */
struct rte_lpm_rcu_config {
struct rte_rcu_qsbr *v; /* RCU QSBR变量 */
/* RCU QSBR模式。RTE_LPM_QSBR_MODE_xxx
* '0'表示默认值:创建延迟队列以进行回收
*/
enum rte_lpm_qsbr_mode mode;
uint32_t dq_size; /* RCU延迟队列大小。默认值:lpm->number_tbl8s */
uint32_t reclaim_thd; /* 触发自动回收的阈值 */
uint32_t reclaim_max; /* 一次回收的最大条目数。默认值:RTE_LPM_RCU_DQ_RECLAIM_MAX */
};
/**
* 创建一个LPM对象。
*
* @param name
* LPM对象名称
* @param socket_id
* 用于LPM表内存分配的NUMA套接字ID
* @param config
* 包含配置信息的结构体
* @return
* 成功时LPM对象句柄,否则为NULL,rte_errno设置为适当的值。可能的rte_errno值包括:
* - E_RTE_NO_CONFIG - 函数无法获取rte_config结构的指针
* - E_RTE_SECONDARY - 从次要进程实例调用该函数
* - EINVAL - 函数传递了无效参数
* - ENOSPC - 已分配了最大数量的内存区域
* - EEXIST - 具有相同名称的内存区域已存在
* - ENOMEM - 未找到适当的内存区域来创建内存区域
*/
struct rte_lpm *
rte_lpm_create(const char *name, int socket_id,
const struct rte_lpm_config *config);
/**
* 查找现有的LPM对象并返回指向它的指针。
*
* @param name
* 与rte_lpm_create()传递的LPM对象名称相同
* @return
* 指向LPM对象的指针,如果未找到对象则为NULL,rte_errno设置为适当的值。可能的rte_errno值包括:
* - ENOENT - 无法返回所需的条目。
*/
struct rte_lpm *
rte_lpm_find_existing(const char *name);
/**
* 释放LPM对象。
*
* @param lpm
* LPM对象句柄
* 如果lpm为NULL,则不执行任何操作。
*/
void
rte_lpm_free(struct rte_lpm *lpm);
/**
* 将RCU QSBR变量与LPM对象关联起来。
*
* @param lpm
* 要添加RCU QSBR的LPM对象
* @param cfg
* RCU QSBR配置
* @return
* 成功返回0,否则返回1,rte_errno中设置错误码。
* 可能的rte_errno代码包括:
* - EINVAL - 无效指针
* - EEXIST - 已经添加过QSBR
* - ENOMEM - 内存分配失败
*/
int rte_lpm_rcu_qsbr_add(struct rte_lpm *lpm, struct rte_lpm_rcu_config *cfg);
/**
* 向LPM表中添加一条规则。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要添加到LPM表的规则的IP
* @param depth
* 要添加到LPM表的规则的深度
* @param next_hop
* 要添加到LPM表的规则的下一跳
* @return
* 成功返回0,否则返回负值
*/
int
rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth, uint32_t next_hop);
/**
* 检查LPM表中是否存在规则,并在存在时提供其下一跳。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要搜索的规则的IP
* @param depth
* 要搜索的规则的深度
* @param next_hop
* 规则的下一跳(仅当找到时有效)
* @return
* 如果规则存在,则返回1,否则返回0,失败时返回负值
*/
int
rte_lpm_is_rule_present(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
uint32_t *next_hop);
/**
* 从LPM表中删除一条规则。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要从LPM表中删除的规则的IP
* @param depth
* 要从LPM表中删除的规则的深度
* @return
* 成功返回0,否则返回负值
*/
int
rte_lpm_delete(struct rte_lpm *lpm, uint32_t ip, uint8_t depth);
/**
* 从LPM表中删除所有规则。
*
* @param lpm
* LPM对象句柄
*/
void
rte_lpm_delete_all(struct rte_lpm *lpm);
/**
* 在LPM表中查找IP。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要在LPM表中查找的IP
* @param next_hop
* IP的最特定规则的下一跳(仅在查找命中时有效)
* @return
* -EINVAL表示参数不正确,-ENOENT表示查找失败,查找命中时返回0
*/
static inline int
rte_lpm_lookup(const struct rte_lpm *lpm, uint32_t ip, uint32_t *next_hop)
{
unsigned tbl24_index = (ip >> 8);
uint32_t tbl_entry;
const uint32_t *ptbl;
/* 调试:检查用户输入参数 */
RTE_LPM_RETURN_IF_TRUE(((lpm == NULL) || (next_hop == NULL)), -EINVAL);
/* 复制tbl24条目 */
ptbl = (const uint32_t *)(&lpm->tbl24[tbl24_index]);
tbl_entry = *ptbl;
/* 在查找中不需要内存顺序。因为存在数据流依赖性,
* 编译器或硬件无法重新排序操作。
*/
/* 复制tbl8条目(仅在需要时) */
if (unlikely((tbl_entry & RTE_LPM_VALID_EXT_ENTRY_BITMASK) ==
RTE_LPM_VALID_EXT_ENTRY_BITMASK)) {
unsigned tbl8_index = (uint8_t)ip +
(((uint32_t)tbl_entry & 0x00FFFFFF) *
RTE_LPM_TBL8_GROUP_NUM_ENTRIES);
ptbl = (const uint32_t *)&lpm->tbl8[tbl8_index];
tbl_entry = *ptbl;
}
*next_hop = ((uint32_t)tbl_entry & 0x00FFFFFF);
return (tbl_entry & RTE_LPM_LOOKUP_SUCCESS) ? 0 : -ENOENT;
}
/**
* 在LPM表中查找多个IP地址。这可能作为宏实现,因此不应使用函数的地址。
*
* @param lpm
* LPM对象句柄
* @param ips
* 要在LPM表中查找的IP数组
* @param next_hops
* IP的最特定规则的下一跳(仅在查找命中时有效)。
* 这是一个包含两个字节值的数组。每个值的最高字节表示查找是否成功(设置了位掩码
* RTE_LPM_LOOKUP_SUCCESS)。最低字节是实际的下一跳。
* @param n
* 要查找的ips数组中的元素数量。这应该是一个编译时常量,并且对于性能最好,
* 应该能够被8整除。
* @return
* -EINVAL表示参数不正确,否则为0
*/
#define rte_lpm_lookup_bulk(lpm, ips, next_hops, n) \
rte_lpm_lookup_bulk_func(lpm, ips, next_hops, n)
static inline int
rte_lpm_lookup_bulk_func(const struct rte_lpm *lpm, const uint32_t *ips,
uint32_t *next_hops, const unsigned n)
{
unsigned i;
unsigned tbl24_indexes[n];
const uint32_t *ptbl;
/* 调试:检查用户输入参数 */
RTE_LPM_RETURN_IF_TRUE(((lpm == NULL) || (ips == NULL) ||
(next_hops == NULL)), -EINVAL);
for (i = 0; i < n; i++) {
tbl24_indexes[i] = ips[i] >> 8;
}
for (i = 0; i < n; i++) {
/* 简单地将tbl24条目复制到输出 */
ptbl = (const uint32_t *)&lpm->tbl24[tbl24_indexes[i]];
next_hops[i] = *ptbl;
/* 如果需要,覆盖输出以获取tbl8条目 */
if (unlikely((next_hops[i] & RTE_LPM_VALID_EXT_ENTRY_BITMASK) ==
RTE_LPM_VALID_EXT_ENTRY_BITMASK)) {
unsigned tbl8_index = (uint8_t)ips[i] +
(((uint32_t)next_hops[i] & 0x00FFFFFF) *
RTE_LPM_TBL8_GROUP_NUM_ENTRIES);
ptbl = (const uint32_t *)&lpm->tbl8[tbl8_index];
next_hops[i] = *ptbl;
}
}
return 0;
}
/* 屏蔽四个结果。 */
#define RTE_LPM_MASKX4_RES UINT64_C(0x00ffffff00ffffff)
/**
* 在LPM表中查找四个IP地址。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要在LPM表中查找的四个IP
* @param hop
* IP的最特定规则的下一跳(仅在查找命中时有效)。这是一个包含四个元素的数组。
* 如果对给定IP的查找成功,则相应元素的最低有效字节是实际的下一跳,最高有效字节为零。
* 如果给定IP的查找失败,则相应元素将包含默认值,请参阅下一个参数的描述。
* @param defv
* 如果查找失败,则将默认值填充到hop[]数组的相应元素中。
*/
static inline void
rte_lpm_lookupx4(const struct rte_lpm *lpm, xmm_t ip, uint32_t hop[4],
uint32_t defv);
#if defined(RTE_ARCH_ARM)
#ifdef RTE_HAS_SVE_ACLE
#include "rte_lpm_sve.h"
#undef rte_lpm_lookup_bulk
#define rte_lpm_lookup_bulk(lpm, ips, next_hops, n) \
__rte_lpm_lookup_vec(lpm, ips, next_hops, n)
#endif
#include "rte_lpm_neon.h"
#elif defined(RTE_ARCH_PPC_64)
#include "rte_lpm_altivec.h"
#elif defined(RTE_ARCH_X86)
#include "rte_lpm_sse.h"
#else
#include "rte_lpm_scalar.h"
#endif
#ifdef __cplusplus
}
#endif
#endif /* _RTE_LPM_H_ */
1.3 rte_lpm_create
创建一个LPM对象
struct rte_lpm *
rte_lpm_create(const char *name, int socket_id, const struct rte_lpm_config *config);
1.3.1 参数说明:
name:
指定长网址前缀匹配实例的名称。
socket_id:
指定长网址前缀匹配实例所在的NUMA节点。
config:
指定长网址前缀匹配实例的配置信息,包括最大表项数量和最大深度等。
1.3.2 返回值:
如果创建成功,则返回指向新创建的长网址前缀匹配实例的指针。
如果创建失败,则返回NULL。
1.3.3 示例代码:
#include <stdio.h>
#include <rte_lpm.h>
#define MAX_RULES 1024
#define SOCKET_ID 0
int main() {
struct rte_lpm *lpm;
struct rte_lpm_config config;
const char *lpm_name = "example_lpm";
uint32_t ip[MAX_RULES] = {0}; // Example IP addresses
uint8_t depth[MAX_RULES] = {0}; // Example prefix depths
uint8_t next_hop[MAX_RULES] = {0}; // Example next hop values
// Initialize configuration
config.max_rules = MAX_RULES;
config.number_tbl8s = MAX_RULES / RTE_LPM_TBL8_GROUP_NUM;
config.flags = 0;
// Create LPM instance
lpm = rte_lpm_create(lpm_name, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Error: Unable to create LPM instance\n");
return -1;
}
// Add example routing table entries
for (int i = 0; i < MAX_RULES; ++i) {
rte_lpm_add(lpm, ip[i], depth[i], next_hop[i]);
}
// Use the LPM instance (e.g., perform lookups)
// Free the LPM instance when done
rte_lpm_free(lpm);
return 0;
}
在上面的示例代码中,我们首先初始化了一个rte_lpm_config结构体作为rte_lpm_create()函数的配置参数。然后,使用rte_lpm_create()函数创建了一个名为example_lpm的长网址前缀匹配实例。接着,我们使用rte_lpm_add()函数向该实例中添加了一些路由表项。最后,在使用完实例后,通过rte_lpm_free()函数释放了其所占用的内存空间。
1.4 rte_lpm_add
1.4.1 函数介绍
函数用于向 Longest Prefix Match (LPM) 表中添加一条规则。这个函数的作用是将给定的 IP 地址和前缀长度与相应的下一跳信息关联起来,以便后续的查找操作可以使用这些规则进行路由。
int rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth, uint32_t next_hop);
1.4.2 参数说明
lpm:
指向已经创建的 LPM 对象的指针,用于添加规则。
ip:
要添加的 IP 地址(IPv4),以 32 位无符号整数表示。
depth:
IP 地址的前缀长度,以位为单位。例如,对于 192.168.1.0/24,前缀长度为 24。
next_hop:
下一跳信息,即与给定 IP 地址匹配时应采取的操作(通常是下一个路由器的 IP 地址)。
函数返回值为整数,表示操作是否成功。如果成功添加规则,则返回 0;如果失败,则返回负值,同时设置 rte_errno 以指示失败原因。
1.4.3 实例
#include <stdio.h>
#include <rte_lpm.h>
int main() {
// 创建一个 LPM 对象
struct rte_lpm_config config = {
.max_rules = 1024,
.number_tbl8s = 1024,
.flags = 0
};
struct rte_lpm *lpm = rte_lpm_create("example_lpm", 0, &config);
if (lpm == NULL) {
printf("Failed to create LPM object.\n");
return -1;
}
// 添加规则到 LPM 表
uint32_t ip = rte_ipv4_to_uint32(192, 168, 1, 0); // IP 地址转换为 32 位整数
uint8_t depth = 24; // 前缀长度为 24
uint32_t next_hop = rte_ipv4_to_uint32(1, 2, 3, 4); // 下一跳 IP 地址
int ret = rte_lpm_add(lpm, ip, depth, next_hop);
if (ret < 0) {
printf("Failed to add rule to LPM table.\n");
rte_lpm_free(lpm); // 清理资源
return -1;
}
// 可以添加更多的规则...
// 释放 LPM 对象
rte_lpm_free(lpm);
return 0;
}
1.5 rte_lpm_delete
1.5.1 函数介绍
函数用于从 Longest Prefix Match (LPM) 表中删除指定的规则。这个函数的作用是移除与给定的 IP 地址和前缀长度相对应的规则
int rte_lpm_delete(struct rte_lpm *lpm, uint32_t ip, uint8_t depth);
1.5.2 参数说明
lpm:
指向已经创建的 LPM 对象的指针,从中删除规则。
ip:
要删除的 IP 地址(IPv4),以 32 位无符号整数表示。
depth:
IP 地址的前缀长度,以位为单位。例如,对于 192.168.1.0/24,前缀长度为 24。
1.5.3 实例
#include <stdio.h>
#include <rte_lpm.h>
int main() {
// 创建一个 LPM 对象
struct rte_lpm_config config = {
.max_rules = 1024,
.number_tbl8s = 1024,
.flags = 0
};
struct rte_lpm *lpm = rte_lpm_create("example_lpm", 0, &config);
if (lpm == NULL) {
printf("Failed to create LPM object.\n");
return -1;
}
// 添加规则到 LPM 表
uint32_t ip = rte_ipv4_to_uint32(192, 168, 1, 0); // IP 地址转换为 32 位整数
uint8_t depth = 24; // 前缀长度为 24
uint32_t next_hop = rte_ipv4_to_uint32(1, 2, 3, 4); // 下一跳 IP 地址
int ret = rte_lpm_add(lpm, ip, depth, next_hop);
if (ret < 0) {
printf("Failed to add rule to LPM table.\n");
rte_lpm_free(lpm); // 清理资源
return -1;
}
// 删除规则
ret = rte_lpm_delete(lpm, ip, depth);
if (ret < 0) {
printf("Failed to delete rule from LPM table.\n");
rte_lpm_free(lpm); // 清理资源
return -1;
}
// 释放 LPM 对象
rte_lpm_free(lpm);
return 0;
}
1.6 rte_lpm_lookup
1.6.1 函数介绍
rte_lpm_lookup 函数用于在 Longest Prefix Match (LPM) 表中查找给定的 IP 地址,并返回与之最匹配的规则的下一跳信息。这个函数的作用是根据输入的 IP 地址在 LPM 表中进行前缀匹配,并返回匹配到的规则的下一跳信息
int rte_lpm_lookup(const struct rte_lpm *lpm, uint32_t ip, uint32_t *next_hop);
1.6.2 参数说明
lpm:
指向已经创建的 LPM 对象的指针,从中进行查找。
ip:
要查找的 IP 地址(IPv4),以 32 位无符号整数表示。
next_hop:
指向存储下一跳信息的指针。如果查找成功,则该指针将被赋值为与给定 IP 地址最匹配的规则的下一跳信息。
函数返回值为整数,表示操作是否成功。如果成功找到匹配的规则,则返回 0,并将下一跳信息存储在 next_hop 中;如果未找到匹配的规则,则返回负值,同时设置 rte_errno 以指示失败原因。
1.6.3 实例
#include <stdio.h>
#include <rte_lpm.h>
int main() {
// 创建一个 LPM 对象
struct rte_lpm_config config = {
.max_rules = 1024,
.number_tbl8s = 1024,
.flags = 0
};
struct rte_lpm *lpm = rte_lpm_create("example_lpm", 0, &config);
if (lpm == NULL) {
printf("Failed to create LPM object.\n");
return -1;
}
// 添加规则到 LPM 表
uint32_t ip = rte_ipv4_to_uint32(192, 168, 1, 0); // IP 地址转换为 32 位整数
uint8_t depth = 24; // 前缀长度为 24
uint32_t next_hop = rte_ipv4_to_uint32(1, 2, 3, 4); // 下一跳 IP 地址
int ret = rte_lpm_add(lpm, ip, depth, next_hop);
if (ret < 0) {
printf("Failed to add rule to LPM table.\n");
rte_lpm_free(lpm); // 清理资源
return -1;
}
// 查找 IP 地址的下一跳信息
uint32_t lookup_ip = rte_ipv4_to_uint32(192, 168, 1, 10); // 要查找的 IP 地址
uint32_t lookup_next_hop;
ret = rte_lpm_lookup(lpm, lookup_ip, &lookup_next_hop);
if (ret < 0) {
printf("Failed to lookup IP address.\n");
} else {
printf("Next hop for IP address %d.%d.%d.%d is %d.%d.%d.%d\n",
(lookup_ip >> 24) & 0xFF, (lookup_ip >> 16) & 0xFF,
(lookup_ip >> 8) & 0xFF, lookup_ip & 0xFF,
(lookup_next_hop >> 24) & 0xFF, (lookup_next_hop >> 16) & 0xFF,
(lookup_next_hop >> 8) & 0xFF, lookup_next_hop & 0xFF);
}
// 释放 LPM 对象
rte_lpm_free(lpm);
return 0;
}
1.7 rte_lpm_is_rule_present
1.7.1 函数介绍
rte_lpm_is_rule_present 函数用于检查指定的 IP 地址和前缀长度是否存在于 Longest Prefix Match (LPM) 表中,并提供其下一跳信息。感觉和上面的rte_lpm_lookup
类似,实际我也是用的rte_lpm_lookup函数。
int rte_lpm_is_rule_present(struct rte_lpm *lpm, uint32_t ip, uint8_t depth, uint32_t *next_hop);
1.7.2 参数说明
lpm:
指向已经创建的 LPM 对象的指针,从中进行规则检查。
ip:
要检查的 IP 地址(IPv4),以 32 位无符号整数表示。
depth:
IP 地址的前缀长度,以位为单位。例如,对于 192.168.1.0/24,前缀长度为 24。
next_hop:
如果规则存在,则存储与给定 IP 地址和前缀长度匹配的规则的下一跳信息。
函数返回值为整数,表示操作的结果。如果规则存在,则返回 1,并将下一跳信息存储在 next_hop 中;如果规则不存在,则返回 0;如果出现错误,则返回负值,同时设置 rte_errno 以指示失败原因。
1.7.3 实例
#include <stdio.h>
#include <rte_lpm.h>
int main() {
// 创建一个 LPM 对象
struct rte_lpm_config config = {
.max_rules = 1024,
.number_tbl8s = 1024,
.flags = 0
};
struct rte_lpm *lpm = rte_lpm_create("example_lpm", 0, &config);
if (lpm == NULL) {
printf("Failed to create LPM object.\n");
return -1;
}
// 添加规则到 LPM 表
uint32_t ip = rte_ipv4_to_uint32(192, 168, 1, 0); // IP 地址转换为 32 位整数
uint8_t depth = 24; // 前缀长度为 24
uint32_t next_hop = rte_ipv4_to_uint32(1, 2, 3, 4); // 下一跳 IP 地址
int ret = rte_lpm_add(lpm, ip, depth, next_hop);
if (ret < 0) {
printf("Failed to add rule to LPM table.\n");
rte_lpm_free(lpm); // 清理资源
return -1;
}
// 检查规则是否存在
uint32_t lookup_ip = rte_ipv4_to_uint32(192, 168, 1, 0); // 要检查的 IP 地址
uint8_t lookup_depth = 24; // 要检查的前缀长度
uint32_t lookup_next_hop;
ret = rte_lpm_is_rule_present(lpm, lookup_ip, lookup_depth, &lookup_next_hop);
if (ret < 0) {
printf("Error occurred while checking rule presence.\n");
} else if (ret == 1) {
printf("Rule for IP address %d.%d.%d.%d with prefix length %d exists.\n",
(lookup_ip >> 24) & 0xFF, (lookup_ip >> 16) & 0xFF,
(lookup_ip >> 8) & 0xFF, lookup_ip & 0xFF, lookup_depth);
printf("Next hop: %d.%d.%d.%d\n",
(lookup_next_hop >> 24) & 0xFF, (lookup_next_hop >> 16) & 0xFF,
(lookup_next_hop >> 8) & 0xFF, lookup_next_hop & 0xFF);
} else {
printf("Rule for IP address %d.%d.%d.%d with prefix length %d does not exist.\n",
(lookup_ip >> 24) & 0xFF, (lookup_ip >> 16) & 0xFF,
(lookup_ip >> 8) & 0xFF, lookup_ip & 0xFF, lookup_depth);
}
// 释放 LPM 对象
rte_lpm_free(lpm);
return 0;
}
1.8 rte_lpm_free
函数用于释放之前创建的 Longest Prefix Match (LPM) 对象及其占用的资源。当你不再需要使用 LPM 对象时,应该调用该函数来释放内存并清理资源,以避免内存泄漏。
1.8.1 实例
#include <stdio.h>
#include <rte_lpm.h>
int main() {
// 创建一个 LPM 对象
struct rte_lpm_config config = {
.max_rules = 1024,
.number_tbl8s = 1024,
.flags = 0
};
struct rte_lpm *lpm = rte_lpm_create("example_lpm", 0, &config);
if (lpm == NULL) {
printf("Failed to create LPM object.\n");
return -1;
}
// 使用 LPM 对象进行一些操作...
// 当不再需要使用 LPM 对象时,释放资源
rte_lpm_free(lpm);
return 0;
}
2.1 rte_lpm6.h
#ifndef _RTE_LPM6_H_
#define _RTE_LPM6_H_
/**
* @file
* IPv6(LPM6)的RTE最长前缀匹配
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define RTE_LPM6_MAX_DEPTH 128
#define RTE_LPM6_IPV6_ADDR_SIZE 16
/** 最大LPM名称中的字符数。 */
#define RTE_LPM6_NAMESIZE 32
/** LPM结构。 */
struct rte_lpm6;
/** LPM配置结构。 */
struct rte_lpm6_config {
uint32_t max_rules; /**< 规则的最大数量。 */
uint32_t number_tbl8s; /**< 要分配的tbl8的数量。 */
int flags; /**< 该字段目前未使用。 */
};
/**
* 创建一个LPM对象。
*
* @param name
* LPM对象名称
* @param socket_id
* 用于LPM表内存分配的NUMA套接字ID
* @param config
* 包含配置的结构
* @return
* 成功时返回LPM对象句柄,否则为NULL,并将rte_errno设置为适当的值。可能的rte_errno值包括:
* - E_RTE_NO_CONFIG - 函数无法获取rte_config结构的指针
* - E_RTE_SECONDARY - 从次要进程实例调用了函数
* - EINVAL - 函数传递的参数无效
* - ENOSPC - 已经分配了最大数量的内存区域
* - EEXIST - 具有相同名称的内存区域已存在
* - ENOMEM - 未找到适当的内存区域来创建内存区域
*/
struct rte_lpm6 *
rte_lpm6_create(const char *name, int socket_id,
const struct rte_lpm6_config *config);
/**
* 查找现有的LPM对象并返回指向它的指针。
*
* @param name
* 作为rte_lpm6_create()传递的LPM对象的名称
* @return
* 指向lpm对象的指针,如果未找到对象,则返回NULL,并设置rte_errno为适当的值。可能的rte_errno值包括:
* - ENOENT - 没有找到要返回的必需条目。
*/
struct rte_lpm6 *
rte_lpm6_find_existing(const char *name);
/**
* 释放LPM对象。
*
* @param lpm
* LPM对象句柄
* 如果lpm为NULL,则不执行任何操作。
*/
void
rte_lpm6_free(struct rte_lpm6 *lpm);
/**
* 向LPM表中添加一条规则。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要添加到LPM表中的规则的IP
* @param depth
* 要添加到LPM表中的规则的深度
* @param next_hop
* 要添加到LPM表中的规则的下一跳
* @return
* 成功返回0,否则返回负值
*/
int
rte_lpm6_add(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth,
uint32_t next_hop);
/**
* 检查LPM表中是否存在规则,
* 并在存在时提供其下一跳。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要搜索的规则的IP
* @param depth
* 要搜索的规则的深度
* @param next_hop
* 规则的下一跳(仅在找到时有效)
* @return
* 规则存在时返回1,不存在时返回0,失败时返回负值
*/
int
rte_lpm6_is_rule_present(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth,
uint32_t *next_hop);
/**
* 从LPM表中删除一条规则。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要从LPM表中删除的规则的IP
* @param depth
* 要从LPM表中删除的规则的深度
* @return
* 成功返回0,否则返回负值
*/
int
rte_lpm6_delete(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth);
/**
* 从LPM表中删除多条规则。
*
* @param lpm
* LPM对象句柄
* @param ips
* 要从LPM表中删除的IP数组
* @param depths
* 要从LPM表中删除的规则的深度数组
* @param n
* 要从LPM表中删除的规则的数量
* @return
* 成功返回0,否则返回负值。
*/
int
rte_lpm6_delete_bulk_func(struct rte_lpm6 *lpm,
uint8_t ips[][RTE_LPM6_IPV6_ADDR_SIZE], uint8_t *depths, unsigned n);
/**
* 从LPM表中删除所有规则。
*
* @param lpm
* LPM对象句柄
*/
void
rte_lpm6_delete_all(struct rte_lpm6 *lpm);
/**
* 在LPM表中查找IP。
*
* @param lpm
* LPM对象句柄
* @param ip
* 要在LPM表中查找的IP
* @param next_hop
* 找到IP的最具体规则的下一跳(仅在查找命中时有效)
* @return
* -不正确的参数时返回EINVAL,查找未命中时返回ENOENT,查找命中时返回0
*/
int
rte_lpm6_lookup(const struct rte_lpm6 *lpm, const uint8_t *ip, uint32_t *next_hop);
/**
* 在LPM表中查找多个IP地址。
*
* @param lpm
* LPM对象句柄
* @param ips
* 要在LPM表中查找的IP数组
* @param next_hops
* 找到IP的最具体规则的下一跳(仅在查找命中时有效)。
* 这是一个两字节值的数组。成功时,下一跳将存储在每个位置上;否则,位置将设置为-1。
* @param n
* 要查找的ips(和next_hops)数组中的元素数。
* @return
* 对于不正确的参数,返回-EINVAL,否则返回0
*/
int
rte_lpm6_lookup_bulk_func(const struct rte_lpm6 *lpm,
uint8_t ips[][RTE_LPM6_IPV6_ADDR_SIZE],
int32_t *next_hops, unsigned int n);
#ifdef __cplusplus
}
#endif
#endif
2.2 rte_lpm6_create
2.2.1 函数介绍
函数用于创建一个 IPv6 的最长前缀匹配(Longest Prefix Match, LPM)对象。
struct rte_lpm6 *rte_lpm6_create(const char *name, int socket_id, const struct rte_lpm6_config *config);
2.2.2 参数说明
name:
LPM 对象的名称。
socket_id:
用于 LPM 表内存分配的 NUMA 套接字 ID。
config:
包含 LPM 配置的结构体指针。
如果创建成功,则返回指向新创建的 LPM 对象的指针。
如果创建失败,则返回 NULL,并设置 rte_errno 以指示错误原因。可能的错误包括:
E_RTE_NO_CONFIG:
无法获取指向 rte_config 结构的指针。
E_RTE_SECONDARY:
函数从次要进程实例调用。
EINVAL:
传递给函数的参数无效。
ENOSPC:
已经分配了最大数量的内存区域。
EEXIST:
具有相同名称的内存区域已存在。
ENOMEM:
未找到适当的内存区域来创建内存区域。
2.2.3 实例
#include <stdio.h>
#include <rte_lpm6.h>
#define LPM_NAME "example_lpm6"
#define SOCKET_ID 0
#define MAX_RULES 1024
#define NUM_TBL8S 1024
int main() {
// 创建 LPM 配置结构
struct rte_lpm6_config config = {
.max_rules = MAX_RULES,
.number_tbl8s = NUM_TBL8S,
.flags = 0 // 此字段当前未使用
};
// 创建 IPv6 LPM 对象
struct rte_lpm6 *lpm = rte_lpm6_create(LPM_NAME, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Failed to create LPM object\n");
return -1;
}
// 使用 LPM 对象进行其他操作(添加规则、查询、删除等)
// 释放 LPM 对象
rte_lpm6_free(lpm);
return 0;
}
2.3 rte_lpm6_add
2.3.1 函数介绍
函数用于向 IPv6 的最长前缀匹配(Longest Prefix Match, LPM)表中添加规则
int rte_lpm6_add(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth, uint32_t next_hop);
2.3.2 参数说明
lpm:
指向要添加规则的 IPv6 LPM 对象的指针。
ip:
要添加到 LPM 表中的 IPv6 地址的字节流表示形式,以 uint8_t 数组形式传递。
depth:
要添加的规则的前缀长度。
next_hop:
与匹配的规则相关联的下一跳信息。
如果成功添加规则,则返回 0。
如果添加规则失败,则返回负值,表示错误的原因。
2.3.3 实例
#include <stdio.h>
#include <stdint.h>
#include <rte_lpm6.h>
#define LPM_NAME "example_lpm6"
#define SOCKET_ID 0
#define MAX_RULES 1024
#define NUM_TBL8S 1024
int main() {
// 创建 LPM 配置结构
struct rte_lpm6_config config = {
.max_rules = MAX_RULES,
.number_tbl8s = NUM_TBL8S,
.flags = 0 // 此字段当前未使用
};
// 创建 IPv6 LPM 对象
struct rte_lpm6 *lpm = rte_lpm6_create(LPM_NAME, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Failed to create LPM object\n");
return -1;
}
// 添加规则
uint8_t ip[] = {0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // IPv6地址
uint8_t depth = 64; // 前缀长度
uint32_t next_hop = 1; // 下一跳信息
int ret = rte_lpm6_add(lpm, ip, depth, next_hop);
if (ret != 0) {
printf("Failed to add rule to LPM\n");
rte_lpm6_free(lpm);
return -1;
}
// 使用 LPM 对象进行其他操作(添加规则、查询、删除等)
// 释放 LPM 对象
rte_lpm6_free(lpm);
return 0;
}
在上面的示例中,我们首先创建了一个 IPv6 LPM 对象,并添加了一个规则。我们定义了一个 IPv6 地址作为规则的前缀,并指定了前缀的长度和与规则关联的下一跳信息。然后,我们检查了规则添加操作的结果,并在结束时释放了创建的 LPM 对象。
2.4 rte_lpm6_delete
2.4.1 函数介绍
函数用于从 IPv6 的最长前缀匹配(Longest Prefix Match, LPM)表中删除规则。
int rte_lpm6_delete(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth);
2.4.2 参数说明
lpm:
指向要从中删除规则的 IPv6 LPM 对象的指针。
ip:
要从 LPM 表中删除的 IPv6 地址的字节流表示形式,以 uint8_t 数组形式传递。
depth:
要删除的规则的前缀长度
如果成功删除规则,则返回 0。
如果删除规则失败,则返回负值,表示错误的原因。
2.4.3 实例
#include <stdio.h>
#include <stdint.h>
#include <rte_lpm6.h>
#define LPM_NAME "example_lpm6"
#define SOCKET_ID 0
#define MAX_RULES 1024
#define NUM_TBL8S 1024
int main() {
// 创建 LPM 配置结构
struct rte_lpm6_config config = {
.max_rules = MAX_RULES,
.number_tbl8s = NUM_TBL8S,
.flags = 0 // 此字段当前未使用
};
// 创建 IPv6 LPM 对象
struct rte_lpm6 *lpm = rte_lpm6_create(LPM_NAME, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Failed to create LPM object\n");
return -1;
}
// 添加规则
uint8_t ip[] = {0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // IPv6地址
uint8_t depth = 64; // 前缀长度
uint32_t next_hop = 1; // 下一跳信息
int ret = rte_lpm6_add(lpm, ip, depth, next_hop);
if (ret != 0) {
printf("Failed to add rule to LPM\n");
rte_lpm6_free(lpm);
return -1;
}
// 删除规则
ret = rte_lpm6_delete(lpm, ip, depth);
if (ret != 0) {
printf("Failed to delete rule from LPM\n");
rte_lpm6_free(lpm);
return -1;
}
// 使用 LPM 对象进行其他操作(添加规则、查询、删除等)
// 释放 LPM 对象
rte_lpm6_free(lpm);
return 0;
}
2.5 rte_lpm6_lookup
2.5.1 函数介绍
函数用于在 IPv6 的最长前缀匹配(Longest Prefix Match, LPM)表中查找给定的 IPv6 地址,并提供与匹配规则关联的下一跳信息
int rte_lpm6_lookup(const struct rte_lpm6 *lpm, const uint8_t *ip, uint32_t *next_hop);
2.5.2 参数说明
lpm:
指向要查询的 IPv6 LPM 对象的指针。
ip:
要查找的 IPv6 地址的字节流表示形式,以 uint8_t 数组形式传递。
next_hop:
用于存储查找到的下一跳信息的指针。
如果查找成功并找到匹配的规则,则返回 0。
如果查找失败,即未找到匹配的规则,则返回负值,表示错误的原因。
2.5.3 实例
#include <stdio.h>
#include <stdint.h>
#include <rte_lpm6.h>
#define LPM_NAME "example_lpm6"
#define SOCKET_ID 0
#define MAX_RULES 1024
#define NUM_TBL8S 1024
int main() {
// 创建 LPM 配置结构
struct rte_lpm6_config config = {
.max_rules = MAX_RULES,
.number_tbl8s = NUM_TBL8S,
.flags = 0 // 此字段当前未使用
};
// 创建 IPv6 LPM 对象
struct rte_lpm6 *lpm = rte_lpm6_create(LPM_NAME, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Failed to create LPM object\n");
return -1;
}
// 添加规则
uint8_t ip[] = {0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // IPv6地址
uint8_t depth = 64; // 前缀长度
uint32_t next_hop = 1; // 下一跳信息
int ret = rte_lpm6_add(lpm, ip, depth, next_hop);
if (ret != 0) {
printf("Failed to add rule to LPM\n");
rte_lpm6_free(lpm);
return -1;
}
// 查找规则
uint32_t found_next_hop;
ret = rte_lpm6_lookup(lpm, ip, &found_next_hop);
if (ret == 0) {
printf("Next hop found: %u\n", found_next_hop);
} else {
printf("Rule not found in LPM\n");
}
// 使用 LPM 对象进行其他操作(添加规则、删除规则等)
// 释放 LPM 对象
rte_lpm6_free(lpm);
return 0;
}
2.6 rte_lpm6_is_rule_present
2.6.1 函数介绍
函数用于检查指定的 IPv6 地址是否存在于 IPv6 的最长前缀匹配(Longest Prefix Match, LPM)表中,并提供与该规则关联的下一跳信息。
int rte_lpm6_is_rule_present(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth, uint32_t *next_hop);
2.6.2 参数说明
lpm:
指向要查询的 IPv6 LPM 对象的指针。
ip:
要查询的 IPv6 地址的字节流表示形式,以 uint8_t 数组形式传递。
depth:
要查询的规则的前缀长度。
next_hop:
用于存储查找到的下一跳信息的指针。
如果规则存在并且成功找到,则返回 1。
如果规则不存在,则返回 0。
如果发生错误,则返回负值,表示错误的原因。
2.6.3 实例
#include <stdio.h>
#include <stdint.h>
#include <rte_lpm6.h>
#define LPM_NAME "example_lpm6"
#define SOCKET_ID 0
#define MAX_RULES 1024
#define NUM_TBL8S 1024
int main() {
// 创建 LPM 配置结构
struct rte_lpm6_config config = {
.max_rules = MAX_RULES,
.number_tbl8s = NUM_TBL8S,
.flags = 0 // 此字段当前未使用
};
// 创建 IPv6 LPM 对象
struct rte_lpm6 *lpm = rte_lpm6_create(LPM_NAME, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Failed to create LPM object\n");
return -1;
}
// 添加规则
uint8_t ip[] = {0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // IPv6地址
uint8_t depth = 64; // 前缀长度
uint32_t next_hop = 1; // 下一跳信息
int ret = rte_lpm6_add(lpm, ip, depth, next_hop);
if (ret != 0) {
printf("Failed to add rule to LPM\n");
rte_lpm6_free(lpm);
return -1;
}
// 检查规则是否存在
uint32_t found_next_hop;
ret = rte_lpm6_is_rule_present(lpm, ip, depth, &found_next_hop);
if (ret == 1) {
printf("Rule is present, next hop: %u\n", found_next_hop);
} else if (ret == 0) {
printf("Rule is not present in LPM\n");
} else {
printf("Error checking rule presence\n");
}
// 使用 LPM 对象进行其他操作(添加规则、删除规则等)
// 释放 LPM 对象
rte_lpm6_free(lpm);
return 0;
}
2.7 rte_lpm6_free
2.7.1 函数介绍
函数用于释放 IPv6 最长前缀匹配(Longest Prefix Match, LPM)对象所占用的资源,并将其销毁。
void rte_lpm6_free(struct rte_lpm6 *lpm);
2.7.2 参数说明
lpm:
要释放的 IPv6 LPM 对象的指针。
2.7.3 实例
#include <stdio.h>
#include <rte_lpm6.h>
#define LPM_NAME "example_lpm6"
#define SOCKET_ID 0
#define MAX_RULES 1024
#define NUM_TBL8S 1024
int main() {
// 创建 LPM 配置结构
struct rte_lpm6_config config = {
.max_rules = MAX_RULES,
.number_tbl8s = NUM_TBL8S,
.flags = 0 // 此字段当前未使用
};
// 创建 IPv6 LPM 对象
struct rte_lpm6 *lpm = rte_lpm6_create(LPM_NAME, SOCKET_ID, &config);
if (lpm == NULL) {
printf("Failed to create LPM object\n");
return -1;
}
// 使用 LPM 对象进行其他操作(添加规则、删除规则等)
// 释放 LPM 对象
rte_lpm6_free(lpm);
return 0;
}