1. 程式人生 > >路由資料庫之路由表組織相關資料結構

路由資料庫之路由表組織相關資料結構

1. 全域性路由雜湊表

核心將所有的路由表(struct fib_table)組織到雜湊表net->ipv4.fib_table_hash中,如下:

//如果不支援策略路由,那麼系統中固定只有兩張路由表,所以雜湊表大小就是2,
//如果支援策略路由,那麼系統中可以支援多個表,所以定義一個衝突鏈數目為256的雜湊表
//來儲存這些路由表
#ifdef CONFIG_IP_ROUTE_MULTIPATH
#define FIB_TABLE_HASHSZ 2
#else
#define FIB_TABLE_HASHSZ 256
#endif

//分配雜湊表並初始化各個衝突鏈的頭結點
static
int __net_init ip_fib_net_init(struct net *net) { ... net->ipv4.fib_table_hash = kzalloc( sizeof(struct hlist_head)*FIB_TABLE_HASHSZ, GFP_KERNEL); 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]); ... }

2. 路由表struct fib_table

struct fib_table {
	//將該路由錶鏈入系統全域性雜湊表中
	struct hlist_node tb_hlist;
	//每張路由表在系統中都有一個唯一的ID
	u32		tb_id;
	
	unsigned	tb_stamp;
	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]; };

3. 路由區struct fn_zone

注意到路由表結構struct fib_table的末尾有一個零長度陣列,在實際分配記憶體時,會在該結構末尾再分配一個struct fn_hash結構,該結構的定義如下:

struct fn_hash {
	struct fn_zone	*fn_zones[33];
	struct fn_zone	*fn_zone_list;
};

成員fn_zones是一個長度為33的指標陣列,系統將路由項按照目的地址的子網掩碼長度不同劃分為33個區管理(對於IPv4,掩碼長度可取的值為0~32,所以是33個),我們後續稱fn_zone為路由區。

成員fn_zone_list用於將所有活動的(含有路由表項)的路由區按照子網掩碼長度由大到小的順序串聯成單鏈表,之所以這樣設計,是為了更加方便按照最大長度匹配原則進行路由查詢。

fib_table和fn_hash的記憶體結果如下圖所示:

在這裡插入圖片描述

一個路由區管理的是一組路由表項,這些路由表項的目的地址的子網掩碼長度相同。然而,子網掩碼相同,網路地址還可以不同,為了高效的存取,路由區進一步用雜湊表來組織這些不同的網路地址(每個網路地址對應一個fib_node,後續我們稱該結構為路由結點),路由區的定義如下:

struct fn_zone {
	//與struct fn_hash結構中的fn_zone_list一起,將所有活動的路由區按照子網掩碼長度由大到小的順序串聯成單鏈表
	struct fn_zone		*fz_next;	/* Next not empty zone	*/
	//儲存struct fib_node(即路由結點)的雜湊表指標
	struct hlist_head	*fz_hash;	/* Hash table pointer	*/
	//雜湊表中路由結點的數目
	int			fz_nent;	/* Number of entries	*/
	//雜湊表桶大小,雜湊表在實際使用過程中可能會重新分配更大的空間,避免衝突過多
	int			fz_divisor;	/* Hash divisor		*/
	//值為fz_divisor-1,用於計算雜湊值
	u32			fz_hashmask;	/* (fz_divisor - 1)	*/
#define FZ_HASHMASK(fz)		((fz)->fz_hashmask)
	//子網掩碼中1的數目,即對於子網掩碼255.255.255.0,那麼fz_order為24
	int			fz_order;	/* Zone order		*/
	//子網掩碼的數值大端表示
	__be32			fz_mask;
#define FZ_MASK(fz)		((fz)->fz_mask)
};

4. 路由結點struct fib_node

路由結點將所有目的地址的網路地址相同的路由表項組織到一起,由於目的地址相同,但是還可以根據tos、priority等引數配置不同的路由項,所以路由結點下面也可能會有多個路由表項,路由結點的定義如下:

