route: Set lwtstate for local traffic and cached input dsts
authorThomas Graf <tgraf@suug.ch>
Wed, 30 Nov 2016 16:10:09 +0000 (17:10 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 2 Dec 2016 15:51:49 +0000 (10:51 -0500)
A route on the output path hitting a RTN_LOCAL route will keep the dst
associated on its way through the loopback device. On the receive path,
the dst_input() call will thus invoke the input handler of the route
created in the output path. Thus, lwt redirection for input must be done
for dsts allocated in the otuput path as well.

Also, if a route is cached in the input path, the allocated dst should
respect lwtunnel configuration on the nexthop as well.

Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/route.c

index f098663..fa5c037 100644 (file)
@@ -1603,6 +1603,19 @@ static void ip_del_fnhe(struct fib_nh *nh, __be32 daddr)
        spin_unlock_bh(&fnhe_lock);
 }
 
+static void set_lwt_redirect(struct rtable *rth)
+{
+       if (lwtunnel_output_redirect(rth->dst.lwtstate)) {
+               rth->dst.lwtstate->orig_output = rth->dst.output;
+               rth->dst.output = lwtunnel_output;
+       }
+
+       if (lwtunnel_input_redirect(rth->dst.lwtstate)) {
+               rth->dst.lwtstate->orig_input = rth->dst.input;
+               rth->dst.input = lwtunnel_input;
+       }
+}
+
 /* called in rcu_read_lock() section */
 static int __mkroute_input(struct sk_buff *skb,
                           const struct fib_result *res,
@@ -1692,14 +1705,7 @@ rt_cache:
        rth->dst.input = ip_forward;
 
        rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag);
-       if (lwtunnel_output_redirect(rth->dst.lwtstate)) {
-               rth->dst.lwtstate->orig_output = rth->dst.output;
-               rth->dst.output = lwtunnel_output;
-       }
-       if (lwtunnel_input_redirect(rth->dst.lwtstate)) {
-               rth->dst.lwtstate->orig_input = rth->dst.input;
-               rth->dst.input = lwtunnel_input;
-       }
+       set_lwt_redirect(rth);
        skb_dst_set(skb, &rth->dst);
 out:
        err = 0;
@@ -1926,8 +1932,18 @@ local_input:
                rth->dst.error= -err;
                rth->rt_flags   &= ~RTCF_LOCAL;
        }
+
        if (do_cache) {
-               if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) {
+               struct fib_nh *nh = &FIB_RES_NH(res);
+
+               rth->dst.lwtstate = lwtstate_get(nh->nh_lwtstate);
+               if (lwtunnel_input_redirect(rth->dst.lwtstate)) {
+                       WARN_ON(rth->dst.input == lwtunnel_input);
+                       rth->dst.lwtstate->orig_input = rth->dst.input;
+                       rth->dst.input = lwtunnel_input;
+               }
+
+               if (unlikely(!rt_cache_route(nh, rth))) {
                        rth->dst.flags |= DST_NOCACHE;
                        rt_add_uncached_list(rth);
                }
@@ -2155,10 +2171,7 @@ add:
        }
 
        rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0);
-       if (lwtunnel_output_redirect(rth->dst.lwtstate)) {
-               rth->dst.lwtstate->orig_output = rth->dst.output;
-               rth->dst.output = lwtunnel_output;
-       }
+       set_lwt_redirect(rth);
 
        return rth;
 }