1. 前缀树说明
1.1 概述
最长前缀树匹配是ovs流表查找的基础,是ovs中很重要的一个模块。和路由条目CIDR类似,它的定义是,有条目A1=a1,A2=a1a2,A3= a1a2a3,那么当用A4(A4= a1a2a3a4)去匹配A1、A2、A3时,A3将会被匹配到,因为它从左到右匹配到了最多值(a1a2a3).
比如存在路由条目:192.168.1.0/24和192.168.0.0/16时,当用192.168.1.1去匹配的时候,192.168.1.0/24将会被匹配到,因为它匹配的更多。
1.2 前缀树功能
最主要的目的是,输入一个值,从现有条目空间中,返回从最高位匹配最多的条目。
1.3 前缀树数据结构表示
ovs中使用二叉树实现最长前缀匹配的。
如上图,每个节点包含一定位数的值(prefix),是对A3= a1a2a3中,a1、a2、a3、ai的表示,在实现中,每个节点对应值的位数不大于32位。因此,从root根节点到达该node上的所有prefix集合就是该节点代表的项的值。比如图中的0100110110/10。
rules:表示该节点对应的有效条目数,如果为0,那么在树的条目空间中,并没有与该值/mask,对应的条目,但有时它必须在树中存在(虽然无效),如果它有两个子节点(如上图中虚线标识的节点1011/4)。
n_bits:表示从root根节点到达该节点所有的位数,同mask的意义。
2. ovs前缀树操作
ovs前缀树中节点表示的值是flow的字段(比如,源ip、目的ip、源端口、目的端口等)。所以在构建前缀树时,都会有个field项,该项表示该前缀树是依据flow流表中哪些字段构建的(比如源ip地址)。
2.1 初始化
树的表示:
/* Prefix trie for a 'field' */
struct cls_trie {
const struct mf_field *field; /* Trie field, or NULL. */
rcu_trie_ptr root; /* NULL if none. */
};
把构建该前缀树依据的flow字段,赋予field。使root为null。
2.2 插入
/*
prefix是mf位置对应的rule flow的值
*/
static void
trie_insert_prefix(rcu_trie_ptr *edge, const ovs_be32 *prefix, int mlen)
edge:根节点root
prefix:要插入项的前缀值,它是一个32位值的数组。
mlen:插入项的前缀长度,即mask长度。
- 从root节点开始匹配每个节点的prefix,如果要插入项的prefix对应的位与该A节点prefix值完全相等,则完全匹配。
- 如果不完全匹配,即只匹配了该A节点的前i位prefix(0< i <该节点的prefix长度),那么把该A节点从i位拆分为2个新节点,拆分后的第2部分(原来A节点从i位后的prefix)为原来的A节点,并更新prefix值。如果要插入的节点完全匹配第1部分,那么更新第1部分为新节点,rules加1。否则新插入的节点作为第1部分的子节点存在。如图中,为了连接两个新节点可能会产生图中虚线标识的连接节点。
- 如果完全匹配,且要插入项的mask长度正好等于A节点的n_bits,即要插入项的所有prefix和从root到A节点的路径上的所有prefix完全匹配,那么该A节点是要插入的节点,把A节点的rules加1,表示该节点增加一个有效项。
- 如果完全匹配,但插入项的mask长度大于A节点的n_bits位,即要插入的项是一个更长mask的项,所有创建一个新节点,该节点的prefix为剩余的前缀,并且根据剩余前缀的最高有效位(0或1),插入到A节点中,作为A节点的子树。并更新rules为1。
2.3 查找
根据要查找的值value,从root节点开始,从value的最高有效位匹配途径节点的prefix,找到叶子节点,或者没找到叶子节点(没有找到最少匹配1位的项)。叶子节点即为查到的最长匹配的节点。
2.4 删除
同查找步骤,先找到匹配的节点,然后把该节点从树种删除,删除节点后可能会遇到合并前后两个节点的情况。比如删除图中的1011 1011/8节点,就会合并1011/4和1011 00/6节点为1个节点。
2.5 操作复杂度
容易得出,不管插入、查找、删除,最坏情况复杂度都是O(h),h是树的深度。h并不一定是log2n(n是前缀树表示值的最大位数),因为该前缀树的每个节点都可以最大表示32位的值。
所以该前缀树有比较好的操作性能和灵活度。
3. ovs前缀树应用
ovs内部的classfier都会用到前缀树,因为ovs的rule查找都是依据flow流字段的,而流字段很容易使用前缀树表示。
具体使用的,包括流表查找,隧道端口查找,ovs路由查找,隧道口映射查找等。当然,后续如果自己有这方面的功能需求,可以借鉴使用