ospf6d: fix intra prefix ecmp

When there are multiple advertisers/paths to reach
Intra-Prefix route, if any path is removed,
re-added back, it does add to the same route node.

ospf6 intra prefix, first stored in oa->route_table
then as part of add cb, it would add to ospf6->route_table
which adds to FIB.

When copying a route with its paths & NHs from oa->table to
ospf6->table the path origin should not be modified otherwise
ospf6->table would not find existing node rather it appends
new node.

Use spf_table to fetch nexthops for a given advertisers/path,
to form effective nexthop list for a route.

Ticket:CM-16139
Testing Done:
    R2 ---- R3
     |       |
    R1 ---- R4
Inject Intra Ara Prefix LSA from R1 & R3, validate R2 and R4
having two origination point/paths to reach for a route.
Trigger link flap, frr restart or remove/readd R3's INP one of the
injection point.
Remove link between R4 to R1 and validate R3 carrying single
path to reach prefix.

Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
This commit is contained in:
Chirag Shah 2018-04-23 15:21:33 -07:00
parent 4fb6f51928
commit 6942698da4
3 changed files with 253 additions and 229 deletions

View File

@ -1314,17 +1314,60 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
return 0;
}
static void ospf6_intra_prefix_update_route_origin(struct ospf6_route *oa_route)
{
struct ospf6_path *h_path;
struct ospf6_route *g_route, *nroute;
/* Update Global ospf6 route path */
g_route = ospf6_route_lookup(&oa_route->prefix,
ospf6->route_table);
for (ospf6_route_lock(g_route); g_route &&
ospf6_route_is_prefix(&oa_route->prefix, g_route);
g_route = nroute) {
nroute = ospf6_route_next(g_route);
if (g_route->type != oa_route->type)
continue;
if (g_route->path.area_id != oa_route->path.area_id)
continue;
if (g_route->path.type != OSPF6_PATH_TYPE_INTRA)
continue;
if (g_route->path.cost != oa_route->path.cost)
continue;
if (ospf6_route_is_same_origin(g_route, oa_route)) {
h_path = (struct ospf6_path *)listgetdata(
listhead(g_route->paths));
g_route->path.origin.type = h_path->origin.type;
g_route->path.origin.id = h_path->origin.id;
g_route->path.origin.adv_router =
h_path->origin.adv_router;
break;
}
}
h_path = (struct ospf6_path *)listgetdata(
listhead(oa_route->paths));
oa_route->path.origin.type = h_path->origin.type;
oa_route->path.origin.id = h_path->origin.id;
oa_route->path.origin.adv_router = h_path->origin.adv_router;
}
void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
struct ospf6_route *old,
struct ospf6_route *route)
{
struct ospf6_route *old_route;
struct ospf6_route *old_route, *ls_entry;
struct ospf6_path *ecmp_path, *o_path = NULL;
struct listnode *anode, *anext;
struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh;
char buf[PREFIX2STR_BUFFER];
bool route_found = false;
struct interface *ifp;
struct ospf6_lsa *lsa;
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
/* check for old entry match with new route origin,
* delete old entry.
@ -1361,7 +1404,7 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
o_path->cost, route->path.cost);
}
/* Remove selected current rout path's nh from
/* Remove selected current path's nh from
* effective nh list.
*/
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
@ -1385,22 +1428,6 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
* Update FIB with effective NHs.
*/
if (listcount(old_route->paths)) {
if (old_route->path.origin.id ==
route->path.origin.id &&
old_route->path.origin.adv_router ==
route->path.origin.adv_router) {
struct ospf6_path *h_path;
h_path = (struct ospf6_path *)
listgetdata(listhead(old_route->paths));
old_route->path.origin.type =
h_path->origin.type;
old_route->path.origin.id =
h_path->origin.id;
old_route->path.origin.adv_router =
h_path->origin.adv_router;
}
if (route_updated) {
for (ALL_LIST_ELEMENTS(old_route->paths,
anode, anext, o_path)) {
@ -1415,6 +1442,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)
(old_route);
if (old_route->path.origin.id ==
route->path.origin.id &&
old_route->path.origin.adv_router ==
route->path.origin.adv_router) {
ospf6_intra_prefix_update_route_origin(
old_route);
}
break;
}
} else {
@ -1426,8 +1461,12 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
old_route->path.cost,
route->path.cost);
}
ospf6_route_remove(old_route,
if (oa->route_table->hook_remove)
ospf6_route_remove(old_route,
oa->route_table);
else
SET_FLAG(old_route->flag,
OSPF6_ROUTE_REMOVE);
break;
}
}
@ -1467,72 +1506,101 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
/* Add a nh_list to new ecmp path */
ospf6_copy_nexthops(ecmp_path->nh_list,
route->nh_list);
/* Merge nexthop to existing route's nh_list */
ospf6_route_merge_nexthops(old_route, route);
/* Add the new path to route's path list */
listnode_add_sort(old_route->paths, ecmp_path);
UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE);
/* Update RIB/FIB */
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)
(old_route);
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf,
sizeof(buf));
zlog_debug("%s: route %s %p another path added with nh %u, effective paths %u nh %u",
zlog_debug(
"%s: route %s %p another path added with nh %u, effective paths %u nh %u",
__PRETTY_FUNCTION__, buf,
(void *)old_route,
listcount(ecmp_path->nh_list),
old_route->paths ?
listcount(old_route->paths)
: 0,
listcount(old_route->paths) : 0,
listcount(old_route->nh_list));
}
} 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,
route->nh_list);
/* Merge nexthop to existing route's nh_list */
ospf6_route_merge_nexthops(old_route,
route);
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix,
buf, sizeof(buf));
zlog_debug("%s: existing route %s %p with effective paths %u nh count %u",
__PRETTY_FUNCTION__, buf,
(void *)old_route,
listcount(old_route->paths),
old_route->nh_list ?
listcount(old_route->nh_list)
: 0);
}
UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE);
/* Update ospf6 route table and RIB/FIB */
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)
(old_route);
}
list_delete_all_node(old_route->nh_list);
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) {
ls_entry = ospf6_route_lookup(
&o_path->ls_prefix,
oa->spf_table);
if (ls_entry == NULL) {
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
zlog_debug("%s: ls_prfix %s ls_entry not found.",
__PRETTY_FUNCTION__,
buf);
continue;
}
lsa = ospf6_lsdb_lookup(o_path->origin.type,
o_path->origin.id,
o_path->origin.adv_router,
oa->lsdb);
if (lsa == NULL) {
if (IS_OSPF6_DEBUG_EXAMIN(
INTRA_PREFIX)) {
struct prefix adv_prefix;
ospf6_linkstate_prefix(
o_path->origin.adv_router,
o_path->origin.id, &adv_prefix);
prefix2str(&adv_prefix, buf,
sizeof(buf));
zlog_debug("%s: adv_router %s lsa not found",
__PRETTY_FUNCTION__,
buf);
}
continue;
}
intra_prefix_lsa =
(struct ospf6_intra_prefix_lsa *)
OSPF6_LSA_HEADER_END(lsa->header);
if (intra_prefix_lsa->ref_adv_router
== oa->ospf6->router_id) {
ifp = if_lookup_prefix(
&old_route->prefix,
VRF_DEFAULT);
if (ifp)
ospf6_route_add_nexthop(
old_route,
ifp->ifindex,
NULL);
} else {
ospf6_route_merge_nexthops(old_route,
ls_entry);
}
}
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug("%s: route %s %p with final effective paths %u nh%u",
__PRETTY_FUNCTION__, buf,
(void *)old_route,
old_route->paths ?
listcount(old_route->paths) : 0,
listcount(old_route->nh_list));
}
/* used in intra_route_calculation() to add to
* global ospf6 route table.
*/
UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
SET_FLAG(old_route->flag, OSPF6_ROUTE_ADD);
/* Update ospf6 route table and RIB/FIB */
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)(old_route);
/* Delete the new route its info added to existing
* route.
*/
@ -1642,7 +1710,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
route->path.metric_type = 1;
route->path.cost =
ls_entry->path.cost + ntohs(op->prefix_metric);
memcpy(&route->path.ls_prefix, &ls_prefix,
sizeof(struct prefix));
if (direct_connect) {
ifp = if_lookup_prefix(&route->prefix, VRF_DEFAULT);
if (ifp)
@ -1660,20 +1729,21 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
if (old && (ospf6_route_cmp(route, old) == 0)) {
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug(" Update route: %s old cost %u new cost %u nh count %u paths %u",
buf,
zlog_debug("%s Update route: %s old cost %u new cost %u paths %u nh %u",
__PRETTY_FUNCTION__, buf,
old->path.cost, route->path.cost,
listcount(route->nh_list),
listcount(route->paths));
listcount(route->paths),
listcount(route->nh_list));
}
ospf6_intra_prefix_route_ecmp_path(oa, old, route);
} else {
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug(" route %s add with cost %u nh %u paths %u",
buf, route->path.cost,
listcount(route->nh_list),
listcount(route->paths));
zlog_debug("%s route %s add with cost %u paths %u nh %u",
__PRETTY_FUNCTION__, buf,
route->path.cost,
listcount(route->paths),
listcount(route->nh_list));
}
ospf6_route_add(route, oa->route_table);
}
@ -1684,12 +1754,102 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
zlog_debug("Trailing garbage ignored");
}
static void ospf6_intra_prefix_lsa_remove_update_route(struct ospf6_lsa *lsa,
struct ospf6_area *oa,
struct ospf6_route *route)
{
struct listnode *anode, *anext;
struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh;
struct ospf6_path *o_path;
bool nh_updated = false;
char buf[PREFIX2STR_BUFFER];
/* Iterate all paths of route to find maching
* with LSA remove info.
* 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) ||
(o_path->origin.adv_router != lsa->header->adv_router) ||
(o_path->origin.id != lsa->header->id))
continue;
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug(
"%s: route %s path found with cost %u nh %u to remove.",
__PRETTY_FUNCTION__, buf, o_path->cost,
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;
break;
}
if (nh_updated) {
/* Iterate all paths and merge nexthop,
* unlesss any of the nexthop similar to
* ones deleted as part of path deletion.
*/
for (ALL_LIST_ELEMENTS(route->paths, anode, anext, o_path))
ospf6_merge_nexthops(route->nh_list, o_path->nh_list);
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug("%s: route %s update paths %u nh %u",
__PRETTY_FUNCTION__, buf,
route->paths ? listcount(route->paths) : 0,
route->nh_list ? listcount(route->nh_list)
: 0);
}
/* Update Global Route table and
* RIB/FIB with effective
* nh_list
*/
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)(route);
/* route's primary path is similar
* to LSA, replace route's primary
* path with route's paths list
* head.
*/
if ((route->path.origin.id == lsa->header->id) &&
(route->path.origin.adv_router ==
lsa->header->adv_router)) {
ospf6_intra_prefix_update_route_origin(route);
}
}
}
void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
{
struct ospf6_area *oa;
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
struct prefix prefix;
struct ospf6_route *route, *nroute, *route_to_del;
struct ospf6_route *route, *nroute;
int prefix_num;
struct ospf6_prefix *op;
char *start, *current, *end;
@ -1717,22 +1877,6 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
break;
prefix_num--;
route_to_del = ospf6_route_create();
memset(&route_to_del->prefix, 0, sizeof(struct prefix));
route_to_del->prefix.family = AF_INET6;
route_to_del->prefix.prefixlen = op->prefix_length;
ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, op);
route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
route_to_del->path.origin.type = lsa->header->type;
route_to_del->path.origin.id = lsa->header->id;
route_to_del->path.origin.adv_router = lsa->header->adv_router;
route_to_del->path.prefix_options = op->prefix_options;
route_to_del->path.area_id = oa->area_id;
route_to_del->path.type = OSPF6_PATH_TYPE_INTRA;
route_to_del->path.metric_type = 1;
memset(&prefix, 0, sizeof(struct prefix));
prefix.family = AF_INET6;
prefix.prefixlen = op->prefix_length;
@ -1757,134 +1901,8 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
* after removal of one of the path.
*/
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 info.
* 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) ||
(o_path->origin.adv_router !=
lsa->header->adv_router) ||
(o_path->origin.id !=
lsa->header->id))
continue;
if (IS_OSPF6_DEBUG_EXAMIN
(INTRA_PREFIX)) {
prefix2str(&prefix, buf,
sizeof(buf));
zlog_debug(
"%s: route %s path found with cost %u nh %u to remove.",
__PRETTY_FUNCTION__,
buf, o_path->cost,
listcount(
o_path->nh_list));
}
/* Remove old route from global
* ospf6 route table.
* nh_update section will add
* back with effective nh.
*/
if (oa->route_table->hook_remove)
(*oa->route_table->hook_remove)
(route);
/* 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;
break;
}
if (nh_updated) {
/* Iterate all paths and merge nexthop,
* unlesss any of the nexthop similar to
* ones deleted as part of path
* deletion.
*/
for (ALL_LIST_ELEMENTS(route->paths,
anode, anext, o_path)) {
ospf6_merge_nexthops(
route->nh_list,
o_path->nh_list);
}
if (IS_OSPF6_DEBUG_EXAMIN(
INTRA_PREFIX)) {
prefix2str(&route->prefix, buf,
sizeof(buf));
assert(route->nh_list);
zlog_debug("%s: route %s update paths %u nh %u"
, __PRETTY_FUNCTION__,
buf,
listcount(route->paths),
listcount(
route->nh_list));
}
/* route's primary path is similar
* to LSA, replace route's primary
* 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;
}
/* Update Global Route table and
* RIB/FIB with effective
* nh_list
*/
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)
(route);
}
continue;
ospf6_intra_prefix_lsa_remove_update_route(
lsa, oa, route);
} else {
if (route->path.origin.type != lsa->header->type
@ -1896,9 +1914,11 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf,
sizeof(buf));
zlog_debug("route remove %s with path %u cost %u nh %u",
buf, route->path.type,
zlog_debug("%s: route remove %s with path type %u cost %u paths %u nh %u",
__PRETTY_FUNCTION__, buf,
route->path.type,
route->path.cost,
listcount(route->paths),
listcount(route->nh_list));
}
ospf6_route_remove(route, oa->route_table);
@ -1906,8 +1926,6 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
}
if (route)
ospf6_route_unlock(route);
ospf6_route_delete(route_to_del);
}
if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))

View File

@ -611,9 +611,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
prefix2str(&route->prefix, buf, sizeof(buf));
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug("%s %p: route add %p: %s",
zlog_debug("%s %p: route add %p: %s paths %u nh %u",
ospf6_route_table_name(table), (void *)table,
(void *)route, buf);
(void *)route, buf, listcount(route->paths),
listcount(route->nh_list));
else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: %s", ospf6_route_table_name(table),
buf);
@ -664,11 +665,13 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug(
"%s %p: route add %p cost %u nh %u: update of %p old cost %u nh %u",
"%s %p: route add %p cost %u paths %u nh %u: update of %p cost %u paths %u nh %u",
ospf6_route_table_name(table), (void *)table,
(void *)route, route->path.cost,
listcount(route->paths),
listcount(route->nh_list), (void *)old,
old->path.cost, listcount(old->nh_list));
old->path.cost, listcount(old->paths),
listcount(old->nh_list));
else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: update",
ospf6_route_table_name(table));

View File

@ -91,6 +91,9 @@ struct ospf6_path {
/* Cost */
uint8_t metric_type;
uint32_t cost;
struct prefix ls_prefix;
union {
uint32_t cost_e2;
uint32_t cost_config;