linux内核 路由fib表之输出查找

2.2.3.2 输出路由查找
    ip_route_output_key
功能:
    调用ip_route_output_flow

ip_route_output_flow
功能:
    1)路由发包查找
    2)ipsec处理流程

int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp,struct sock *sk, int flags)
//参数说明:net为网络设备;
//         rp当查询成功,返回查询到的路由缓存项;
//         flp用于查询缓存项的条件;
//         sk,flags用于ipsec路由查找
{
    int err;
    //路由查找
    if ((err = __ip_route_output_key(net, rp, flp)) != 0)
        return err;
    //ipsec路由查找
    if (flp->proto) {
        if (!flp->fl4_src)
            flp->fl4_src = (*rp)->rt_src;
        if (!flp->fl4_dst)
            flp->fl4_dst = (*rp)->rt_dst;
        err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk,
                    flags ? XFRM_LOOKUP_WAIT : 0);//ipsec处理
        if (err == -EREMOTE)
            err = ipv4_dst_blackhole(net, rp, flp);

        return err;
    }
    return 0;
}
__ip_route_output_key
功能:
    查找路由缓存
    未找到缓存,慢速发送

int __ip_route_output_key(struct net *net, struct rtable **rp,const struct flowi *flp)
{
    unsigned hash;
    struct rtable *rth;

    if (!rt_caching(net)) //查路由缓存
        goto slow_output;

    hash = rt_hash(flp->fl4_dst, flp->fl4_src, flp->oif, rt_genid(net));//计算hash

    rcu_read_lock_bh();
    for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;
        rth = rcu_dereference(rth->u.dst.rt_next)) {
        if (rth->fl.fl4_dst == flp->fl4_dst &&
            rth->fl.fl4_src == flp->fl4_src &&
            rth->fl.iif == 0 &&
            rth->fl.oif == flp->oif &&
            rth->fl.mark == flp->mark &&
            !((rth->fl.fl4_tos ^ flp->fl4_tos) &
                (IPTOS_RT_MASK | RTO_ONLINK)) &&
            net_eq(dev_net(rth->u.dst.dev), net) &&
            !rt_is_expired(rth)) {
            dst_use(&rth->u.dst, jiffies);
            RT_CACHE_STAT_INC(out_hit);
            rcu_read_unlock_bh();
            *rp = rth;  //将路由缓存表项取出
            return 0;
        }
        RT_CACHE_STAT_INC(out_hlist_search);
    }
    rcu_read_unlock_bh();

slow_output:
    return ip_route_output_slow(net, rp, flp);//慢速发送
}
2.2.3.2.1 ip_route_output_slow慢速查找输出
功能:

     查路由表
    不论是否查到,都进行策略路由查找

程序流程图:

linux内核 路由fib表之输出查找_第1张图片

