日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表

發布時間:2024/7/19 linux 89 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

路由表的創建

inet_init() -> ip_init() -> ip_fib_init() -> fib_net_init() -> ip_fib_net_init()[net\ipv4\fib_frontend.c]

首先為路由表分配空間,這里的每個表項hlist_head實際都會鏈接一個單獨的路由表,FIB_TABLE_HASHSZ表示了分配多少個路由表,一般情況下至少有兩個–LOCAL和MAIN。注意這里僅僅是表頭的空間分配,還沒有真正分配路由表空間。

net->ipv4.fib_table_hash = kzalloc(

sizeof(struct hlist_head)*FIB_TABLE_HASHSZ, GFP_KERNEL);

ip_fib_net_init() -> fib4_rules_init(),這里真正分配了路由表空間

local_table = fib_hash_table(RT_TABLE_LOCAL);

main_table= fib_hash_table(RT_TABLE_MAIN);

然后將local和main表鏈入之前的fib_table_hash中

hlist_add_head_rcu(&local_table->tb_hlist,

&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]);

hlist_add_head_rcu(&main_table->tb_hlist,

&net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]);

最終生成結構如圖,LOCAL表位于fib_table_hash[0],MAIN表位于fib_table_hash[1];兩張表通過結構tb_hlist鏈入鏈表,而tb_id則標識了功能,255是LOCAL表,254是MAIN表。

關于這里的struct fn_hash,它表示了不同子網掩碼長度的hash表[即fn_zone],對于ipv4,從0~32共33個。而fn_hash的實現則是fib_table的最后一個參數unsigned char tb_data[0]。

注意到這里fn_zone還只是空指針,我們還只完成了路由表初始化的一部分。在啟動階段還會調用inet_rtm_newroute() -> fib_table_insert() -> fn_new_zone() [fib_hash.c]來創建fn_zone結構,前面已經講過,fn_zone一共有33個,其中掩碼長度為0[/0]表示為默認路由,fn_zone可以理解為相同掩碼的地址集合。

首先為fn_zone分配空間

struct fn_zone *fz = kzalloc(sizeof(struct fn_zone), GFP_KERNEL);

傳入參數z代表掩碼長度,z = 0的掩碼用于默認路由,一般只有一個,所以fz_divisor只需設為1;其它設為16;這里要提到fz_divisor的作用,fz->fz_hash并不是個單鏈表,而是一個哈希表,而哈希表的大小就是fz_divisor。

if (z) {

fz->fz_divisor = 16;

} else {

fz->fz_divisor = 1;

}

fz_hashmask實際是用于求余數的,當算出hash值,再hash & fz_hashmask就得出了在哈希表的位置;而fz_hash就是下一層的哈希表了,前面已經提過路由表被多組分層了,這里fz_hash就是根據fz_divisor大小來創建的;fz_order就是子網掩碼長度;fz_mask就是子網掩碼。

fz->fz_hashmask = (fz->fz_divisor - 1);

fz->fz_hash = fz_hash_alloc(fz->fz_divisor);

fz->fz_order = z;

fz->fz_mask = inet_make_mask(z);

從子網長度大于新添加fz的fn_zone中挑選一個不為空的fn_zones[i],將新創建的fz設成fn_zones[i].next;然后將fz根據掩碼長度添加到fn_zones[]中相應位置;fn_zone_list始終指向掩碼長度最長的fn_zone。

for (i=z+1; i<=32; i++)

if (table->fn_zones[i])

break;

if (i>32) {

fz->fz_next = table->fn_zone_list;

table->fn_zone_list = fz;

} else {

fz->fz_next = table->fn_zones[i]->fz_next;

table->fn_zones[i]->fz_next = fz;

}

table->fn_zones[z] = fz;

這里的fn_hash是數組與鏈表的結合體,看下fn_hash定義

struct fn_hash {

struct fn_zone*fn_zones[33];

struct fn_zone*fn_zone_list;

};

fn_hash包含33數組元素,每個元素存放一定掩碼長度的fn_zone,其中fn_zone[i]存儲掩碼長度為i。而fn_zone通過內部屬性fz_next又彼此串連起來,形成單向鏈表,其中fn_zone_list可以看作鏈表頭,而這里鏈表的組織順序是倒序的,即從掩碼長到短。

到這里,fz_hash所分配的哈希表還沒有插入內容,這部分為fib_insert_node()完成。

inet_rtm_newroute() -> fib_table_insert() -> fib_insert_node() [net\ipv4\fib_hash.c]

這里f是fib_node,可以理解為具有相同網絡地址的路由項集合。根據fn_key(網絡地址)和fz(掩碼長度)來計算hash值,決定將f插入fz_hash的哪個項。

struct hlist_head *head = &fz->fz_hash[fn_hash(f->fn_key, fz)];

hlist_add_head(&f->fn_hash, head);

}

如何fib_node還不存在,則會創建它,這里的kmem_cache_zalloc()其實就是內存分配

new_f = kmem_cache_zalloc(fn_hash_kmem, GFP_KERNEL);

if (new_f == NULL)

goto out;

INIT_HLIST_NODE(&new_f->fn_hash);

INIT_LIST_HEAD(&new_f->fn_alias);

new_f->fn_key = key;

f = new_f;

路由表最后一層是fib_info,具體的路由信息都存儲在此,它由fib_create_info()創建。

首先為fib_info分配空間,由于fib_info的最后一個屬性是struct fib_nh fib_nh[0],因此大小是fib_info + nhs * fib_nh,這里的fib_nh代表了下一跳(next hop)的信息,nhs代表了下一跳的數目,一般情況下nhs=1,除非配置了支持多路徑。

fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);

設置fi的相關屬性

fi->fib_net = hold_net(net);

fi->fib_protocol = cfg->fc_protocol;

fi->fib_flags = cfg->fc_flags;

fi->fib_priority = cfg->fc_priority;

fi->fib_prefsrc = cfg->fc_prefsrc;

fi->fib_nhs = nhs;

使fi后面所有的nh->nh_parent指向fi,設置后如圖所示

change_nexthops(fi) {

nexthop_nh->nh_parent = fi;

} endfor_nexthops(fi)

設置fib_nh的屬性,這里僅展示了單一路徑的情況:

struct fib_nh *nh = fi->fib_nh;

nh->nh_oif = cfg->fc_oif;

nh->nh_gw = cfg->fc_gw;

nh->nh_flags = cfg->fc_flags;

然后,再根據cfg->fc_scope值來設置nh的其余屬性。如果scope是RT_SCOPE_HOST,則設置下一跳scope為RT_SCOPE_NOWHERE

if (cfg->fc_scope == RT_SCOPE_HOST) {

struct fib_nh *nh = fi->fib_nh;

nh->nh_scope = RT_SCOPE_NOWHERE;

nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);

}

如果scope是RT_SCOPE_LINK或RT_SCOPE_UNIVERSE,則設置下跳

change_nexthops(fi) {

if ((err = fib_check_nh(cfg, fi, nexthop_nh)) != 0)

goto failure;

} endfor_nexthops(fi)

最后,將fi鏈入鏈表中,這里要注意的是所有的fib_info(只要創建了的)都會加入fib_info_hash中,如果路由項使用了優先地址屬性,還會加入fib_info_laddrhash中。

hlist_add_head(&fi->fib_hash,

&fib_info_hash[fib_info_hashfn(fi)]);

if (fi->fib_prefsrc) {

struct hlist_head *head;

head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];

hlist_add_head(&fi->fib_lhash, head);

}

總結

以上是生活随笔為你收集整理的linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。