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(
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)
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);
}
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);
}
}
}
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);
}
}
}
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;
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->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;
}
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);
}
}
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;
vty_out(vty, "Metric: %d (%d)\n", route->path.cost,
route->path.u.cost_e2);
+ vty_out(vty, "Paths count: %u\n", route->paths->count);
vty_out(vty, "Nexthop count: %u\n", route->nh_list->count);
/* Nexthops */
vty_out(vty, "Nexthop:\n");