上一章我們看到了通過fib_lookup去尋找了路由資訊,這一章我們就看看fib到底是什麼
FIB(Forward Information Base) 轉寄資訊庫
inet_init()->ip_init()->ip_rt_init()->
ipv4_dst_ops.kmem_cachep =
kmem_cache_create("ip_dst_cache", sizeof(struct rtable), 0, rtable結構快取的建立
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);rt_hash_table = (struct rt_hash_bucket *) 路由緩衝的hash表建立
alloc_large_system_hash("IP route cache",
sizeof(struct rt_hash_bucket),
rhash_entries,
(totalram_pages >= 128 * 1024) ?
15 : 17,
0,
&rt_hash_log,
&rt_hash_mask,
rhash_entries ? 0 : 512 * 1024);
memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket));ip_fib_init()-> 註冊與路由相關的rtnetlink 訊息以及他的處理函數,主要處理路由添加刪除
rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL); rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
register_pernet_subsys(&fib_net_ops); 註冊協議棧子系統,也就是路由系統。 重點--fib_net_ops
register_netdevice_notifier(&fib_netdev_notifier); 通知鏈註冊
register_inetaddr_notifier(&fib_inetaddr_notifier);
fib_hash_init()fib_hash_init() 該函數出現在fib_hash.c fib_trie.c兩個檔案中都有,系統可配置兩種路由表組織演算法,預設hash演算法void __init fib_hash_init(void)
{
fn_hash_kmem = kmem_cache_create("ip_fib_hash", sizeof(struct fib_node), fib_node快取建立 0, SLAB_PANIC, NULL);
fn_alias_kmem = kmem_cache_create("ip_fib_alias", sizeof(struct fib_alias), fib_alias快取建立 0, SLAB_PANIC, NULL);
}
主要資料結構:struct fib_node { 代表一個路由指向的子網
struct hlist_node fn_hash; hash節點
struct list_head fn_alias; 路由別名
__be32 fn_key; 子網地址
struct fib_alias fn_embedded_alias; 內嵌的路由別名
};struct fib_alias {
struct list_head fa_list;
struct fib_info *fa_info; 路由資訊結構儲存著如何處理資料包
u8 fa_tos; TOS
u8 fa_type; 路由類型
u8 fa_scope; 路由範圍
u8 fa_state; 狀態標誌
#ifdef CONFIG_IP_FIB_TRIE
struct rcu_head rcu;
#endif
};fib_net_ops:static struct pernet_operations fib_net_ops = {
.init = fib_net_init,
.exit = fib_net_exit,
};fib_net_init(){ ip_fib_net_init(net);
nl_fib_lookup_init(net); netlink初始化相關
fib_proc_init(net); 初始化proc檔案系統}
static int __net_init ip_fib_net_init(struct net *net)
{
int err;
unsigned int i;
net->ipv4.fib_table_hash = kzalloc(
sizeof(struct hlist_head)*FIB_TABLE_HASHSZ, GFP_KERNEL); 申請256大小的hash表
if (net->ipv4.fib_table_hash == NULL)
return -ENOMEM;
for (i = 0; i < FIB_TABLE_HASHSZ; i++)
INIT_HLIST_HEAD(&net->ipv4.fib_table_hash[i]); 初始化每個hash表的衝突鏈
err = fib4_rules_init(net); 有了256個表,就得提一提Linux的尋找路由表的規則, 也就是策略路由ip rule add/del ...
if (err < 0)
goto fail;
return 0;
fail:
kfree(net->ipv4.fib_table_hash);
return err;
}
fib4_rules_init() 有兩個,如果開啟多路由表的宏時,CONFIG_IP_MULTIPLE_TABLES 我們看fib_rules.c中的 int __net_init fib4_rules_init(struct net *net)
{
int err;
struct fib_rules_ops *ops;
給ops分配空間,並以fib4_rules_ops_template初始化ops
ops = kmemdup(&fib4_rules_ops_template, sizeof(*ops), GFP_KERNEL);
if (ops == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&ops->rules_list);
ops->fro_net = net;
fib_rules_register(ops); 把協議族不同的路由表規則鏈起來 通過list串到net的rules_ops
list_add_tail_rcu(&ops->list, &net->rules_ops);
err = fib_default_rules_init(ops);
if (err < 0)
goto fail;
net->ipv4.rules_ops = ops;
return 0;
fail:
/* also cleans all rules already added */
fib_rules_unregister(ops);
kfree(ops);
return err;
}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,
};
fib_default_rules_init() 建立三個最基本的路由表local,main,default的規則表static int fib_default_rules_init(struct fib_rules_ops *ops)
{
int err;
err = fib_default_rule_add(ops, 0, RT_TABLE_LOCAL, FIB_RULE_PERMANENT);
if (err < 0)
return err;
err = fib_default_rule_add(ops, 0x7FFE, RT_TABLE_MAIN, 0);
if (err < 0)
return err;
err = fib_default_rule_add(ops, 0x7FFF, RT_TABLE_DEFAULT, 0);
if (err < 0)
return err;
return 0;
}
int fib_default_rule_add(struct fib_rules_ops *ops,u32 pref, u32 table, u32 flags)
{
struct fib_rule *r;
r = kzalloc(ops->rule_size, GFP_KERNEL); 申請fib rule結構
if (r == NULL)
return -ENOMEM;
atomic_set(&r->refcnt, 1);
r->action = FR_ACT_TO_TBL;
r->pref = pref;
r->table = table;
r->flags = flags;
r->fr_net = hold_net(ops->fro_net);
/* The lock is not required here, the list in unreacheable
* at the moment this function is called */
list_add_tail(&r->list, &ops->rules_list); 加入fib_rules_ops的rules_list中
return 0;
}
主要資料結構 路由規則函數表的結構定義struct fib_rules_ops
{
int family; 協議棧
struct list_head list; 鏈入net的net->rules_ops鏈中
int rule_size; 規則結構長度
int addr_size; 地址長度
int unresolved_rules;
int nr_goto_rules;
int (*action)(struct fib_rule *, 動作函數指標
struct flowi *, int,
struct fib_lookup_arg *);
int (*match)(struct fib_rule *, 匹配函數指標
struct flowi *, int);
int (*configure)(struct fib_rule *, 配置函數指標
struct sk_buff *,
struct fib_rule_hdr *,
struct nlattr **);
int (*compare)(struct fib_rule *, 比較函數指標
struct fib_rule_hdr *,
struct nlattr **);
int (*fill)(struct fib_rule *, struct sk_buff *, 填寫函數指標
struct fib_rule_hdr *);
u32 (*default_pref)(struct fib_rules_ops *ops); 尋找優先順序函數指標
size_t (*nlmsg_payload)(struct fib_rule *); 統計負載資料能力函數指標
/* Called after modifications to the rules set, must flush
* the route cache if one exists. */
void (*flush_cache)(struct fib_rules_ops *ops); 修改規則隊列後,必須重新整理緩衝的函數指標
int nlgroup;
const struct nla_policy *policy;
struct list_head rules_list; 針對各個路由表的規則鏈表
struct module *owner;
struct net *fro_net; net與ops之間的關係
};struct fib_rule
{
struct list_head list;
atomic_t refcnt; 引用計數
int ifindex; 網路裝置id
char ifname[IFNAMSIZ]; 用於儲存網路裝置名稱
u32 mark; 用於過濾作用
u32 mark_mask; 掩碼
u32 pref; 優先順序
u32 flags; 標誌位
u32 table; 路由函數表id
u8 action; 動作標識
u32 target;
struct fib_rule * ctarget; 當前規則
struct rcu_head rcu;
struct net * fr_net; net結構指標
};上述幾個結構的關係
如果沒有開啟CONFIG_IP_MULTIPLE_TABLES宏時,我們看fib_frontend.cstatic int __net_init fib4_rules_init(struct net *net)
{
struct fib_table *local_table, *main_table;
local_table = fib_hash_table(RT_TABLE_LOCAL); 建立本地路由函數表
return -ENOMEM;
main_table = fib_hash_table(RT_TABLE_MAIN); 建立主路由函數表
if (main_table == NULL)
goto fail;
hlist_add_head_rcu(&local_table->tb_hlist, 插入統一管理隊列中
&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]); 這裡就是ip_fib_net_init中申請的那個256大小的hash表
hlist_add_head_rcu(&main_table->tb_hlist,
&net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]);
return 0;
fail:
kfree(local_table);
return -ENOMEM;
}
struct fib_table *fib_hash_table(u32 id)
{
struct fib_table *tb;
tb = kmalloc(sizeof(struct fib_table) + sizeof(struct fn_hash), 這裡又冒出了個兩個新fib相關的結構
GFP_KERNEL);
if (tb == NULL)
return NULL;
tb->tb_id = id;
tb->tb_default = -1;
tb->tb_lookup = fn_hash_lookup;
tb->tb_insert = fn_hash_insert;
tb->tb_delete = fn_hash_delete;
tb->tb_flush = fn_hash_flush;
tb->tb_select_default = fn_hash_select_default;
tb->tb_dump = fn_hash_dump;
memset(tb->tb_data, 0, sizeof(struct fn_hash));
return tb;
}
主要資料結構struct fib_table { fib_table給路由表提供了查詢路由資訊的方法
struct hlist_node tb_hlist; hash節點
u32 tb_id; 標示符
int tb_default;
int (*tb_lookup)(struct fib_table *tb, const struct flowi *flp, struct fib_result *res); 查詢
int (*tb_insert)(struct fib_table *, struct fib_config *); 插入
int (*tb_delete)(struct fib_table *, struct fib_config *); 刪除
int (*tb_dump)(struct fib_table *table, struct sk_buff *skb, 路由轉寄
struct netlink_callback *cb);
int (*tb_flush)(struct fib_table *table);
void (*tb_select_default)(struct fib_table *table, 選擇預設路由
const struct flowi *flp, struct fib_result *res);
unsigned char tb_data[0];
};struct fn_hash { 路由區隊列結構定義
struct fn_zone *fn_zones[33]; 路由區隊列
struct fn_zone *fn_zone_list; 指向第一個路由區表
};struct fn_zone { 路由區結構定義
struct fn_zone *fz_next; /* Next not empty zone */ 指向下一個非空路由區結構
struct hlist_head *fz_hash; /* Hash table pointer */ hash隊列
int fz_nent; /* Number of entries */ 包含的路由項個數
int fz_divisor; /* Hash divisor */ hash頭數量
u32 fz_hashmask; /* (fz_divisor - 1) */ hash頭的掩碼
#define FZ_HASHMASK(fz) ((fz)->fz_hashmask)
int fz_order; /* Zone order */ 子網路遮罩位元
__be32 fz_mask; 子網路遮罩
#define FZ_MASK(fz) ((fz)->fz_mask)
};
從fib相關的初始化來看,冒出了相當多的資料結構,看都看暈了,我們下面就從處理操作等角度來看看這些結構是如何使用,並達到什麼樣的路由操作目的