Skip to content

Commit

Permalink
ospf6d: Fix External routes ECMP
Browse files Browse the repository at this point in the history
Handle RFC 2328 16.4 Calculating AS external routes with ECMP

For ASBR route, if it is learnt via new LSA and contains
different nexthop list. First lookup route in ospf6 route table
if it exists, merge nexthop list to existing and call the callback
to install into FIB (zebra). Delete created new route as it is
identical to existing entry in route table.

Ticket:CM-16139
Testing Done:
Run two ASBR with 2 ECMP paths from each
DUT neighbor receievs 4 ECMP path to a external route.
ospf6 installs all 4 ECMP path to FIB/RIB

Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
  • Loading branch information
chiragshah6 committed Dec 19, 2017
1 parent acf43be commit 99e3379
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 16 deletions.
10 changes: 9 additions & 1 deletion ospf6d/ospf6_abr.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)

ospf6_route_copy_nexthops(route, abr_entry);


/* (7) If the routes are identical, copy the next hops over to existing
route. ospf6's route table implementation will otherwise string both
routes, but keep the older one as the best route since the routes
Expand All @@ -910,6 +911,12 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)

if (old && (ospf6_route_cmp(route, old) == 0)) {
ospf6_route_merge_nexthops(old, route);

if (is_debug)
zlog_debug("%s: Update route: %s nh count %u",
__PRETTY_FUNCTION__,
buf, listcount(route->nh_list));

/* Update RIB/FIB */
if (table->hook_add)
(*table->hook_add)(old);
Expand All @@ -918,7 +925,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
ospf6_route_delete(route);
} else {
if (is_debug)
zlog_debug("Install route: %s", buf);
zlog_debug("Install route: %s nh count %u",
buf, listcount(route->nh_list));
/* ospf6_ia_add_nw_route (table, &prefix, route); */
ospf6_route_add(route, table);
}
Expand Down
4 changes: 3 additions & 1 deletion ospf6d/ospf6_area.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ static void ospf6_area_lsdb_hook_remove(struct ospf6_lsa *lsa)

static void ospf6_area_route_hook_add(struct ospf6_route *route)
{
struct ospf6_route *copy = ospf6_route_copy(route);
struct ospf6_route *copy;

copy = ospf6_route_copy(route);
ospf6_route_add(copy, ospf6->route_table);
}

Expand Down
193 changes: 184 additions & 9 deletions ospf6d/ospf6_asbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
struct prefix asbr_id;
struct ospf6_route *asbr_entry, *route;
struct ospf6_route *asbr_entry, *route, *old_route;
struct ospf6_path *path, *ecmp_path;
char buf[PREFIX2STR_BUFFER];

external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
Expand Down Expand Up @@ -245,12 +246,105 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)

ospf6_route_copy_nexthops(route, asbr_entry);

path = ospf6_path_dup(&route->path);
ospf6_copy_nexthops(path->nh_list, asbr_entry->nh_list);
listnode_add_sort(route->paths, path);


if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug("AS-External route add: %s", buf);
zlog_debug("%s: AS-External %u route add: %s nh_list %u",
__PRETTY_FUNCTION__,
route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
? 1 : 2, buf, listcount(asbr_entry->nh_list));
}

ospf6_route_add(route, ospf6->route_table);
/* RFC 2328 16.4 (6)
* ECMP: Keep new equal preference path in current
* route's path list, update zebra with new effective
* list along with addition of ECMP path. */
old_route = ospf6_route_lookup(&route->prefix, ospf6->route_table);
if (old_route && (old_route->path.type == route->path.type) &&
old_route->path.cost == route->path.cost) {
struct listnode *anode;
struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh;
struct ospf6_path *o_path = NULL;

/* check if this path exists already in route->paths list
* if it exist simply replace nh_list from asbr_entry */
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) {
if ((o_path->origin.id == route->path.origin.id) &&
(o_path->origin.adv_router ==
route->path.origin.adv_router))
break;
}
/* If path is not found in old_route paths's list,
* add a new path to route paths list and merge
* nexthops in route->path->nh_list.
* Otherwise replace existing path's nh_list */
if (o_path == NULL) {
ecmp_path = ospf6_path_dup(&route->path);

/* Add a nh_list to new ecmp path */
ospf6_copy_nexthops(ecmp_path->nh_list,
asbr_entry->nh_list);
/* Merge nexthop to existing route's nh_list */
ospf6_route_merge_nexthops(old_route, asbr_entry);

/* Update RIB/FIB */
if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)(old_route);