static int ip_route_output_slow(struct net *net, struct rtable **rp,
                const struct flowi *oldflp)
{
    u32 tos = RT_FL_TOS(oldflp);
    struct flowi fl = { .nl_u = { .ip4_u =//构造路由查找对象
                      { .daddr = oldflp->fl4_dst,
                    .saddr = oldflp->fl4_src,
                    .tos = tos & IPTOS_RT_MASK,
                    .scope = ((tos & RTO_ONLINK) ? //根据tos和RTO_ONLINK,得出路由的scope
                          RT_SCOPE_LINK :
                          RT_SCOPE_UNIVERSE),
                      } },
                .mark = oldflp->mark,
                .iif = net->loopback_dev->ifindex,
                .oif = oldflp->oif };
    struct fib_result res;
    unsigned flags = 0;
    struct net_device *dev_out = NULL;
    int free_res = 0;
    int err;
    struct fib_result pr_res;
    int ret = 0;
    int proute_err = 0;
    res.prset = 0;


    res.fi      = NULL;
#ifdef CONFIG_IP_MULTIPLE_TABLES
    res.r       = NULL;
#endif

    if (oldflp->fl4_src) { //对源地址类型进行合法性检查
        err = -EINVAL;
        if (ipv4_is_multicast(oldflp->fl4_src) ||//组播
            ipv4_is_lbcast(oldflp->fl4_src) ||//受限广播255.255.255.255
            ipv4_is_zeronet(oldflp->fl4_src))//0.x.x.x地址 
            goto out;

        if (oldflp->oif == 0//发包接口为lo
            && (ipv4_is_multicast(oldflp->fl4_dst) ||//目的地址为组播
            oldflp->fl4_dst == htonl(0xFFFFFFFF))) {//广播
            /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
            dev_out = ip_dev_find(net, oldflp->fl4_src);
            ////将源地址作为目的地址,强制在local表中查找 
            //返回与给定的源地址相等的第一个设备
            if (dev_out == NULL)
                goto out;

            fl.oif = dev_out->ifindex;//将源ip对应的dev 赋值给出接口
            goto make_route;//**1**
        }

        if (!(oldflp->flags & FLOWI_FLAG_ANYSRC)) {
            /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
            dev_out = ip_dev_find(net, oldflp->fl4_src);
            if (dev_out == NULL)
                goto out;
            dev_put(dev_out);//递减引用计数
            dev_out = NULL;
        }
    }


    if (oldflp->oif) { //若出接口不为空
        dev_out = dev_get_by_index(net, oldflp->oif);//根据输出设备索引查找设备接口是否存在
        err = -ENODEV;
        if (dev_out == NULL)//检测出接口是否存在
            goto out;

        /* RACE: Check return value of inet_select_addr instead. */
        if (__in_dev_get_rtnl(dev_out) == NULL) {//返回出接口对应的入设备in_device
            dev_put(dev_out);
            goto out;   /* Wrong error code */
        }

        if (ipv4_is_local_multicast(oldflp->fl4_dst) ||//目的地址是本地多播或者受限广播
            oldflp->fl4_dst == htonl(0xFFFFFFFF)) {
            if (!fl.fl4_src)
                fl.fl4_src = inet_select_addr(dev_out, 0,//选择输出设备地址作为源地址
                                  RT_SCOPE_LINK);
            goto make_route;//**2**
        }
        if (!fl.fl4_src) {//没有指定源地址,则选输出设备作为源地址
            if (ipv4_is_multicast(oldflp->fl4_dst))
                fl.fl4_src = inet_select_addr(dev_out, 0,
                                  fl.fl4_scope);
            else if (!oldflp->fl4_dst)
                fl.fl4_src = inet_select_addr(dev_out, 0,
                                  RT_SCOPE_HOST);
        }
    }

    if (!fl.fl4_dst) {//若目的地址为0
        fl.fl4_dst = fl.fl4_src;
        if (!fl.fl4_dst)//目的和源地址都为0,则修改为127.0.0.1
            fl.fl4_dst = fl.fl4_src = htonl(INADDR_LOOPBACK);
        if (dev_out)
            dev_put(dev_out);
        dev_out = net->loopback_dev;//输出设备设置为lo
        dev_hold(dev_out);
        fl.oif = net->loopback_dev->ifindex;
        res.type = RTN_LOCAL;
        flags |= RTCF_LOCAL;
        goto make_route;//**3**
    }

        DEBUG_V4Route("%s-->\n",__FUNCTION__);

    if (fib_lookup(net, &fl, &res)) {//0表示成功,1表示失败
#ifdef CONFIG_FW_POLICY //策略路由
        if(FW_PR_CONFIG)
            ret = lookup_pr_policy_by_flow(NULL,&res, &fl, &proute_err);
        if(1 == ret){
            free_res = 1;
            goto proute_ok;
        }
#endif
        res.fi = NULL;
        if (oldflp->oif) {//当指定了输出接口
            if (fl.fl4_src == 0)//无源地址,取输出设备地址作为源地址
                fl.fl4_src = inet_select_addr(dev_out, 0,
                                  RT_SCOPE_LINK);
            res.type = RTN_UNICAST;
            goto make_route;//**4**
        }
        if (dev_out)
            dev_put(dev_out);
        err = -ENETUNREACH;
        goto out;
    }
    free_res = 1;

    if (res.type == RTN_LOCAL) {//路由查找结果为本地,则进行本地流程处理
        if (!fl.fl4_src)
            fl.fl4_src = fl.fl4_dst;
        if (dev_out)
            dev_put(dev_out);
        dev_out = net->loopback_dev;
        dev_hold(dev_out);
        fl.oif = dev_out->ifindex;
        if (res.fi)
            fib_info_put(res.fi);
        res.fi = NULL;
        flags |= RTCF_LOCAL;
        goto make_route;//**5**
    }
#ifdef CONFIG_FW_POLICY
    else if(FIB_RES_NH(res).nh_scope == RT_SCOPE_HOST){
        ;
    }else{
        if(FW_PR_CONFIG)
            ret = lookup_pr_policy_by_flow(&pr_res, &res, &fl, &proute_err);
        if(1 == ret){
            fib_res_put(&res);
            memcpy(&res, &pr_res, sizeof(struct fib_result));
        }
    }
#endif

proute_ok:

#ifdef CONFIG_IP_ROUTE_MULTIPATH
    if (res.fi->fib_nhs > 1 && fl.oif == 0)
        fib_select_multipath(&fl, &res);
    else
#endif//如果路由的子网掩码为0.0.0.0,同时路由为单播,且没有设置输出接口,则选择默认网关
    if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif)
        fib_select_default(net, &fl, &res);

    if (!fl.fl4_src)
        fl.fl4_src = FIB_RES_PREFSRC(res);

    if (dev_out)
        dev_put(dev_out);
    dev_out = FIB_RES_DEV(res);
    dev_hold(dev_out);
    fl.oif = dev_out->ifindex;//将输出设备信息替换为查找到的路由条目的输出接口


make_route:
    err = ip_mkroute_output(rp, &res, &fl, oldflp, dev_out, flags);//构造输出缓存项

    if(res.prset)
    {
        FIB_RES_GW(res) = res.oldnexthop;
        FIB_RES_NH(res).nh_scope = res.oldtype;
        res.prset = 0;
    }
    if (free_res)
        fib_res_put(&res);
    if (dev_out)
        dev_put(dev_out);
out:    return err;
}


你可能感兴趣的:(linux,网络协议)