利用fib_lookup来查找路由表的方法、 source code 和参数说明
路由查找都是利用fib_lookup来查找路由表的。下面介绍它的方法和一些参数说明。
fib_lookup有两个版本,一个是当内核支持策略路由时使用;一个是当内核不支持策略路由时使用。
选择哪一个函数是在编译时决定的,所以当ip_route_input_slow和ip_route_output_slow调用fib_lookup时,它们透明地激活正确的查找函数。
fib_lookup程序是对每一个路由表所提供的查找函数的一个包装(wrapper)。当不支持策略路由时,查找函数版本是针对local表和main表。当支持策略路由时,逻辑更为复杂,需要查找由策略路由提供的路由表。
先看看没有策略路由时的source code:
- static inline int fib_lookup(struct net *net, const struct flowi *flp, struct fib_result *res)
- {
- struct fib_table *table;
- table = fib_get_table(net, RT_TABLE_LOCAL);
- if (!fib_table_lookup(table, flp, res)) return 0;
- table = fib_get_table(net, RT_TABLE_MAIN);
- if (!fib_table_lookup(table, flp, res)) return 0;
- return -ENETUNREACH;
- }
在系统初始化时总是创建下面两个路由表,它们与内核配置选项无关:
RT_TABLE_LOCAL
内核将到本地地址的路由表项放在该表中,包括到相关的网段地址以及网段广播地址的路由表项,用户不能够直接配置该路由表。
RT_TABLE_MAIN
所有其他的路由表项(包括用户配置的静态路由表项,路由协议生成的动态路由表项)都放在该表内。
继续跟进到fib_table_lookup
- int fib_table_lookup(struct fib_table *tb, const struct flowi *flp, struct fib_result *res)
- {
- int err;
- struct fn_zone *fz;
- struct fn_hash *t = (struct fn_hash *)tb->tb_data;
- read_lock(&fib_hash_lock);
- //对说有的网络掩码zone进行循环处理
- for (fz = t->fn_zone_list; fz; fzfz = fz->fz_next) {
- struct hlist_head *head;
- struct hlist_node *node;
- struct fib_node *f;
- __be32 k = fz_key(flp->fl4_dst, fz);//根据目标ip地址得到网络掩码
- head = &fz->fz_hash[fn_hash(k, fz)];//fib_node为链接元素的hash表
- //对每个fib_node进行循环处理
- hlist_for_each_entry(f, node, head, fn_hash) {
- if (f->fn_key != k) continue;//网络掩码不同就退出
- err = fib_semantic_match(&f->fn_alias, flp, res, fz->fz_order);//详细查找
- if (err <= 0) goto out;
- }
- }
- err = 1;
- out:
- read_unlock(&fib_hash_lock);
- return err;
- }
参数说明如下:
tb 搜索的路由表。因为fn_hash_lookup是一个通用的查找程序,每次只能够查找一张表。
调用方根据是否支持策略路由和相关的因素,来决定搜索哪些路由表。
flp 搜索key。
res 查找成功时,利用路由信息来初始化res。
下面给出可能的返回值:
0:成功 已经根据转发信息初始化res(通过fib_semantic_match函数)。
1:失败 没有与搜索key匹配的路由项。
小于0: 管理失败(Administrative failure)
这表示查找不成功,因为查找到的路由没有价值:例如相关的主机可能被标记为不可达。
查找说明:
路由查找使用的算法是LPM,即最长匹配。匹配的网络掩码越长,说明路由更准确。
比如,10.1.1.0/16和10.1.1.0/24这两个网络掩码,覆盖的主机个数完全不是一个等级的。
根据LPM算法,10.1.1.0/24将首先进行匹配,匹配不符,才匹配10.1.1.0/16。
使用了的网络掩码zone都被连接到fn_zone_list这个双向链表上了,链接时,顺序就已经按照网络掩码长度,进行排序了。所以查找时,肯定是按照,最长匹配原则来查找。
在fib_table_lookup函数中,将目的IP地址与检查的active zone的网络掩码相与(ANDs),与操作的结果作为搜索key。例如,如果正在检查/24 zone,目的地址flp->fl4_dst为10.0.1.2,则搜索key k为10.0.1.2 & 255.255.255.0,结果为10.0.1.0。这意味着接下来的代码要搜索到子网10.0.1.0/24的路由:
u32 k = fz_key(flp->fl4_dst, fz);
因为路由被存储在哈希表(fz_hash)内,所以首先通过一个哈希函数对搜索key k进行哈希,得到相应的哈希桶head。接下来是遍历与该哈希桶相关的路由链表(fib_node结构),查找与k匹配的路由项。
一个fib_node覆盖了同一子网内的所有路由项,但这些路由项在诸如TOS等其他字段上可能不同。现在,如果fib_table_lookup函数找到了与搜索key k匹配的fib_node,它还需要检查每一个潜在的路由项,来查找与输入参数flp中其他搜索字段相匹配的路由。这个详细检查是由fib_semantic_match来完成的。
fib_semantic_match也用查找结果来初始化输入参数res,如果它返回成功,fn_hash_lookup就将该结果返回给调用方。fn_hash_lookup遍历路由表中所有的active zone,直到fib_semantic_match或者返回一个成功结果,或者发现该路由表中没有可用的路由(即这些路由项都不匹配)。