DPDK — L3 Forwarding 与 IP 路由选择算法

目录

L3 Forwarding Application

L3 Forwarding Application 是一个实现了 LPM(最长前缀匹配)和 EM(精确匹配)路由选择算法的 IP 数据包转发应用程序。

  • EM(精确匹配):是最基本的路由匹配算法,对数据包的 IP 5-tuple 执行 Hash function 得到 Hash value,与 Route table 中的 Hash key 完全一致时匹配下一跳。基于 HASH match 具有更高的性能。

  • LPM(最长前缀匹配):是基于 CIDR 的路由匹配算法,当数据包的 dstIP 地址和 Route table 中最长的 IP/Netmask 匹配时,找到下一跳。具有更好的灵活性。

安装部署

部署拓扑

                      |----------------|
                      |     l3fwd      |
                      |                |
                      |  eth1   eth2   |
              1.1.1.1 |----|------|----|  2.1.1.1
    52:54:00:5E:8C:DF      |      |       52:54:00:A7:A7:F6
                          /        \
                         /          \
                        /            \
                       /              \
  52:54:00:4a:1f:6d   /                \   52:54:00:53:5a:d2
           1.1.1.2   |                  |  2.1.1.2
            |--------|------|   |-------|-------|
            |      eth1     |   |     eth2      |
            |               |   |               |
            |    server0    |   |    server1    |
            |---------------|   |---------------|

编译运行 L3fwd

  • 编译
$ cat dpdk.rc
export RTE_SDK=/opt/dpdk-18.08
export RTE_TARGET=x86_64-native-linuxapp-gcc
export DPDK_BUILD=${DPDK_DIR}/${RTE_TARGET}
export LD_LIBRARY_PATH=${RTE_SDK}/${RTE_TARGET}/lib:/usr/local/lib:/usr/lib:

$ source dpdk.rc

$ cd ${RTE_SDK}/examples/l3fwd
$ make
$ ll build/l3fwd
  • 指令行格式
    • -p PORTMASK:指定要使用的 Ports 的十六进制位掩码(Bitmap)。
    • -P:将所有 Ports 都设置为混杂模式,使得无论 Frame 的 dstMAC 是不是本地网卡都可以接收。
    • -E:启用精确匹配算法(Exact match)。
    • -L:启动最长前缀匹配算法(Longest prefix match)。
    • –config:指示 port、queue、lcore 的映射关系。和 -l {lcore_list} 、-p {PORTMASK} 都要能够对应上。
    • –eth dest:指示 PortX 的 dstMAC 地址。
    • –enable-jumbo:启用 Jumbo 数据帧。Jumbo 数据帧是一种比标准以太网帧(MTU 1500Byte)更大的数据帧。它的 MTU 可达 9000Byte,这使得它比标准以太网帧能够承载更多的数据。Jumbo 数据帧需要被所有传输数据的设备都支持,否则就会出现传输错误或丢失数据的情况。
    • –max-pkt-len:在启用 Jumbo 的前提下,以十进制表示最大的 MTU。
    • –no-numa:禁用 NUMA 亲和。
    • –hash-entry-num:指示十六进制 HASH Entry 的数量。
    • –parse-ptype:指示使用软件的方式分析数据包的 protocol type(协议类型),默认采用硬件分析的方式。
./l3fwd [EAL options] -- -p PORTMASK
                         [-P]
                         [-E]
                         [-L]
                         --config(port,queue,lcore)[,(port,queue,lcore)]
                         [--eth-dest=X,MM:MM:MM:MM:MM:MM]
                         [--enable-jumbo [--max-pkt-len PKTLEN]]
                         [--no-numa]
                         [--hash-entry-num]
                         [--ipv6]
                         [--parse-ptype]
  • 运行
    • -l 1:只使用一个 lcore,因为测试机的网卡不支持多队列,而 l3fwd 会为每个 lcore 分配一个 Tx queue,所以只能使用 1 个 lcore,否则会在 rte_eth_dev_configure() 的时候失败
    • 52:54:00:4a:1f:6d - server0 eth1 发出的 Frame 的 dstMAC 地址。因为 l3fwd 没有 ARP 协议,所以需要手动指定每个 Ports 的 dstMAC。
    • 52:54:00:53:5a:d2- server1 eth2 发出的 Frame 的 dstMAC 地址。
