Commit b23dd4fe authored by David S. Miller's avatar David S. Miller
Browse files

ipv4: Make output route lookup return rtable directly.



Instead of on the stack.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 452edd59
......@@ -469,7 +469,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
.proto = IPPROTO_IPIP
};
if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
rt = ip_route_output_key(dev_net(dev), &fl);
if (IS_ERR(rt)) {
dev->stats.tx_carrier_errors++;
goto tx_error_icmp;
}
......@@ -590,9 +591,9 @@ static void ipip_tunnel_bind_dev(struct net_device *dev)
.fl4_tos = RT_TOS(iph->tos),
.proto = IPPROTO_IPIP
};
struct rtable *rt;
struct rtable *rt = ip_route_output_key(dev_net(dev), &fl);
if (!ip_route_output_key(dev_net(dev), &rt, &fl)) {
if (!IS_ERR(rt)) {
tdev = rt->dst.dev;
ip_rt_put(rt);
}
......
......@@ -1618,8 +1618,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
.fl4_tos = RT_TOS(iph->tos),
.proto = IPPROTO_IPIP
};
if (ip_route_output_key(net, &rt, &fl))
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt))
goto out_free;
encap = sizeof(struct iphdr);
} else {
......@@ -1629,8 +1629,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
.fl4_tos = RT_TOS(iph->tos),
.proto = IPPROTO_IPIP
};
if (ip_route_output_key(net, &rt, &fl))
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt))
goto out_free;
}
......
......@@ -38,7 +38,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type)
fl.oif = skb->sk ? skb->sk->sk_bound_dev_if : 0;
fl.mark = skb->mark;
fl.flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0;
if (ip_route_output_key(net, &rt, &fl) != 0)
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt))
return -1;
/* Drop old route. */
......@@ -48,7 +49,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type)
/* non-local src, find valid iif to satisfy
* rp-filter when calling ip_route_input. */
fl.fl4_dst = iph->saddr;
if (ip_route_output_key(net, &rt, &fl) != 0)
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt))
return -1;
orefdst = skb->_skb_refdst;
......@@ -221,7 +223,11 @@ static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook,
static int nf_ip_route(struct dst_entry **dst, struct flowi *fl)
{
return ip_route_output_key(&init_net, (struct rtable **)dst, fl);
struct rtable *rt = ip_route_output_key(&init_net, fl);
if (IS_ERR(rt))
return PTR_ERR(rt);
*dst = &rt->dst;
return 0;
}
static const struct nf_afinfo nf_ip_afinfo = {
......
......@@ -564,10 +564,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
security_sk_classify_flow(sk, &fl);
err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk);
rt = ip_route_output_flow(sock_net(sk), &fl, sk);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
goto done;
}
}
if (err)
goto done;
err = -EACCES;
if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST))
......
......@@ -1014,8 +1014,8 @@ static int slow_chain_length(const struct rtable *head)
return length >> FRACT_BITS;
}
static int rt_intern_hash(unsigned hash, struct rtable *rt,
struct rtable **rp, struct sk_buff *skb, int ifindex)
static struct rtable *rt_intern_hash(unsigned hash, struct rtable *rt,
struct sk_buff *skb, int ifindex)
{
struct rtable *rth, *cand;
struct rtable __rcu **rthp, **candp;
......@@ -1056,7 +1056,7 @@ static int rt_intern_hash(unsigned hash, struct rtable *rt,
printk(KERN_WARNING
"Neighbour table failure & not caching routes.\n");
ip_rt_put(rt);
return err;
return ERR_PTR(err);
}
}
......@@ -1093,11 +1093,9 @@ static int rt_intern_hash(unsigned hash, struct rtable *rt,
spin_unlock_bh(rt_hash_lock_addr(hash));
rt_drop(rt);
if (rp)
*rp = rth;
else
if (skb)
skb_dst_set(skb, &rth->dst);
return 0;
return rth;
}
if (!atomic_read(&rth->dst.__refcnt)) {
......@@ -1154,7 +1152,7 @@ static int rt_intern_hash(unsigned hash, struct rtable *rt,
if (err != -ENOBUFS) {
rt_drop(rt);
return err;
return ERR_PTR(err);
}
/* Neighbour tables are full and nothing
......@@ -1175,7 +1173,7 @@ static int rt_intern_hash(unsigned hash, struct rtable *rt,
if (net_ratelimit())
printk(KERN_WARNING "ipv4: Neighbour table overflow.\n");
rt_drop(rt);
return -ENOBUFS;
return ERR_PTR(-ENOBUFS);
}
}
......@@ -1201,11 +1199,9 @@ static int rt_intern_hash(unsigned hash, struct rtable *rt,
spin_unlock_bh(rt_hash_lock_addr(hash));
skip_hashing:
if (rp)
*rp = rt;
else
if (skb)
skb_dst_set(skb, &rt->dst);
return 0;
return rt;
}
static atomic_t __rt_peer_genid = ATOMIC_INIT(0);
......@@ -1896,7 +1892,10 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
RT_CACHE_STAT_INC(in_slow_mc);
hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev)));
return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex);
rth = rt_intern_hash(hash, rth, skb, dev->ifindex);
err = 0;
if (IS_ERR(rth))
err = PTR_ERR(rth);
e_nobufs:
return -ENOBUFS;
......@@ -2051,7 +2050,10 @@ static int ip_mkroute_input(struct sk_buff *skb,
/* put it into the cache */
hash = rt_hash(daddr, saddr, fl->iif,
rt_genid(dev_net(rth->dst.dev)));
return rt_intern_hash(hash, rth, NULL, skb, fl->iif);
rth = rt_intern_hash(hash, rth, skb, fl->iif);
if (IS_ERR(rth))
return PTR_ERR(rth);
return 0;
}
/*
......@@ -2194,7 +2196,10 @@ out: return err;
}
rth->rt_type = res.type;
hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));
err = rt_intern_hash(hash, rth, NULL, skb, fl.iif);
rth = rt_intern_hash(hash, rth, skb, fl.iif);
err = 0;
if (IS_ERR(rth))
err = PTR_ERR(rth);
goto out;
no_route:
......@@ -2422,8 +2427,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
* called with rcu_read_lock();
*/
static int ip_route_output_slow(struct net *net, struct rtable **rp,
const struct flowi *oldflp)
static struct rtable *ip_route_output_slow(struct net *net,
const struct flowi *oldflp)
{
u32 tos = RT_FL_TOS(oldflp);
struct flowi fl = { .fl4_dst = oldflp->fl4_dst,
......@@ -2438,8 +2443,6 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
unsigned int flags = 0;
struct net_device *dev_out = NULL;
struct rtable *rth;
int err;
res.fi = NULL;
#ifdef CONFIG_IP_MULTIPLE_TABLES
......@@ -2448,7 +2451,7 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
rcu_read_lock();
if (oldflp->fl4_src) {
err = -EINVAL;
rth = ERR_PTR(-EINVAL);
if (ipv4_is_multicast(oldflp->fl4_src) ||
ipv4_is_lbcast(oldflp->fl4_src) ||
ipv4_is_zeronet(oldflp->fl4_src))
......@@ -2499,13 +2502,13 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
if (oldflp->oif) {
dev_out = dev_get_by_index_rcu(net, oldflp->oif);
err = -ENODEV;
rth = ERR_PTR(-ENODEV);
if (dev_out == NULL)
goto out;
/* RACE: Check return value of inet_select_addr instead. */
if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) {
err = -ENETUNREACH;
rth = ERR_PTR(-ENETUNREACH);
goto out;
}
if (ipv4_is_local_multicast(oldflp->fl4_dst) ||
......@@ -2563,7 +2566,7 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
res.type = RTN_UNICAST;
goto make_route;
}
err = -ENETUNREACH;
rth = ERR_PTR(-ENETUNREACH);
goto out;
}
......@@ -2598,23 +2601,20 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
make_route:
rth = __mkroute_output(&res, &fl, oldflp, dev_out, flags);
if (IS_ERR(rth))
err = PTR_ERR(rth);
else {
if (!IS_ERR(rth)) {
unsigned int hash;
hash = rt_hash(oldflp->fl4_dst, oldflp->fl4_src, oldflp->oif,
rt_genid(dev_net(dev_out)));
err = rt_intern_hash(hash, rth, rp, NULL, oldflp->oif);
rth = rt_intern_hash(hash, rth, NULL, oldflp->oif);
}
out:
rcu_read_unlock();
return err;
return rth;
}
int __ip_route_output_key(struct net *net, struct rtable **rp,
const struct flowi *flp)
struct rtable *__ip_route_output_key(struct net *net, const struct flowi *flp)
{
struct rtable *rth;
unsigned int hash;
......@@ -2639,15 +2639,14 @@ int __ip_route_output_key(struct net *net, struct rtable **rp,
dst_use(&rth->dst, jiffies);
RT_CACHE_STAT_INC(out_hit);
rcu_read_unlock_bh();
*rp = rth;
return 0;
return rth;
}
RT_CACHE_STAT_INC(out_hlist_search);
}
rcu_read_unlock_bh();
slow_output:
return ip_route_output_slow(net, rp, flp);
return ip_route_output_slow(net, flp);
}
EXPORT_SYMBOL_GPL(__ip_route_output_key);
......@@ -2717,34 +2716,29 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or
return rt ? &rt->dst : ERR_PTR(-ENOMEM);
}
int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp,
struct sock *sk)
struct rtable *ip_route_output_flow(struct net *net, struct flowi *flp,
struct sock *sk)
{
int err;
struct rtable *rt = __ip_route_output_key(net, flp);
if ((err = __ip_route_output_key(net, rp, flp)) != 0)
return err;
if (IS_ERR(rt))
return rt;
if (flp->proto) {
if (!flp->fl4_src)
flp->fl4_src = (*rp)->rt_src;
flp->fl4_src = rt->rt_src;
if (!flp->fl4_dst)
flp->fl4_dst = (*rp)->rt_dst;
*rp = (struct rtable *) xfrm_lookup(net, &(*rp)->dst, flp, sk, 0);
if (IS_ERR(*rp)) {
err = PTR_ERR(*rp);
*rp = NULL;
return err;
}
flp->fl4_dst = rt->rt_dst;
rt = (struct rtable *) xfrm_lookup(net, &rt->dst, flp, sk, 0);
}
return 0;
return rt;
}
EXPORT_SYMBOL_GPL(ip_route_output_flow);
int ip_route_output_key(struct net *net, struct rtable **rp, struct flowi *flp)
struct rtable *ip_route_output_key(struct net *net, struct flowi *flp)
{
return ip_route_output_flow(net, rp, flp, NULL);
return ip_route_output_flow(net, flp, NULL);
}
EXPORT_SYMBOL(ip_route_output_key);
......@@ -2915,7 +2909,11 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
.oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0,
.mark = mark,
};
err = ip_route_output_key(net, &rt, &fl);
rt = ip_route_output_key(net, &fl);
err = 0;
if (IS_ERR(rt))
err = PTR_ERR(rt);
}
if (err)
......
......@@ -355,7 +355,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
.fl_ip_sport = th->dest,
.fl_ip_dport = th->source };
security_req_classify_flow(req, &fl);
if (ip_route_output_key(sock_net(sk), &rt, &fl)) {
rt = ip_route_output_key(sock_net(sk), &fl);
if (IS_ERR(rt)) {
reqsk_free(req);
goto out;
}
......
......@@ -152,7 +152,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
__be16 orig_sport, orig_dport;
struct rtable *rt;
__be32 daddr, nexthop;
int tmp;
int err;
if (addr_len < sizeof(struct sockaddr_in))
......@@ -170,14 +169,15 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
orig_sport = inet->inet_sport;
orig_dport = usin->sin_port;
tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr,
RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
IPPROTO_TCP,
orig_sport, orig_dport, sk, true);
if (tmp < 0) {
if (tmp == -ENETUNREACH)
rt = ip_route_connect(nexthop, inet->inet_saddr,
RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
IPPROTO_TCP,
orig_sport, orig_dport, sk, true);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
if (err == -ENETUNREACH)
IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
return tmp;
return err;
}
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
......@@ -236,12 +236,14 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (err)
goto failure;
err = ip_route_newports(&rt, IPPROTO_TCP,
orig_sport, orig_dport,
inet->inet_sport, inet->inet_dport, sk);
if (err)
rt = ip_route_newports(rt, IPPROTO_TCP,
orig_sport, orig_dport,
inet->inet_sport, inet->inet_dport, sk);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
rt = NULL;
goto failure;
}
/* OK, now commit destination to socket. */
sk->sk_gso_type = SKB_GSO_TCPV4;
sk_setup_caps(sk, &rt->dst);
......
......@@ -922,8 +922,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
struct net *net = sock_net(sk);
security_sk_classify_flow(sk, &fl);
err = ip_route_output_flow(net, &rt, &fl, sk);
if (err) {
rt = ip_route_output_flow(net, &fl, sk);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
if (err == -ENETUNREACH)
IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
goto out;
......
......@@ -26,18 +26,16 @@ static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos,
.fl4_dst = daddr->a4,
.fl4_tos = tos,
};
struct dst_entry *dst;
struct rtable *rt;
int err;
if (saddr)
fl.fl4_src = saddr->a4;
err = __ip_route_output_key(net, &rt, &fl);
dst = &rt->dst;
if (err)
dst = ERR_PTR(err);
return dst;
rt = __ip_route_output_key(net, &fl);
if (!IS_ERR(rt))
return &rt->dst;
return ERR_CAST(rt);
}
static int xfrm4_get_saddr(struct net *net,
......
......@@ -581,7 +581,8 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
fl.fl4_dst = eiph->saddr;
fl.fl4_tos = RT_TOS(eiph->tos);
fl.proto = IPPROTO_IPIP;
if (ip_route_output_key(dev_net(skb->dev), &rt, &fl))
rt = ip_route_output_key(dev_net(skb->dev), &fl);
if (IS_ERR(rt))
goto out;
skb2->dev = rt->dst.dev;
......@@ -593,12 +594,14 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
fl.fl4_dst = eiph->daddr;
fl.fl4_src = eiph->saddr;
fl.fl4_tos = eiph->tos;
if (ip_route_output_key(dev_net(skb->dev), &rt, &fl) ||
rt = ip_route_output_key(dev_net(skb->dev), &fl);
if (IS_ERR(rt) ||
rt->dst.dev->type != ARPHRD_TUNNEL) {
ip_rt_put(rt);
if (!IS_ERR(rt))
ip_rt_put(rt);
goto out;
}
skb_dst_set(skb2, (struct dst_entry *)rt);
skb_dst_set(skb2, &rt->dst);
} else {
ip_rt_put(rt);
if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos,
......
......@@ -738,7 +738,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
.fl4_tos = RT_TOS(tos),
.oif = tunnel->parms.link,
.proto = IPPROTO_IPV6 };
if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
rt = ip_route_output_key(dev_net(dev), &fl);
if (IS_ERR(rt)) {
dev->stats.tx_carrier_errors++;
goto tx_error_icmp;
}
......@@ -862,8 +863,9 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
.fl4_tos = RT_TOS(iph->tos),
.oif = tunnel->parms.link,
.proto = IPPROTO_IPV6 };
struct rtable *rt;
if (!ip_route_output_key(dev_net(dev), &rt, &fl)) {
struct rtable *rt = ip_route_output_key(dev_net(dev), &fl);
if (!IS_ERR(rt)) {
tdev = rt->dst.dev;
ip_rt_put(rt);
}
......
......@@ -320,11 +320,12 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
if (ipv4_is_multicast(lsa->l2tp_addr.s_addr))
goto out;
rc = ip_route_connect(&rt, lsa->l2tp_addr.s_addr, saddr,
rt = ip_route_connect(lsa->l2tp_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
IPPROTO_L2TP,
0, 0, sk, true);
if (rc) {
if (IS_ERR(rt)) {
rc = PTR_ERR(rt);
if (rc == -ENETUNREACH)
IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES);
goto out;
......@@ -489,7 +490,8 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
* itself out.
*/
security_sk_classify_flow(sk, &fl);
if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk))
rt = ip_route_output_flow(sock_net(sk), &fl, sk);
if (IS_ERR(rt))
goto no_route;
}
sk_setup_caps(sk, &rt->dst);
......
......@@ -103,7 +103,8 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest,
.fl4_tos = rtos,
};
if (ip_route_output_key(net, &rt, &fl)) {
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt)) {
spin_unlock(&dest->dst_lock);
IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n",
&dest->addr.ip);
......@@ -121,7 +122,8 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest,
.fl4_tos = rtos,
};
if (ip_route_output_key(net, &rt, &fl)) {
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt)) {
IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n",
&daddr);
return NULL;
......@@ -180,7 +182,8 @@ __ip_vs_reroute_locally(struct sk_buff *skb)
.mark = skb->mark,
};
if (ip_route_output_key(net, &rt, &fl))
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt))
return 0;
if (!(rt->rt_flags & RTCF_LOCAL)) {
ip_rt_put(rt);
......
......@@ -73,7 +73,8 @@ tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info)
fl.fl4_dst = info->gw.ip;
fl.fl4_tos = RT_TOS(iph->tos);
fl.fl4_scope = RT_SCOPE_UNIVERSE;
if (ip_route_output_key(net, &rt, &fl) != 0)
rt = ip_route_output_key(net, &fl);
if (IS_ERR(rt))
return false;
skb_dst_drop(skb);
......
......@@ -37,7 +37,6 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
{
struct rtable *rt;
struct flowi fl;
int ret;
peer->if_mtu = 1500;
......@@ -58,9 +57,9 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
BUG();
}
ret = ip_route_output_key(&init_net, &rt, &fl);
if (ret < 0) {
_leave(" [route err %d]", ret);
rt = ip_route_output_key(&init_net, &fl);
if (IS_ERR(rt)) {
_leave(" [route err %ld]", PTR_ERR(rt));
return;
}
......
......@@ -491,9 +491,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
__func__, &fl.fl4_dst, &fl.fl4_src);
if (!ip_route_output_key(&init_net, &rt, &fl)) {
rt = ip_route_output_key(&init_net, &fl);
if (!IS_ERR(rt))
dst = &rt->dst;
}
/* If there is no association or if a source address is passed, no
* more validation is required.
......@@ -535,7 +535,8 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
(AF_INET == laddr->a.sa.sa_family)) {
fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
fl.fl_ip_sport = laddr->a.v4.sin_port;
if (!ip_route_output_key(&init_net, &rt, &fl)) {
rt = ip_route_output_key(&init_net, &fl);
if (!IS_ERR(rt)) {
dst = &rt->dst;
goto out_unlock;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment