net/ipv6: Defer initialization of dst to data path

Defer setting dst input, output and error until fib entry is copied.

The reject path from ip6_route_info_create is moved to a new function
ip6_rt_init_dst_reject with a helper doing the conversion from fib6_type
to dst error.

The remainder of the new ip6_rt_init_dst is an amalgamtion of dst code
from addrconf_dst_alloc and the non-reject path of ip6_route_info_create.
The dst output function is always ip6_output and the input function is
either ip6_input (local routes), ip6_mc_input (multicast routes) or
ip6_forward (anything else).

A couple of places using dst.error are updated to look at rt6i_flags.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David Ahern 2018-04-17 17:33:15 -07:00 committed by David S. Miller
parent 5e670d844b
commit 6edb3c96a5

View File

@ -920,6 +920,75 @@ static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt)
return dev; return dev;
} }
static const int fib6_prop[RTN_MAX + 1] = {
[RTN_UNSPEC] = 0,
[RTN_UNICAST] = 0,
[RTN_LOCAL] = 0,
[RTN_BROADCAST] = 0,
[RTN_ANYCAST] = 0,
[RTN_MULTICAST] = 0,
[RTN_BLACKHOLE] = -EINVAL,
[RTN_UNREACHABLE] = -EHOSTUNREACH,
[RTN_PROHIBIT] = -EACCES,
[RTN_THROW] = -EAGAIN,
[RTN_NAT] = -EINVAL,
[RTN_XRESOLVE] = -EINVAL,
};
static int ip6_rt_type_to_error(u8 fib6_type)
{
return fib6_prop[fib6_type];
}
static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct rt6_info *ort)
{
rt->dst.error = ip6_rt_type_to_error(ort->fib6_type);
switch (ort->fib6_type) {
case RTN_BLACKHOLE:
rt->dst.output = dst_discard_out;
rt->dst.input = dst_discard;
break;
case RTN_PROHIBIT:
rt->dst.output = ip6_pkt_prohibit_out;
rt->dst.input = ip6_pkt_prohibit;
break;
case RTN_THROW:
case RTN_UNREACHABLE:
default:
rt->dst.output = ip6_pkt_discard_out;
rt->dst.input = ip6_pkt_discard;
break;
}
}
static void ip6_rt_init_dst(struct rt6_info *rt, struct rt6_info *ort)
{
if (ort->rt6i_flags & RTF_REJECT) {
ip6_rt_init_dst_reject(rt, ort);
return;
}
rt->dst.error = 0;
rt->dst.output = ip6_output;
if (ort->fib6_type == RTN_LOCAL) {
rt->dst.flags |= DST_HOST;
rt->dst.input = ip6_input;
} else if (ipv6_addr_type(&ort->rt6i_dst.addr) & IPV6_ADDR_MULTICAST) {
rt->dst.input = ip6_mc_input;
} else {
rt->dst.input = ip6_forward;
}
if (ort->fib6_nh.nh_lwtstate) {
rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate);
lwtunnel_set_redirect(&rt->dst);
}
rt->dst.lastuse = jiffies;
}
static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
{ {
BUG_ON(from->from); BUG_ON(from->from);
@ -932,14 +1001,12 @@ static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
{ {
rt->dst.input = ort->dst.input; ip6_rt_init_dst(rt, ort);
rt->dst.output = ort->dst.output;
rt->rt6i_dst = ort->rt6i_dst; rt->rt6i_dst = ort->rt6i_dst;
rt->dst.error = ort->dst.error;
rt->rt6i_idev = ort->rt6i_idev; rt->rt6i_idev = ort->rt6i_idev;
if (rt->rt6i_idev) if (rt->rt6i_idev)
in6_dev_hold(rt->rt6i_idev); in6_dev_hold(rt->rt6i_idev);
rt->dst.lastuse = jiffies;
rt->rt6i_gateway = ort->fib6_nh.nh_gw; rt->rt6i_gateway = ort->fib6_nh.nh_gw;
rt->rt6i_flags = ort->rt6i_flags; rt->rt6i_flags = ort->rt6i_flags;
rt6_set_from(rt, ort); rt6_set_from(rt, ort);
@ -2329,7 +2396,7 @@ restart:
continue; continue;
if (rt6_check_expired(rt)) if (rt6_check_expired(rt))
continue; continue;
if (rt->dst.error) if (rt->rt6i_flags & RTF_REJECT)
break; break;
if (!(rt->rt6i_flags & RTF_GATEWAY)) if (!(rt->rt6i_flags & RTF_GATEWAY))
continue; continue;
@ -2357,7 +2424,7 @@ restart:
if (!rt) if (!rt)
rt = net->ipv6.ip6_null_entry; rt = net->ipv6.ip6_null_entry;
else if (rt->dst.error) { else if (rt->rt6i_flags & RTF_REJECT) {
rt = net->ipv6.ip6_null_entry; rt = net->ipv6.ip6_null_entry;
goto out; goto out;
} }
@ -2900,15 +2967,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
addr_type = ipv6_addr_type(&cfg->fc_dst); addr_type = ipv6_addr_type(&cfg->fc_dst);
if (addr_type & IPV6_ADDR_MULTICAST)
rt->dst.input = ip6_mc_input;
else if (cfg->fc_flags & RTF_LOCAL)
rt->dst.input = ip6_input;
else
rt->dst.input = ip6_forward;
rt->dst.output = ip6_output;
if (cfg->fc_encap) { if (cfg->fc_encap) {
struct lwtunnel_state *lwtstate; struct lwtunnel_state *lwtstate;
@ -2918,7 +2976,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
if (err) if (err)
goto out; goto out;
rt->fib6_nh.nh_lwtstate = lwtstate_get(lwtstate); rt->fib6_nh.nh_lwtstate = lwtstate_get(lwtstate);
lwtunnel_set_redirect(&rt->dst);
} }
ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
@ -2958,27 +3015,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
} }
} }
rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
switch (cfg->fc_type) {
case RTN_BLACKHOLE:
rt->dst.error = -EINVAL;
rt->dst.output = dst_discard_out;
rt->dst.input = dst_discard;
break;
case RTN_PROHIBIT:
rt->dst.error = -EACCES;
rt->dst.output = ip6_pkt_prohibit_out;
rt->dst.input = ip6_pkt_prohibit;
break;
case RTN_THROW:
case RTN_UNREACHABLE:
default:
rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
: (cfg->fc_type == RTN_UNREACHABLE)
? -EHOSTUNREACH : -ENETUNREACH;
rt->dst.output = ip6_pkt_discard_out;
rt->dst.input = ip6_pkt_discard;
break;
}
goto install_route; goto install_route;
} }
@ -3623,12 +3659,9 @@ struct rt6_info *addrconf_dst_alloc(struct net *net,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
in6_dev_hold(idev); in6_dev_hold(idev);
rt->dst.flags |= DST_HOST;
rt->dst.input = ip6_input;
rt->dst.output = ip6_output;
rt->rt6i_idev = idev; rt->rt6i_idev = idev;
rt->dst.flags |= DST_HOST;
rt->rt6i_protocol = RTPROT_KERNEL; rt->rt6i_protocol = RTPROT_KERNEL;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
if (anycast) { if (anycast) {