當然,我們選擇路由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函數,和本文上述無開啟多路由表的情況是一樣的了