linux网络协议栈分析笔记13-路由3-FIB2

来源:互联网 发布:百中搜优化软件 编辑:程序博客网 时间:2024/06/05 19:56
当然,我们选择路由1那章的遗留的接口进入分析
fib_lookup()       我们前面提到过fib初始化时,CONFIG_IP_MULTIPLE_TABLES  宏导致了两种方式的fib表初始化,
因此存在了有多路由表存在和无多路由表存在的情况,我们先看无多路由表的情况

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);            先查LOCAL 
     if (!table->tb_lookup(table, flp, res))
          return 0;

     table = fib_get_table(net, RT_TABLE_MAIN);               再查MAIN
     if (!table->tb_lookup(table, flp, res))
          return 0;
     return -ENETUNREACH;
}

static inline struct fib_table *fib_get_table(struct net *net, u32 id)
{
     struct hlist_head *ptr;

     ptr = id == RT_TABLE_LOCAL ?
          &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] :
          &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX];
     return hlist_entry(ptr->first, struct fib_table, tb_hlist);                 根据传入的id找到fib_table
}
上一章的一幅图说明了这种结构

!table->tb_lookup(table, flp, res))   即fn_hash_lookup
static int
fn_hash_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);
     for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {              扫描网络区
          struct hlist_head *head;
          struct hlist_node *node;
          struct fib_node *f;
          __be32 k = fz_key(flp->fl4_dst, fz);         取目标地址在该网络区的网络号  fl4_det&((fz)->fz_mask)

          head = &fz->fz_hash[fn_hash(k, fz)];            fn_hash(k, fz)得到hash关键字    获得hash链头
          hlist_for_each_entry(f, node, head, fn_hash) {       
               if (f->fn_key != k)                                  通过fn_key找到匹配的fib node节点                        
                    continue;

               err = fib_semantic_match(&f->fn_alias,           进入fib semantic查找
                              flp, res,
                              fz->fz_order);

               if (err <= 0)
                    goto out;
          }
     }
     err = 1;
out:
     read_unlock(&fib_hash_lock);
     return err;
}

int fib_semantic_match(struct list_head *head, const struct flowi *flp,
                 struct fib_result *res, int prefixlen)          这里head是f->fn_alias结构

{
     struct fib_alias *fa;
     int nh_sel = 0;

     list_for_each_entry_rcu(fa, head, fa_list) {
          int err;

          if (fa->fa_tos &&
              fa->fa_tos != flp->fl4_tos)                比较TOS
               continue;

          if (fa->fa_scope < flp->fl4_scope)         比较路由范围 scope
               continue;

          fa->fa_state |= FA_S_ACCESSED;

          err = fib_props[fa->fa_type].error;            取转发类型错误码       根据错误码进行特定处理
          if (err == 0) {                                            允许的转发类型
               struct fib_info *fi = fa->fa_info;

               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) {            对于转发信息中的每一个转发地址         取每个fib_nh结构
                         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) {             非多径路由转发地址编号必须小于1
                         goto out_fill_res;             跳转
                    }
#endif
                    endfor_nexthops(fi);
                    continue;

               default:
                    printk(KERN_WARNING "fib_semantic_match bad type %#x\n",
                         fa->fa_type);
                    return -EINVAL;
               }
          }
          return err;
     }
     return 1;

out_fill_res:
     res->prefixlen = prefixlen;            填充查询结果             到了这里算是从fib中找到了路由信息
     res->nh_sel = nh_sel;
     res->type = fa->fa_type;
     res->scope = fa->fa_scope;
     res->fi = fa->fa_info;
     atomic_inc(&res->fi->fib_clntref);
     return 0;                    成功返回
}
根据上面的流程,我跟踪了数据结构变量的查询过程,梳理了以下的图  当然具体每个结构的每个成员的作用,这里不再详细分析,这个都具体得深入的慢慢的研究,我们这里只梳理流程

我们再看下开启多路由表的情况
->int fib_lookup(struct net *net, struct flowi *flp, struct fib_result *res)
{
     struct fib_lookup_arg arg = {
          .result = res,
     };
     int err;

     err = fib_rules_lookup(net->ipv4.rules_ops, flp, 0, &arg);             提供rules_ops进行fib rule的查找
     res->r = arg.rule;

     return err;
}

