Skip to content

Commit

Permalink
net/ipv6: Defer initialization of dst to data path
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Ahern authored and David S. Miller committed Apr 18, 2018
1 parent 5e670d8 commit 6edb3c9
Showing 1 changed file with 74 additions and 41 deletions.
115 changes: 74 additions & 41 deletions net/ipv6/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,75 @@ static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt)
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)
{
BUG_ON(from->from);
Expand All @@ -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)
{
rt->dst.input = ort->dst.input;
rt->dst.output = ort->dst.output;
ip6_rt_init_dst(rt, ort);

rt->rt6i_dst = ort->rt6i_dst;
rt->dst.error = ort->dst.error;
rt->rt6i_idev = ort->rt6i_idev;
if (rt->rt6i_idev)
in6_dev_hold(rt->rt6i_idev);
rt->dst.lastuse = jiffies;
rt->rt6i_gateway = ort->fib6_nh.nh_gw;
rt->rt6i_flags = ort->rt6i_flags;
rt6_set_from(rt, ort);
Expand Down Expand Up @@ -2329,7 +2396,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
continue;
if (rt6_check_expired(rt))
continue;
if (rt->dst.error)
if (rt->rt6i_flags & RTF_REJECT)
break;
if (!(rt->rt6i_flags & RTF_GATEWAY))
continue;
Expand Down Expand Up @@ -2357,7 +2424,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,

if (!rt)
rt = net->ipv6.ip6_null_entry;
else if (rt->dst.error) {
else if (rt->rt6i_flags & RTF_REJECT) {
rt = net->ipv6.ip6_null_entry;
goto out;
}
Expand Down Expand Up @@ -2900,15 +2967,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,

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) {
struct lwtunnel_state *lwtstate;

Expand All @@ -2918,7 +2976,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
if (err)
goto out;
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);
Expand Down Expand Up @@ -2958,27 +3015,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
}
}
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;
}

Expand Down Expand Up @@ -3623,12 +3659,9 @@ struct rt6_info *addrconf_dst_alloc(struct net *net,
return ERR_PTR(-ENOMEM);

in6_dev_hold(idev);

rt->dst.flags |= DST_HOST;
rt->dst.input = ip6_input;
rt->dst.output = ip6_output;
rt->rt6i_idev = idev;

rt->dst.flags |= DST_HOST;
rt->rt6i_protocol = RTPROT_KERNEL;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
if (anycast) {
Expand Down

0 comments on commit 6edb3c9

Please sign in to comment.