struct fib_node {
	//用於將路由結點組織到路由區的雜湊表中
	struct hlist_node	fn_hash;
	//路由結點將可能存在的多個路由項組織成連結串列,連結串列成員為struct fib_alias
	struct list_head	fn_alias;
	//該路由結點代表的網路地址,即IP地址與子網掩碼相與後的結果
	__be32			fn_key;
	//分配路由節點的時候同時也分配一個路由別名,所以稱為嵌入式的
	struct fib_alias        fn_embedded_alias;
};

5. 路由表項struct fib_alias

真正的路由表項是struct fib_alias,所有的目的地址的網路地址相同的路由表項被組織成一個路由結點,它們被路由結點組織成連結串列,路由表項的定義如下:

struct fib_alias {
	//路由表項被路由結點組織成一個連結串列
	struct list_head	fa_list;
	//命中該路由後,對資料包應該執行怎樣的操作,即如何路由,這些資訊被組織成struct fib_info
	struct fib_info		*fa_info;
	//服務型別TOS,對於IP資料包,對應其頭部的TOS欄位
	u8			fa_tos;
	//路由型別
	u8			fa_type;
	//路由作用範圍
	u8			fa_scope;
	//路由項的狀態
	u8			fa_state;
#ifdef CONFIG_IP_FIB_TRIE
	struct rcu_head		rcu;
#endif
};

6. 路由資訊struct fib_info

在實際中,可能有很多的路由表項命中後要執行的動作是一樣的,這些資訊是可以共用的,沒有必要每個路由表項都維護一份,所以將這部分資訊抽象成路由資訊,這就是struct fib_info,該結構定義如下:

/*
 * This structure contains data shared by many of routes.
 */
struct fib_info {
	//用於將所有的fib_info結構組織到fib_info_hash佇列中
	struct hlist_node	fib_hash;
	//用於將所有的fib_info結構組織到fib_hash_laddrhash佇列中
	struct hlist_node	fib_lhash;
	struct net		*fib_net;
	//該fib_info結構的引用計數
	int			fib_treeref;
	atomic_t		fib_clntref;
	int			fib_dead;
	//RTNH_F_DEAD: 表示該路由資訊的下一跳地址無效
	unsigned		fib_flags;
	int			fib_protocol;
	//優選IP地址,當路由所指網絡卡有多個IP地址時,可以通過該欄位指示優先選用什麼IP地址
	__be32			fib_prefsrc;
	u32			fib_priority;
	u32			fib_metrics[RTAX_MAX];
#define fib_mtu fib_metrics[RTAX_MTU-1]
#define fib_window fib_metrics[RTAX_WINDOW-1]
#define fib_rtt fib_metrics[RTAX_RTT-1]
#define fib_advmss fib_metrics[RTAX_ADVMSS-1]
	//fib_nh陣列的元素個數,即可用下一條地址數目,通常為1,只有在支援多路徑路由時
	//該值才有可能大於1
	int			fib_nhs;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
	int			fib_power;
#endif
	//零長度陣列,指向一個struct fib_nh,該結構決定了下一跳地址資訊
	struct fib_nh		fib_nh[0];
#define fib_dev		fib_nh[0].nh_dev
};

7. 路由下一跳struct fib_nh

進一步考慮,一臺機器的網路裝置畢竟是有限的,所以需要路由表項最終的下一條地址資訊是重複的,將這種下一跳資訊抽象出來就是struct fib_nh,定義如下:

struct fib_nh {
	struct net_device	*nh_dev;
	struct hlist_node	nh_hash;
	struct fib_info		*nh_parent;
	unsigned		nh_flags;
	unsigned char		nh_scope;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
	int			nh_weight;
	int			nh_power;
#endif
#ifdef CONFIG_NET_CLS_ROUTE
	__u32			nh_tclassid;
#endif
	int			nh_oif;
	__be32			nh_gw;
};

以上就是路由表本身涉及到的核心資料結構,實際上還是非常複雜的,它們之間的連線關係見下圖:

在這裡插入圖片描述