ipv6: add support of equal cost multipath (ECMP)

Each nexthop is added like a single route in the routing table. All routes
that have the same metric/weight and destination but not the same gateway
are considering as ECMP routes. They are linked together, through a list called
rt6i_siblings.

ECMP routes can be added in one shot, with RTA_MULTIPATH attribute or one after
the other (in both case, the flag NLM_F_EXCL should not be set).

The patch is based on a previous work from
Luc Saillard <luc.saillard@6wind.com>.

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Nicolas Dichtel
2012-10-22 03:42:09 +00:00
committed by David S. Miller
parent d94ce9b283
commit 51ebd31815
3 changed files with 200 additions and 3 deletions

View File

@@ -672,6 +672,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
iter->rt6i_idev == rt->rt6i_idev &&
ipv6_addr_equal(&iter->rt6i_gateway,
&rt->rt6i_gateway)) {
if (rt->rt6i_nsiblings)
rt->rt6i_nsiblings = 0;
if (!(iter->rt6i_flags & RTF_EXPIRES))
return -EEXIST;
if (!(rt->rt6i_flags & RTF_EXPIRES))
@@ -680,6 +682,21 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
rt6_set_expires(iter, rt->dst.expires);
return -EEXIST;
}
/* If we have the same destination and the same metric,
* but not the same gateway, then the route we try to
* add is sibling to this route, increment our counter
* of siblings, and later we will add our route to the
* list.
* Only static routes (which don't have flag
* RTF_EXPIRES) are used for ECMPv6.
*
* To avoid long list, we only had siblings if the
* route have a gateway.
*/
if (rt->rt6i_flags & RTF_GATEWAY &&
!(rt->rt6i_flags & RTF_EXPIRES) &&
!(iter->rt6i_flags & RTF_EXPIRES))
rt->rt6i_nsiblings++;
}
if (iter->rt6i_metric > rt->rt6i_metric)
@@ -692,6 +709,35 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
if (ins == &fn->leaf)
fn->rr_ptr = NULL;
/* Link this route to others same route. */
if (rt->rt6i_nsiblings) {
unsigned int rt6i_nsiblings;
struct rt6_info *sibling, *temp_sibling;
/* Find the first route that have the same metric */
sibling = fn->leaf;
while (sibling) {
if (sibling->rt6i_metric == rt->rt6i_metric) {
list_add_tail(&rt->rt6i_siblings,
&sibling->rt6i_siblings);
break;
}
sibling = sibling->dst.rt6_next;
}
/* For each sibling in the list, increment the counter of
* siblings. BUG() if counters does not match, list of siblings
* is broken!
*/
rt6i_nsiblings = 0;
list_for_each_entry_safe(sibling, temp_sibling,
&rt->rt6i_siblings, rt6i_siblings) {
sibling->rt6i_nsiblings++;
BUG_ON(sibling->rt6i_nsiblings != rt->rt6i_nsiblings);
rt6i_nsiblings++;
}
BUG_ON(rt6i_nsiblings != rt->rt6i_nsiblings);
}
/*
* insert node
*/
@@ -1193,6 +1239,17 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
if (fn->rr_ptr == rt)
fn->rr_ptr = NULL;
/* Remove this entry from other siblings */
if (rt->rt6i_nsiblings) {
struct rt6_info *sibling, *next_sibling;
list_for_each_entry_safe(sibling, next_sibling,
&rt->rt6i_siblings, rt6i_siblings)
sibling->rt6i_nsiblings--;
rt->rt6i_nsiblings = 0;
list_del_init(&rt->rt6i_siblings);
}
/* Adjust walkers */
read_lock(&fib6_walker_lock);
FOR_WALKERS(w) {