/* Add the new path to route's path list */
listnode_add_sort(old_route->paths, ecmp_path);

if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug("%s: %s Another Path added with nh %u, Paths %u",
__PRETTY_FUNCTION__, buf,
listcount(ecmp_path->nh_list),
listcount(old_route->paths));
}
} else {
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
for (ALL_LIST_ELEMENTS(old_route->nh_list,
rnode, rnext, rnh)) {
if (!ospf6_nexthop_is_same(rnh, nh))
continue;

listnode_delete(old_route->nh_list,
rnh);
ospf6_nexthop_delete(rnh);
}
}
list_delete_all_node(o_path->nh_list);
ospf6_copy_nexthops(o_path->nh_list,
asbr_entry->nh_list);

/* Merge nexthop to existing route's nh_list */
ospf6_route_merge_nexthops(old_route,
asbr_entry);

if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug("%s: existing route with effective nh count %u",
__PRETTY_FUNCTION__,
listcount(old_route->nh_list));
}

/* Update RIB/FIB */
if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)(old_route);

}
/* Delete the new route */
ospf6_route_delete(route);
} else {
/* Add the new route to ospf6 instance route table. */
ospf6_route_add(route, ospf6->route_table);
}
}

void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
Expand Down Expand Up @@ -291,16 +385,97 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
nroute = ospf6_route_next(route);
if (route->type != OSPF6_DEST_TYPE_NETWORK)
continue;
if (route->path.origin.type != lsa->header->type)
continue;
if (route->path.origin.id != lsa->header->id)
continue;
if (route->path.origin.adv_router != lsa->header->adv_router)

/* Route has multiple ECMP paths remove,
* matching path and update effective route's nh list. */
if (listcount(route->paths) > 1) {
struct listnode *anode, *anext;
struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh;
struct ospf6_path *o_path;
bool nh_updated = false;

/* Iterate all paths of route to find maching with LSA
* remove from route path list. If route->path is same,
* replace from paths list. */
for (ALL_LIST_ELEMENTS(route->paths, anode, anext,
o_path)) {
if (o_path->origin.type != lsa->header->type)
continue;
if (o_path->origin.id != lsa->header->id)
continue;
if (o_path->origin.adv_router !=
lsa->header->adv_router)
continue;

if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&prefix, buf, sizeof(buf));
zlog_debug(
"%s: route %s path found with nh %u",
__PRETTY_FUNCTION__, buf,
listcount(o_path->nh_list));
}

/* Remove found path's nh_list from
* the route's nh_list */
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
nnode, nh)) {
for (ALL_LIST_ELEMENTS(route->nh_list,
rnode, rnext, rnh)) {
if (!ospf6_nexthop_is_same(rnh,
nh))
continue;
listnode_delete(route->nh_list,
rnh);
ospf6_nexthop_delete(rnh);
}
}
/* Delete the path from route's path list */
listnode_delete(route->paths, o_path);
ospf6_path_free(o_path);
nh_updated = true;
}

if (nh_updated) {
/* Update RIB/FIB w/ effective nh_list */
if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)(route);

/* route's path is similar to lsa header,
* replace route's path with route's
* paths list head. */
if (route->path.origin.id == lsa->header->id &&
route->path.origin.adv_router ==
lsa->header->adv_router) {
struct ospf6_path *h_path;

h_path = (struct ospf6_path *)
listgetdata(listhead(route->paths));
route->path.origin.type =
h_path->origin.type;
route->path.origin.id =
h_path->origin.id;
route->path.origin.adv_router =
h_path->origin.adv_router;
}
}
continue;

} else {
if (route->path.origin.type != lsa->header->type)
continue;
if (route->path.origin.id != lsa->header->id)
continue;
if (route->path.origin.adv_router !=
lsa->header->adv_router)
continue;
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug("AS-External route remove: %s", buf);
zlog_debug("%s: AS-External %u route remove: %s",
__PRETTY_FUNCTION__,
route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
? 1 : 2, buf);
}
ospf6_route_remove(route, ospf6->route_table);
}
Expand Down
3 changes: 2 additions & 1 deletion ospf6d/ospf6_intra.c
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)

if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug(" route %s add", buf);
zlog_debug(" route %s add with nh count %u", buf,
listcount(route->nh_list));
}

