Skip to content

Commit 7764b9d

Browse files
committed
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>
2 parents 892bd62 + 2759647 commit 7764b9d

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

net/ipv6/ip6_fib.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
693693
{
694694
struct rt6_info *iter = NULL;
695695
struct rt6_info **ins;
696+
struct rt6_info **fallback_ins = NULL;
696697
int replace = (info->nlh &&
697698
(info->nlh->nlmsg_flags & NLM_F_REPLACE));
698699
int add = (!info->nlh ||
@@ -716,8 +717,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
716717
(info->nlh->nlmsg_flags & NLM_F_EXCL))
717718
return -EEXIST;
718719
if (replace) {
719-
found++;
720-
break;
720+
if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
721+
found++;
722+
break;
723+
}
724+
if (rt_can_ecmp)
725+
fallback_ins = fallback_ins ?: ins;
726+
goto next_iter;
721727
}
722728

723729
if (iter->dst.dev == rt->dst.dev &&
@@ -753,9 +759,17 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
753759
if (iter->rt6i_metric > rt->rt6i_metric)
754760
break;
755761

762+
next_iter:
756763
ins = &iter->dst.rt6_next;
757764
}
758765

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+
759773
/* Reset round-robin state, if necessary */
760774
if (ins == &fn->leaf)
761775
fn->rr_ptr = NULL;
@@ -815,6 +829,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
815829
}
816830

817831
} else {
832+
int nsiblings;
833+
818834
if (!found) {
819835
if (add)
820836
goto add;
@@ -835,8 +851,27 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
835851
info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
836852
fn->fn_flags |= RTN_RTINFO;
837853
}
854+
nsiblings = iter->rt6i_nsiblings;
838855
fib6_purge_rt(iter, fn, info->nl_net);
839856
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+
}
840875
}
841876

842877
return 0;

net/ipv6/route.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,9 +2504,9 @@ static int ip6_route_multipath(struct fib6_config *cfg, int add)
25042504
int attrlen;
25052505
int err = 0, last_err = 0;
25062506

2507+
remaining = cfg->fc_mp_len;
25072508
beginning:
25082509
rtnh = (struct rtnexthop *)cfg->fc_mp;
2509-
remaining = cfg->fc_mp_len;
25102510

25112511
/* Parse a Multipath Entry */
25122512
while (rtnh_ok(rtnh, remaining)) {
@@ -2536,15 +2536,19 @@ static int ip6_route_multipath(struct fib6_config *cfg, int add)
25362536
* next hops that have been already added.
25372537
*/
25382538
add = 0;
2539+
remaining = cfg->fc_mp_len - remaining;
25392540
goto beginning;
25402541
}
25412542
}
25422543
/* Because each route is added like a single route we remove
2543-
* this flag after the first nexthop (if there is a collision,
2544-
* we have already fail to add the first nexthop:
2545-
* fib6_add_rt2node() has reject it).
2544+
* these flags after the first nexthop: if there is a collision,
2545+
* we have already failed to add the first nexthop:
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.
25462549
*/
2547-
cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL;
2550+
cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
2551+
NLM_F_REPLACE);
25482552
rtnh = rtnh_next(rtnh, &remaining);
25492553
}
25502554

0 commit comments

Comments
 (0)