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>
This commit is contained in:
Коммит
7764b9dd38
|
@ -693,6 +693,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
|
|||
{
|
||||
struct rt6_info *iter = NULL;
|
||||
struct rt6_info **ins;
|
||||
struct rt6_info **fallback_ins = NULL;
|
||||
int replace = (info->nlh &&
|
||||
(info->nlh->nlmsg_flags & NLM_F_REPLACE));
|
||||
int add = (!info->nlh ||
|
||||
|
@ -716,8 +717,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
|
|||
(info->nlh->nlmsg_flags & NLM_F_EXCL))
|
||||
return -EEXIST;
|
||||
if (replace) {
|
||||
found++;
|
||||
break;
|
||||
if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
if (rt_can_ecmp)
|
||||
fallback_ins = fallback_ins ?: ins;
|
||||
goto next_iter;
|
||||
}
|
||||
|
||||
if (iter->dst.dev == rt->dst.dev &&
|
||||
|
@ -753,9 +759,17 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
|
|||
if (iter->rt6i_metric > rt->rt6i_metric)
|
||||
break;
|
||||
|
||||
next_iter:
|
||||
ins = &iter->dst.rt6_next;
|
||||
}
|
||||
|
||||
if (fallback_ins && !found) {
|
||||
/* No ECMP-able route found, replace first non-ECMP one */
|
||||
ins = fallback_ins;
|
||||
iter = *ins;
|
||||
found++;
|
||||
}
|
||||
|
||||
/* Reset round-robin state, if necessary */
|
||||
if (ins == &fn->leaf)
|
||||
fn->rr_ptr = NULL;
|
||||
|
@ -815,6 +829,8 @@ add:
|
|||
}
|
||||
|
||||
} else {
|
||||
int nsiblings;
|
||||
|
||||
if (!found) {
|
||||
if (add)
|
||||
goto add;
|
||||
|
@ -835,8 +851,27 @@ add:
|
|||
info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
|
||||
fn->fn_flags |= RTN_RTINFO;
|
||||
}
|
||||
nsiblings = iter->rt6i_nsiblings;
|
||||
fib6_purge_rt(iter, fn, info->nl_net);
|
||||
rt6_release(iter);
|
||||
|
||||
if (nsiblings) {
|
||||
/* Replacing an ECMP route, remove all siblings */
|
||||
ins = &rt->dst.rt6_next;
|
||||
iter = *ins;
|
||||
while (iter) {
|
||||
if (rt6_qualify_for_ecmp(iter)) {
|
||||
*ins = iter->dst.rt6_next;
|
||||
fib6_purge_rt(iter, fn, info->nl_net);
|
||||
rt6_release(iter);
|
||||
nsiblings--;
|
||||
} else {
|
||||
ins = &iter->dst.rt6_next;
|
||||
}
|
||||
iter = *ins;
|
||||
}
|
||||
WARN_ON(nsiblings != 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2504,9 +2504,9 @@ static int ip6_route_multipath(struct fib6_config *cfg, int add)
|
|||
int attrlen;
|
||||
int err = 0, last_err = 0;
|
||||
|
||||
remaining = cfg->fc_mp_len;
|
||||
beginning:
|
||||
rtnh = (struct rtnexthop *)cfg->fc_mp;
|
||||
remaining = cfg->fc_mp_len;
|
||||
|
||||
/* Parse a Multipath Entry */
|
||||
while (rtnh_ok(rtnh, remaining)) {
|
||||
|
@ -2536,15 +2536,19 @@ beginning:
|
|||
* next hops that have been already added.
|
||||
*/
|
||||
add = 0;
|
||||
remaining = cfg->fc_mp_len - remaining;
|
||||
goto beginning;
|
||||
}
|
||||
}
|
||||
/* Because each route is added like a single route we remove
|
||||
* this flag after the first nexthop (if there is a collision,
|
||||
* we have already fail to add the first nexthop:
|
||||
* fib6_add_rt2node() has reject it).
|
||||
* these flags after the first nexthop: if there is a collision,
|
||||
* we have already failed to add the first nexthop:
|
||||
* fib6_add_rt2node() has rejected it; when replacing, old
|
||||
* nexthops have been replaced by first new, the rest should
|
||||
* be added to it.
|
||||
*/
|
||||
cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL;
|
||||
cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
|
||||
NLM_F_REPLACE);
|
||||
rtnh = rtnh_next(rtnh, &remaining);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче