创建struct fib_info

本文详细解析了在协议栈的FIB模块中,为本地网络接口生成路由信息的过程,包括消息头、消息体的结构及内容,以及路由信息创建函数的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在协议栈的FIB(Forwarding Information Base)模块中,结构体struct 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;
        u32             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
#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
        u32             fib_mp_alg;
#endif
        struct fib_nh   fib_nh[0];
#define fib_dev         fib_nh[0].nh_dev
    };
    现在就结合模块加载并初始化过程中为一个本地网络接口生成路由信息的实例,分析路由信息的创建过程。本地主机的网络设备接口eth0上配置的ip地址是 172.16.48.2。协议栈的fib_add_ifaddr函数会在模块被加载的时候主动往FIB表中插入五条路由相关信息,下面是一个完整列表:
    类型                            目的地址        地址长度
    RTN_LOCAL             172.16.48.2         32
    RTN_BROADCAST  172.16.48.255       32
    RTN_UNICAST         172.16.48.0         24
    RTN_BROADCAST   172.16.48.0         32
    RTN_BROADCAST   172.16.48.255       32
    其中第二条跟第五条完全相同,所以实际插入的是四条。第一条是本地接收,第二条是本地接收的广播,第三条是子网内的直接路由或者网关路由。第四条是子网广播。
        首先看第一条会产生怎么样的路由信息struct fib_info。在这之前,fib_magic函数要为它产生三个结构体信息,用于传递给路由信息创建函数:
    struct nlmsghdr
    {
        __u32       nlmsg_len = sizeof( struct nlmsghdr + struct rtmsg );
        __u16       nlmsg_type = RTM_NEWROUTE;
        __u16       nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
        __u32       nlmsg_seq = 0;
        __u32       nlmsg_pid = 0;
    };
    这是一个消息头结构,表示本条消息的内容是创建一个新的路由(RTM_NEWROUTE),这是一个请求消息(NLM_F_REQUEST),如果新的路 由不存在,则要求创建(NLM_F_CREATE),并且新创建的路由加到列表尾部(NLM_F_APPEND)。由于这是内核发出的请求命令,所以序列 号跟进程号都为0。
    struct rtmsg
    {
        unsigned char       rtm_family;
        unsigned char       rtm_dst_len = 32;
        unsigned char       rtm_src_len;
        unsigned char       rtm_tos;
        unsigned char       rtm_table = RT_TABLE_LOCAL;
        unsigned char       rtm_protocol = RTPROT_KERNEL;
        unsigned char       rtm_scope = RT_SCOPE_HOST;
        unsigned char       rtm_type = RTN_LOCAL;
        unsigned                rtm_flags;
    };
    这是消息体,表示这一条由内核发起的消息(RTPROT_KERNEL),因为路由类型是本地接收(RTN_LOCAL),所以操作对象是本地路由表 (RT_TABLE_LOCAL),同时目的地址也就是本地地址(RT_SCOPE_HOST),目的地址长度是32位。
    struct kern_rta {
        void        *rta_dst = 172.16.48.2;
        void        *rta_src;
        int         *rta_iif;
        int         *rta_oif = indexof(eth0);
        void        *rta_gw;
        u32         *rta_priority;
        void        *rta_prefsrc = 172.16.48.2  //接口上的primary地址。
        struct rtattr   *rta_mx;
        struct rtattr   *rta_mp;
        unsigned char   *rta_protoinfo;
        u32             *rta_flow;
        struct rta_cacheinfo    *rta_ci;
        struct rta_session  *rta_sess;
        u32                 *rta_mp_alg;
    };
    该结构体提供该路由的属性,即本地地址,目的地址,输出接口。
    函数fib_create_info用于创建路由信息,它首先要对请求信息的scope的有效性作一个判断,关于scope,内核定义了如下的枚举量:
    enum rt_scope_t
    {
        RT_SCOPE_UNIVERSE=0,
        /* User defined values  */
        RT_SCOPE_SITE=200,
        RT_SCOPE_LINK=253,
        RT_SCOPE_HOST=254,
        RT_SCOPE_NOWHERE=255
    };
    当前路由请求信息的type为RTN_LOCAL,它对应的scope应该是RT_SCOPE_HOST,如果scope小于这个值,则错误。
    新创建的struct fib_info结构都会维护在一个哈希表struct hlist_head *fib_info_hash中备用。该哈项表的长度由变量fib_hash_size记录,变量fib_info_cnt记录哈希表中当前的路由信息数 量。函数fib_create_info总是确保fib_hash_size>fib_info_cnt,如果不够了,就把哈希表扩大一倍。
    结构体struct fib_info的最后一个成员是struct fib_nh fib_nh[0],它是一个路由的下一跳信息,目前还没看到它的实际用途。缺省struct fib_nh的数量为1,如果不为1,则由struct kern_rta的rta_mp传入具体信息。下面是该结构体的定义:
    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;
    };
    新创建的struct fib_info需要进行部分值的初始化,下面是被初始化的内容:
    struct fib_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_NOWHERE;
            .nh_dev     = eth0;
        }
    }
    由于创建完成的struct fib_info,还要拿到哈项表fib_info_hash中去匹配,因为有可能哈希表中已存在同样的一个路由信息了,如果存在,则要删掉刚刚创建的 struct fib_info,把表中已有的那项的成员fib_treeref加1即可。判断两个路由信息相同的依据 是:fib_protocol,fib_prefsrc,fib_priority,fib_metrics,fib_flags全部相同,并且 fib_nh的数量和内容全部相同。
    如果哈希表中还不存在,则把新创建的fib_info添加到哈项表中。并且,如果成员fib_prefsrc不为0,则同时把fib_info添加到哈希 表fib_info_laddrhash中,fib_info_laddrhash是一个跟fib_info_hash同步维护的哈希表。
    最后,还要把fib_info的所有struct fib_nh成员放到哈希表fib_info_devhash中,nh_dev相同的放到同一项中。
    同理,第二项创建的struct fib_info如下:
    struct fib_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;
        }
    }
    依次类推,第三,第四项创建的struct fib_info与第二项完全相同,所以,最后,为网络设备接口eth0创建的struct fib_info共有两个,第一个的fib_treeref为1,第二个为3。
