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