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

来源:互联网 发布:中国体育彩票关注软件 编辑:程序博客网 时间:2024/06/05 11:14
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慢速查找输出
功能:

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

程序流程图:


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));        }    }#endifproute_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;}


0 0
原创粉丝点击