[ 2830.840525][ T4357] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000038 [ 2830.842010][ T4357] Mem abort info: [ 2830.842793][ T4357] ESR = 0x0000000096000044 [ 2830.843685][ T4357] EC = 0x25: DABT (current EL), IL = 32 bits [ 2830.844787][ T4357] SET = 0, FnV = 0 [ 2830.845589][ T4357] EA = 0, S1PTW = 0 [ 2830.846408][ T4357] FSC = 0x04: level 0 translation fault [ 2830.847425][ T4357] Data abort info: [ 2830.848254][ T4357] ISV = 0, ISS = 0x00000044 [ 2830.849148][ T4357] CM = 0, WnR = 1 [ 2830.849937][ T4357] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000102693000 [ 2830.851162][ T4357] [0000000000000038] pgd=0000000000000000, p4d=0000000000000000 [ 2830.852472][ T4357] Internal error: Oops: 0000000096000044 [#1] SMP [ 2830.853593][ T4357] Modules linked in: virtio_rdma(E+) ib_uverbs(E) ib_core(E) xt_CHECKSUM xt_MASQUERADE xt_conntrack ipt_REJECT nf_nat_tftp nf_conntrack_tftp nft_fib_inet nft_fib_ipv4 nf t_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject bridge nft_ct stp llc nft_chain_nat nf_tables ebtable_nat ebtable_broute ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c iptable_mangle iptable_raw iptable_security rfkill ip_set nfnetlink ebtable_filter ebtables ip6tabl e_filter ip6_tables iptable_filter ip_tables sunrpc vfat fat arm_smccc_trng sch_fq_codel fuse ext4 mbcache jbd2 virtio_gpu virtio_dma_buf drm_shmem_helper crct10dif_ce ghash_ce drm_kms_helpe r sha2_ce sha256_arm64 virtio_net net_failover sha1_ce virtio_blk failover virtio_mmio dm_mirror dm_region_hash dm_log dm_multipath dm_mod virtio_pci virtio_pci_legacy_dev virtio_pci_modern_ dev virtio virtio_ring aes_neon_bs aes_neon_blk aes_ce_blk aes_ce_cipher [ 2830.867784][ T4357] CPU: 7 PID: 4357 Comm: insmod Kdump: loaded Tainted: G E 6.4.0 #3 [ 2830.869442][ T4357] Hardware name: QEMU KVM Virtual Machine, BIOS 0.0.0 02/06/2015 [ 2830.870894][ T4357] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=–) [ 2830.872368][ T4357] pc : create_qp+0x90/0x238 [ib_core] [ 2830.873585][ T4357] lr : create_qp+0x1c4/0x238 [ib_core] [ 2830.874795][ T4357] sp : ffff80000c52b670 [ 2830.875802][ T4357] x29: ffff80000c52b670 x28: 0000000000000038 x27: 0000000000000000 [ 2830.877295][ T4357] x26: ffff8000014bc960 x25: ffff0000c3995000 x24: 0000000000000000 [ 2830.878795][ T4357] x23: ffff0000c39cad00 x22: 0000000000000000 x21: ffff8000014bc960 [ 2830.880291][ T4357] x20: ffff80000c52b750 x19: 0000000000000010 x18: ffffffffffffffff [ 2830.881776][ T4357] x17: 0000000000000020 x16: 0000000000000000 x15: ffffffffffffffff [ 2830.883276][ T4357] x14: 0000000000000000 x13: 3020737574617473 x12: 0000000000000001 [ 2830.884762][ T4357] x11: 00000000ffff7fff x10: ffff80000a043268 x9 : ffff80000848232c [ 2830.886260][ T4357] x8 : ffff80000c52b7b8 x7 : ffff80000146fb48 x6 : ffff0000c39928f8 [ 2830.887753][ T4357] x5 : ffff8000014bc960 x4 : 0000000000000000 x3 : 0000000000000000 [ 2830.889254][ T4357] x2 : 0000000000000000 x1 : 0000000000000dc0 x0 : 0000000000000038 [ 2830.890755][ T4357] Call trace: [ 2830.891658][ T4357] create_qp+0x90/0x238 [ib_core] [ 2830.892820][ T4357] ib_create_qp_kernel+0x50/0x128 [ib_core] [ 2830.894087][ T4357] create_mad_qp+0x9c/0x118 [ib_core] [ 2830.895318][ T4357] ib_mad_port_open+0x1bc/0x500 [ib_core] [ 2830.896569][ T4357] ib_mad_init_device+0x6c/0x1e0 [ib_core] [ 2830.897831][ T4357] add_client_context+0x13c/0x208 [ib_core] [ 2830.899111][ T4357] enable_device_and_get+0xd4/0x1b8 [ib_core] [ 2830.900409][ T4357] ib_register_device.part.0+0x104/0x1d8 [ib_core] [ 2830.901759][ T4357] ib_register_device+0x34/0x50 [ib_core] [ 2830.903018][ T4357] virtio_rdma_register_ib_device+0x90/0xb8 [virtio_rdma]
最新发布
04-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值