aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_fib.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r--net/ipv6/ip6_fib.c32
1 files changed, 26 insertions, 6 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 667b1b1ea25d..11f9660a4796 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -80,6 +80,7 @@ static DEFINE_RWLOCK(fib6_walker_lock);
80#endif 80#endif
81 81
82static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt); 82static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt);
83static struct rt6_info * fib6_find_prefix(struct fib6_node *fn);
83static struct fib6_node * fib6_repair_tree(struct fib6_node *fn); 84static struct fib6_node * fib6_repair_tree(struct fib6_node *fn);
84static int fib6_walk(struct fib6_walker_t *w); 85static int fib6_walk(struct fib6_walker_t *w);
85static int fib6_walk_continue(struct fib6_walker_t *w); 86static int fib6_walk_continue(struct fib6_walker_t *w);
@@ -697,7 +698,7 @@ void fib6_force_start_gc(void)
697 698
698int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) 699int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
699{ 700{
700 struct fib6_node *fn; 701 struct fib6_node *fn, *pn = NULL;
701 int err = -ENOMEM; 702 int err = -ENOMEM;
702 703
703 fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), 704 fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
@@ -706,6 +707,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
706 if (fn == NULL) 707 if (fn == NULL)
707 goto out; 708 goto out;
708 709
710 pn = fn;
711
709#ifdef CONFIG_IPV6_SUBTREES 712#ifdef CONFIG_IPV6_SUBTREES
710 if (rt->rt6i_src.plen) { 713 if (rt->rt6i_src.plen) {
711 struct fib6_node *sn; 714 struct fib6_node *sn;
@@ -751,10 +754,6 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
751 /* Now link new subtree to main tree */ 754 /* Now link new subtree to main tree */
752 sfn->parent = fn; 755 sfn->parent = fn;
753 fn->subtree = sfn; 756 fn->subtree = sfn;
754 if (fn->leaf == NULL) {
755 fn->leaf = rt;
756 atomic_inc(&rt->rt6i_ref);
757 }
758 } else { 757 } else {
759 sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, 758 sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
760 sizeof(struct in6_addr), rt->rt6i_src.plen, 759 sizeof(struct in6_addr), rt->rt6i_src.plen,
@@ -764,6 +763,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
764 goto st_failure; 763 goto st_failure;
765 } 764 }
766 765
766 if (fn->leaf == NULL) {
767 fn->leaf = rt;
768 atomic_inc(&rt->rt6i_ref);
769 }
767 fn = sn; 770 fn = sn;
768 } 771 }
769#endif 772#endif
@@ -777,8 +780,25 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
777 } 780 }
778 781
779out: 782out:
780 if (err) 783 if (err) {
784#ifdef CONFIG_IPV6_SUBTREES
785 /*
786 * If fib6_add_1 has cleared the old leaf pointer in the
787 * super-tree leaf node we have to find a new one for it.
788 */
789 if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) {
790 pn->leaf = fib6_find_prefix(pn);
791#if RT6_DEBUG >= 2
792 if (!pn->leaf) {
793 BUG_TRAP(pn->leaf != NULL);
794 pn->leaf = &ip6_null_entry;
795 }
796#endif
797 atomic_inc(&pn->leaf->rt6i_ref);
798 }
799#endif
781 dst_free(&rt->u.dst); 800 dst_free(&rt->u.dst);
801 }
782 return err; 802 return err;
783 803
784#ifdef CONFIG_IPV6_SUBTREES 804#ifdef CONFIG_IPV6_SUBTREES