summaryrefslogtreecommitdiffstats
path: root/ospf6d/ospf6_asbr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ospf6d/ospf6_asbr.c')
-rw-r--r--ospf6d/ospf6_asbr.c275
1 files changed, 266 insertions, 9 deletions
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 5fbf2dafa..745b87b89 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -173,11 +173,136 @@ static route_tag_t ospf6_as_external_lsa_get_tag(struct ospf6_lsa *lsa)
return ntohl(network_order);
}
+void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
+ struct ospf6_route *route)
+{
+ struct ospf6_route *old_route;
+ struct ospf6_path *ecmp_path, *o_path = NULL;
+ struct listnode *anode;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ char buf[PREFIX2STR_BUFFER];
+ bool route_found = false;
+
+ 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)) {
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ prefix2str(&old_route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: old route %s path cost %u [%u]",
+ __PRETTY_FUNCTION__, buf,
+ old_route->path.cost,
+ ospf6_route_is_same(old_route,
+ route));
+ }
+ route_found = true;
+ /* check if this path exists already in
+ * route->paths list, if so, 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,
+ route->nh_list);
+ /* Merge nexthop to existing route's nh_list */
+ ospf6_route_merge_nexthops(old_route, route);
+
+ /* 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: route %s another path added with nh %u, Paths %u",
+ __PRETTY_FUNCTION__, buf,
+ listcount(ecmp_path->nh_list),
+ old_route->paths ?
+ listcount(old_route->paths)
+ : 0);
+ }
+ } 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(AS_EXTERNAL)) {
+ prefix2str(&route->prefix,
+ buf, sizeof(buf));
+ zlog_debug("%s: existing route %s with effective nh count %u",
+ __PRETTY_FUNCTION__, buf,
+ old_route->nh_list ?
+ listcount(old_route->nh_list)
+ : 0);
+ }
+
+ /* Update RIB/FIB */
+ if (ospf6->route_table->hook_add)
+ (*ospf6->route_table->hook_add)
+ (old_route);
+
+ }
+ /* Delete the new route its info added to existing
+ * route.
+ */
+ ospf6_route_delete(route);
+ break;
+ }
+ }
+
+ if (!route_found) {
+ /* Add new route to existing node in ospf6 route table. */
+ ospf6_route_add(route, ospf6->route_table);
+ }
+}
+
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;
+ struct ospf6_path *path;
char buf[PREFIX2STR_BUFFER];
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
@@ -245,12 +370,34 @@ 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 cost %u(%u) nh %u",
+ __PRETTY_FUNCTION__,
+ (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1)
+ ? 1 : 2, buf, route->path.cost,
+ route->path.u.cost_e2,
+ listcount(route->nh_list));
+ }
+
+ old = ospf6_route_lookup(&route->prefix, ospf6->route_table);
+ if (!old) {
+ /* Add the new route to ospf6 instance route table. */
+ ospf6_route_add(route, ospf6->route_table);
+ } else {
+ /* 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.
+ */
+ ospf6_asbr_update_route_ecmp_path(old, route);
}
- ospf6_route_add(route, ospf6->route_table);
}
void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
@@ -291,16 +438,126 @@ 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) {
+ /* 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(AS_EXTERNAL)) {
+ prefix2str(&route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: AS-External %u route %s update paths %u nh %u"
+ , __PRETTY_FUNCTION__,
+ (route->path.type ==
+ OSPF6_PATH_TYPE_EXTERNAL1)
+ ? 1 : 2, buf,
+ listcount(route->paths),
+ listcount(route->nh_list));
+ }
+
+ /* 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 cost %u(%u) nh %u",
+ __PRETTY_FUNCTION__,
+ route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
+ ? 1 : 2, buf, route->path.cost,
+ route->path.u.cost_e2,
+ listcount(route->nh_list));
}
ospf6_route_remove(route, ospf6->route_table);
}