------------------------------------------------------------------------------------------
以下是我根据 linux-2.6.23.9版本内核源代码所做阅读笔记,属个人兴趣而为,希望找到有共同兴趣
的朋友一起讨论和研究,有谬误之处,笔者水平有限,欢迎大家拍砖:)
------------------------------------------------------------------------------------------
路由查找
//这个函数负责从fib_table中查找fn_key相匹配的fib_node,然后找到相应的fib_info,
//将结果存放在fib_result结构中
//传入参数fz_divisor是linux kernel 2.6的变化,在2.4.x中是直接传入key的,而flp参数里面包含了数据包的全部地址信息,传入之后再用目的地址来计算key.
static int fn_hash_lookup(struct fib_table *tb, const struct flowi *flp, struct fib_result *res)
{
int err;
struct fn_zone *fz;
//fn_table->tb_data 就是 fn_hash 结构
struct fn_hash *t = (struct fn_hash*)tb->tb_data;
read_lock(&fib_hash_lock);
//遍历可用的fz_zone列表
for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {
struct hlist_head *head;
struct hlist_node *node;
struct fib_node *f;
//通过目的地址,计算出所要查找的fn_key
//对于本地接收路由,它就是代表本地网络设备接口的IP地址,如172.16.48.2,对于子网单播,它就是子网号,比如172.16.48.0
__be32 k = fz_key(flp->fl4_dst, fz);
//fz_hash实际上是一个哈希表,每个数组单元存储了一个fib_node链表的头指针
//fn_new_zone函数用于创建一个新的fz_zone,可以看到fz_hash的初始化过程:
//fz->fz_hash = fz_hash_alloc(fz->fz_divisor); 所以,fz_hash的长度是由fz_divisor成员指定的
//这个操作取得了fib_node链头指针
head = &fz->fz_hash[fn_hash(k, fz)];
hlist_for_each_entry(f, node, head, fn_hash) {
if (f->fn_key != k)
continue;
err = fib_semantic_match(&f->fn_alias,flp, res,f->fn_key, fz->fz_mask,fz->fz_order);
if (err <= 0)
goto out;
}
}
err = 1;
out:
read_unlock(&fib_hash_lock);
return err;
}
接下来看这个函数调用的另外一个函数fib_semantic_match
int fib_semantic_match(struct list_head *head, const struct flowi *flp,struct fib_result *res, __be32 zone, __be32 mask,int prefixlen)
{
struct fib_alias *fa;
int nh_sel = 0;
//遍历fib_node中的fib_alias
list_for_each_entry_rcu(fa, head, fa_list) {
int err;
if (fa->fa_tos && fa->fa_tos != flp->fl4_tos)
continue;
if (fa->fa_scope < flp->fl4_scope)
continue;
fa->fa_state |= FA_S_ACCESSED;
//取转发类型错误码
err = fib_props[fa->fa_type].error;
if (err == 0) {
//取得fib_info
struct fib_info *fi = fa->fa_info;
//此标志表明该节点转发不通,直接return
if (fi->fib_flags & RTNH_F_DEAD)
continue;
switch (fa->fa_type) {
//单目转发
case RTN_UNICAST:
//本地转发
case RTN_LOCAL:
//广播转发
case RTN_BROADCAST:
//任意转发
case RTN_ANYCAST:
//多目转发
case RTN_MULTICAST:
for_nexthops(fi) {
//如果不通,转向下一个转发地址
if (nh->nh_flags&RTNH_F_DEAD)
continue;
if (!flp->oif || flp->oif == nh->nh_oif)
break;
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
if (nhsel < fi->fib_nhs) {
nh_sel = nhsel;//转发地址编号赋值
goto out_fill_res;
}
#else
if (nhsel < 1) {
goto out_fill_res;
}
#endif
endfor_nexthops(fi);
continue;
default:
printk(KERN_DEBUG "impossible 102\n");
return -EINVAL;
}
}
return err;
}
return 1;
//省去部分代码
}