ipv6: honor RT6_LOOKUP_F_DST_NOREF in rule lookup logic
authorWei Wang <weiwan@google.com>
Fri, 21 Jun 2019 00:36:39 +0000 (17:36 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 23 Jun 2019 20:24:17 +0000 (13:24 -0700)
This patch specifically converts the rule lookup logic to honor this
flag and not release refcnt when traversing each rule and calling
lookup() on each routing table.
Similar to previous patch, we also need some special handling of dst
entries in uncached list because there is always 1 refcnt taken for them
even if RT6_LOOKUP_F_DST_NOREF flag is set.

Signed-off-by: Wei Wang <weiwan@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ip6_route.h
net/ipv6/fib6_rules.c
net/ipv6/ip6_fib.c

index 82bced2fc1e3103c0a429ed6f6620168665c36a1..0709835c01ada797f5d11346c6ab37d8fc66a5a7 100644 (file)
@@ -94,6 +94,16 @@ static inline struct dst_entry *ip6_route_output(struct net *net,
        return ip6_route_output_flags(net, sk, fl6, 0);
 }
 
+/* Only conditionally release dst if flags indicates
+ * !RT6_LOOKUP_F_DST_NOREF or dst is in uncached_list.
+ */
+static inline void ip6_rt_put_flags(struct rt6_info *rt, int flags)
+{
+       if (!(flags & RT6_LOOKUP_F_DST_NOREF) ||
+           !list_empty(&rt->rt6i_uncached))
+               ip6_rt_put(rt);
+}
+
 struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
                                   const struct sk_buff *skb, int flags);
 struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
index bcfae13409b51ec3cc1be3a395f2a678fe27ba02..d22b6c140f23155dc13e5da9460028377bf05e09 100644 (file)
@@ -113,14 +113,15 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
                rt = lookup(net, net->ipv6.fib6_local_tbl, fl6, skb, flags);
                if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN)
                        return &rt->dst;
-               ip6_rt_put(rt);
+               ip6_rt_put_flags(rt, flags);
                rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags);
                if (rt->dst.error != -EAGAIN)
                        return &rt->dst;
-               ip6_rt_put(rt);
+               ip6_rt_put_flags(rt, flags);
        }
 
-       dst_hold(&net->ipv6.ip6_null_entry->dst);
+       if (!(flags & RT6_LOOKUP_F_DST_NOREF))
+               dst_hold(&net->ipv6.ip6_null_entry->dst);
        return &net->ipv6.ip6_null_entry->dst;
 }
 
@@ -237,13 +238,14 @@ static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
                        goto out;
        }
 again:
-       ip6_rt_put(rt);
+       ip6_rt_put_flags(rt, flags);
        err = -EAGAIN;
        rt = NULL;
        goto out;
 
 discard_pkt:
-       dst_hold(&rt->dst);
+       if (!(flags & RT6_LOOKUP_F_DST_NOREF))
+               dst_hold(&rt->dst);
 out:
        res->rt6 = rt;
        return err;
index 1d16a01eccf566e1103f269ff0349bf452cc6663..5b1c9b5b9247b6316e941c0837ab5aa41972a694 100644 (file)
@@ -316,9 +316,10 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
 
        rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags);
        if (rt->dst.error == -EAGAIN) {
-               ip6_rt_put(rt);
+               ip6_rt_put_flags(rt, flags);
                rt = net->ipv6.ip6_null_entry;
-               dst_hold(&rt->dst);
+               if (!(flags | RT6_LOOKUP_F_DST_NOREF))
+                       dst_hold(&rt->dst);
        }
 
        return &rt->dst;