]> git.proxmox.com Git - mirror_frr.git/commitdiff
ospf6d: ECMP for external routes
authorChirag Shah <chirag@cumulusnetworks.com>
Thu, 15 Feb 2018 03:02:11 +0000 (19:02 -0800)
committerChirag Shah <chirag@cumulusnetworks.com>
Thu, 1 Mar 2018 15:47:17 +0000 (07:47 -0800)
The route being added check its origin matches,
with any of the existing path (list of paths).
Remove the existing path, add if its cost is
eqaual or less than any of the existing path.

For a given route and of existing path cost is lower
(better) than one being added, discard new route update.

The existing path cost is higher (lower) than one being
added, ospf6_route_add replaces existing with new route
info.

Compare cost between delete request and with existing
route.

Ticket:CM-16139

Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
ospf6d/ospf6_asbr.c
ospf6d/ospf6_asbr.h
ospf6d/ospf6_route.c
ospf6d/ospf6_top.c

index 11f9e7c7b6e4dfc2dfcdde6268a4a96e1d596525..b52f1cef6c8992b5ff86074b7f046277c2b33055 100644 (file)
@@ -204,26 +204,142 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
 {
        struct ospf6_route *old_route;
        struct ospf6_path *ecmp_path, *o_path = NULL;
-       struct listnode *anode;
+       struct listnode *anode, *anext;
        struct listnode *nnode, *rnode, *rnext;
        struct ospf6_nexthop *nh, *rnh;
        char buf[PREFIX2STR_BUFFER];
        bool route_found = false;
 
+       /* check for old entry match with new route origin,
+        * delete old entry.
+        */
        for (old_route = old; old_route; old_route = old_route->next) {
-               if (ospf6_route_is_same(old_route, route) &&
-                       (old_route->path.type == route->path.type) &&
-                       (old_route->path.cost == route->path.cost) &&
-                       (old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
+               bool route_updated = false;
+
+               if (!ospf6_route_is_same(old_route, route) ||
+                       (old_route->path.type != route->path.type))
+                       continue;
+
+               /* Current and New route has same origin,
+                * delete old entry.
+                */
+               for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext,
+                                                 o_path)) {
+                       /* Check old route path and route has same
+                        * origin.
+                        */
+                       if (o_path->area_id != route->path.area_id ||
+                           (memcmp(&(o_path)->origin, &(route)->path.origin,
+                                  sizeof(struct ospf6_ls_origin)) != 0))
+                               continue;
+
+                       /* Cost is not same then delete current path */
+                       if ((o_path->cost == route->path.cost) &&
+                           (o_path->u.cost_e2 == route->path.u.cost_e2))
+                               continue;
 
                        if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
                                prefix2str(&old_route->prefix, buf,
                                           sizeof(buf));
-                               zlog_debug("%s: old route %s path  cost %u [%u]",
+                               zlog_debug("%s: route %s cost old %u new %u is not same, replace route",
+                                          __PRETTY_FUNCTION__, buf,
+                                          o_path->cost, route->path.cost);
+                       }
+
+                       /* Remove selected current rout path's nh from
+                        * effective nh list.
+                        */
+                       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);
+                                       route_updated = true;
+                               }
+                       }
+
+                       listnode_delete(old_route->paths, o_path);
+                       ospf6_path_free(o_path);
+
+                       /* Current route's path (adv_router info) is similar
+                        * to route being added.
+                        * Replace current route's path with paths list head.
+                        * 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)) {
+                                               ospf6_merge_nexthops(
+                                                       old_route->nh_list,
+                                                       o_path->nh_list);
+                                       }
+                                       /* Update RIB/FIB with effective
+                                        * nh_list
+                                        */
+                                       if (ospf6->route_table->hook_add)
+                                               (*ospf6->route_table->hook_add)
+                                                       (old_route);
+                                       break;
+                               }
+                       } else {
+                               if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+                                       prefix2str(&old_route->prefix, buf,
+                                                  sizeof(buf));
+                                       zlog_debug("%s: route %s old cost %u new cost %u, delete old entry.",
+                                                  __PRETTY_FUNCTION__, buf,
+                                                  old_route->path.cost,
+                                                  route->path.cost);
+                               }
+                               ospf6_route_remove(old_route,
+                                                  ospf6->route_table);
+                               break;
+                       }
+               }
+               if (route_updated)
+                       break;
+       }
+
+       /* Add new route */
+       for (old_route = old; old_route; old_route = old_route->next) {
+
+               /* Current and New Route prefix or route type
+                * is not same skip this current node.
+                */
+               if (!ospf6_route_is_same(old_route, route) ||
+                       (old_route->path.type != route->path.type))
+                       continue;
+
+               /* Old Route and New Route have Equal Cost, Merge NHs */
+               if ((old_route->path.cost == route->path.cost) &&
+                   (old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
+
+                       if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+                               prefix2str(&old_route->prefix, buf,
+                                          sizeof(buf));
+                               zlog_debug("%s: old route %s path  cost %u e2 %u",
                                           __PRETTY_FUNCTION__, buf,
                                           old_route->path.cost,
-                                          ospf6_route_is_same(old_route,
-                                                              route));
+                                          old_route->path.u.cost_e2);
                        }
                        route_found = true;
                        /* check if this path exists already in
@@ -232,9 +348,10 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
                         */
                        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))
