Linux 内核路由发现过程

 

路由查找

 

//这个函数负责从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;

            //省去部分代码


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值