aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-05-20 12:02:26 -0400
committerDavid S. Miller <davem@davemloft.net>2015-05-20 12:02:26 -0400
commit7764b9dd38f31ce58e6f764e2d7d8399d9b62486 (patch)
tree2151136cf03e0dadcff04de695f10f91633acf4f
parent892bd6291ac122e3cdb354ef39567c9127768916 (diff)
parent27596472473a02cfef2908a6bcda7e55264ba6b7 (diff)
Merge branch 'ipv6_ecmp_fixes'
Michal Kubecek says: ==================== IPv6 ECMP route add/replace fixes (1) When adding a nexthop of a multipath route fails (e.g. because of a conflict with an existing route), we are supposed to delete nexthops already added. However, currently we try to also delete all nexthops we haven't even tried to add yet so that a "ip route add" command can actually remove pre-existing routes if it fails. (2) Attempt to replace a multipath route results in a broken siblings linked list. Following commands (like "ip route del") can then either follow a link into freed memory or end in an infinite loop (if the slab object has been reused). v2: fix an omission in first patch v3: change the semantics of replace operation to better match IPv4 ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv6/ip6_fib.c39
-rw-r--r--net/ipv6/route.c14
2 files changed, 46 insertions, 7 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 96dbffff5a24..bde57b113009 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -693,6 +693,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
693{ 693{
694 struct rt6_info *iter = NULL; 694 struct rt6_info *iter = NULL;
695 struct rt6_info **ins; 695 struct rt6_info **ins;
696 struct rt6_info **fallback_ins = NULL;
696 int replace = (info->nlh && 697 int replace = (info->nlh &&
697 (info->nlh->nlmsg_flags & NLM_F_REPLACE)); 698 (info->nlh->nlmsg_flags & NLM_F_REPLACE));
698 int add = (!info->nlh || 699 int add = (!info->nlh ||
@@ -716,8 +717,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
716 (info->nlh->nlmsg_flags & NLM_F_EXCL)) 717 (info->nlh->nlmsg_flags & NLM_F_EXCL))
717 return -EEXIST; 718 return -EEXIST;
718 if (replace) { 719 if (replace) {
719 found++; 720 if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
720 break; 721 found++;
722 break;
723 }
724 if (rt_can_ecmp)
725 fallback_ins = fallback_ins ?: ins;
726 goto next_iter;
721 } 727 }
722 728
723 if (iter->dst.dev == rt->dst.dev && 729 if (iter->dst.dev == rt->dst.dev &&
@@ -753,9 +759,17 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
753 if (iter->rt6i_metric > rt->rt6i_metric) 759 if (iter->rt6i_metric > rt->rt6i_metric)
754 break; 760 break;
755 761
762next_iter:
756 ins = &iter->dst.rt6_next; 763 ins = &iter->dst.rt6_next;
757 } 764 }
758 765
766 if (fallback_ins && !found) {
767 /* No ECMP-able route found, replace first non-ECMP one */
768 ins = fallback_ins;
769 iter = *ins;
770 found++;
771 }
772
759 /* Reset round-robin state, if necessary */ 773 /* Reset round-robin state, if necessary */
760 if (ins == &fn->leaf) 774 if (ins == &fn->leaf)
761 fn->rr_ptr = NULL; 775 fn->rr_ptr = NULL;
@@ -815,6 +829,8 @@ add:
815 } 829 }
816 830
817 } else { 831 } else {
832 int nsiblings;
833
818 if (!found) { 834 if (!found) {
819 if (add) 835 if (add)
820 goto add; 836 goto add;
@@ -835,8 +851,27 @@ add:
835 info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 851 info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
836 fn->fn_flags |= RTN_RTINFO; 852 fn->fn_flags |= RTN_RTINFO;
837 } 853 }
854 nsiblings = iter->rt6i_nsiblings;
838 fib6_purge_rt(iter, fn, info->nl_net); 855 fib6_purge_rt(iter, fn, info->nl_net);
839 rt6_release(iter); 856 rt6_release(iter);
857
858 if (nsiblings) {
859 /* Replacing an ECMP route, remove all siblings */
860 ins = &rt->dst.rt6_next;
861 iter = *ins;
862 while (iter) {
863 if (rt6_qualify_for_ecmp(iter)) {
864 *ins = iter->dst.rt6_next;
865 fib6_purge_rt(iter, fn, info->nl_net);
866 rt6_release(iter);
867 nsiblings--;
868 } else {
869 ins = &iter->dst.rt6_next;
870 }
871 iter = *ins;
872 }
873 WARN_ON(nsiblings != 0);
874 }
840 } 875 }
841 876
842 return 0; 877 return 0;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index d3588885f097..c73ae5039e46 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2504,9 +2504,9 @@ static int ip6_route_multipath(struct fib6_config *cfg, int add)
2504 int attrlen; 2504 int attrlen;
2505 int err = 0, last_err = 0; 2505 int err = 0, last_err = 0;
2506 2506
2507 remaining = cfg->fc_mp_len;
2507beginning: 2508beginning:
2508 rtnh = (struct rtnexthop *)cfg->fc_mp; 2509 rtnh = (struct rtnexthop *)cfg->fc_mp;
2509 remaining = cfg->fc_mp_len;
2510 2510
2511 /* Parse a Multipath Entry */ 2511 /* Parse a Multipath Entry */
2512 while (rtnh_ok(rtnh, remaining)) { 2512 while (rtnh_ok(rtnh, remaining)) {
@@ -2536,15 +2536,19 @@ beginning:
2536 * next hops that have been already added. 2536 * next hops that have been already added.
2537 */ 2537 */
2538 add = 0; 2538 add = 0;
2539 remaining = cfg->fc_mp_len - remaining;
2539 goto beginning; 2540 goto beginning;
2540 } 2541 }
2541 } 2542 }
2542 /* Because each route is added like a single route we remove 2543 /* Because each route is added like a single route we remove
2543 * this flag after the first nexthop (if there is a collision, 2544 * these flags after the first nexthop: if there is a collision,
2544 * we have already fail to add the first nexthop: 2545 * we have already failed to add the first nexthop:
2545 * fib6_add_rt2node() has reject it). 2546 * fib6_add_rt2node() has rejected it; when replacing, old
2547 * nexthops have been replaced by first new, the rest should
2548 * be added to it.
2546 */ 2549 */
2547 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL; 2550 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
2551 NLM_F_REPLACE);
2548 rtnh = rtnh_next(rtnh, &remaining); 2552 rtnh = rtnh_next(rtnh, &remaining);
2549 } 2553 }
2550 2554