./build/l3fwd -l 1 -- -p 0x3 -P --config="(0,0,1),(1,0,1)" --parse-ptype --eth-dest=0,52:54:00:4a:1f:6d --eth-dest=1,52:54:00:53:5a:d2

soft parse-ptype is enabled
LPM or EM none selected, default LPM on

Initializing port 0 ... Creating queues: nb_rxq=1 nb_txq=1... Port 0 modified RSS hash function based on hardware support,requested:0xa38c configured:0
 Address:52:54:00:5E:8C:DF, Destination:52:54:00:4A:1F:6D, Allocated mbuf pool on socket 0

LPM: Adding route 0x01010100 / 24 (0)
LPM: Adding route 0x02010100 / 24 (1)
LPM: Adding route IPV6 / 48 (0)
LPM: Adding route IPV6 / 48 (1)
txq=1,0,0

Initializing port 1 ... Creating queues: nb_rxq=1 nb_txq=1... Port 1 modified RSS hash function based on hardware support,requested:0xa38c configured:0
 Address:52:54:00:A7:A7:F6, Destination:52:54:00:53:5A:D2, txq=1,0,0

Skipping disabled port 2

Initializing rx queues on lcore 1 ... rxq=0,0,0 rxq=1,0,0

Port 0: softly parse packet type info
Port 1: softly parse packet type info

Checking link statusdone
Port0 Link Up. Speed 10000 Mbps -full-duplex
Port1 Link Up. Speed 10000 Mbps -full-duplex
L3FWD: entering main loop on lcore 1
L3FWD:  -- lcoreid=1 portid=0 rxqueueid=0
L3FWD:  -- lcoreid=1 portid=1 rxqueueid=0

测试 L3 转发功能

  • l3fwd_lpm.c 中定义的默认路由表,port0 为 1.1.1.0/24,port1 为 2.1.1.0/24。
static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
	{IPv4(1, 1, 1, 0), 24, 0},
	{IPv4(2, 1, 1, 0), 24, 1},
	{IPv4(3, 1, 1, 0), 24, 2},
	{IPv4(4, 1, 1, 0), 24, 3},
	{IPv4(5, 1, 1, 0), 24, 4},
	{IPv4(6, 1, 1, 0), 24, 5},
	{IPv4(7, 1, 1, 0), 24, 6},
	{IPv4(8, 1, 1, 0), 24, 7},
};
  • server01:因为 l3fwd 不能处理 ARP 协议,所以需要手动添加 l3fwd port0 的静态 MAC 地址表。
$ ip addr add dev eth1 1.1.1.2/24
$ ip route add 2.1.1.2 via 1.1.1.1 dev eth1
$ ip nei add 1.1.1.1 lladdr 52:54:00:5E:8C:DF dev eth1
  • server02
$ ip addr add dev eth2 2.1.1.2/24
$ ip route add 1.1.1.2 via 2.1.1.1 dev eth2
$ ip nei add 2.1.1.1 lladdr 52:54:00:A7:A7:F6 dev eth2
  • server01 ping server02
$ ping 2.1.1.2
PING 2.1.1.2 (2.1.1.2) 56(84) bytes of data.
64 bytes from 2.1.1.2: icmp_seq=30 ttl=63 time=0.659 ms

性能参数

  • 10GbE、Intel x710、2NUMA、4ports

在这里插入图片描述

实现分析

代码注释

  • Github:https://github.com/JmilkFan/dpdk-samples

函数关系调用图

在这里插入图片描述

LPM Library

  • Docs:http://doc.dpdk.org/guides/prog_guide/lpm_lib.html#implementation-details

DPDK LPM(Longest Prefix Match)库是一个高性能的前缀路由匹配库,用于在数据包转发过程中快速查找与 dstIP 地址最长匹配的路由表项。

