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指针

 

以上几种数据结构之间的关系

linux路由内核实现分析(二)---FIB相关数据结构(4)

 

 

可以这样理解这些结构之间的关系:

 

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里面

Logo

更多推荐