int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
               int flags, struct fib_lookup_arg *arg)
{
     struct fib_rule *rule;
     int err;

     rcu_read_lock();

     list_for_each_entry_rcu(rule, &ops->rules_list, list) {
jumped:
          if (!fib_rule_match(rule, ops, fl, flags))            对rule类型进行match
               continue;

          if (rule->action == FR_ACT_GOTO) {           检查动作标志,是否转到另一个规则
               struct fib_rule *target;

               target = rcu_dereference(rule->ctarget);
               if (target == NULL) {
                    continue;
               } else {
                    rule = target;
                    goto jumped;
               }
          } else if (rule->action == FR_ACT_NOP)       无指定动作,则继续查找
               continue;
          else
               err = ops->action(rule, fl, flags, arg);        执行rule的action动作

          if (err != -EAGAIN) {
               fib_rule_get(rule);
               arg->rule = rule;
               goto out;
          }
     }

     err = -ESRCH;
out:
     rcu_read_unlock();

     return err;
}
这个函数又涉及了上一章的另个结构图

static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
                 struct flowi *fl, int flags)
{
     int ret = 0;

     if (rule->ifindex && (rule->ifindex != fl->iif))             检测设备id
          goto out;

     if ((rule->mark ^ fl->mark) & rule->mark_mask)      掩码匹配
          goto out;

     ret = ops->match(rule, fl, flags);
out:
     return (rule->flags & FIB_RULE_INVERT) ? !ret : ret;
}

static struct fib_rules_ops fib4_rules_ops_template = {
     .family          = AF_INET,                                   协议栈
     .rule_size     = sizeof(struct fib4_rule),
     .addr_size     = sizeof(u32),
     .action          = fib4_rule_action,
     .match          = fib4_rule_match,
     .configure     = fib4_rule_configure,
     .compare     = fib4_rule_compare,
     .fill          = fib4_rule_fill,
     .default_pref     = fib4_rule_default_pref,
     .nlmsg_payload     = fib4_rule_nlmsg_payload,
     .flush_cache     = fib4_rule_flush_cache,
     .nlgroup     = RTNLGRP_IPV4_RULE,
     .policy          = fib4_rule_policy,
     .owner          = THIS_MODULE,
};

fib4_rule_match()
static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
{
     struct fib4_rule *r = (struct fib4_rule *) rule;
     __be32 daddr = fl->fl4_dst;
     __be32 saddr = fl->fl4_src;

     if (((saddr ^ r->src) & r->srcmask) ||         源地址 目的地址与路由规则进行对比
         ((daddr ^ r->dst) & r->dstmask))
          return 0;

     if (r->tos && (r->tos != fl->fl4_tos))            tos匹配
          return 0;

     return 1;
}
static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp,
                   int flags, struct fib_lookup_arg *arg)
{
     int err = -EAGAIN;
     struct fib_table *tbl;

     switch (rule->action) {        检查action标识码
     case FR_ACT_TO_TBL:
          break;

     case FR_ACT_UNREACHABLE:
          err = -ENETUNREACH;
          goto errout;

     case FR_ACT_PROHIBIT:
          err = -EACCES;
          goto errout;

     case FR_ACT_BLACKHOLE:
     default:
          err = -EINVAL;
          goto errout;
     }

     if ((tbl = fib_get_table(rule->fr_net, rule->table)) == NULL)     查找路由表函数
          goto errout;

     err = tbl->tb_lookup(tbl, flp, (struct fib_result *) arg->result);  使用路由表函数记性查询
     if (err > 0)
          err = -EAGAIN;
errout:
     return err;
}

struct fib_table *fib_get_table(struct net *net, u32 id)
{
     struct fib_table *tb;
     struct hlist_node *node;
     struct hlist_head *head;
     unsigned int h;

     if (id == 0)
          id = RT_TABLE_MAIN;
     h = id & (FIB_TABLE_HASHSZ - 1);

     rcu_read_lock();
     head = &net->ipv4.fib_table_hash[h];                        这里的hash table就是那个256大小的hash表  
                                                                                      其中的table是通过fib_new_table创建
     hlist_for_each_entry_rcu(tb, node, head, tb_hlist) {
          if (tb->tb_id == id) {
               rcu_read_unlock();
               return tb;
          }
     }
     rcu_read_unlock();
     return NULL;
}
tbl->tb_lookup  这个就是注册的tb的lookup函数,和本文上述无开启多路由表的情况是一样的了