LPM 库具有以下特点:

  • 高性能:LPM 库使用基于前缀树的算法实现快速匹配。
  • 多核(多线程)安全:LPM 库支持多线程并发安全,能够充分利用多核处理器的计算资源。
  • 灵活配置:LPM 库支持动态配置路由表,可以在运行时添加、删除或修改路由表项,以适应网络拓扑的变化。
  • 内存管理:LPM 库使用 Memory Pool 来管理内存。

算法设计

两级 HASH 表

LPM Library 具有很好的灵活性,同时为了兼顾性能,底层实现仍是基于 HASH 算法,并且将 32bit 的 IP 地址分为 2 个部分:

  1. tbl24(24bit):1 张 2^24 entries 的 HASH 表。
  2. tbl8(8bit):最多 256 张 2^8 entries 的 HASH 表。

在这里插入图片描述

查询算法

两级 HASH 表带来的效果是,当 Lookup IP/Netmask <= 24bit 时,只需要查一次表就可以得到 NextHop;而当 > 24bit 时,就需要查 2 次表,而这种情况相对较少。

在这里插入图片描述

示例

在这里插入图片描述

核心数据结构

在这里插入图片描述

rte_lpm

struct rte_lpm {

	/* LPM metadata. */
	char name[RTE_LPM_NAMESIZE];  // 表名
	uint32_t max_rules;           // 最大 Entries 数量
	uint32_t number_tbl8s;        // 最大 tbl8 表数量
	struct rte_lpm_rule_info rule_info[RTE_LPM_MAX_DEPTH];  // 存储 entry 信息的结构体数组,长 32。

	/* LPM Tables. */
	struct rte_lpm_tbl_entry tbl24[RTE_LPM_TBL24_NUM_ENTRIES] __rte_cache_aligned;  // tbl24 表数组,长度 2^24
	struct rte_lpm_tbl_entry *tbl8;  // tbl8 表空间指针,空间为 255 * number_tbl8s
	struct rte_lpm_rule *rules_tbl;  // Entries 空间指针,长度为 max_rules
};


/* 用来存储 entry 信息,具有相同掩码的条目从 first_rule 开始到 first_rule + used_rules - 1 结束。*/
struct rte_lpm_rule_info {
	uint32_t used_rules;  /**< Used rules so far. */
	uint32_t first_rule;  /**< Indexes the first rule of a given depth. */
};


/**
 * 用来存储 tbl24 和 tbl8 HASH 表。
 */
struct rte_lpm_tbl_entry {
	/**
	 * Stores Next hop (tbl8 or tbl24 when valid_group is not set) or
	 * a group index pointing to a tbl8 structure (tbl24 only, when
	 * valid_group is set)
	 */
	uint32_t next_hop    :24;  // 当为 tbl24 节点且此节点上挂载 tbl8 的时候, 此数据表示 tbl8 的开始索引;当为 tbl24 节点, 但是此节点上不存在 tbl8 的时候, 此数据为真实的下一跳。
	/* Using single uint8_t to store 3 values. */
	uint32_t valid       :1;   // 表明此节点是否有效
	/**
	 * For tbl24:
	 *  - valid_group == 0: entry stores a next hop
	 *  - valid_group == 1: entry stores a group_index pointing to a tbl8
	 * For tbl8:
	 *  - valid_group indicates whether the current tbl8 is in use or not
	 */
	uint32_t valid_group :1;  // 当为 tbl24 节点的时候 valid_group 为 1 表明 next_hop 为 tbl8 的开始索引,为 0 则表示真实的下一跳。
	uint32_t depth       :6;  // 此 HASH 节点的掩码位。
};

/* 存储的具体的路由表项 */
struct rte_lpm_rule {
	uint32_t ip;        // dstIP 地址
	uint32_t next_hop;  // 下一跳
};

rte_lpm_config

