diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 5344f4cb05..f8595ef3f5 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -85,6 +85,83 @@ OSPF6 router change to take effect, user can use this cli instead of restarting the ospf6d daemon. +ASBR Summarisation Support in OSPFv3 +==================================== + + External routes in OSPFv3 are carried by type 5/7 LSA (external LSAs). + External LSAs are generated by ASBR (Autonomous System Boundary Router). + Large topology database requires a large amount of router memory, which + slows down all processes, including SPF calculations. + It is necessary to reduce the size of the OSPFv3 topology database, + especially in a large network. Summarising routes keeps the routing + tables smaller and easier to troubleshoot. + + External route summarization must be configured on ASBR. + Stub area do not allow ASBR because they don’t allow type 5 LSAs. + + An ASBR will inject a summary route into the OSPFv3 domain. + + Summary route will only be advertised if you have at least one subnet + that falls within the summary range. + + Users will be allowed an option in the CLI to not advertise range of + ipv6 prefixes as well. + + The configuration of ASBR Summarisation is supported using the CLI command + +.. clicmd:: summary-address X:X::X:X/M [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}] + + This command will advertise a single External LSA on behalf of all the + prefixes falling under this range configured by the CLI. + The user is allowed to configure tag, metric and metric-type as well. + By default, tag is not configured, default metric as 20 and metric-type + as type-2 gets advertised. + A summary route is created when one or more specific routes are learned and + removed when no more specific route exist. + The summary route is also installed in the local system with Null0 as + next-hop to avoid leaking traffic. + +.. clicmd:: no summary-address X:X::X:X/M [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}] + + This command can be used to remove the summarisation configuration. + This will flush the single External LSA if it was originated and advertise + the External LSAs for all the existing individual prefixes. + +.. clicmd:: summary-address X:X::X:X/M no-advertise + + This command can be used when user do not want to advertise a certain + range of prefixes using the no-advertise option. + This command when configured will flush all the existing external LSAs + falling under this range. + +.. clicmd:: no summary-address X:X::X:X/M no-advertise + + This command can be used to remove the previous configuration. + When configured, tt will resume originating external LSAs for all the prefixes + falling under the configured range. + +.. clicmd:: aggregation timer (5-1800) + + The summarisation command takes effect after the aggregation timer expires. + By default the value of this timer is 5 seconds. User can modify the time + after which the external LSAs should get originated using this command. + +.. clicmd:: no aggregation timer (5-1800) + + This command removes the timer configuration. It reverts back to default + 5 second timer. + +.. clicmd:: show ipv6 ospf6 summary-address [detail] [json] + + This command can be used to see all the summary-address related information. + When detail option is used, it shows all the prefixes falling under each + summary-configuration apart from other information. + +.. clicmd:: debug ospf6 lsa aggregation + + This command can be used to enable the debugs related to the summarisation + of these LSAs. + .. _ospf6-debugging: OSPFv3 Debugging diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 9dd232dae5..69be807c13 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -715,7 +715,7 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o) if (!o->backbone) return; - def = ospf6_route_create(); + def = ospf6_route_create(o); def->type = OSPF6_DEST_TYPE_NETWORK; def->prefix.family = AF_INET6; def->prefix.prefixlen = 0; @@ -1150,7 +1150,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) /* (5),(6): the path preference is handled by the sorting in the routing table. Always install the path by substituting old route (if any). */ - route = ospf6_route_create(); + route = ospf6_route_create(oa->ospf6); route->type = type; route->prefix = prefix; @@ -1237,7 +1237,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) listcount(old_route->nh_list)); } } else { - struct ospf6_route *tmp_route = ospf6_route_create(); + struct ospf6_route *tmp_route; + + tmp_route = ospf6_route_create(oa->ospf6); ospf6_copy_nexthops(tmp_route->nh_list, o_path->nh_list); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 355b8441bd..f4d9964a57 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -519,7 +519,7 @@ DEFUN (area_range, range = ospf6_route_lookup(&prefix, oa->range_table); if (range == NULL) { - range = ospf6_route_create(); + range = ospf6_route_create(ospf6); range->type = OSPF6_DEST_TYPE_RANGE; range->prefix = prefix; range->path.area_id = oa->area_id; diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 84111e4b7d..165e409eed 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -57,6 +57,7 @@ DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_RT_AGGR, "OSPF6 ASBR Summarisation"); static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type); static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, @@ -70,9 +71,28 @@ unsigned char conf_debug_ospf6_asbr = 0; #define ZROUTE_NAME(x) zebra_route_string(x) +/* Originate Type-5 and Type-7 LSA */ +static struct ospf6_lsa *ospf6_originate_type5_type7_lsas( + struct ospf6_route *route, + struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + struct listnode *lnode; + struct ospf6_area *oa = NULL; + + lsa = ospf6_as_external_lsa_originate(route, ospf6); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + if (IS_AREA_NSSA(oa)) + ospf6_nssa_lsa_originate(route, oa); + } + + return lsa; +} + /* AS External LSA origination */ -void ospf6_as_external_lsa_originate(struct ospf6_route *route, - struct ospf6 *ospf6) +struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route, + struct ospf6 *ospf6) { char buffer[OSPF6_MAX_LSASIZE]; struct ospf6_lsa_header *lsa_header; @@ -164,6 +184,8 @@ void ospf6_as_external_lsa_originate(struct ospf6_route *route, /* Originate */ ospf6_lsa_originate_process(lsa, ospf6); + + return lsa; } int ospf6_orig_as_external_lsa(struct thread *thread) @@ -583,7 +605,7 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) } } - route = ospf6_route_create(); + route = ospf6_route_create(ospf6); route->type = OSPF6_DEST_TYPE_NETWORK; route->prefix.family = AF_INET6; route->prefix.prefixlen = external->prefix.prefix_length; @@ -705,7 +727,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, return; } - route_to_del = ospf6_route_create(); + route_to_del = ospf6_route_create(ospf6); route_to_del->type = OSPF6_DEST_TYPE_NETWORK; route_to_del->prefix.family = AF_INET6; route_to_del->prefix.prefixlen = external->prefix.prefix_length; @@ -1301,6 +1323,28 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) } } +static struct ospf6_external_aggr_rt * +ospf6_external_aggr_match(struct ospf6 *ospf6, struct prefix *p) +{ + struct route_node *node; + + node = route_node_match(ospf6->rt_aggr_tbl, p); + if (node == NULL) + return NULL; + + if (IS_OSPF6_DEBUG_AGGR) { + struct ospf6_external_aggr_rt *ag = node->info; + zlog_debug("%s: Matching aggregator found.prefix: %pFX Aggregator %pFX", + __func__, + p, + &ag->p); + } + + route_unlock_node(node); + + return node->info; +} + void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct prefix *prefix, unsigned int nexthop_num, @@ -1308,8 +1352,6 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct ospf6 *ospf6) { route_map_result_t ret; - struct listnode *lnode; - struct ospf6_area *oa; struct ospf6_route troute; struct ospf6_external_info tinfo; struct ospf6_route *route, *match; @@ -1378,6 +1420,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, match->path.cost = troute.path.cost; else match->path.cost = metric_value(ospf6, type, 0); + if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding)) memcpy(&info->forwarding, &tinfo.forwarding, sizeof(struct in6_addr)); @@ -1414,25 +1457,22 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, } match->path.origin.id = htonl(info->id); - ospf6_as_external_lsa_originate(match, ospf6); + ospf6_handle_external_lsa_origination(ospf6, match, prefix); + ospf6_asbr_status_update(ospf6, ospf6->redistribute); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { - if (IS_AREA_NSSA(oa)) - ospf6_nssa_lsa_originate(match, oa); - } return; } /* create new entry */ - route = ospf6_route_create(); + route = ospf6_route_create(ospf6); route->type = OSPF6_DEST_TYPE_NETWORK; prefix_copy(&route->prefix, prefix); + route->ospf6 = ospf6; info = (struct ospf6_external_info *)XCALLOC( MTYPE_OSPF6_EXTERNAL_INFO, sizeof(struct ospf6_external_info)); route->route_option = info; - info->id = ospf6->external_id++; /* copy result of route-map */ if (ROUTEMAP(red)) { @@ -1463,43 +1503,109 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, else ospf6_route_add_nexthop(route, ifindex, NULL); - /* create/update binding in external_id_table */ - prefix_id.family = AF_INET; - prefix_id.prefixlen = IPV4_MAX_BITLEN; - prefix_id.u.prefix4.s_addr = htonl(info->id); - node = route_node_get(ospf6->external_id_table, &prefix_id); - node->info = route; - route = ospf6_route_add(route, ospf6->external_table); - route->route_option = info; + ospf6_handle_external_lsa_origination(ospf6, route, prefix); - if (IS_OSPF6_DEBUG_ASBR) { - inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf, sizeof(ibuf)); - zlog_debug( - "Advertise as AS-External Id:%s prefix %pFX metric %u", - ibuf, prefix, route->path.metric_type); - } - - route->path.origin.id = htonl(info->id); - ospf6_as_external_lsa_originate(route, ospf6); ospf6_asbr_status_update(ospf6, ospf6->redistribute); + +} + +static void ospf6_asbr_external_lsa_remove_by_id(struct ospf6 *ospf6, + uint32_t id) +{ + struct ospf6_lsa *lsa; + struct ospf6_area *oa; + struct listnode *lnode; + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(id), ospf6->router_id, ospf6->lsdb); + if (!lsa) + return; + + ospf6_external_lsa_purge(ospf6, lsa); + + /* Delete the NSSA LSA */ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { - if (IS_AREA_NSSA(oa)) - ospf6_nssa_lsa_originate(route, oa); + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), + htonl(id), ospf6->router_id, + oa->lsdb); + if (lsa) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("withdraw type 7 lsa, LS ID: %u", + htonl(id)); + + ospf6_lsa_purge(lsa); + } } + +} + +static void +ospf6_link_route_to_aggr(struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + hash_get(aggr->match_extnl_hash, rt, hash_alloc_intern); + rt->aggr_route = aggr; +} + +static void +ospf6_asbr_summary_remove_lsa_and_route(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + + /* Send a Max age LSA if it is already originated.*/ + if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) + return; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Flushing Aggregate route (%pFX)", + __func__, + &aggr->p); + + ospf6_asbr_external_lsa_remove_by_id(ospf6, aggr->id); + + if (aggr->route) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Remove the blackhole route", + __func__); + ospf6_zebra_route_update_remove(aggr->route, ospf6); + ospf6_route_delete(aggr->route); + aggr->route = NULL; + } + + aggr->id = 0; + /* Unset the Origination flag */ + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); +} + +static void +ospf6_unlink_route_from_aggr(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Unlinking external route(%pFX) from aggregator(%pFX), external route count:%ld", + __func__, + &rt->prefix, + &aggr->p, + OSPF6_EXTERNAL_RT_COUNT(aggr)); + + hash_release(aggr->match_extnl_hash, rt); + rt->aggr_route = NULL; + + /* Flush the aggregate route if matching + * external route count becomes zero. + */ + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); } void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, struct prefix *prefix, struct ospf6 *ospf6) { - struct ospf6_area *oa; struct ospf6_route *match; struct ospf6_external_info *info = NULL; - struct listnode *lnode; - struct route_node *node; - struct ospf6_lsa *lsa; - struct prefix prefix_id; - char ibuf[16]; match = ospf6_route_lookup(prefix, ospf6->external_table); if (match == NULL) { @@ -1517,44 +1623,17 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, return; } - if (IS_OSPF6_DEBUG_ASBR) { - inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf, sizeof(ibuf)); - zlog_debug("Withdraw %pFX (AS-External Id:%s)", prefix, ibuf); - } + /* This means aggregation on this route was not done, hence remove LSA + * if any originated for this prefix + */ + if (!match->aggr_route) + ospf6_asbr_external_lsa_remove_by_id(ospf6, info->id); + else + ospf6_unlink_route_from_aggr(ospf6, match->aggr_route, match); - lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), - htonl(info->id), ospf6->router_id, ospf6->lsdb); - if (lsa) { - if (IS_OSPF6_DEBUG_ASBR) { - zlog_debug("withdraw type 5 LSA for route %pFX", - prefix); - } - ospf6_lsa_purge(lsa); - } - - /* Delete the NSSA LSA */ - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { - lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), - htonl(info->id), ospf6->router_id, - oa->lsdb); - if (lsa) { - if (IS_OSPF6_DEBUG_ASBR) { - zlog_debug("withdraw type 7 LSA for route %pFX", - prefix); - } - ospf6_lsa_purge(lsa); - } - } - - /* remove binding in external_id_table */ - prefix_id.family = AF_INET; - prefix_id.prefixlen = IPV4_MAX_BITLEN; - prefix_id.u.prefix4.s_addr = htonl(info->id); - node = route_node_lookup(ospf6->external_id_table, &prefix_id); - assert(node); - node->info = NULL; - route_unlock_node(node); /* to free the lookup lock */ - route_unlock_node(node); /* to free the original lock */ + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Removing route from external table %pFX", + prefix); ospf6_route_remove(match, ospf6->external_table); XFREE(MTYPE_OSPF6_EXTERNAL_INFO, info); @@ -1574,6 +1653,7 @@ DEFUN (ospf6_redistribute, VTY_DECLVAR_CONTEXT(ospf6, ospf6); char *proto = argv[argc - 1]->text; + type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; @@ -2613,3 +2693,988 @@ void install_element_ospf6_debug_asbr(void) install_element(CONFIG_NODE, &debug_ospf6_asbr_cmd); install_element(CONFIG_NODE, &no_debug_ospf6_asbr_cmd); } + +/* ASBR Summarisation */ +void ospf6_fill_aggr_route_details(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct ospf6_route *rt_aggr = aggr->route; + struct ospf6_external_info *ei_aggr = rt_aggr->route_option; + + rt_aggr->prefix = aggr->p; + ei_aggr->tag = aggr->tag; + ei_aggr->type = 0; + ei_aggr->id = aggr->id; + + /* When metric is not configured, apply the default metric */ + rt_aggr->path.cost = ((aggr->metric == -1) ? + DEFAULT_DEFAULT_METRIC + : (unsigned int)(aggr->metric)); + rt_aggr->path.metric_type = aggr->mtype; + + rt_aggr->path.origin.id = htonl(aggr->id); +} + +static void ospf6_originate_new_aggr_lsa(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + + struct prefix prefix_id; + struct route_node *node; + struct ospf6_lsa *lsa = NULL; + struct ospf6_route *rt_aggr; + struct ospf6_external_info *info; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Originate new aggregate route(%pFX)", __func__, + &aggr->p); + + aggr->id = ospf6->external_id++; + /* create/update binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl(aggr->id); + node = route_node_get(ospf6->external_id_table, &prefix_id); + node->info = aggr; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "Advertise AS-External Id:%pI4 prefix %pFX metric %u", + &prefix_id.u.prefix4, &aggr->p, aggr->metric); + + /* Create summary route and save it. */ + rt_aggr = ospf6_route_create(ospf6); + rt_aggr->type = OSPF6_DEST_TYPE_NETWORK; + /* Needed to install route while calling zebra api */ + SET_FLAG(rt_aggr->flag, OSPF6_ROUTE_BEST); + + info = XCALLOC(MTYPE_OSPF6_EXTERNAL_INFO, sizeof(*info)); + rt_aggr->route_option = info; + aggr->route = rt_aggr; + + /* Prepare the external_info for aggregator + * Fill all the details which will get advertised + */ + ospf6_fill_aggr_route_details(ospf6, aggr); + + /* Add next-hop to Null interface. */ + ospf6_add_route_nexthop_blackhole(rt_aggr); + + ospf6_zebra_route_update_add(rt_aggr, ospf6); + + /* Originate summary LSA */ + lsa = ospf6_originate_type5_type7_lsas(rt_aggr, ospf6); + if (lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Set the origination bit for aggregator", + __func__); + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + } +} + +static void +ospf6_aggr_handle_advertise_change(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + /* Check if advertise option modified. */ + if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.", + __func__); + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); + + return; + } + + /* There are no routes present under this aggregation config, hence + * nothing to originate here + */ + if (OSPF6_EXTERNAL_RT_COUNT(aggr) == 0) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: No routes present under this aggregation", + __func__); + return; + } + + if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Now it is advertisable", + __func__); + + ospf6_originate_new_aggr_lsa(ospf6, aggr); + + return; + } +} + +static void +ospf6_originate_summary_lsa(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + struct ospf6_lsa *lsa = NULL, *aggr_lsa = NULL; + struct ospf6_external_info *info = NULL; + struct ospf6_external_aggr_rt *old_aggr; + struct ospf6_as_external_lsa *external; + struct ospf6_route *rt_aggr = NULL; + route_tag_t tag = 0; + unsigned int metric = 0; + int mtype; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Prepare to originate Summary route(%pFX)", + __func__, &aggr->p); + + /* This case to handle when the overlapping aggregator address + * is available. Best match will be considered.So need to delink + * from old aggregator and link to the new aggr. + */ + if (rt->aggr_route) { + if (rt->aggr_route != aggr) { + old_aggr = rt->aggr_route; + ospf6_unlink_route_from_aggr(ospf6, old_aggr, rt); + } + } + + /* Add the external route to hash table */ + ospf6_link_route_to_aggr(aggr, rt); + + /* The key for ID field is a running number and not prefix */ + info = rt->route_option; + assert(info); + if (info->id) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, + ospf6->lsdb); + assert(lsa); + } + + aggr_lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(aggr->id), ospf6->router_id, ospf6->lsdb); + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Aggr LSA ID: %d flags %x.", + __func__, aggr->id, aggr->aggrflags); + /* Dont originate external LSA, + * If it is configured not to advertise. + */ + if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) { + /* If it is already originated as external LSA, + * But, it is configured not to advertise then + * flush the originated external lsa. + */ + if (lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Purge the external LSA %s.", + __func__, lsa->name); + ospf6_external_lsa_purge(ospf6, lsa); + info->id = 0; + rt->path.origin.id = 0; + } + + if (aggr_lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Purge the aggr external LSA %s.", + __func__, lsa->name); + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); + } + + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.", + __func__); + return; + } + + /* Summary route already originated, + * So, Do nothing. + */ + if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) { + if (!aggr_lsa) { + zlog_warn( + "%s: Could not refresh/originate %pFX", + __func__, + &aggr->p); + /* Remove the assert later */ + assert(aggr_lsa); + return; + } + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END + (aggr_lsa->header); + metric = (unsigned long)OSPF6_ASBR_METRIC(external); + tag = ospf6_as_external_lsa_get_tag(aggr_lsa); + mtype = CHECK_FLAG(external->bits_metric, + OSPF6_ASBR_BIT_E) ? 2 : 1; + + /* Prepare the external_info for aggregator */ + ospf6_fill_aggr_route_details(ospf6, aggr); + rt_aggr = aggr->route; + /* If tag/metric/metric-type modified , then re-originate the + * route with modified tag/metric/metric-type details. + */ + if ((tag != aggr->tag) + || (metric != (unsigned int)rt_aggr->path.cost) + || (mtype != aggr->mtype)) { + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Routetag(old:%d new:%d)/Metric(o:%u,n:%u)/mtype(o:%d n:%d) modified,So refresh the summary route.(%pFX)", + __func__, tag, aggr->tag, + metric, + aggr->metric, + mtype, aggr->mtype, + &aggr->p); + + aggr_lsa = ospf6_originate_type5_type7_lsas(aggr->route, + ospf6); + if (aggr_lsa) + SET_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_ORIGINATED); + } + + return; + } + + /* If the external route prefix same as aggregate route + * and if external route is already originated as TYPE-5 + * then it need to be refreshed and originate bit should + * be set. + */ + if (lsa && prefix_same(&aggr->p, &rt->prefix)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: External route prefix is same as aggr so refreshing LSA(%pFX)", + __PRETTY_FUNCTION__, + &aggr->p); + + THREAD_OFF(lsa->refresh); + thread_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); + aggr->id = info->id; + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + return; + } + + ospf6_originate_new_aggr_lsa(ospf6, aggr); +} + +static void ospf6_aggr_handle_external_info(void *data) +{ + struct ospf6_route *rt = (struct ospf6_route *)data; + struct ospf6_external_aggr_rt *aggr = NULL; + struct ospf6_lsa *lsa = NULL; + struct ospf6_external_info *info; + struct ospf6 *ospf6 = NULL; + struct prefix prefix_id; + struct route_node *node; + + rt->aggr_route = NULL; + + rt->to_be_processed = true; + + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) + zlog_debug("%s: Handle external route for origination/refresh (%pFX)", + __func__, + &rt->prefix); + + ospf6 = rt->ospf6; + assert(ospf6); + + aggr = ospf6_external_aggr_match(ospf6, + &rt->prefix); + if (aggr) { + ospf6_originate_summary_lsa(ospf6, aggr, rt); + return; + } + + info = rt->route_option; + if (info->id) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, + ospf6->lsdb); + if (lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: LSA found, refresh it", + __func__); + THREAD_OFF(lsa->refresh); + thread_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); + return; + } + } + + info->id = ospf6->external_id++; + rt->path.origin.id = htonl(info->id); + + /* create/update binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl(info->id); + node = route_node_get(ospf6->external_id_table, &prefix_id); + node->info = rt; + + (void)ospf6_originate_type5_type7_lsas(rt, ospf6); +} + +static void +ospf6_asbr_summary_config_delete(struct ospf6 *ospf6, struct route_node *rn) +{ + struct ospf6_external_aggr_rt *aggr = rn->info; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Deleting Aggregate route (%pFX)", + __func__, + &aggr->p); + + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); + + rn->info = NULL; + route_unlock_node(rn); +} + +static int +ospf6_handle_external_aggr_modify(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_as_external_lsa *asel = NULL; + struct ospf6_route *rt_aggr; + unsigned int metric = 0; + route_tag_t tag = 0; + int mtype; + + lsa = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(aggr->id), ospf6->router_id, + ospf6->lsdb); + if (!lsa) { + zlog_warn( + "%s: Could not refresh/originate %pFX", + __func__, + &aggr->p); + + return OSPF6_FAILURE; + } + + asel = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END(lsa->header); + metric = (unsigned long)OSPF6_ASBR_METRIC(asel); + tag = ospf6_as_external_lsa_get_tag(lsa); + mtype = CHECK_FLAG(asel->bits_metric, + OSPF6_ASBR_BIT_E) ? 2 : 1; + + /* Fill all the details for advertisement */ + ospf6_fill_aggr_route_details(ospf6, aggr); + rt_aggr = aggr->route; + /* If tag/metric/metric-type modified , then + * re-originate the route with modified + * tag/metric/metric-type details. + */ + if ((tag != aggr->tag) + || (metric + != (unsigned int)rt_aggr->path.cost) + || (mtype + != aggr->mtype)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Changed tag(old:%d new:%d)/metric(o:%u n:%d)/mtype(o:%d n:%d),So refresh the summary route.(%pFX)", + __func__, tag, + aggr->tag, + metric, + (unsigned int)rt_aggr->path.cost, + mtype, aggr->mtype, + &aggr->p); + + (void)ospf6_originate_type5_type7_lsas( + aggr->route, + ospf6); + } + + return OSPF6_SUCCESS; +} + +static void ospf6_handle_external_aggr_update(struct ospf6 *ospf6) +{ + struct route_node *rn = NULL; + int ret; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Process modified aggregators.", __func__); + + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + struct ospf6_external_aggr_rt *aggr; + + if (!rn->info) + continue; + + aggr = rn->info; + + if (aggr->action == OSPF6_ROUTE_AGGR_DEL) { + aggr->action = OSPF6_ROUTE_AGGR_NONE; + ospf6_asbr_summary_config_delete(ospf6, rn); + + if (OSPF6_EXTERNAL_RT_COUNT(aggr)) + hash_clean(aggr->match_extnl_hash, + ospf6_aggr_handle_external_info); + + hash_free(aggr->match_extnl_hash); + XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr); + + } else if (aggr->action == OSPF6_ROUTE_AGGR_MODIFY) { + + aggr->action = OSPF6_ROUTE_AGGR_NONE; + + /* Check if tag/metric/metric-type modified */ + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_ORIGINATED) + && !CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) { + + ret = ospf6_handle_external_aggr_modify(ospf6, + aggr); + if (ret == OSPF6_FAILURE) + continue; + } + + /* Advertise option modified ? + * If so, handled it here. + */ + ospf6_aggr_handle_advertise_change(ospf6, aggr); + } + } +} + +static void ospf6_aggr_unlink_external_info(void *data) +{ + struct ospf6_route *rt = (struct ospf6_route *)data; + + rt->aggr_route = NULL; + + rt->to_be_processed = true; +} + +void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr) +{ + if (OSPF6_EXTERNAL_RT_COUNT(aggr)) + hash_clean(aggr->match_extnl_hash, + ospf6_aggr_unlink_external_info); + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Release the aggregator Address(%pFX)", + __func__, + &aggr->p); + + hash_free(aggr->match_extnl_hash); + aggr->match_extnl_hash = NULL; + + XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr); +} + +static void +ospf6_delete_all_marked_aggregators(struct ospf6 *ospf6) +{ + struct route_node *rn = NULL; + struct ospf6_external_aggr_rt *aggr; + + /* Loop through all the aggregators, Delete all aggregators + * which are marked as DELETE. Set action to NONE for remaining + * aggregators + */ + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + if (aggr->action != OSPF6_ROUTE_AGGR_DEL) { + aggr->action = OSPF6_ROUTE_AGGR_NONE; + continue; + } + ospf6_asbr_summary_config_delete(ospf6, rn); + ospf6_external_aggregator_free(aggr); + } +} + +static void ospf6_handle_exnl_rt_after_aggr_del(struct ospf6 *ospf6, + struct ospf6_route *rt) +{ + struct ospf6_lsa *lsa; + + /* Process only marked external routes. + * These routes were part of a deleted + * aggregator.So, originate now. + */ + if (!rt->to_be_processed) + return; + + rt->to_be_processed = false; + + lsa = ospf6_find_external_lsa(ospf6, &rt->prefix); + + if (lsa) { + THREAD_OFF(lsa->refresh); + thread_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); + } else { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Originate external route(%pFX)", + __func__, + &rt->prefix); + + (void)ospf6_originate_type5_type7_lsas(rt, ospf6); + } +} + +static void ospf6_handle_aggregated_exnl_rt(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + struct ospf6_lsa *lsa; + struct ospf6_as_external_lsa *ext_lsa; + struct ospf6_external_info *info; + + /* Handling the case where the external route prefix + * and aggegate prefix is same + * If same dont flush the originated external LSA. + */ + if (prefix_same(&aggr->p, &rt->prefix)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: External Route prefix same as Aggregator(%pFX), so dont flush.", + __func__, + &rt->prefix); + + return; + } + + info = rt->route_option; + assert(info); + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, ospf6->lsdb); + if (lsa) { + ext_lsa = (struct ospf6_as_external_lsa + *)((char *)(lsa->header) + + sizeof(struct ospf6_lsa_header)); + + if (rt->prefix.prefixlen != ext_lsa->prefix.prefix_length) + return; + + ospf6_external_lsa_purge(ospf6, lsa); + + /* Resetting the ID of route */ + rt->path.origin.id = 0; + info->id = 0; + } +} + +static void +ospf6_handle_external_aggr_add(struct ospf6 *ospf6) +{ + struct ospf6_route *rt = NULL; + struct ospf6_external_info *ei = NULL; + struct ospf6_external_aggr_rt *aggr; + + /* Delete all the aggregators which are marked as + * OSPF6_ROUTE_AGGR_DEL. + */ + ospf6_delete_all_marked_aggregators(ospf6); + + for (rt = ospf6_route_head(ospf6->external_table); rt; + rt = ospf6_route_next(rt)) { + ei = rt->route_option; + if (ei == NULL) + continue; + + if (is_default_prefix(&rt->prefix)) + continue; + + aggr = ospf6_external_aggr_match(ospf6, + &rt->prefix); + + /* If matching aggregator found, Add + * the external route refrenace to the + * aggregator and originate the aggr + * route if it is advertisable. + * flush the external LSA if it is + * already originated for this external + * prefix. + */ + if (aggr) { + ospf6_originate_summary_lsa(ospf6, aggr, rt); + + /* All aggregated external rts + * are handled here. + */ + ospf6_handle_aggregated_exnl_rt( + ospf6, aggr, rt); + continue; + } + + /* External routes which are only out + * of aggregation will be handled here. + */ + ospf6_handle_exnl_rt_after_aggr_del( + ospf6, rt); + } +} + +static int ospf6_asbr_summary_process(struct thread *thread) +{ + struct ospf6 *ospf6 = THREAD_ARG(thread); + int operation = 0; + + ospf6->t_external_aggr = NULL; + operation = ospf6->aggr_action; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: operation:%d", + __func__, + operation); + + switch (operation) { + case OSPF6_ROUTE_AGGR_ADD: + ospf6_handle_external_aggr_add(ospf6); + break; + case OSPF6_ROUTE_AGGR_DEL: + case OSPF6_ROUTE_AGGR_MODIFY: + ospf6_handle_external_aggr_update(ospf6); + break; + default: + break; + } + + return OSPF6_SUCCESS; +} + +static void +ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + ospf6_aggr_action_t operation) +{ + aggr->action = operation; + + if (ospf6->t_external_aggr) { + if (ospf6->aggr_action == OSPF6_ROUTE_AGGR_ADD) { + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Not required to restart timer,set is already added.", + __func__); + return; + } + + if (operation == OSPF6_ROUTE_AGGR_ADD) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s, Restarting Aggregator delay timer.", + __func__); + THREAD_OFF(ospf6->t_external_aggr); + } + } + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Start Aggregator delay timer %d(in seconds).", + __func__, ospf6->aggr_delay_interval); + + ospf6->aggr_action = operation; + thread_add_timer(master, + ospf6_asbr_summary_process, + ospf6, ospf6->aggr_delay_interval, + &ospf6->t_external_aggr); +} + +int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6, + struct prefix *p) +{ + struct route_node *rn; + struct ospf6_external_aggr_rt *aggr; + + rn = route_node_lookup(ospf6->rt_aggr_tbl, p); + if (!rn) + return OSPF6_INVALID; + + aggr = rn->info; + + route_unlock_node(rn); + + if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + return OSPF6_INVALID; + + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) + return OSPF6_SUCCESS; + + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_MODIFY); + + return OSPF6_SUCCESS; +} + +int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6, + unsigned int interval) +{ + ospf6->aggr_delay_interval = interval; + + return OSPF6_SUCCESS; +} + +static unsigned int ospf6_external_rt_hash_key(const void *data) +{ + const struct ospf6_route *rt = data; + unsigned int key = 0; + + key = prefix_hash_key(&rt->prefix); + return key; +} + +static bool ospf6_external_rt_hash_cmp(const void *d1, const void *d2) +{ + const struct ospf6_route *rt1 = d1; + const struct ospf6_route *rt2 = d2; + + return prefix_same(&rt1->prefix, &rt2->prefix); +} + +static struct ospf6_external_aggr_rt * +ospf6_external_aggr_new(struct prefix *p) +{ + struct ospf6_external_aggr_rt *aggr; + + aggr = XCALLOC(MTYPE_OSPF6_EXTERNAL_RT_AGGR, + sizeof(struct ospf6_external_aggr_rt)); + + prefix_copy(&aggr->p, p); + aggr->metric = -1; + aggr->mtype = DEFAULT_METRIC_TYPE; + aggr->match_extnl_hash = hash_create(ospf6_external_rt_hash_key, + ospf6_external_rt_hash_cmp, + "Ospf6 external route hash"); + return aggr; +} + +static void ospf6_external_aggr_add(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct route_node *rn; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Adding Aggregate route to Aggr table (%pFX)", + __func__, + &aggr->p); + + rn = route_node_get(ospf6->rt_aggr_tbl, &aggr->p); + if (rn->info) + route_unlock_node(rn); + else + rn->info = aggr; +} + +int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6, + struct prefix *p) +{ + struct ospf6_external_aggr_rt *aggr; + route_tag_t tag = 0; + + aggr = ospf6_external_aggr_config_lookup(ospf6, p); + if (aggr) { + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + return OSPF6_SUCCESS; + + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + + aggr->tag = tag; + aggr->metric = -1; + + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) + return OSPF6_SUCCESS; + + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_MODIFY); + } else { + aggr = ospf6_external_aggr_new(p); + + if (!aggr) + return OSPF6_FAILURE; + + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + ospf6_external_aggr_add(ospf6, aggr); + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_ADD); + } + + return OSPF6_SUCCESS; +} + +struct ospf6_external_aggr_rt * +ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p) +{ + struct route_node *rn; + + rn = route_node_lookup(ospf6->rt_aggr_tbl, p); + if (rn) { + route_unlock_node(rn); + return rn->info; + } + + return NULL; +} + + +int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p, + route_tag_t tag, int metric, int mtype) +{ + struct ospf6_external_aggr_rt *aggregator; + + aggregator = ospf6_external_aggr_config_lookup(ospf6, p); + + if (aggregator) { + if (CHECK_FLAG(aggregator->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + UNSET_FLAG(aggregator->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + else if ((aggregator->tag == tag) + && (aggregator->metric == metric) + && (aggregator->mtype == mtype)) + return OSPF6_SUCCESS; + + aggregator->tag = tag; + aggregator->metric = metric; + aggregator->mtype = mtype; + + ospf6_start_asbr_summary_delay_timer(ospf6, aggregator, + OSPF6_ROUTE_AGGR_MODIFY); + } else { + aggregator = ospf6_external_aggr_new(p); + if (!aggregator) + return OSPF6_FAILURE; + + aggregator->tag = tag; + aggregator->metric = metric; + aggregator->mtype = mtype; + + ospf6_external_aggr_add(ospf6, aggregator); + ospf6_start_asbr_summary_delay_timer(ospf6, aggregator, + OSPF6_ROUTE_AGGR_ADD); + } + + return OSPF6_SUCCESS; +} + +int ospf6_external_aggr_config_unset(struct ospf6 *ospf6, + struct prefix *p) +{ + struct route_node *rn; + struct ospf6_external_aggr_rt *aggr; + + rn = route_node_lookup(ospf6->rt_aggr_tbl, p); + if (!rn) + return OSPF6_INVALID; + + aggr = rn->info; + + route_unlock_node(rn); + + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) { + ospf6_asbr_summary_config_delete(ospf6, rn); + ospf6_external_aggregator_free(aggr); + return OSPF6_SUCCESS; + } + + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_DEL); + + return OSPF6_SUCCESS; +} + +void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6, + struct ospf6_route *rt, + struct prefix *p) +{ + + struct ospf6_external_aggr_rt *aggr; + struct ospf6_external_info *info; + struct prefix prefix_id; + struct route_node *node; + + if (!is_default_prefix(p)) { + aggr = ospf6_external_aggr_match(ospf6, + p); + + if (aggr) { + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Send Aggregate LSA (%pFX)", + __func__, + &aggr->p); + + ospf6_originate_summary_lsa( + ospf6, aggr, rt); + + /* Handling the case where the + * external route prefix + * and aggegate prefix is same + * If same dont flush the + * originated + * external LSA. + */ + ospf6_handle_aggregated_exnl_rt( + ospf6, aggr, rt); + return; + } + } + + info = rt->route_option; + + /* When the info->id = 0, it means it is being originated for the + * first time. + */ + if (!info->id) { + info->id = ospf6->external_id++; + + /* create/update binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl(info->id); + node = route_node_get(ospf6->external_id_table, &prefix_id); + node->info = rt; + + } else { + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl(info->id); + } + + rt->path.origin.id = htonl(info->id); + + if (IS_OSPF6_DEBUG_ASBR) { + zlog_debug("Advertise new AS-External Id:%pI4 prefix %pFX metric %u", + &prefix_id.u.prefix4, p, rt->path.metric_type); + } + + ospf6_originate_type5_type7_lsas(rt, ospf6); + +} + +void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6) +{ + struct route_node *rn = NULL; + struct ospf6_external_aggr_rt *aggr; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("Unset the origination bit for all aggregator"); + + /* Resetting the running external ID counter so that the origination + * of external LSAs starts from the beginning 0.0.0.1 + */ + ospf6->external_id = OSPF6_EXT_INIT_LS_ID; + + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + } +} diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 7ccd1c992b..0aa1374a46 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -46,6 +46,52 @@ struct ospf6_external_info { route_tag_t tag; ifindex_t ifindex; + +}; + +/* OSPF6 ASBR Summarisation */ +typedef enum { + OSPF6_ROUTE_AGGR_NONE = 0, + OSPF6_ROUTE_AGGR_ADD, + OSPF6_ROUTE_AGGR_DEL, + OSPF6_ROUTE_AGGR_MODIFY +} ospf6_aggr_action_t; + +#define OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE 0x1 +#define OSPF6_EXTERNAL_AGGRT_ORIGINATED 0x2 + +#define OSPF6_EXTERNAL_RT_COUNT(aggr) \ + (((struct ospf6_external_aggr_rt *)aggr)->match_extnl_hash->count) + +struct ospf6_external_aggr_rt { + /* range address and masklen */ + struct prefix p; + + /* use bits for OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE and + * OSPF6_EXTERNAL_AGGRT_ORIGINATED + */ + uint16_t aggrflags; + + /* To store external metric-type */ + uint8_t mtype; + + /* Route tag for summary address */ + route_tag_t tag; + + /* To store aggregated metric config */ + int metric; + + /* To Store the LS ID when LSA is originated */ + uint32_t id; + + /* Action to be done after delay timer expiry */ + int action; + + /* OSPFv3 route generated by summary address. */ + struct ospf6_route *route; + + /* Hash table of matching external routes */ + struct hash *match_extnl_hash; }; /* AS-External-LSA */ @@ -110,8 +156,31 @@ extern void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6, struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type, unsigned short instance); extern void ospf6_asbr_routemap_update(const char *mapname); -extern void ospf6_as_external_lsa_originate(struct ospf6_route *route, - struct ospf6 *ospf6); +extern struct ospf6_lsa * +ospf6_as_external_lsa_originate(struct ospf6_route *route, + struct ospf6 *ospf6); extern void ospf6_asbr_status_update(struct ospf6 *ospf6, int status); +int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6, + struct prefix *p); +int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6, + unsigned int interval); +int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6, + struct prefix *p); + +struct ospf6_external_aggr_rt * +ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p); + +int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p, + route_tag_t tag, int metric, int mtype); + +int ospf6_external_aggr_config_unset(struct ospf6 *ospf6, + struct prefix *p); +void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6, + struct ospf6_route *rt, + struct prefix *p); +void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr); +void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6); +void ospf6_fill_aggr_route_details(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr); #endif /* OSPF6_ASBR_H */ diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 0a384a98e6..3d52597161 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -116,7 +116,7 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa) lsdb_self = ospf6_get_scoped_lsdb_self(lsa); ospf6_lsdb_add(ospf6_lsa_copy(lsa), lsdb_self); - lsa->refresh = NULL; + THREAD_OFF(lsa->refresh); thread_add_timer(master, ospf6_lsa_refresh, lsa, OSPF_LS_REFRESH_TIME, &lsa->refresh); @@ -149,6 +149,31 @@ void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa, ospf6_lsa_originate(lsa); } +void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6, + uint32_t id) +{ + struct prefix prefix_id; + struct route_node *node; + + /* remove binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = id; + node = route_node_lookup(ospf6->external_id_table, &prefix_id); + assert(node); + node->info = NULL; + route_unlock_node(node); /* to free the lookup lock */ + route_unlock_node(node); /* to free the original lock */ + +} + +void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa) +{ + ospf6_lsa_purge(lsa); + + ospf6_remove_id_from_external_id_table(ospf6, lsa->header->id); +} + void ospf6_lsa_purge(struct ospf6_lsa *lsa) { struct ospf6_lsa *self; diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h index 5515a1c3fe..4e4fc55ed4 100644 --- a/ospf6d/ospf6_flood.h +++ b/ospf6d/ospf6_flood.h @@ -39,6 +39,9 @@ extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, struct ospf6_area *oa); extern void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa, struct ospf6_interface *oi); +void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6, + uint32_t id); +void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa); extern void ospf6_lsa_purge(struct ospf6_lsa *lsa); extern void ospf6_lsa_purge_multi_ls_id(struct ospf6_area *oa, diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 468a4b8e81..ec672d8f3f 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -435,7 +435,7 @@ void ospf6_interface_connected_route_update(struct interface *ifp) } } - route = ospf6_route_create(); + route = ospf6_route_create(oi->area->ospf6); memcpy(&route->prefix, c->address, sizeof(struct prefix)); apply_mask(&route->prefix); route->type = OSPF6_DEST_TYPE_NETWORK; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 06f64bbc40..e4db8f3a02 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1327,7 +1327,7 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread) || current + OSPF6_PREFIX_SIZE(op) > end) break; - route = ospf6_route_create(); + route = ospf6_route_create(oi->area->ospf6); route->type = OSPF6_DEST_TYPE_NETWORK; route->prefix.family = AF_INET6; @@ -1790,7 +1790,7 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) continue; } - route = ospf6_route_create(); + route = ospf6_route_create(oa->ospf6); memset(&route->prefix, 0, sizeof(struct prefix)); route->prefix.family = AF_INET6; diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index bab5fdaae8..9c03ce21ed 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -45,6 +45,10 @@ #include "ospf6_flood.h" #include "ospf6d.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_lsa_clippy.c" +#endif + DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_SUMMARY, "OSPF6 LSA summary"); @@ -822,6 +826,8 @@ int ospf6_lsa_expire(struct thread *thread) if (CHECK_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY)) return 0; /* dbexchange will do something ... */ ospf6 = ospf6_get_by_lsdb(lsa); + assert(ospf6); + /* reinstall lsa */ ospf6_install_lsa(lsa); @@ -994,6 +1000,30 @@ static char *ospf6_lsa_handler_name(const struct ospf6_lsa_handler *h) return buf; } +DEFPY (debug_ospf6_lsa_aggregation, + debug_ospf6_lsa_aggregation_cmd, + "[no] debug ospf6 lsa aggregation", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "External LSA Aggregation\n") +{ + + struct ospf6_lsa_handler *handler; + + handler = ospf6_get_lsa_handler(OSPF6_LSTYPE_AS_EXTERNAL); + if (handler == NULL) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR); + else + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR); + + return CMD_SUCCESS; +} + DEFUN (debug_ospf6_lsa_type, debug_ospf6_lsa_hex_cmd, "debug ospf6 lsa []", @@ -1105,6 +1135,9 @@ void install_element_ospf6_debug_lsa(void) install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd); install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd); install_element(CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd); + + install_element(ENABLE_NODE, &debug_ospf6_lsa_aggregation_cmd); + install_element(CONFIG_NODE, &debug_ospf6_lsa_aggregation_cmd); } int config_write_ospf6_debug_lsa(struct vty *vty) @@ -1128,6 +1161,8 @@ int config_write_ospf6_debug_lsa(struct vty *vty) if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD)) vty_out(vty, "debug ospf6 lsa %s flooding\n", ospf6_lsa_handler_name(handler)); + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR)) + vty_out(vty, "debug ospf6 lsa aggregation\n"); } return 0; diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 15b0d4ebbc..4c95ee69bd 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -28,6 +28,7 @@ #define OSPF6_LSA_DEBUG_ORIGINATE 0x02 #define OSPF6_LSA_DEBUG_EXAMIN 0x04 #define OSPF6_LSA_DEBUG_FLOOD 0x08 +#define OSPF6_LSA_DEBUG_AGGR 0x10 /* OSPF LSA Default metric values */ #define DEFAULT_DEFAULT_METRIC 20 @@ -51,6 +52,8 @@ (ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_EXAMIN) #define IS_OSPF6_DEBUG_FLOOD_TYPE(type) \ (ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_FLOOD) +#define IS_OSPF6_DEBUG_AGGR \ + (ospf6_lstype_debug(OSPF6_LSTYPE_AS_EXTERNAL) & OSPF6_LSA_DEBUG_AGGR) \ /* LSA definition */ @@ -263,4 +266,6 @@ extern void install_element_ospf6_debug_lsa(void); extern void ospf6_lsa_age_set(struct ospf6_lsa *lsa); extern void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6); extern struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa); +struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, + struct prefix *p); #endif /* OSPF6_LSA_H */ diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 304f03fde8..039c65d739 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -30,6 +30,7 @@ #include "ospf6_proto.h" #include "ospf6_lsa.h" #include "ospf6_lsdb.h" +#include "ospf6_asbr.h" #include "ospf6_route.h" #include "ospf6d.h" #include "bitfield.h" @@ -194,6 +195,28 @@ struct ospf6_lsa *ospf6_lsdb_lookup(uint16_t type, uint32_t id, return (struct ospf6_lsa *)node->info; } +struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, struct prefix *p) +{ + struct ospf6_route *match; + struct ospf6_lsa *lsa; + struct ospf6_external_info *info; + + match = ospf6_route_lookup(p, ospf6->external_table); + if (match == NULL) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("No such route %pFX to withdraw", p); + + return NULL; + } + + info = match->route_option; + assert(info); + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, ospf6->lsdb); + return lsa; +} + struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, uint32_t adv_router, struct ospf6_lsdb *lsdb) diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index 34d17941f4..470a5b1338 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -1159,10 +1159,49 @@ static void ospf6_nssa_flush_area(struct ospf6_area *area) } } -static void ospf6_area_nssa_update(struct ospf6_area *area) +static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area) { struct ospf6_route *route; + struct route_node *rn = NULL; + struct ospf6_external_aggr_rt *aggr; + /* Loop through the external_table to find the LSAs originated + * without aggregation and originate type-7 LSAs for them. + */ + for (route = ospf6_route_head( + area->ospf6->external_table); + route; route = ospf6_route_next(route)) { + /* This means the Type-5 LSA was originated for this route */ + if (route->path.origin.id != 0) + ospf6_nssa_lsa_originate(route, area); + + } + + /* Loop through the aggregation table to originate type-7 LSAs + * for the aggregated type-5 LSAs + */ + for (rn = route_top(area->ospf6->rt_aggr_tbl); rn; + rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_ORIGINATED)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "Originating Type-7 LSAs for area %s", + area->name); + + ospf6_nssa_lsa_originate(aggr->route, area); + } + } + +} + +static void ospf6_area_nssa_update(struct ospf6_area *area) +{ if (IS_AREA_NSSA(area)) { if (!ospf6_check_and_set_router_abr(area->ospf6)) OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); @@ -1194,10 +1233,7 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) zlog_debug("NSSA area %s", area->name); /* Originate NSSA LSA */ - for (route = ospf6_route_head( - area->ospf6->external_table); - route; route = ospf6_route_next(route)) - ospf6_nssa_lsa_originate(route, area); + ospf6_check_and_originate_type7_lsa(area); } } else { /* Disable NSSA */ diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 80f0e7d26b..cd3139d28a 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -284,12 +284,21 @@ void ospf6_add_nexthop(struct list *nh_list, int ifindex, struct in6_addr *addr) struct ospf6_nexthop nh_match; if (nh_list) { - nh_match.ifindex = ifindex; - if (addr != NULL) + if (addr) { + if (ifindex) + nh_match.type = NEXTHOP_TYPE_IPV6_IFINDEX; + else + nh_match.type = NEXTHOP_TYPE_IPV6; + memcpy(&nh_match.address, addr, sizeof(struct in6_addr)); - else + } else { + nh_match.type = NEXTHOP_TYPE_IFINDEX; + memset(&nh_match.address, 0, sizeof(struct in6_addr)); + } + + nh_match.ifindex = ifindex; if (!ospf6_route_find_nexthop(nh_list, &nh_match)) { nh = ospf6_nexthop_create(); @@ -299,36 +308,76 @@ void ospf6_add_nexthop(struct list *nh_list, int ifindex, struct in6_addr *addr) } } +void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route) +{ + struct ospf6_nexthop *nh; + struct ospf6_nexthop nh_match = {}; + + /* List not allocated. */ + if (route->nh_list == NULL) + return; + + /* Entry already exists. */ + nh_match.type = NEXTHOP_TYPE_BLACKHOLE; + if (ospf6_route_find_nexthop(route->nh_list, &nh_match)) + return; + + nh = ospf6_nexthop_create(); + ospf6_nexthop_copy(nh, &nh_match); + listnode_add(route->nh_list, nh); +} + void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route, struct zapi_nexthop nexthops[], int entries, vrf_id_t vrf_id) { struct ospf6_nexthop *nh; struct listnode *node; - char buf[64]; int i; if (route) { i = 0; for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { if (IS_OSPF6_DEBUG_ZEBRA(SEND)) { - const char *ifname; - inet_ntop(AF_INET6, &nh->address, buf, - sizeof(buf)); - ifname = ifindex2ifname(nh->ifindex, vrf_id); - zlog_debug(" nexthop: %s%%%.*s(%d)", buf, - IFNAMSIZ, ifname, nh->ifindex); + zlog_debug(" nexthop: %s %pI6%%%.*s(%d)", + nexthop_type_to_str(nh->type), + &nh->address, IFNAMSIZ, + ifindex2ifname(nh->ifindex, vrf_id), + nh->ifindex); } + if (i >= entries) return; nexthops[i].vrf_id = vrf_id; - nexthops[i].ifindex = nh->ifindex; - if (!IN6_IS_ADDR_UNSPECIFIED(&nh->address)) { + nexthops[i].type = nh->type; + + switch (nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + /* NOTHING */ + break; + + case NEXTHOP_TYPE_IFINDEX: + nexthops[i].ifindex = nh->ifindex; + break; + + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4: + /* + * OSPFv3 with IPv4 routes is not supported + * yet. Skip this next hop. + */ + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Skipping IPv4 next hop"); + continue; + + case NEXTHOP_TYPE_IPV6_IFINDEX: + nexthops[i].ifindex = nh->ifindex; + /* FALLTHROUGH */ + case NEXTHOP_TYPE_IPV6: nexthops[i].gate.ipv6 = nh->address; - nexthops[i].type = NEXTHOP_TYPE_IPV6_IFINDEX; - } else - nexthops[i].type = NEXTHOP_TYPE_IFINDEX; + break; + } i++; } } @@ -404,7 +453,7 @@ void ospf6_copy_paths(struct list *dst, struct list *src) } } -struct ospf6_route *ospf6_route_create(void) +struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6) { struct ospf6_route *route; @@ -415,6 +464,8 @@ struct ospf6_route *ospf6_route_create(void) route->paths = list_new(); route->paths->cmp = (int (*)(void *, void *))ospf6_path_cmp; route->paths->del = (void (*)(void *))ospf6_path_free; + route->ospf6 = ospf6; + return route; } @@ -433,7 +484,7 @@ struct ospf6_route *ospf6_route_copy(struct ospf6_route *route) { struct ospf6_route *new; - new = ospf6_route_create(); + new = ospf6_route_create(route->ospf6); new->type = route->type; memcpy(&new->prefix, &route->prefix, sizeof(struct prefix)); new->prefix_options = route->prefix_options; diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index ecfb45d1ea..991720ec2e 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -24,6 +24,7 @@ #include "command.h" #include "zclient.h" #include "lib/json.h" +#include "lib/nexthop.h" #define OSPF6_MULTI_PATH_LIMIT 4 @@ -44,23 +45,60 @@ struct ospf6_nexthop { /* IP address, if any */ struct in6_addr address; + + /** Next-hop type information. */ + enum nexthop_types_t type; }; -#define ospf6_nexthop_is_set(x) \ - ((x)->ifindex || !IN6_IS_ADDR_UNSPECIFIED(&(x)->address)) -#define ospf6_nexthop_is_same(a, b) \ - ((a)->ifindex == (b)->ifindex \ - && IN6_ARE_ADDR_EQUAL(&(a)->address, &(b)->address)) -#define ospf6_nexthop_clear(x) \ - do { \ - (x)->ifindex = 0; \ - memset(&(x)->address, 0, sizeof(struct in6_addr)); \ - } while (0) -#define ospf6_nexthop_copy(a, b) \ - do { \ - (a)->ifindex = (b)->ifindex; \ - memcpy(&(a)->address, &(b)->address, sizeof(struct in6_addr)); \ - } while (0) +static inline bool ospf6_nexthop_is_set(const struct ospf6_nexthop *nh) +{ + return nh->type != 0; +} + +static inline bool ospf6_nexthop_is_same(const struct ospf6_nexthop *nha, + const struct ospf6_nexthop *nhb) +{ + if (nha->type != nhb->type) + return false; + + switch (nha->type) { + case NEXTHOP_TYPE_BLACKHOLE: + /* NOTHING */ + break; + + case NEXTHOP_TYPE_IFINDEX: + if (nha->ifindex != nhb->ifindex) + return false; + break; + + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4: + /* OSPFv3 does not support IPv4 next hops. */ + return false; + + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (nha->ifindex != nhb->ifindex) + return false; + /* FALLTHROUGH */ + case NEXTHOP_TYPE_IPV6: + if (!IN6_ARE_ADDR_EQUAL(&nha->address, &nhb->address)) + return false; + break; + } + + return true; +} + +static inline void ospf6_nexthop_clear(struct ospf6_nexthop *nh) +{ + memset(nh, 0, sizeof(*nh)); +} + +static inline void ospf6_nexthop_copy(struct ospf6_nexthop *nha, + const struct ospf6_nexthop *nhb) +{ + memcpy(nha, nhb, sizeof(*nha)); +} /* Path */ struct ospf6_ls_origin { @@ -124,6 +162,9 @@ struct ospf6_route { struct ospf6_route *prev; struct ospf6_route *next; + /* Back pointer to ospf6 */ + struct ospf6 *ospf6; + unsigned int lock; /* Destination Type */ @@ -161,6 +202,12 @@ struct ospf6_route { /* nexthop */ struct list *nh_list; + + /* points to the summarised route */ + struct ospf6_external_aggr_rt *aggr_route; + + /* For Aggr routes */ + bool to_be_processed; }; #define OSPF6_DEST_TYPE_NONE 0 @@ -279,6 +326,7 @@ extern void ospf6_copy_nexthops(struct list *dst, struct list *src); extern void ospf6_merge_nexthops(struct list *dst, struct list *src); extern void ospf6_add_nexthop(struct list *nh_list, int ifindex, struct in6_addr *addr); +extern void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route); extern int ospf6_num_nexthops(struct list *nh_list); extern int ospf6_route_cmp_nexthops(struct ospf6_route *a, struct ospf6_route *b); @@ -294,7 +342,7 @@ extern int ospf6_route_get_first_nh_index(struct ospf6_route *route); #define ospf6_route_add_nexthop(route, ifindex, addr) \ ospf6_add_nexthop(route->nh_list, ifindex, addr) -extern struct ospf6_route *ospf6_route_create(void); +extern struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6); extern void ospf6_route_delete(struct ospf6_route *); extern struct ospf6_route *ospf6_route_copy(struct ospf6_route *route); extern int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 051b3a63ef..4e7a7146eb 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -374,7 +374,7 @@ static int ospf6_spf_install(struct ospf6_vertex *v, up to here. */ assert(route == NULL); - route = ospf6_route_create(); + route = ospf6_route_create(v->area->ospf6); memcpy(&route->prefix, &v->vertex_id, sizeof(struct prefix)); route->type = OSPF6_DEST_TYPE_LINKSTATE; route->path.type = OSPF6_PATH_TYPE_INTRA; diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 92f1e50c65..6105e2c24b 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -409,13 +409,31 @@ static struct ospf6 *ospf6_create(const char *name) o->external_table = OSPF6_ROUTE_TABLE_CREATE(GLOBAL, EXTERNAL_ROUTES); o->external_table->scope = o; - + /* Setting this to 1, so that the LS ID 0 can be considered as invalid + * for self originated external LSAs. This helps in differentiating if + * an LSA is originated for any route or not in the route data. + * rt->route_option->id is by default 0 + * Consider a route having id as 0 and prefix as 1::1, an external LSA + * is originated with ID 0.0.0.0. Now consider another route 2::2 + * and for this LSA was not originated because of some configuration + * but the ID field rt->route_option->id is still 0.Consider now this + * 2::2 is being deleted, it will search LSA with LS ID as 0 and it + * will find the LSA and hence delete it but the LSA belonged to prefix + * 1::1, this happened because of LS ID 0. + */ + o->external_id = OSPF6_EXT_INIT_LS_ID; o->external_id_table = route_table_init(); o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; o->distance_table = route_table_init(); + + o->rt_aggr_tbl = route_table_init(); + o->aggr_delay_interval = OSPF6_EXTL_AGGR_DEFAULT_DELAY; + o->t_external_aggr = NULL; + o->aggr_action = OSPF6_ROUTE_AGGR_NONE; + o->fd = -1; o->max_multipath = MULTIPATH_NUM; @@ -461,6 +479,7 @@ struct ospf6 *ospf6_instance_create(const char *name) void ospf6_delete(struct ospf6 *o) { struct listnode *node, *nnode; + struct route_node *rn = NULL; struct ospf6_area *oa; struct vrf *vrf; @@ -499,6 +518,11 @@ void ospf6_delete(struct ospf6 *o) ospf6_vrf_unlink(o, vrf); } + for (rn = route_top(o->rt_aggr_tbl); rn; rn = route_next(rn)) + if (rn->info) + ospf6_external_aggregator_free(rn->info); + route_table_finish(o->rt_aggr_tbl); + XFREE(MTYPE_OSPF6_TOP, o->name); XFREE(MTYPE_OSPF6_TOP, o); } @@ -527,6 +551,7 @@ static void ospf6_disable(struct ospf6 *o) THREAD_OFF(o->t_ase_calc); THREAD_OFF(o->t_distribute_update); THREAD_OFF(o->t_ospf6_receive); + THREAD_OFF(o->t_external_aggr); } } @@ -690,6 +715,7 @@ static void ospf6_process_reset(struct ospf6 *ospf6) struct interface *ifp; struct vrf *vrf = vrf_lookup_by_id(ospf6->vrf_id); + ospf6_unset_all_aggr_flag(ospf6); ospf6_flush_self_originated_lsas_now(ospf6); ospf6->inst_shutdown = 0; ospf6_db_clear(ospf6); @@ -1654,6 +1680,424 @@ DEFUN(show_ipv6_ospf6_route_type_detail, show_ipv6_ospf6_route_type_detail_cmd, return CMD_SUCCESS; } +bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p) +{ + struct in6_addr addr_zero; + + memset(&addr_zero, 0, sizeof(struct in6_addr)); + + /* Default prefix validation*/ + if ((is_default_prefix((struct prefix *)p)) + || (!memcmp(&p->u.prefix6, &addr_zero, sizeof(struct in6_addr)))) { + vty_out(vty, "Default address should not be configured as summary address.\n"); + return false; + } + + /* Host route should not be configured as summary address */ + if (p->prefixlen == IPV6_MAX_BITLEN) { + vty_out(vty, "Host route should not be configured as summary address.\n"); + return false; + } + + return true; +} + +/* External Route Aggregation */ +DEFPY (ospf6_external_route_aggregation, + ospf6_external_route_aggregation_cmd, + "summary-address X:X::X:X/M$prefix [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)$mtype}]", + "External summary address\n" + "Specify IPv6 prefix\n" + "Router tag \n" + "Router tag value\n" + "Metric \n" + "Advertised metric for this route\n" + "OSPFv3 exterior metric type for summarised routes\n" + "Set OSPFv3 External Type 1/2 metrics\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + p.family = AF_INET6; + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask((struct prefix *)&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + if (!tag_str) + tag = 0; + + if (!metric_str) + metric = -1; + + if (!mtype_str) + mtype = DEFAULT_METRIC_TYPE; + + ret = ospf6_external_aggr_config_set(ospf6, &p, tag, metric, mtype); + if (ret == OSPF6_FAILURE) { + vty_out(vty, "Invalid configuration!!\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_external_route_aggregation, + no_ospf6_external_route_aggregation_cmd, + "no summary-address X:X::X:X/M$prefix [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}]", + NO_STR + "External summary address\n" + "Specify IPv6 prefix\n" + "Router tag\n" + "Router tag value\n" + "Metric \n" + "Advertised metric for this route\n" + "OSPFv3 exterior metric type for summarised routes\n" + "Set OSPFv3 External Type 1/2 metrics\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask((struct prefix *)&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = ospf6_external_aggr_config_unset(ospf6, &p); + if (ret == OSPF6_INVALID) + vty_out(vty, "Invalid configuration!!\n"); + + return CMD_SUCCESS; +} + +DEFPY (ospf6_external_route_aggregation_no_advertise, + ospf6_external_route_aggregation_no_advertise_cmd, + "summary-address X:X::X:X/M$prefix no-advertise", + "External summary address\n" + "Specify IPv6 prefix\n" + "Don't advertise summary route \n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask((struct prefix *)&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = ospf6_asbr_external_rt_no_advertise(ospf6, &p); + if (ret == OSPF6_INVALID) + vty_out(vty, "!!Invalid configuration\n"); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf6_external_route_aggregation_no_advertise, + no_ospf6_external_route_aggregation_no_advertise_cmd, + "no summary-address X:X::X:X/M$prefix no-advertise", + NO_STR + "External summary address\n" + "Specify IPv6 prefix\n" + "Adverise summary route to the AS \n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask((struct prefix *)&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = ospf6_asbr_external_rt_advertise(ospf6, &p); + if (ret == OSPF6_INVALID) + vty_out(vty, "!!Invalid configuration\n"); + + return CMD_SUCCESS; +} + +DEFPY (ospf6_route_aggregation_timer, + ospf6_route_aggregation_timer_cmd, + "aggregation timer (5-1800)", + "External route aggregation\n" + "Delay timer (in seconds)\n" + "Timer interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_external_aggr_delay_timer_set(ospf6, timer); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf6_route_aggregation_timer, + no_ospf6_route_aggregation_timer_cmd, + "no aggregation timer [5-1800]", + NO_STR + "External route aggregation\n" + "Delay timer\n" + "Timer interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_external_aggr_delay_timer_set(ospf6, + OSPF6_EXTL_AGGR_DEFAULT_DELAY); + return CMD_SUCCESS; +} + +static int +ospf6_print_vty_external_routes_walkcb(struct hash_bucket *bucket, void *arg) +{ + struct ospf6_route *rt = bucket->data; + struct vty *vty = (struct vty *)arg; + static unsigned int count; + + vty_out(vty, "%pFX ", &rt->prefix); + + count++; + + if (count%5 == 0) + vty_out(vty, "\n"); + + if (OSPF6_EXTERNAL_RT_COUNT(rt->aggr_route) == count) + count = 0; + + return HASHWALK_CONTINUE; +} + +static int +ospf6_print_json_external_routes_walkcb(struct hash_bucket *bucket, void *arg) +{ + struct ospf6_route *rt = bucket->data; + struct json_object *json = (struct json_object *)arg; + char buf[PREFIX2STR_BUFFER]; + char exnalbuf[20]; + static unsigned int count; + + prefix2str(&rt->prefix, buf, sizeof(buf)); + + snprintf(exnalbuf, sizeof(exnalbuf), "Exnl Addr-%d", count); + + json_object_string_add(json, exnalbuf, buf); + + count++; + + if (OSPF6_EXTERNAL_RT_COUNT(rt->aggr_route) == count) + count = 0; + + return HASHWALK_CONTINUE; +} + +static void +ospf6_show_vrf_name(struct vty *vty, struct ospf6 *ospf6, + json_object *json) +{ + if (json) { + if (ospf6->vrf_id == VRF_DEFAULT) + json_object_string_add(json, "vrfName", + "default"); + else + json_object_string_add(json, "vrfName", + ospf6->name); + json_object_int_add(json, "vrfId", ospf6->vrf_id); + } else { + if (ospf6->vrf_id == VRF_DEFAULT) + vty_out(vty, "VRF Name: %s\n", "default"); + else if (ospf6->name) + vty_out(vty, "VRF Name: %s\n", ospf6->name); + } +} + +static int +ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6, + json_object *json, + bool uj, const char *detail) +{ + struct route_node *rn; + static const char header[] = "Summary-address Metric-type Metric Tag External_Rt_count\n"; + json_object *json_vrf = NULL; + + if (!uj) { + ospf6_show_vrf_name(vty, ospf6, json_vrf); + vty_out(vty, "aggregation delay interval :%d(in seconds)\n\n", + ospf6->aggr_delay_interval); + vty_out(vty, "%s\n", header); + } else { + json_vrf = json_object_new_object(); + + ospf6_show_vrf_name(vty, ospf6, json_vrf); + + json_object_int_add(json_vrf, "aggregation delay interval", + ospf6->aggr_delay_interval); + } + + + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + struct ospf6_external_aggr_rt *aggr = rn->info; + json_object *json_aggr = NULL; + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&aggr->p, buf, sizeof(buf)); + + if (uj) { + + json_aggr = json_object_new_object(); + + json_object_object_add(json_vrf, + buf, + json_aggr); + + json_object_string_add(json_aggr, + "Summary address", + buf); + + json_object_string_add( + json_aggr, "Metric-type", + (aggr->mtype == DEFAULT_METRIC_TYPE) + ? "E2" + : "E1"); + + json_object_int_add(json_aggr, "Metric", + (aggr->metric != -1) + ? aggr->metric + : DEFAULT_DEFAULT_METRIC); + + json_object_int_add(json_aggr, "Tag", + aggr->tag); + + json_object_int_add(json_aggr, + "External route count", + OSPF6_EXTERNAL_RT_COUNT(aggr)); + + if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) { + json_object_int_add(json_aggr, "ID", + aggr->id); + json_object_int_add(json_aggr, "Flags", + aggr->aggrflags); + hash_walk(aggr->match_extnl_hash, + ospf6_print_json_external_routes_walkcb, + json_aggr); + } + + } else { + vty_out(vty, "%-22s", buf); + + (aggr->mtype == DEFAULT_METRIC_TYPE) + ? vty_out(vty, "%-16s", "E2") + : vty_out(vty, "%-16s", "E1"); + vty_out(vty, "%-11d", (aggr->metric != -1) + ? aggr->metric + : DEFAULT_DEFAULT_METRIC); + + vty_out(vty, "%-12u", aggr->tag); + + vty_out(vty, "%-5ld\n", + OSPF6_EXTERNAL_RT_COUNT(aggr)); + + if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) { + vty_out(vty, + "Matched External routes:\n"); + hash_walk(aggr->match_extnl_hash, + ospf6_print_vty_external_routes_walkcb, + vty); + vty_out(vty, "\n"); + } + + vty_out(vty, "\n"); + } + } + + if (uj) + json_object_object_add(json, ospf6->name, + json_vrf); + + return CMD_SUCCESS; +} + +DEFPY (show_ipv6_ospf6_external_aggregator, + show_ipv6_ospf6_external_aggregator_cmd, + "show ipv6 ospf6 [vrf ] summary-address [detail$detail] [json]", + SHOW_STR + IP6_STR + OSPF6_STR + VRF_CMD_HELP_STR + "All VRFs\n" + "Show external summary addresses\n" + "detailed informtion\n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + struct ospf6 *ospf6 = NULL; + json_object *json = NULL; + const char *vrf_name = NULL; + struct listnode *node; + bool all_vrf = false; + int idx_vrf = 0; + + if (uj) + json = json_object_new_object(); + + OSPF6_CMD_CHECK_RUNNING(); + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + + ospf6_show_summary_address(vty, ospf6, json, uj, + detail); + + if (!all_vrf) + break; + } + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + static void ospf6_stub_router_config_write(struct vty *vty, struct ospf6 *ospf6) { if (CHECK_FLAG(ospf6->flag, OSPF6_STUB_ROUTER)) { @@ -1693,6 +2137,44 @@ static int ospf6_distance_config_write(struct vty *vty, struct ospf6 *ospf6) return 0; } +static int ospf6_asbr_summary_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + struct route_node *rn; + struct ospf6_external_aggr_rt *aggr; + char buf[PREFIX2STR_BUFFER]; + + if (ospf6->aggr_delay_interval != OSPF6_EXTL_AGGR_DEFAULT_DELAY) + vty_out(vty, " aggregation timer %u\n", + ospf6->aggr_delay_interval); + + /* print 'summary-address A:B::C:D/M' */ + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + prefix2str(&aggr->p, buf, sizeof(buf)); + vty_out(vty, " summary-address %s", buf); + if (aggr->tag) + vty_out(vty, " tag %u", aggr->tag); + + if (aggr->metric != -1) + vty_out(vty, " metric %d", aggr->metric); + + if (aggr->mtype != DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type %d", aggr->mtype); + + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + vty_out(vty, " no-advertise"); + + vty_out(vty, "\n"); + } + + return 0; +} + /* OSPF configuration write function. */ static int config_write_ospf6(struct vty *vty) { @@ -1750,6 +2232,7 @@ static int config_write_ospf6(struct vty *vty) ospf6_spf_config_write(vty, ospf6); ospf6_distance_config_write(vty, ospf6); ospf6_distribute_config_write(vty, ospf6); + ospf6_asbr_summary_config_write(vty, ospf6); vty_out(vty, "!\n"); } @@ -1808,6 +2291,17 @@ void ospf6_top_init(void) install_element(OSPF6_NODE, &ospf6_max_multipath_cmd); install_element(OSPF6_NODE, &no_ospf6_max_multipath_cmd); + /* ASBR Summarisation */ + install_element(OSPF6_NODE, &ospf6_external_route_aggregation_cmd); + install_element(OSPF6_NODE, &no_ospf6_external_route_aggregation_cmd); + install_element(OSPF6_NODE, + &ospf6_external_route_aggregation_no_advertise_cmd); + install_element(OSPF6_NODE, + &no_ospf6_external_route_aggregation_no_advertise_cmd); + install_element(OSPF6_NODE, &ospf6_route_aggregation_timer_cmd); + install_element(OSPF6_NODE, &no_ospf6_route_aggregation_timer_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_external_aggregator_cmd); + install_element(OSPF6_NODE, &ospf6_distance_cmd); install_element(OSPF6_NODE, &no_ospf6_distance_cmd); install_element(OSPF6_NODE, &ospf6_distance_ospf6_cmd); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index b546ee87ae..fe02cd3f84 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -91,6 +91,7 @@ struct ospf6 { struct ospf6_route_table *external_table; struct route_table *external_id_table; +#define OSPF6_EXT_INIT_LS_ID 1 uint32_t external_id; /* OSPF6 redistribute configuration */ @@ -130,6 +131,7 @@ struct ospf6 { struct thread *maxage_remover; struct thread *t_distribute_update; /* Distirbute update timer. */ struct thread *t_ospf6_receive; /* OSPF6 receive timer */ + struct thread *t_external_aggr; /* OSPF6 aggregation timer */ #define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20 struct thread *t_write; @@ -158,6 +160,16 @@ struct ospf6 { struct list *oi_write_q; uint32_t redist_count; + + /* Action for aggregation of external LSAs */ + int aggr_action; + +#define OSPF6_EXTL_AGGR_DEFAULT_DELAY 5 + /* For ASBR summary delay timer */ + int aggr_delay_interval; + /* Table of configured Aggregate addresses */ + struct route_table *rt_aggr_tbl; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf6); @@ -184,4 +196,5 @@ struct ospf6 *ospf6_lookup_by_vrf_id(vrf_id_t vrf_id); struct ospf6 *ospf6_lookup_by_vrf_name(const char *name); const char *ospf6_vrf_id_to_name(vrf_id_t vrf_id); void ospf6_vrf_init(void); +bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p); #endif /* OSPF6_TOP_H */ diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index e054803df3..5afece9b0a 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -49,6 +49,10 @@ extern struct thread_master *master; #define MSG_OK 0 #define MSG_NG 1 +#define OSPF6_SUCCESS 1 +#define OSPF6_FAILURE 0 +#define OSPF6_INVALID -1 + /* cast macro: XXX - these *must* die, ick ick. */ #define OSPF6_PROCESS(x) ((struct ospf6 *) (x)) #define OSPF6_AREA(x) ((struct ospf6_area *) (x)) diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 3f9ff76f3b..78fb26b00e 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -90,6 +90,7 @@ ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la clippy_scan += \ ospf6d/ospf6_top.c \ ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_lsa.c \ # end nodist_ospf6d_ospf6d_SOURCES = \ diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 6aa7a2c0a9..40da7c8fbe 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -1517,7 +1517,7 @@ def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): @retry(retry_timeout=20) -def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True): +def verify_ospf_summary(tgen, topo, dut, input_dict, ospf=None, expected=True): """ This API is to verify ospf routes by running show ip ospf interface command. @@ -1528,7 +1528,6 @@ def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True): * `topo` : topology descriptions * `dut`: device under test * `input_dict` : Input dict data, required when configuring from testcase - * `expected` : expected results from API, by-default True Usage ----- @@ -1548,18 +1547,30 @@ def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True): True or False (Error Message) """ - logger.debug("Entering lib API: verify_ospf_summary()") + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) result = False router = dut logger.info("Verifying OSPF summary on router %s:", router) - if "ospf" not in topo["routers"][dut]: - errormsg = "[DUT: {}] OSPF is not configured on the router.".format(router) - return errormsg - rnode = tgen.routers()[dut] - show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json", isjson=True) + + if ospf: + if 'ospf6' not in topo['routers'][dut]: + errormsg = "[DUT: {}] OSPF6 is not configured on the router.".format( + router) + return errormsg + + show_ospf_json = run_frr_cmd(rnode, "show ipv6 ospf summary detail json", + isjson=True) + else: + if 'ospf' not in topo['routers'][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format( + router) + return errormsg + + show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json", + isjson=True) # Verifying output dictionary show_ospf_json is empty or not if not bool(show_ospf_json): @@ -1568,35 +1579,31 @@ def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True): # To find neighbor ip type ospf_summary_data = input_dict + + if ospf: + show_ospf_json = show_ospf_json['default'] + for ospf_summ, summ_data in ospf_summary_data.items(): if ospf_summ not in show_ospf_json: continue - summary = ospf_summary_data[ospf_summ]["Summary address"] + summary = ospf_summary_data[ospf_summ]['Summary address'] + if summary in show_ospf_json: for summ in summ_data: if summ_data[summ] == show_ospf_json[summary][summ]: - logger.info( - "[DUT: %s] OSPF summary %s:%s is %s", - router, - summary, - summ, - summ_data[summ], - ) + logger.info("[DUT: %s] OSPF summary %s:%s is %s", + router, summary, summ, summ_data[summ]) result = True else: - errormsg = ( - "[DUT: {}] OSPF summary {}:{} is %s, " - "Expected is {}".format( - router, summary, summ, show_ospf_json[summary][summ] - ) - ) + errormsg = ("[DUT: {}] OSPF summary {} : {} is {}, " + "Expected is {}".format(router, summary, summ,show_ospf_json[ + summary][summ], summ_data[summ] )) return errormsg - logger.debug("Exiting API: verify_ospf_summary()") + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result - @retry(retry_timeout=30) def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None): diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json new file mode 100644 index 0000000000..74a0de489f --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json @@ -0,0 +1,198 @@ +{ + "address_types": [ + "ipv6" + ], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r3-link0": { + "ipv6": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf6": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv6": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf6": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r0-link0": { + "ipv6": "auto", + "description": "DummyIntftoR0" + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv6": "auto", + "description": "DummyIntftoR1", + "ospf6": { + "area": "0.0.0.0" + } + } + }, + "ospf6": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py new file mode 100644 index 0000000000..6a4b60fbed --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py @@ -0,0 +1,1928 @@ +#!/usr/bin/python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Summarisation Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from copy import deepcopy +from ipaddress import IPv4Address +from lib.topotest import frr_unicode + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +import ipaddress +from time import sleep + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + kill_router_daemons, + write_test_footer, + reset_config_on_routers, + stop_router, + start_router, + verify_rib, + create_static_routes, + step, + start_router_daemons, + create_route_maps, + shutdown_bringup_interface, + create_prefix_lists, + create_route_maps, + create_interfaces_cfg, + topo_daemons, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf6_neighbor, + clear_ospf, + verify_ospf6_rib, + create_router_ospf, + verify_ospf_summary, +) + + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospfv3_asbr_summary_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": [ + "2011:0:20::1/128", + "2011:0:20::2/128", + "2011:0:20::3/128", + "2011:0:20::4/128", + "2011:0:20::5/128", + ], +} +NETWORK_11 = { + "ipv4": ["11.0.20.6/32", "11.0.20.7/32"], + "ipv6": ["2011:0:20::6/128", "2011:0:20::7/128"], +} + +NETWORK2 = { + "ipv4": [ + "12.0.20.1/32", + "12.0.20.2/32", + "12.0.20.3/32", + "12.0.20.4/32", + "12.0.20.5/32", + ], + "ipv6": [ + "2012:0:20::1/128", + "2012:0:20::2/128", + "2012:0:20::3/128", + "2012:0:20::4/128", + "2012:0:20::5/128", + ], +} +SUMMARY = { + "ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"], + "ipv6": ["2011::/32", "2012::/32", "2011::/64", "2011::/24"], +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A0 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A0 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A0 +---+ + +TESTCASES = +1. OSPF summarisation functionality. +2. OSPF summarisation with advertise and no advertise option +3. OSPF summarisation with route map modification of metric type. +4. OSPF CLI Show verify ospf ASBR summary config and show commands behaviours. +5. OSPF summarisation Chaos. +""" + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf6_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """ + Local 'def' for Redstribute static routes inside ospf. + + Parameters + ---------- + * `dut` : DUT on which configs have to be made. + * `config` : True or False, True by default for configure, set False for + unconfiguration. + """ + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]} + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """ + Local 'def' for Redstribute connected routes inside ospf + + Parameters + ---------- + * `dut` : DUT on which configs have to be made. + * `config` : True or False, True by default for configure, set False for + unconfiguration. + """ + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf6": { + "redistribute": [{"redist_type": "connected", "delete": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + +def test_ospfv3_type5_summary_tc42_p0(request): + """OSPF summarisation functionality.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + protocol = 'ospf' + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + }, + { + "network": NETWORK2["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r0' + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + result = verify_rib(tgen, "ipv6", dut, + input_dict_static_rtes, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" + " routes to one route. with aggregate timer as 6 sec") + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }], + "aggr_timer": 6 + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step("Delete the configured summary") + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "del_aggr_timer": True, + "delete": True + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Summary Route still present in RIB".format(tc_name) + + step("show ip ospf summary should not have any summary address.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6', expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Summary still present in DB".format(tc_name) + + dut = 'r1' + step("All 5 routes are advertised after deletion of configured summary.") + + result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_static_rtes, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("configure the summary again and delete static routes .") + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole", + "delete": True + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + step("Verify that summary route is withdrawn from R1.") + + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step("Add back static routes.") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and only one route is sent to R1.") + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib( + tgen, "ipv6", dut, input_dict_static_rtes, + protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show configure summaries.") + + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step("Configure new static route which is matching configured summary.") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK_11["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Delete one of the static route.") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK_11["ipv6"], + "next_hop": "blackhole", + "delete": True + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + # step("Verify that deleted static route is removed from ospf LSDB.") + # show ip ospf database command is not working, waiting for DEV fix. + + step( + "Configure redistribute connected and configure ospf external" + " summary address to summarise the connected routes.") + + dut = 'r0' + red_connected(dut) + clear_ospf(tgen, dut, ospf='ospf6') + + ip = topo['routers']['r0']['links']['r3']['ipv6'] + + ip_net = str(ipaddress.ip_interface(u'{}'.format(ip)).network) + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": ip_net.split('/')[0], + "mask": "8" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured " + "summary address on R0 and only one route is sent to R1.") + + input_dict_summary = { + "r0": { + "static_routes": [{"network": "fd00::/64"}] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Shut one of the interface") + intf = topo['routers']['r0']['links']['r3-link0']['interface'] + shutdown_bringup_interface(tgen, dut, intf, False) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + # step("Verify that deleted connected route is removed from ospf LSDB.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Un do shut the interface") + shutdown_bringup_interface(tgen, dut, intf, True) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + # step("Verify that deleted connected route is removed from ospf LSDB.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Delete OSPF process.") + ospf_del = { + "r0": { + "ospf6": { + "delete": True + } + } + } + result = create_router_ospf(tgen, topo, ospf_del) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Reconfigure ospf process with summary") + reset_config_on_routers(tgen) + + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + }, + { + "network": NETWORK2["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r0' + red_static(dut) + red_connected(dut) + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + step( + "Verify that external routes are summarised to configured summary " + "address on R0 and only one route is sent to R1.") + + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Delete the redistribute command in ospf.") + dut = 'r0' + red_connected(dut, config=False) + red_static(dut, config=False) + + step("Verify that summary route is withdrawn from the peer.") + + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "metric": "1234" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_ospfv3_type5_summary_tc46_p0(request): + """OSPF summarisation with advertise and no advertise option""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step("Configure OSPF on all the routers of the topology.") + reset_config_on_routers(tgen) + + protocol = 'ospf' + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + }, + { + "network": NETWORK2["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r0' + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + result = verify_rib(tgen, "ipv6", dut, + input_dict_static_rtes, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" + " routes to one route with no advertise option.") + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "advertise": False + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and summary route is not advertised to neighbor as" + " no advertise is configured..") + + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict_summary, + protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step( + "Verify that show ip ospf summary should show the " + "configured summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step("Delete the configured summary") + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "delete": True + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Summary has 5 sec delay timer, sleep 5 secs...") + sleep(5) + + step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Summary Route still present in RIB".format(tc_name) + + step("show ip ospf summary should not have any summary address.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1234, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6', expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Summary still present in DB".format(tc_name) + + step("Reconfigure summary with no advertise.") + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "advertise": False + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and summary route is not advertised to neighbor as" + " no advertise is configured..") + + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict_summary, + protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step( + "Verify that show ip ospf summary should show the " + "configured summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Change summary address from no advertise to advertise " + "(summary-address 10.0.0.0 255.255.0.0)") + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "advertise": False + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes is present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_ospfv3_type5_summary_tc48_p0(request): + """OSPF summarisation with route map modification of metric type.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + protocol = 'ospf' + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + }, + { + "network": NETWORK2["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r0' + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + result = verify_rib(tgen, "ipv6", dut, + input_dict_static_rtes, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" + " routes to one route.") + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step( + "Configure route map and & rule to permit configured summary address," + " redistribute static & connected routes with the route map.") + step("Configure prefixlist to permit the static routes, add to route map.") + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 10, + "network": "any", + "action": "permit" + } + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv6": [{ + "action": "permit", + "seq_id": '1', + "match": { + "ipv6": { + "prefix_lists": + "pf_list_1_ipv6" + } + } + }] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r1 = { + "r0": { + "ospf6": { + "redistribute": [{ + "redist_type": "static", + "route_map": "rmap_ipv6" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured" + "summary address on R0 and only one route is sent to R1. Verify that " + "show ip ospf summary should show the configure summaries.") + + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step("Configure metric type as 1 in route map.") + + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv6": [{ + "seq_id": '1', + "action": "permit", + "set":{ + "metric-type": "type-1" + } + }] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes(static / connected) are summarised" + " to configured summary address with metric type 2.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step("Un configure metric type from route map.") + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv6": [{ + "action": "permit", + "seq_id": '1', + "set":{ + "metric-type": "type-1", + "delete": True + } + }] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes(static / connected) are summarised" + " to configured summary address with metric type 2.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step("Change rule from permit to deny in prefix list.") + pfx_list = { + "r0": { + "prefix_lists": { + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 10, + "network": "any", + "action": "deny" + } + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that previously originated summary lsa " + "is withdrawn from the neighbor.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + step("summary route has delay of 5 secs, wait for 5 secs") + + sleep(5) + + result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol, expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_ospfv3_type5_summary_tc51_p2(request): + """OSPF CLI Show. + + verify ospf ASBR summary config and show commands behaviours. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step("Configure all the supported OSPF ASBR summary commands on DUT.") + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32", + "tag": 4294967295 + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "16", + "advertise": True + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "24", + "advertise": False + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "24", + "advertise": False + }, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + + step("Configure and re configure all the commands 10 times in a loop.") + + for itrate in range(0,10): + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "8", + "tag": 4294967295 + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "16", + "advertise": True + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "24", + "advertise": False + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "24", + "advertise": False + }, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "8", + "tag": 4294967295, + "delete": True + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "16", + "advertise": True, + "delete": True + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "24", + "advertise": False, + "delete": True + }, + { + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "24", + "advertise": False, + "delete": True + }, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify the show commands") + + input_dict = { + SUMMARY["ipv6"][3]: { + "Summary address": SUMMARY["ipv6"][3], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 0 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + write_test_footer(tc_name) + + +def test_ospfv3_type5_summary_tc49_p2(request): + """OSPF summarisation Chaos.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + protocol = 'ospf' + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + }, + { + "network": NETWORK2["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r0' + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + result = verify_rib(tgen, "ipv6", dut, + input_dict_static_rtes, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" + " routes to one route.") + + ospf_summ_r1 = { + "r0": { + "ospf6": { + "summary-address": [{ + "prefix": SUMMARY["ipv6"][0].split('/')[0], + "mask": "32" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step('Reload the FRR router') + # stop/start -> restart FRR router and verify + stop_router(tgen, 'r0') + start_router(tgen, 'r0') + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step("Kill OSPF6d daemon on R0.") + kill_router_daemons(tgen, "r0", ["ospf6d"]) + + step("Bring up OSPF6d daemon on R0.") + start_router_daemons(tgen, "r0", ["ospf6d"]) + + step("Verify OSPF neighbors are up after bringing back ospf6d in R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf6_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + step("restart zebrad") + kill_router_daemons(tgen, "r0", ["zebra"]) + + step("Bring up zebra daemon on R0.") + start_router_daemons(tgen, "r0", ["zebra"]) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1.") + input_dict_summary = { + "r0": { + "static_routes": [{"network": SUMMARY["ipv6"][0]}] + } + } + dut = 'r1' + + result = verify_ospf6_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, + input_dict_summary, protocol=protocol) + assert result is True, "Testcase {} : Failed" \ + "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv6"][0]: { + "Summary address": SUMMARY["ipv6"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + dut = 'r0' + result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf='ospf6') + assert result is True, "Testcase {} : Failed" \ + "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Verify that originally advertised routes are withdraw from there" + " peer.") + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv6"], + "next_hop": "blackhole" + } + ] + } + } + dut = 'r1' + result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: "\ + "Routes still present in OSPF RIB {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed" \ + "Error: Routes still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))