+                               if (o_path->area_id == route->path.area_id &&
+                                   (memcmp(&(o_path)->origin,
+                                       &(route)->path.origin,
+                                       sizeof(struct ospf6_ls_origin)) == 0))
                                        break;
                        }
                        /* If path is not found in old_route paths's list,
@@ -262,12 +379,13 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
                                if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
                                        prefix2str(&route->prefix, buf,
                                                   sizeof(buf));
-                                       zlog_debug("%s: route %s another path added with nh %u, Paths %u",
+                                       zlog_debug("%s: route %s another path added with nh %u, effective paths %u nh %u",
                                                __PRETTY_FUNCTION__, buf,
                                                listcount(ecmp_path->nh_list),
                                                old_route->paths ?
                                                listcount(old_route->paths)
-                                               : 0);
+                                               : 0,
+                                               listcount(old_route->nh_list));
                                }
                        } else {
                                for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
@@ -313,6 +431,7 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
                         * route.
                         */
                        ospf6_route_delete(route);
+
                        break;
                }
        }
@@ -426,11 +545,12 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
 
 }
 
-void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
+void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
+                          struct ospf6_route *asbr_entry)
 {
        struct ospf6_as_external_lsa *external;
        struct prefix prefix;
-       struct ospf6_route *route, *nroute;
+       struct ospf6_route *route, *nroute, *route_to_del;
        char buf[PREFIX2STR_BUFFER];
 
        external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
@@ -445,6 +565,35 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
                return;
        }
 
+       route_to_del = ospf6_route_create();
+       route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
+       route_to_del->prefix.family = AF_INET6;
+       route_to_del->prefix.prefixlen = external->prefix.prefix_length;
+       ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6,
+                             &external->prefix);
+
+       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;
+
+       if (asbr_entry) {
+               route_to_del->path.area_id = asbr_entry->path.area_id;
+               if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
+                       route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
+                       route_to_del->path.metric_type = 2;
+                       route_to_del->path.cost = asbr_entry->path.cost;
+                       route_to_del->path.u.cost_e2 =
+                               OSPF6_ASBR_METRIC(external);
+               } else {
+                       route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
+                       route_to_del->path.metric_type = 1;
+                       route_to_del->path.cost =
+                               asbr_entry->path.cost +
+                               OSPF6_ASBR_METRIC(external);
+                       route_to_del->path.u.cost_e2 = 0;
+               }
+       }
+
        memset(&prefix, 0, sizeof(struct prefix));
        prefix.family = AF_INET6;
        prefix.prefixlen = external->prefix.prefix_length;
@@ -459,14 +608,25 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
                return;
        }
 
