开源世界中的算法与数据结构 3 -- Linux IPv6 FIB表实现
在Linux 2.4 IPv4 FIB的数据结构基础上实现IPv6的FIB是否可行呢?如果读了我前面这篇文字你应该会有一个判断。IPv4的FIB实现可以说有些拙劣,如果照搬一个IPv6版本,最差情况下需要进行128次hash key计算这还不包括链表的查找过程。看了一下Linux 2.6的IPv6 FIB实现,已经有了调整,用了Patrix(Radix)树实现了这个算法,下面是一些背景知识:
首先是Trie树,下图是Wiki http://en.wikipedia.org/wiki/Trie 之中的一个例子
Trie树,尤其是二叉Trie树属于:是一直被使用 ,从未被(教科书)重视的东东。其特点是键值的内容成为树的检索路径,例如上图的to,tea等几条键值标明的路径。如果要对trie分类的话,我想只能按照出度来分类,上图假定键值的每一字节取值a-z,则这个trie是26叉trie树,最小出度的Trie树是二叉Trie树,简称BTrie。
所谓PATRICIA Tree或Radix Tree是同一种数据结构, 不是标准Trie树,是Trie树的优化变异,Wiki http://en.wikipedia.org/wiki/Radix_tree上有如下图:
romane和romanus以及romulus有共同前缀rom,此处r是根,om本应为两个节点,现在合并为一个,这个就是Radix树的优化思路,优化之后,可以减少tree的层次,提高匹配速度。而IPv6 FIB就实现为一种二叉 Radix树,其实现结构同这种标准的Radix树还有少许改动,下面简介其实现:
其节点数据结构如下:
struct fib6_node {
struct fib6_node *parent; -- 因为其lookup算法实现要求回溯,所以引入了parent指针,如果按照标准的radix树Wiki实现,则不需要这个节点。
struct fib6_node *left;
struct fib6_node *right;
#ifdef CONFIG_IPV6_SUBTREES
struct fib6_node *subtree; -- 用于MobileIPv6
#endif
struct rt6_info *leaf; -- 存储路由信息
__u16 fn_bit; Prefix长度
__u16 fn_flags;
__u32 fn_sernum;
struct rt6_info *rr_ptr;
};
其查找算法实现为如下函数fib6_lookup_1:
static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
struct lookup_args *args)
{
struct fib6_node *fn;
__be32 dir;
if (unlikely(args->offset == 0))
return NULL;
/*
* Descend on a tree
*/
fn = root;
/*
这一段for循环按照输入的IP进行最长掩码匹配
由于fib node之中没有保存父子点common的prefix,
因此无法进行精确匹配,只能按照分支bit进行匹配,决定前进方向
*/
for (;;) {
struct fib6_node *next;
dir = addr_bit_set(args->addr, fn->fn_bit);
next = dir ? fn->right : fn->left;
if (next) {
fn = next;
continue;
}
break;
}
/*
上述for循环结束时,找到了最match的节点。但是由于是模糊匹配,
需要回溯每个节点,找到一个最终的正确的最长掩码匹配。
*/
while(fn) {
if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
struct rt6key *key;
key = (struct rt6key *) ((u8 *) fn->leaf +
args->offset);
if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
#ifdef CONFIG_IPV6_SUBTREES
if (fn->subtree)
fn = fib6_lookup_1(fn->subtree, args + 1);
#endif
if (!fn || fn->fn_flags & RTN_RTINFO)
return fn;
}
}
if (fn->fn_flags & RTN_ROOT)
break;
fn = fn->parent;
}
return NULL;
}
算法过程可以用上图表明,有三类节点:
1.叶子节点:带有路由信息
2.中间节点:同某些叶子节点有覆盖关系的父节点,也带有路径信息
3.纯内部节点。
上图假定检索到叶子节点6,但是查找后发现路由不匹配,而后通过parent回溯,如果内部节点有路由,则继续匹配,直到匹配了3,符合最长掩码匹配。
参照fib6实现的注释,作者Yuji SEKIYA是日本人。Linux的IPv6协议实现源于日本的USAGI项目,而BSD的IPv6协议栈实现源于Kame项目,也是日本人的项目。一方面反映了老美IPv4地址充足,不care这个领域,一方面也应该让国内Linux牛人汗颜
建议继续学习:
- 浅谈MySQL索引背后的数据结构及算法 (阅读:9961)
- 爱喝啤酒的程序员是如何学习数据结构的 (阅读:5185)
- 分布式系统的数据结构 (阅读:5021)
- accept_ra 的一个例子 (阅读:4519)
- stream.js :一个新的JavaScript数据结构 (阅读:4101)
- debian开启与关闭IPV6 (阅读:3274)
- 开源世界中的算法与数据结构 3 -- Linux Kernel List 和GList (阅读:2957)
- 开源世界中的算法与数据结构 2 -- Linux Skbuff实现 (阅读:2779)
- 数据结构之treap (阅读:2747)
- 解决 IPv6 路由发现协议得到错误地址的问题 (阅读:2701)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:appleleaf 来源: kernelchina blogs
- 标签: FIB IPv6 数据结构
- 发布时间:2012-02-05 23:22:36
- [70] Twitter/微博客的学习摘要
- [65] IOS安全–浅谈关于IOS加固的几种方法
- [65] 如何拿下简短的域名
- [65] find命令的一点注意事项
- [63] Go Reflect 性能
- [63] android 开发入门
- [61] 流程管理与用户研究
- [59] 读书笔记-壹百度:百度十年千倍的29条法则
- [59] Oracle MTS模式下 进程地址与会话信
- [59] 图书馆的世界纪录