/** LPM configuration structure. */
struct rte_lpm_config {
	uint32_t max_rules;      /**< Max number of rules. */
	uint32_t number_tbl8s;   /**< Number of tbl8s to allocate. */
	int flags;               /**< This field is currently unused. */
};

接口函数

接口实际实现功能
rte_lpm_createrte_lpm_create_v1604创建路由表
rte_lpm_freerte_lpm_free_v1604释放路由表占用的空间
rte_lpm_addrte_lpm_add_v1604添加路由
rte_lpm_deleterte_lpm_delete_v1604删除路由
rte_lpm_delete_allrte_lpm_delete_all_v1604销毁路由表
rte_lpm_lookup路由查找实现
rte_lpm_find_existingrte_lpm_find_existing_v1604根据名字找到路由表
rte_lpm_is_rule_presentrte_lpm_is_rule_present_v1604检查是否条目存在

rte_lpm_create()

struct rte_lpm *rte_lpm_create(const char *name, int socket_id, struct rte_lpm_config *config)

功能:创建一个新的 LPM table。

参数:

  • name:LPM table 的名称。
  • socket_id:LPM table 在哪个 NUMA socket 上分配。
  • config:LPM table 的配置信息。

返回值:

  • 成功:指向新创建的 LPM table 的指针。
  • 失败:NULL。

rte_lpm_free()

void rte_lpm_free(struct rte_lpm *lpm)

功能:释放 LPM table 所占用的内存。

参数:

  • lpm:指向已初始化的 LPM table 的指针。

返回值:无。

rte_lpm_add()

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

功能:添加一条 Entry 到 LPM table 中。

参数:

  • lpm:指向已初始化的 LPM table 的指针。
  • ip:dstIP 地址
  • depth:dstIP 地址的地址掩码长度
  • next_hop:下一跳

返回值:

  • 成功:0。
  • 失败:负数。

rte_lpm_delete()

int rte_lpm_delete(struct rte_lpm *lpm, uint32_t ip, uint8_t depth)

功能:从 LPM table 中删除一条 Entry。

参数:

  • lpm:指向已初始化的 LPM table 的指针。
  • ip:dstIP 地址
  • depth:dstIP 地址的地址掩码长度

返回值:

  • 成功:0。
  • 失败:负数。

rte_lpm_lookup()

int rte_lpm_lookup(const struct rte_lpm *lpm, uint32_t ip, uint32_t *next_hop)

功能:在 LPM table 中查找与指定 dstIP 地址最长匹配的前缀,并返回其下一跳。

参数:

  • lpm:指向已初始化的 LPM table 的指针。
  • ip:指定的 dstIP 地址。
  • next_hop:查找成功后,存储匹配前缀的下一跳。

返回值:

  • 匹配成功:匹配前缀的前缀长度。
  • 匹配失败:-ENOENT。

rte_lpm_is_rule_present()

int rte_lpm_is_rule_present(const struct rte_lpm *lpm, uint32_t ip, uint8_t depth, uint32_t *next_hop)

功能:检查给定的 Entry 是否存在于 LPM table 中。

参数:

  • lpm:指向已初始化的 LPM table 的指针。
  • ip:dstIP 地址
  • depth:dstIP 地址的地址掩码长度
  • next_hop:下一跳

返回值:

  • Entry 存在:前缀长度。
  • Entry 不存在:-ENOENT。

rte_lpm_check_params()

int rte_lpm_check_params(const struct rte_lpm_config *config)

功能:检查 LPM table 的 config 配置参数是否有效。

参数:

  • config:LPM table 配置参数。

返回值:

  • 参数有效:0。
  • 参数无效:-EINVAL。

rte_lpm_find_existing()

int rte_lpm_find_existing(const char *name, int socket_id, struct rte_lpm_config *config)

功能:在指定的 NUMA socket 中查找 LPM table。

参数:

  • name:LPM table 的名称。
  • socket_id:NUMA socket ID。
  • config:LPM table 配置参数。

返回值:

  • 找到:指向现有 LPM table 的指针。
  • 没有找到:NULL。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范桂飓

文章对您有帮助就请一键三连:)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值