linux路由内核实现分析(二)---FIB相关数据结构
FIB结构 用于保存路由规则,路由的查找过程如下:1, 在缓存中搜索路由表项,如果能查到,就直接将对应的一项作为路由规则2, 如果不能查到,就从FIB规则中换算出来,并且在路由缓存中添加表项 fib_table结构 struct fib_table { struct hlist_node tb_hlist; u32
FIB结构
用于保存路由规则,路由的查找过程如下:
1, 在缓存中搜索路由表项,如果能查到,就直接将对应的一项作为路由规则
2, 如果不能查到,就从FIB规则中换算出来,并且在路由缓存中添加表项
fib_table结构
struct fib_table {
struct hlist_node tb_hlist;
u32 tb_id;
unsigned tb_stamp;
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];
};
u32 tb_id;//是fib_table唯一的标识,例如local_table(局部转发表,用于本地),标记是255,对main_table(主转发表,用于数据转发),标记是254,只有在查找local_table表时没有找到匹配的路由(不是发给本地的)它才会去查找main_table
主转发表(main_table)用于描述对设备路由规则
局部转发表(local_table)用于记录本地地址信息
unsigned char tb_data[0];这个指针指向一个fn_hash结构.
在系统初始化的时候 memset(tb->tb_data, 0, sizeof(struct fn_hash));
在linux 2.6内核中本结构添加了struct hlist_node tb_hlist,所有的fib_table可以使用这个结构连成一个双向链表,这个结构定义如下:
struct hlist_node {
struct hlist_node *next, **pprev;
};
可以使用一个宏hlist_for_each_entry_rcu使用RCU模式对所有的fib_table进行遍历,相关的函数还有hlist_add_head_rcu, hlist_del_rcu()等等.RCU模式不需要读写互斥,仅需要禁用抢占.
内核中fib_new_table函数用于创建一个新的fib_table结构.
fn_hash结构
struct fn_hash {
struct fn_zone *fn_zones[33];
struct fn_zone *fn_zone_list;
};
struct fn_zone *fn_zones[33];//每个数组元素对应着一个fn_zone结构指针
fz_zone[0] : 0000,0000,0000,0000,0000,0000,0000,0000
fz_zone[1] : 1000,0000,0000,0000,0000,0000,0000,0000
fz_zone[2] : 1100,0000,0000,0000,0000,0000,0000,0000
实际上用于标识子网掩码,可以看出,fz_zone[0]实际上对应默认网关
struct fn_zone *fn_zone_list;//当前正在使用的fz_zone的一个链表
fn_zone结构
struct fn_zone {
struct fn_zone *fz_next;
struct hlist_head *fz_hash;
int fz_nent;
int fz_divisor;
u32 fz_hashmask;
#define FZ_HASHMASK(fz) ((fz)->fz_hashmask)
int fz_order;
__be32 fz_mask;
#define FZ_MASK(fz) ((fz)->fz_mask)
};
struct fn_zone *fz_next;//用于联系fz_zone链表的指针。
struct hlist_head *fz_hash;
//这个结构2.6版本才引入的,在2.4.x下是
struct fib_node **fz_hash;
前面已经提到,2.6内核使用RCU的方式来管理双向链表,但是在2.4内核中,直接引入
一个fib_node的的链表头
int fz_nent;
//用于记录 fib_node结构的数目
int fz_order;
//记录了当前fz_zone结构在fz_hash中的位置
通过函数inet_make_mask来经由fz_order计算出相对应的fz_mask,
__u32 inet_make_mask(fz)
{
return htonl(~((1<<(32-fz))-1));
}
这样,如果fz_order = 0,那么fz_mask = 0x00000000,如果fz_order = 1,fz_mask = 0x80000000
正好和fz_hash中定义的子网掩码相同,fz_order=0的时候对应默认网关。
fib_node结构
struct fib_node {
struct hlist_node fn_hash;
struct list_head fn_alias;
u32 fn_key;
};
这个结构实际上代表一个路由节点,fn_key是这个路由节点的IP地址或者网络号(对于本地接收路由,它就是代表本地网络设备接口的IP地址,如 172.16.48.2,对于子网单播,它就是子网号,比如172.16.48.0)。对于子网号172.16.48.0,其子网掩码就是 255.255.255.0,该路由项的目的地址长度就是24。而对于本地接收和广播路由,其掩码是255.255.255.255,目的地址长度是 32,同一目的地址长度的路由节点被维护在同一个路由域fn_zone中
struct list_head fn_alias;
//指向fib_alias结构的链表
相同的子网可以共享一个fib_node,fib_alias结构用以标明不同的路由信息,127.10.0.1和127.0.0.1两个A类地址,可以共享一个路由节点(fib_node),但是他们的fib_alias有所不同
一个典型的路由节点示例:
fn_alias{
fa_info{
.fib_protocol = RTPROT_KERNEL;
.fib_nhs = 1;
.fib_flags = 0;
.fib_prefsrc = 172.16.48.2;
struct fib_nh{
.nh_oif = indexof(eth0);
.nh_flags = 0;
.nh_weight = 1;
.nh_scope = RT_SCOPE_HOST;
.nh_dev = eth0;
}
}
.fa_tos = 0;
.fa_type = RTN_UNICAST;
.fa_scope = RT_SCOPE_LINK;
.fa_state = 0;
}
.fn_key = 172.16.48.0;
}
这个路由节点的目的地址长度是24(子网掩码是255.255.255.0),所以它被放到fz_order的值为24的路由域fz_zone中去
fib_info结构
struct fib_info {
struct hlist_node fib_hash;
struct hlist_node fib_lhash;
int fib_treeref;
atomic_t fib_clntref;
int fib_dead;
unsigned fib_flags;
int fib_protocol;
__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]
int fib_nhs;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
int fib_power;
#endif
struct fib_nh fib_nh[0];
#define fib_dev fib_nh[0].nh_dev
};
这个结构表示一个路由信息,一个路由信息可以被多个路由复用
例如:
struct fib_info{
.fib_protocol = RTPROT_KERNEL;// 表示这一条由内核发起的消息
.fib_nhs = 1;//表示fib_nh的数目
.fib_flags = 0;
.fib_prefsrc = 172.16.48.2; //接口上的primary地址
struct fib_nh{
.nh_oif = indexof(eth0);
.nh_flags = 0;
.nh_weight = 1;
.nh_scope = RT_SCOPE_NOWHERE;
.nh_dev = eth0;
}
}
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;
u32 nh_gw;
};
fib_nh结构存放着下一跳路由的地址(nh_gw)。一个路由(fib_alias)可能有多个fib_nh结构,它表示这个路由有多个下一跳地址,即它是多路径(multipath)的。下一跳地址的选择也有多种算法,这些算法都是基于nh_weight, nh_power域的。nh_hash域则是用于将nh_hash链入HASH表的。
u32 nh_gw;//下以跳的路由地址
struct net_device *nh_dev; //下一跳的DEV指针
以上几种数据结构之间的关系
可以这样理解这些结构之间的关系:
1,fib_table中包含fn_hash结构指针
2,fn_hash中包含fn_zone的数组,按照目的地址长度进行分类,相同长度的地址共用一个fz_zone
3, fn_key相同的两条路由(同一子网),共享一个路由节点(fn_node)
4,根据具体子网内地址的不同,使用不同的fib_alias和fib_info
5,目的地址相同的情况下,也可以使用多条路由,不同的路由存放在不同的fib_nh里面
更多推荐
所有评论(0)