ospf6_route_add(route, oa->route_table);
Expand Down
1 change: 1 addition & 0 deletions ospf6d/ospf6_memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ DEFINE_MTYPE(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex")
DEFINE_MTYPE(OSPF6D, OSPF6_SPFTREE, "OSPF6 SPF tree")
DEFINE_MTYPE(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop")
DEFINE_MTYPE(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info")
DEFINE_MTYPE(OSPF6D, OSPF6_PATH, "OSPF6 Path")
DEFINE_MTYPE(OSPF6D, OSPF6_OTHER, "OSPF6 other")
1 change: 1 addition & 0 deletions ospf6d/ospf6_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ DECLARE_MTYPE(OSPF6_VERTEX)
DECLARE_MTYPE(OSPF6_SPFTREE)
DECLARE_MTYPE(OSPF6_NEXTHOP)
DECLARE_MTYPE(OSPF6_EXTERNAL_INFO)
DECLARE_MTYPE(OSPF6_PATH)
DECLARE_MTYPE(OSPF6_OTHER)

#endif /* _QUAGGA_OSPF6_MEMORY_H */
49 changes: 45 additions & 4 deletions ospf6d/ospf6_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ void ospf6_copy_nexthops(struct list *dst, struct list *src)
if (ospf6_nexthop_is_set(nh)) {
nh_new = ospf6_nexthop_create();
ospf6_nexthop_copy(nh_new, nh);
listnode_add(dst, nh_new);
listnode_add_sort(dst, nh_new);
}
}
}
Expand All @@ -231,7 +231,7 @@ void ospf6_merge_nexthops(struct list *dst, struct list *src)
if (!ospf6_route_find_nexthop(dst, nh)) {
nh_new = ospf6_nexthop_create();
ospf6_nexthop_copy(nh_new, nh);
listnode_add(dst, nh_new);
listnode_add_sort(dst, nh_new);
}
}
}
Expand Down Expand Up @@ -338,7 +338,7 @@ int ospf6_route_get_first_nh_index(struct ospf6_route *route)
return (-1);
}

static int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b)
int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b)
{
if (a->ifindex < b->ifindex)
return -1;
Expand All @@ -351,13 +351,46 @@ static int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b)
return 0;
}

static int ospf6_path_cmp(struct ospf6_path *a, struct ospf6_path *b)
{
if (a->origin.adv_router < b->origin.adv_router)
return -1;
else if (a->origin.adv_router > b->origin.adv_router)
return 1;
else
return 0;
}

void ospf6_path_free(struct ospf6_path *op)
{
if (op->nh_list)
list_delete_and_null(&op->nh_list);
XFREE(MTYPE_OSPF6_PATH, op);
}

struct ospf6_path *ospf6_path_dup(struct ospf6_path *path)
{
struct ospf6_path *new;

new = XCALLOC(MTYPE_OSPF6_PATH, sizeof(struct ospf6_path));
memcpy(new, path, sizeof(struct ospf6_path));
new->nh_list = list_new();
new->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp;
new->nh_list->del = (void (*) (void *))ospf6_nexthop_delete;

return new;
}

struct ospf6_route *ospf6_route_create(void)
{
struct ospf6_route *route;
route = XCALLOC(MTYPE_OSPF6_ROUTE, sizeof(struct ospf6_route));
route->nh_list = list_new();
route->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp;
route->nh_list->del = (void (*) (void *))ospf6_nexthop_delete;
route->paths = list_new();
route->paths->cmp = (int (*)(void *, void *))ospf6_path_cmp;
route->paths->del = (void (*)(void *))ospf6_path_free;
return route;
}

Expand All @@ -366,6 +399,8 @@ void ospf6_route_delete(struct ospf6_route *route)
if (route) {
if (route->nh_list)
list_delete_and_null(&route->nh_list);
if (route->paths)
list_delete_and_null(&route->paths);
XFREE(MTYPE_OSPF6_ROUTE, route);
}
}
Expand Down Expand Up @@ -464,7 +499,13 @@ ospf6_route_lookup_identical(struct ospf6_route *route,

for (target = ospf6_route_lookup(&route->prefix, table); target;
target = target->next) {
if (ospf6_route_is_identical(target, route))
if (target->type == route->type &&
(memcmp(&target->prefix, &route->prefix,
sizeof(struct prefix)) == 0) &&
target->path.type == route->path.type &&
target->path.cost == route->path.cost &&
target->path.u.cost_e2 == route->path.u.cost_e2 &&
ospf6_route_cmp_nexthops(target, route) == 0)
return target;
}
return NULL;
Expand Down
Loading

0 comments on commit 99e3379

Please sign in to comment.