ipv6: Avoid double dst_free
[cascardo/linux.git] / net / ipv6 / ip6_fib.c
index 418d982..e68350b 100644 (file)
@@ -933,6 +933,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
        int replace_required = 0;
        int sernum = fib6_new_sernum(info->nl_net);
 
+       if (WARN_ON_ONCE((rt->dst.flags & DST_NOCACHE) &&
+                        !atomic_read(&rt->dst.__refcnt)))
+               return -EINVAL;
+
        if (info->nlh) {
                if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
                        allow_create = 0;
@@ -1025,6 +1029,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
                fib6_start_gc(info->nl_net, rt);
                if (!(rt->rt6i_flags & RTF_CACHE))
                        fib6_prune_clones(info->nl_net, pn);
+               rt->dst.flags &= ~DST_NOCACHE;
        }
 
 out:
@@ -1049,7 +1054,8 @@ out:
                        atomic_inc(&pn->leaf->rt6i_ref);
                }
 #endif
-               dst_free(&rt->dst);
+               if (!(rt->dst.flags & DST_NOCACHE))
+                       dst_free(&rt->dst);
        }
        return err;
 
@@ -1060,7 +1066,8 @@ out:
 st_failure:
        if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
                fib6_repair_tree(info->nl_net, fn);
-       dst_free(&rt->dst);
+       if (!(rt->dst.flags & DST_NOCACHE))
+               dst_free(&rt->dst);
        return err;
 #endif
 }