linux網路通訊協定棧分析筆記13-路由3-FIB2

來源:互聯網
上載者:User
當然,我們選擇路由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_lookupstatic 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函數,和本文上述無開啟多路由表的情況是一樣的了

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.