ipv4: Use caller's on-stack flowi as-is in output route lookups.

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2011-04-28 14:48:42 -07:00
parent cf91166223
commit 813b3b5db8
2 changed files with 81 additions and 79 deletions

View File

@ -115,7 +115,7 @@ extern void ip_rt_redirect(__be32 old_gw, __be32 dst, __be32 new_gw,
__be32 src, struct net_device *dev); __be32 src, struct net_device *dev);
extern void rt_cache_flush(struct net *net, int how); extern void rt_cache_flush(struct net *net, int how);
extern void rt_cache_flush_batch(struct net *net); extern void rt_cache_flush_batch(struct net *net);
extern struct rtable *__ip_route_output_key(struct net *, const struct flowi4 *flp); extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
struct sock *sk); struct sock *sk);
extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig);

View File

@ -1767,7 +1767,7 @@ static unsigned int ipv4_default_mtu(const struct dst_entry *dst)
return mtu; return mtu;
} }
static void rt_init_metrics(struct rtable *rt, const struct flowi4 *oldflp4, static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4,
struct fib_info *fi) struct fib_info *fi)
{ {
struct inet_peer *peer; struct inet_peer *peer;
@ -1776,7 +1776,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *oldflp4,
/* If a peer entry exists for this destination, we must hook /* If a peer entry exists for this destination, we must hook
* it up in order to get at cached metrics. * it up in order to get at cached metrics.
*/ */
if (oldflp4 && (oldflp4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) if (fl4 && (fl4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS))
create = 1; create = 1;
rt->peer = peer = inet_getpeer_v4(rt->rt_dst, create); rt->peer = peer = inet_getpeer_v4(rt->rt_dst, create);
@ -1803,7 +1803,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *oldflp4,
} }
} }
static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *oldflp4, static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *fl4,
const struct fib_result *res, const struct fib_result *res,
struct fib_info *fi, u16 type, u32 itag) struct fib_info *fi, u16 type, u32 itag)
{ {
@ -1813,7 +1813,7 @@ static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *oldflp4,
if (FIB_RES_GW(*res) && if (FIB_RES_GW(*res) &&
FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
rt->rt_gateway = FIB_RES_GW(*res); rt->rt_gateway = FIB_RES_GW(*res);
rt_init_metrics(rt, oldflp4, fi); rt_init_metrics(rt, fl4, fi);
#ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_ROUTE_CLASSID
dst->tclassid = FIB_RES_NH(*res).nh_tclassid; dst->tclassid = FIB_RES_NH(*res).nh_tclassid;
#endif #endif
@ -2354,12 +2354,12 @@ EXPORT_SYMBOL(ip_route_input_common);
/* called with rcu_read_lock() */ /* called with rcu_read_lock() */
static struct rtable *__mkroute_output(const struct fib_result *res, static struct rtable *__mkroute_output(const struct fib_result *res,
const struct flowi4 *fl4, const struct flowi4 *fl4,
const struct flowi4 *oldflp4, __be32 orig_daddr, __be32 orig_saddr,
struct net_device *dev_out, int orig_oif, struct net_device *dev_out,
unsigned int flags) unsigned int flags)
{ {
struct fib_info *fi = res->fi; struct fib_info *fi = res->fi;
u32 tos = RT_FL_TOS(oldflp4); u32 tos = RT_FL_TOS(fl4);
struct in_device *in_dev; struct in_device *in_dev;
u16 type = res->type; u16 type = res->type;
struct rtable *rth; struct rtable *rth;
@ -2386,8 +2386,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
fi = NULL; fi = NULL;
} else if (type == RTN_MULTICAST) { } else if (type == RTN_MULTICAST) {
flags |= RTCF_MULTICAST | RTCF_LOCAL; flags |= RTCF_MULTICAST | RTCF_LOCAL;
if (!ip_check_mc_rcu(in_dev, oldflp4->daddr, oldflp4->saddr, if (!ip_check_mc_rcu(in_dev, fl4->daddr, fl4->saddr,
oldflp4->flowi4_proto)) fl4->flowi4_proto))
flags &= ~RTCF_LOCAL; flags &= ~RTCF_LOCAL;
/* If multicast route do not exist use /* If multicast route do not exist use
* default one, but do not gateway in this case. * default one, but do not gateway in this case.
@ -2405,8 +2405,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
rth->dst.output = ip_output; rth->dst.output = ip_output;
rth->rt_key_dst = oldflp4->daddr; rth->rt_key_dst = orig_daddr;
rth->rt_key_src = oldflp4->saddr; rth->rt_key_src = orig_saddr;
rth->rt_genid = rt_genid(dev_net(dev_out)); rth->rt_genid = rt_genid(dev_net(dev_out));
rth->rt_flags = flags; rth->rt_flags = flags;
rth->rt_type = type; rth->rt_type = type;
@ -2414,9 +2414,9 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
rth->rt_dst = fl4->daddr; rth->rt_dst = fl4->daddr;
rth->rt_src = fl4->saddr; rth->rt_src = fl4->saddr;
rth->rt_route_iif = 0; rth->rt_route_iif = 0;
rth->rt_iif = oldflp4->flowi4_oif ? : dev_out->ifindex; rth->rt_iif = orig_oif ? : dev_out->ifindex;
rth->rt_oif = oldflp4->flowi4_oif; rth->rt_oif = orig_oif;
rth->rt_mark = oldflp4->flowi4_mark; rth->rt_mark = fl4->flowi4_mark;
rth->rt_gateway = fl4->daddr; rth->rt_gateway = fl4->daddr;
rth->rt_spec_dst= fl4->saddr; rth->rt_spec_dst= fl4->saddr;
rth->rt_peer_genid = 0; rth->rt_peer_genid = 0;
@ -2439,7 +2439,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
#ifdef CONFIG_IP_MROUTE #ifdef CONFIG_IP_MROUTE
if (type == RTN_MULTICAST) { if (type == RTN_MULTICAST) {
if (IN_DEV_MFORWARD(in_dev) && if (IN_DEV_MFORWARD(in_dev) &&
!ipv4_is_local_multicast(oldflp4->daddr)) { !ipv4_is_local_multicast(fl4->daddr)) {
rth->dst.input = ip_mr_input; rth->dst.input = ip_mr_input;
rth->dst.output = ip_mc_output; rth->dst.output = ip_mc_output;
} }
@ -2447,7 +2447,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
#endif #endif
} }
rt_set_nexthop(rth, oldflp4, res, fi, type, 0); rt_set_nexthop(rth, fl4, res, fi, type, 0);
return rth; return rth;
} }
@ -2457,36 +2457,37 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
* called with rcu_read_lock(); * called with rcu_read_lock();
*/ */
static struct rtable *ip_route_output_slow(struct net *net, static struct rtable *ip_route_output_slow(struct net *net, struct flowi4 *fl4)
const struct flowi4 *oldflp4)
{ {
u32 tos = RT_FL_TOS(oldflp4);
struct flowi4 fl4;
struct fib_result res;
unsigned int flags = 0;
struct net_device *dev_out = NULL; struct net_device *dev_out = NULL;
u32 tos = RT_FL_TOS(fl4);
unsigned int flags = 0;
struct fib_result res;
struct rtable *rth; struct rtable *rth;
__be32 orig_daddr;
__be32 orig_saddr;
int orig_oif;
res.fi = NULL; res.fi = NULL;
#ifdef CONFIG_IP_MULTIPLE_TABLES #ifdef CONFIG_IP_MULTIPLE_TABLES
res.r = NULL; res.r = NULL;
#endif #endif
fl4.flowi4_oif = oldflp4->flowi4_oif; orig_daddr = fl4->daddr;
fl4.flowi4_iif = net->loopback_dev->ifindex; orig_saddr = fl4->saddr;
fl4.flowi4_mark = oldflp4->flowi4_mark; orig_oif = fl4->flowi4_oif;
fl4.daddr = oldflp4->daddr;
fl4.saddr = oldflp4->saddr; fl4->flowi4_iif = net->loopback_dev->ifindex;
fl4.flowi4_tos = tos & IPTOS_RT_MASK; fl4->flowi4_tos = tos & IPTOS_RT_MASK;
fl4.flowi4_scope = ((tos & RTO_ONLINK) ? fl4->flowi4_scope = ((tos & RTO_ONLINK) ?
RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
rcu_read_lock(); rcu_read_lock();
if (oldflp4->saddr) { if (fl4->saddr) {
rth = ERR_PTR(-EINVAL); rth = ERR_PTR(-EINVAL);
if (ipv4_is_multicast(oldflp4->saddr) || if (ipv4_is_multicast(fl4->saddr) ||
ipv4_is_lbcast(oldflp4->saddr) || ipv4_is_lbcast(fl4->saddr) ||
ipv4_is_zeronet(oldflp4->saddr)) ipv4_is_zeronet(fl4->saddr))
goto out; goto out;
/* I removed check for oif == dev_out->oif here. /* I removed check for oif == dev_out->oif here.
@ -2497,11 +2498,11 @@ static struct rtable *ip_route_output_slow(struct net *net,
of another iface. --ANK of another iface. --ANK
*/ */
if (oldflp4->flowi4_oif == 0 && if (fl4->flowi4_oif == 0 &&
(ipv4_is_multicast(oldflp4->daddr) || (ipv4_is_multicast(fl4->daddr) ||
ipv4_is_lbcast(oldflp4->daddr))) { ipv4_is_lbcast(fl4->daddr))) {
/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
dev_out = __ip_dev_find(net, oldflp4->saddr, false); dev_out = __ip_dev_find(net, fl4->saddr, false);
if (dev_out == NULL) if (dev_out == NULL)
goto out; goto out;
@ -2520,20 +2521,20 @@ static struct rtable *ip_route_output_slow(struct net *net,
Luckily, this hack is good workaround. Luckily, this hack is good workaround.
*/ */
fl4.flowi4_oif = dev_out->ifindex; fl4->flowi4_oif = dev_out->ifindex;
goto make_route; goto make_route;
} }
if (!(oldflp4->flowi4_flags & FLOWI_FLAG_ANYSRC)) { if (!(fl4->flowi4_flags & FLOWI_FLAG_ANYSRC)) {
/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
if (!__ip_dev_find(net, oldflp4->saddr, false)) if (!__ip_dev_find(net, fl4->saddr, false))
goto out; goto out;
} }
} }
if (oldflp4->flowi4_oif) { if (fl4->flowi4_oif) {
dev_out = dev_get_by_index_rcu(net, oldflp4->flowi4_oif); dev_out = dev_get_by_index_rcu(net, fl4->flowi4_oif);
rth = ERR_PTR(-ENODEV); rth = ERR_PTR(-ENODEV);
if (dev_out == NULL) if (dev_out == NULL)
goto out; goto out;
@ -2543,37 +2544,37 @@ static struct rtable *ip_route_output_slow(struct net *net,
rth = ERR_PTR(-ENETUNREACH); rth = ERR_PTR(-ENETUNREACH);
goto out; goto out;
} }
if (ipv4_is_local_multicast(oldflp4->daddr) || if (ipv4_is_local_multicast(fl4->daddr) ||
ipv4_is_lbcast(oldflp4->daddr)) { ipv4_is_lbcast(fl4->daddr)) {
if (!fl4.saddr) if (!fl4->saddr)
fl4.saddr = inet_select_addr(dev_out, 0, fl4->saddr = inet_select_addr(dev_out, 0,
RT_SCOPE_LINK); RT_SCOPE_LINK);
goto make_route; goto make_route;
} }
if (!fl4.saddr) { if (fl4->saddr) {
if (ipv4_is_multicast(oldflp4->daddr)) if (ipv4_is_multicast(fl4->daddr))
fl4.saddr = inet_select_addr(dev_out, 0, fl4->saddr = inet_select_addr(dev_out, 0,
fl4.flowi4_scope); fl4->flowi4_scope);
else if (!oldflp4->daddr) else if (!fl4->daddr)
fl4.saddr = inet_select_addr(dev_out, 0, fl4->saddr = inet_select_addr(dev_out, 0,
RT_SCOPE_HOST); RT_SCOPE_HOST);
} }
} }
if (!fl4.daddr) { if (!fl4->daddr) {
fl4.daddr = fl4.saddr; fl4->daddr = fl4->saddr;
if (!fl4.daddr) if (!fl4->daddr)
fl4.daddr = fl4.saddr = htonl(INADDR_LOOPBACK); fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK);
dev_out = net->loopback_dev; dev_out = net->loopback_dev;
fl4.flowi4_oif = net->loopback_dev->ifindex; fl4->flowi4_oif = net->loopback_dev->ifindex;
res.type = RTN_LOCAL; res.type = RTN_LOCAL;
flags |= RTCF_LOCAL; flags |= RTCF_LOCAL;
goto make_route; goto make_route;
} }
if (fib_lookup(net, &fl4, &res)) { if (fib_lookup(net, fl4, &res)) {
res.fi = NULL; res.fi = NULL;
if (oldflp4->flowi4_oif) { if (fl4->flowi4_oif) {
/* Apparently, routing tables are wrong. Assume, /* Apparently, routing tables are wrong. Assume,
that the destination is on link. that the destination is on link.
@ -2592,9 +2593,9 @@ static struct rtable *ip_route_output_slow(struct net *net,
likely IPv6, but we do not. likely IPv6, but we do not.
*/ */
if (fl4.saddr == 0) if (fl4->saddr == 0)
fl4.saddr = inet_select_addr(dev_out, 0, fl4->saddr = inet_select_addr(dev_out, 0,
RT_SCOPE_LINK); RT_SCOPE_LINK);
res.type = RTN_UNICAST; res.type = RTN_UNICAST;
goto make_route; goto make_route;
} }
@ -2603,44 +2604,45 @@ static struct rtable *ip_route_output_slow(struct net *net,
} }
if (res.type == RTN_LOCAL) { if (res.type == RTN_LOCAL) {
if (!fl4.saddr) { if (!fl4->saddr) {
if (res.fi->fib_prefsrc) if (res.fi->fib_prefsrc)
fl4.saddr = res.fi->fib_prefsrc; fl4->saddr = res.fi->fib_prefsrc;
else else
fl4.saddr = fl4.daddr; fl4->saddr = fl4->daddr;
} }
dev_out = net->loopback_dev; dev_out = net->loopback_dev;
fl4.flowi4_oif = dev_out->ifindex; fl4->flowi4_oif = dev_out->ifindex;
res.fi = NULL; res.fi = NULL;
flags |= RTCF_LOCAL; flags |= RTCF_LOCAL;
goto make_route; goto make_route;
} }
#ifdef CONFIG_IP_ROUTE_MULTIPATH #ifdef CONFIG_IP_ROUTE_MULTIPATH
if (res.fi->fib_nhs > 1 && fl4.flowi4_oif == 0) if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0)
fib_select_multipath(&res); fib_select_multipath(&res);
else else
#endif #endif
if (!res.prefixlen && if (!res.prefixlen &&
res.table->tb_num_default > 1 && res.table->tb_num_default > 1 &&
res.type == RTN_UNICAST && !fl4.flowi4_oif) res.type == RTN_UNICAST && !fl4->flowi4_oif)
fib_select_default(&res); fib_select_default(&res);
if (!fl4.saddr) if (!fl4->saddr)
fl4.saddr = FIB_RES_PREFSRC(net, res); fl4->saddr = FIB_RES_PREFSRC(net, res);
dev_out = FIB_RES_DEV(res); dev_out = FIB_RES_DEV(res);
fl4.flowi4_oif = dev_out->ifindex; fl4->flowi4_oif = dev_out->ifindex;
make_route: make_route:
rth = __mkroute_output(&res, &fl4, oldflp4, dev_out, flags); rth = __mkroute_output(&res, fl4, orig_daddr, orig_saddr, orig_oif,
dev_out, flags);
if (!IS_ERR(rth)) { if (!IS_ERR(rth)) {
unsigned int hash; unsigned int hash;
hash = rt_hash(oldflp4->daddr, oldflp4->saddr, oldflp4->flowi4_oif, hash = rt_hash(orig_daddr, orig_saddr, orig_oif,
rt_genid(dev_net(dev_out))); rt_genid(dev_net(dev_out)));
rth = rt_intern_hash(hash, rth, NULL, oldflp4->flowi4_oif); rth = rt_intern_hash(hash, rth, NULL, orig_oif);
} }
out: out:
@ -2648,7 +2650,7 @@ out:
return rth; return rth;
} }
struct rtable *__ip_route_output_key(struct net *net, const struct flowi4 *flp4) struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp4)
{ {
struct rtable *rth; struct rtable *rth;
unsigned int hash; unsigned int hash;