-       for (ospf6_route_lock(route);
-            route && ospf6_route_is_prefix(&prefix, route); route = nroute) {
+       if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+               prefix2str(&prefix, buf, sizeof(buf));
+               zlog_debug("%s: Current route %s cost %u e2 %u, route to del cost %u e2 %u",
+                          __PRETTY_FUNCTION__, buf, route->path.cost,
+                          route->path.u.cost_e2,
+                          route_to_del->path.cost,
+                          route_to_del->path.u.cost_e2);
+       }
+
+       for (ospf6_route_lock(route); route &&
+            ospf6_route_is_prefix(&prefix, route); route = nroute) {
                nroute = ospf6_route_next(route);
+
                if (route->type != OSPF6_DEST_TYPE_NETWORK)
                        continue;
 
-               /* Route has multiple ECMP paths remove,
-                * matching path and update effective route's  nh list.
+               /* Route has multiple ECMP paths, remove matching
+                * path. Update current route's effective nh list
+                * after removal of one of the path.
                 */
                if (listcount(route->paths) > 1) {
                        struct listnode *anode, *anext;
@@ -481,18 +641,36 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
                         */
                        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)
+                               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 (o_path->origin.adv_router !=
-                                       lsa->header->adv_router)
+
+                               /* Compare LSA cost with current
+                                * route info.
+                                */
+                               if (!asbr_entry && (o_path->cost !=
+                                               route_to_del->path.cost ||
+                                               o_path->u.cost_e2 !=
+                                               route_to_del->path.u.cost_e2)) {
+                                       if (IS_OSPF6_DEBUG_EXAMIN(
+                                                       AS_EXTERNAL)) {
+                                               prefix2str(&prefix, buf,
+                                                          sizeof(buf));
+                                               zlog_debug(
+                                               "%s: route %s to delete is not same, cost %u del cost %u. skip",
+                                               __PRETTY_FUNCTION__, buf,
+                                               route->path.cost,
+                                               route_to_del->path.cost);
+                                       }
                                        continue;
+                               }
 
                                if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))  {
                                        prefix2str(&prefix, buf, sizeof(buf));
                                        zlog_debug(
-                                               "%s: route %s path found with nh %u",
+                                               "%s: route %s path found with nh %u to remove.",
                                                __PRETTY_FUNCTION__, buf,
                                                listcount(o_path->nh_list));
                                }
@@ -542,13 +720,13 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
                                                   listcount(route->nh_list));
                                }
 
-                               /* Update RIB/FIB w/ effective nh_list */
+                               /* Update RIB/FIB with 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.
+                               /* 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 ==
@@ -568,12 +746,29 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
                        continue;
 
                } else {
-                       if (route->path.origin.type != lsa->header->type)
-                               continue;
-                       if (route->path.origin.id != lsa->header->id)
+                       /* Compare LSA origin and cost with current route info.
+                        * if any check fails skip del this route node.
+                        */
+                       if (asbr_entry && (!ospf6_route_is_same_origin(route,
+                                                       route_to_del) ||
+                           (route->path.type != route_to_del->path.type) ||
+                           (route->path.cost != route_to_del->path.cost) ||
+                           (route->path.u.cost_e2 !=
+                            route_to_del->path.u.cost_e2))) {
+                               if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+                                       prefix2str(&prefix, buf, sizeof(buf));
+                                       zlog_debug("%s: route %s to delete is not same, cost %u del cost %u. skip",
+                                       __PRETTY_FUNCTION__, buf,
+                                       route->path.cost,
+                                       route_to_del->path.cost);
+                               }
                                continue;
-                       if (route->path.origin.adv_router !=
-                                       lsa->header->adv_router)
+                       }
+
+                       if ((route->path.origin.type != lsa->header->type) ||
+                           (route->path.origin.adv_router !=
+                                       lsa->header->adv_router) ||
+                           (route->path.origin.id != lsa->header->id))
                                continue;
                }
                if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
@@ -589,6 +784,8 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
        }
        if (route != NULL)
                ospf6_route_unlock(route);
+
+       ospf6_route_delete(route_to_del);
 }
 
 void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry)
@@ -622,7 +819,7 @@ void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry)
        type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
        router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix);
        for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa))
-               ospf6_asbr_lsa_remove(lsa);
+               ospf6_asbr_lsa_remove(lsa, asbr_entry);
 }
 
 
index cc4f0272aabbfc4d33c6710cb1253df341760af3..bd160a6456cc5a3e30f5c19376962d469f4a008e 100644 (file)
@@ -71,7 +71,8 @@ struct ospf6_as_external_lsa {
        }
 
 extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa);
-extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa);
+extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
+                                 struct ospf6_route *asbr_entry);
 extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry);
 extern void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry);
 
index 19eb9a3fe660fa3d32629bb5c1358cf31be8038c..ef0a093d13804f6c53d357ffe7432b8289347ded 100644 (file)
@@ -469,6 +469,8 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb)
        if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) {
                if (ra->path.u.cost_e2 != rb->path.u.cost_e2)
                        return (ra->path.u.cost_e2 - rb->path.u.cost_e2);
+               else
+                       return (ra->path.cost - rb->path.cost);
        } else {
                if (ra->path.cost != rb->path.cost)
                        return (ra->path.cost - rb->path.cost);
@@ -627,10 +629,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
                if (ospf6_route_is_identical(old, route)) {
                        if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
                                zlog_debug(
-                                       "%s %p: route add %p: needless update of %p",
+                                       "%s %p: route add %p: needless update of %p old cost %u",
                                        ospf6_route_table_name(table),
                                        (void *)table, (void *)route,
-                                       (void *)old);
+                                       (void *)old, old->path.cost);
                        else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
                                zlog_debug("%s: route add: needless update",
                                           ospf6_route_table_name(table));
@@ -645,9 +647,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
                }
 
                if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
-                       zlog_debug("%s %p: route add %p: update of %p",
+                       zlog_debug("%s %p: route add %p cost %u: update of %p old cost %u",
                                   ospf6_route_table_name(table), (void *)table,
-                                  (void *)route, (void *)old);
+                                  (void *)route, route->path.cost, (void *)old,
+                                  old->path.cost);
                else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
                        zlog_debug("%s: route add: update",
                                   ospf6_route_table_name(table));
@@ -686,13 +689,14 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
        if (prev || next) {
                if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
                        zlog_debug(
-                               "%s %p: route add %p: another path: prev %p, next %p node refcount %u",
+                               "%s %p: route add %p cost %u: another path: prev %p, next %p node ref %u",
                                ospf6_route_table_name(table), (void *)table,
-                               (void *)route, (void *)prev, (void *)next,
-                               node->lock);
+                               (void *)route, route->path.cost, (void *)prev,
+                               (void *)next, node->lock);
                else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
-                       zlog_debug("%s: route add: another path found",
-                                  ospf6_route_table_name(table));
+                       zlog_debug("%s: route add cost %u: another path found",
+                                  ospf6_route_table_name(table),
+                                  route->path.cost);
 
                if (prev == NULL)
                        prev = next->prev;
@@ -814,9 +818,9 @@ void ospf6_route_remove(struct ospf6_route *route,
                prefix2str(&route->prefix, buf, sizeof(buf));
 
        if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
-               zlog_debug("%s %p: route remove %p: %s refcount %u",
+               zlog_debug("%s %p: route remove %p: %s cost %u refcount %u",
                           ospf6_route_table_name(table), (void *)table,
-                          (void *)route, buf, route->lock);
+                          (void *)route, buf, route->path.cost, route->lock);
        else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
                zlog_debug("%s: route remove: %s",
                           ospf6_route_table_name(table), buf);
index 25d968fb689e575dc3c5ada7240769ea6f2a2710..afe2d7397b2efa05aed679c3421155ab3bd46fc9 100644 (file)
@@ -72,7 +72,7 @@ static void ospf6_top_lsdb_hook_remove(struct ospf6_lsa *lsa)
 {
        switch (ntohs(lsa->header->type)) {
        case OSPF6_LSTYPE_AS_EXTERNAL:
-               ospf6_asbr_lsa_remove(lsa);
+               ospf6_asbr_lsa_remove(lsa, NULL);
                break;
 
        default:
@@ -96,11 +96,16 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route)
 static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
 {
        if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
-               char buf[PREFIX2STR_BUFFER];
-
-               prefix2str(&route->prefix, buf, sizeof(buf));
-               zlog_debug("%s: brouter %s add with nh count %u",
-                          __PRETTY_FUNCTION__, buf, listcount(route->nh_list));
+               uint32_t brouter_id;
+               char brouter_name[16];
+
+               brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
+               inet_ntop(AF_INET, &brouter_id, brouter_name,
+                         sizeof(brouter_name));
+               zlog_debug("%s: brouter %s add with adv router %x nh count %u",
+                          __PRETTY_FUNCTION__, brouter_name,
+                          route->path.origin.adv_router,
+                          listcount(route->nh_list));
        }
        ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));
        ospf6_asbr_lsentry_add(route);
@@ -110,11 +115,15 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
 static void ospf6_top_brouter_hook_remove(struct ospf6_route *route)
 {
        if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
-               char buf[PREFIX2STR_BUFFER];
+               uint32_t brouter_id;
+               char brouter_name[16];
 
-               prefix2str(&route->prefix, buf, sizeof(buf));
+               brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
+               inet_ntop(AF_INET, &brouter_id, brouter_name,
+                         sizeof(brouter_name));
                zlog_debug("%s: brouter %s del with nh count %u",
-                          __PRETTY_FUNCTION__, buf, listcount(route->nh_list));
+                          __PRETTY_FUNCTION__, brouter_name,
+                          listcount(route->nh_list));
        }
        route->flag |= OSPF6_ROUTE_REMOVE;
        ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));