From 6f7bbc0cec557e3f87a83c7481f92de68485cdb9 Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Mon, 21 Nov 2022 02:32:53 -0800 Subject: [PATCH 1/7] ospfd: Datastructure changes for OSPFv2 Flood reduction. Description: Code changes involve following things. 1. an additional structure containing flood reduction related info per area. 2. a knob variable in the ospf structure for enabling/disabling the feature. 3. initialization of above mentioned variables. Signed-off-by: Manoj Naragund --- ospfd/ospfd.c | 12 ++++++++++++ ospfd/ospfd.h | 26 +++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 023dc32a7b..b70f3ebdde 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -467,6 +467,8 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) */ ospf_gr_nvm_read(new); + new->fr_configured = false; + return new; } @@ -817,6 +819,7 @@ static void ospf_finish_final(struct ospf *ospf) THREAD_OFF(ospf->t_maxage); THREAD_OFF(ospf->t_maxage_walker); THREAD_OFF(ospf->t_abr_task); + THREAD_OFF(ospf->t_abr_fr); THREAD_OFF(ospf->t_asbr_check); THREAD_OFF(ospf->t_asbr_nssa_redist_update); THREAD_OFF(ospf->t_distribute_update); @@ -962,6 +965,15 @@ struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id) /* Self-originated LSAs initialize. */ new->router_lsa_self = NULL; + /* Initialize FR field */ + new->fr_info.enabled = false; + new->fr_info.configured = false; + new->fr_info.state_changed = false; + new->fr_info.router_lsas_recv_dc_bit = 0; + new->fr_info.indication_lsa_self = NULL; + new->fr_info.area_ind_lsa_recvd = false; + new->fr_info.area_dc_clear = false; + ospf_opaque_type10_lsa_init(new); new->oiflist = list_new(); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 3a43010f85..f044a7e217 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -116,7 +116,25 @@ struct ospf_redist { struct route_map *map; } route_map; /* +1 is for default-information */ #define ROUTEMAP_NAME(R) (R->route_map.name) -#define ROUTEMAP(R) (R->route_map.map) +#define ROUTEMAP(R) (R->route_map.map) +}; + +/* OSPF area flood reduction info */ +struct ospf_area_fr_info { + bool enabled; /* Area support for Flood Reduction */ + bool configured; /* Flood Reduction configured per area knob */ + bool state_changed; /* flood reduction state change info */ + int router_lsas_recv_dc_bit; /* Number of unique router lsas + * received with DC bit set. + * (excluding self) + */ + bool area_ind_lsa_recvd; /* Indication lsa received in this area */ + bool area_dc_clear; /* Area has atleast one lsa with dc bit 0( + * excluding indication lsa) + */ + struct ospf_lsa *indication_lsa_self; /* Indication LSA generated + * in the area. + */ }; /* ospf->config */ @@ -255,6 +273,7 @@ struct ospf { /* Threads. */ struct thread *t_abr_task; /* ABR task timer. */ + struct thread *t_abr_fr; /* ABR FR timer. */ struct thread *t_asbr_check; /* ASBR check timer. */ struct thread *t_asbr_nssa_redist_update; /* ASBR NSSA redistribution update timer. */ @@ -406,6 +425,9 @@ struct ospf { bool ti_lfa_enabled; enum protection_type ti_lfa_protection_type; + /* Flood Reduction configuration state */ + bool fr_configured; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf); @@ -591,6 +613,8 @@ struct ospf_area { uint32_t act_ints; /* Active interfaces. */ uint32_t full_nbrs; /* Fully adjacent neighbors. */ uint32_t full_vls; /* Fully adjacent virtual neighbors. */ + + struct ospf_area_fr_info fr_info; /* Flood reduction info. */ }; /* OSPF config network structure. */ From 75b837a3d5908e8c41954e1a659f8e44de610c04 Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Mon, 21 Nov 2022 02:37:53 -0800 Subject: [PATCH 2/7] ospfd: CLI changes to enable/disable OSPFv2 Flood reduction. Description: The CLIs for enabling/disabling FR, at instance level and at the area. Signed-off-by: Manoj Naragund --- ospfd/ospf_vty.c | 406 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 300 insertions(+), 106 deletions(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 12ccc8bcea..4e438cb281 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3053,6 +3053,43 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, ospf_lsdb_checksum(area->lsdb, OSPF_OPAQUE_AREA_LSA)); } + if (area->fr_info.configured) { + if (use_json) + json_object_string_add(json_area, "areaFloodReduction", + "configured"); + else + vty_out(vty, " Flood Reduction is configured.\n"); + } + + if (area->fr_info.enabled) { + if (use_json) { + json_object_boolean_true_add( + json_area, "areaFloodReductionEnabled"); + if (area->fr_info.router_lsas_recv_dc_bit) + json_object_boolean_true_add( + json_area, "lsasRecvDCbitSet"); + if (area->fr_info.area_ind_lsa_recvd) + json_object_string_add(json_area, + "areaIndicationLsaRecv", + "received"); + if (area->fr_info.indication_lsa_self) + json_object_string_addf( + json_area, "areaIndicationLsa", "%pI4", + &area->fr_info.indication_lsa_self->data + ->id); + } else { + vty_out(vty, " Flood Reduction is enabled.\n"); + vty_out(vty, " No of LSAs rcv'd with DC bit set %d\n", + area->fr_info.router_lsas_recv_dc_bit); + if (area->fr_info.area_ind_lsa_recvd) + vty_out(vty, " Ind LSA by other abr.\n"); + if (area->fr_info.indication_lsa_self) + vty_out(vty, " Ind LSA generated %pI4\n", + &area->fr_info.indication_lsa_self->data + ->id); + } + } + if (use_json) json_object_object_add(json_areas, inet_ntop(AF_INET, &area->area_id, @@ -3288,6 +3325,14 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, : ZEBRA_OSPF_DISTANCE_DEFAULT); } + if (ospf->fr_configured) { + if (json) + json_object_string_add(json_vrf, "floodReduction", + "configured"); + else + vty_out(vty, " Flood Reduction is configured.\n"); + } + /* Show ABR/ASBR flags. */ if (CHECK_FLAG(ospf->flags, OSPF_FLAG_ABR)) { if (json) @@ -5964,118 +6009,109 @@ static int show_lsa_summary(struct vty *vty, struct ospf_lsa *lsa, int self, struct as_external_lsa *asel; struct prefix_ipv4 p; - if (lsa != NULL) { - /* If self option is set, check LSA self flag. */ - if (self == 0 || IS_LSA_SELF(lsa)) { + if (lsa == NULL) + return 0; - if (!json_lsa) { - /* LSA common part show. */ - vty_out(vty, "%-15pI4", - &lsa->data->id); - vty_out(vty, "%-15pI4 %4d 0x%08lx 0x%04x", - &lsa->data->adv_router, LS_AGE(lsa), - (unsigned long)ntohl( - lsa->data->ls_seqnum), - ntohs(lsa->data->checksum)); - } else { - char seqnum[10]; - char checksum[10]; + /* If self option is set, check LSA self flag. */ + if (self == 0 || IS_LSA_SELF(lsa)) { - snprintf(seqnum, sizeof(seqnum), "%x", - ntohl(lsa->data->ls_seqnum)); - snprintf(checksum, sizeof(checksum), "%x", - ntohs(lsa->data->checksum)); - json_object_string_addf(json_lsa, "lsId", - "%pI4", &lsa->data->id); - json_object_string_addf( - json_lsa, "advertisedRouter", "%pI4", - &lsa->data->adv_router); - json_object_int_add(json_lsa, "lsaAge", - LS_AGE(lsa)); - json_object_string_add( - json_lsa, "sequenceNumber", seqnum); - json_object_string_add(json_lsa, "checksum", - checksum); - } + if (!json_lsa) { + /* LSA common part show. */ + vty_out(vty, "%-15pI4", &lsa->data->id); + vty_out(vty, "%-15pI4 %4d 0x%08lx 0x%04x", + &lsa->data->adv_router, LS_AGE(lsa), + (unsigned long)ntohl(lsa->data->ls_seqnum), + ntohs(lsa->data->checksum)); + } else { + char seqnum[10]; + char checksum[10]; - /* LSA specific part show. */ - switch (lsa->data->type) { - case OSPF_ROUTER_LSA: - rl = (struct router_lsa *)lsa->data; - - if (!json_lsa) - vty_out(vty, " %-d", ntohs(rl->links)); - else - json_object_int_add(json_lsa, - "numOfRouterLinks", - ntohs(rl->links)); - break; - case OSPF_SUMMARY_LSA: - sl = (struct summary_lsa *)lsa->data; - - p.family = AF_INET; - p.prefix = sl->header.id; - p.prefixlen = ip_masklen(sl->mask); - apply_mask_ipv4(&p); - - if (!json_lsa) - vty_out(vty, " %pFX", &p); - else { - json_object_string_addf( - json_lsa, "summaryAddress", - "%pFX", &p); - } - break; - case OSPF_AS_EXTERNAL_LSA: - case OSPF_AS_NSSA_LSA: - asel = (struct as_external_lsa *)lsa->data; - - p.family = AF_INET; - p.prefix = asel->header.id; - p.prefixlen = ip_masklen(asel->mask); - apply_mask_ipv4(&p); - - if (!json_lsa) - vty_out(vty, " %s %pFX [0x%lx]", - IS_EXTERNAL_METRIC( - asel->e[0].tos) - ? "E2" - : "E1", - &p, - (unsigned long)ntohl( - asel->e[0].route_tag)); - else { - json_object_string_add( - json_lsa, "metricType", - IS_EXTERNAL_METRIC( - asel->e[0].tos) - ? "E2" - : "E1"); - json_object_string_addf( - json_lsa, "route", "%pFX", &p); - json_object_int_add( - json_lsa, "tag", - (unsigned long)ntohl( - asel->e[0].route_tag)); - } - break; - case OSPF_NETWORK_LSA: - case OSPF_ASBR_SUMMARY_LSA: - case OSPF_OPAQUE_LINK_LSA: - case OSPF_OPAQUE_AREA_LSA: - case OSPF_OPAQUE_AS_LSA: - default: - break; - } - - if (!json_lsa) - vty_out(vty, "\n"); + snprintf(seqnum, sizeof(seqnum), "%x", + ntohl(lsa->data->ls_seqnum)); + snprintf(checksum, sizeof(checksum), "%x", + ntohs(lsa->data->checksum)); + json_object_string_addf(json_lsa, "lsId", "%pI4", + &lsa->data->id); + json_object_string_addf(json_lsa, "advertisedRouter", + "%pI4", &lsa->data->adv_router); + json_object_int_add(json_lsa, "lsaAge", LS_AGE(lsa)); + json_object_string_add(json_lsa, "sequenceNumber", + seqnum); + json_object_string_add(json_lsa, "checksum", checksum); } - return 1; + /* LSA specific part show. */ + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + rl = (struct router_lsa *)lsa->data; + + if (!json_lsa) + vty_out(vty, " %-d", ntohs(rl->links)); + else + json_object_int_add(json_lsa, + "numOfRouterLinks", + ntohs(rl->links)); + break; + case OSPF_SUMMARY_LSA: + sl = (struct summary_lsa *)lsa->data; + + p.family = AF_INET; + p.prefix = sl->header.id; + p.prefixlen = ip_masklen(sl->mask); + apply_mask_ipv4(&p); + + if (!json_lsa) + vty_out(vty, " %pFX", &p); + else { + json_object_string_addf( + json_lsa, "summaryAddress", "%pFX", &p); + } + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + asel = (struct as_external_lsa *)lsa->data; + + p.family = AF_INET; + p.prefix = asel->header.id; + p.prefixlen = ip_masklen(asel->mask); + apply_mask_ipv4(&p); + + if (!json_lsa) + vty_out(vty, " %s %pFX [0x%lx]", + IS_EXTERNAL_METRIC(asel->e[0].tos) + ? "E2" + : "E1", + &p, + (unsigned long)ntohl( + asel->e[0].route_tag)); + else { + json_object_string_add( + json_lsa, "metricType", + IS_EXTERNAL_METRIC(asel->e[0].tos) + ? "E2" + : "E1"); + json_object_string_addf(json_lsa, "route", + "%pFX", &p); + json_object_int_add( + json_lsa, "tag", + (unsigned long)ntohl( + asel->e[0].route_tag)); + } + break; + case OSPF_NETWORK_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + default: + break; + } + + if (!json_lsa) + vty_out(vty, "\n"); } - return 0; + return 1; } static const char *const show_database_desc[] = { @@ -6144,7 +6180,16 @@ static void show_ip_ospf_database_header(struct vty *vty, struct ospf_lsa *lsa, struct router_lsa *rlsa = (struct router_lsa *)lsa->data; if (!json) { - vty_out(vty, " LS age: %d\n", LS_AGE(lsa)); + if (IS_LSA_SELF(lsa)) + vty_out(vty, " LS age: %d%s\n", LS_AGE(lsa), + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) + ? "(S-DNA)" + : ""); + else + vty_out(vty, " LS age: %d%s\n", LS_AGE(lsa), + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) + ? "(DNA)" + : ""); vty_out(vty, " Options: 0x%-2x : %s\n", lsa->data->options, ospf_options_dump(lsa->data->options)); vty_out(vty, " LS Flags: 0x%-2x %s\n", lsa->flags, @@ -12235,6 +12280,9 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) if (PREFIX_NAME_OUT(area)) vty_out(vty, " area %s filter-list prefix %s out\n", buf, PREFIX_NAME_OUT(area)); + + if (area->fr_info.configured) + vty_out(vty, " area %s flood-reduction\n", buf); } return 0; @@ -12612,6 +12660,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (ospf->lsa_refresh_interval != OSPF_LSA_REFRESH_INTERVAL_DEFAULT) vty_out(vty, " refresh timer %d\n", ospf->lsa_refresh_interval); + if (ospf->fr_configured) + vty_out(vty, " flood-reduction\n"); + /* Redistribute information print. */ config_write_ospf_redistribute(vty, ospf); @@ -12994,6 +13045,143 @@ DEFPY_HIDDEN(ospf_maxage_delay_timer, ospf_maxage_delay_timer_cmd, return CMD_SUCCESS; } +/* + * ------------------------------------------------------------------------* + * Following is (vty) configuration functions for flood-reduction handling. + * ------------------------------------------------------------------------ + */ + +DEFPY(flood_reduction, flood_reduction_cmd, "flood-reduction", + "flood reduction feature\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *area; + struct listnode *node; + + /* Turn on the Flood Reduction feature for the router. */ + if (!ospf->fr_configured) { + ospf->fr_configured = true; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction: OFF -> ON"); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area) { + ospf_area_update_fr_state(area); + ospf_refresh_area_self_lsas(area); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(flood_reduction_area, flood_reduction_area_cmd, + "area flood-reduction", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable flood reduction for area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *oa; + int idx = 1; + int format; + int ret; + const char *areaid; + struct in_addr area_id; + + areaid = argv[idx]->arg; + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + oa = ospf_area_lookup_by_area_id(ospf, area_id); + if (!oa) { + vty_out(vty, "OSPF area ID not present\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn on the Flood Reduction feature for the area. */ + if (!oa->fr_info.configured) { + oa->fr_info.configured = true; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction area %pI4 : OFF -> ON", + &oa->area_id); + ospf_area_update_fr_state(oa); + ospf_refresh_area_self_lsas(oa); + } + + return CMD_SUCCESS; +} + +DEFPY(no_flood_reduction, no_flood_reduction_cmd, "no flood-reduction", + NO_STR "flood reduction feature\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct listnode *node; + struct ospf_area *area; + + /* Turn off the Flood Reduction feature for the router. */ + if (ospf->fr_configured) { + ospf->fr_configured = false; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction: ON -> OFF"); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area) { + ospf_area_update_fr_state(area); + ospf_refresh_area_self_lsas(area); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(no_flood_reduction_area, no_flood_reduction_area_cmd, + "no area flood-reduction", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Disable flood reduction for area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *oa; + int idx = 2; + int format; + int ret; + const char *areaid; + struct in_addr area_id; + + areaid = argv[idx]->arg; + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + oa = ospf_area_lookup_by_area_id(ospf, area_id); + if (!oa) { + vty_out(vty, "OSPF area ID not present\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn off the Flood Reduction feature for the area. */ + if (oa->fr_info.configured) { + oa->fr_info.configured = false; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction area %pI4 : ON -> OFF", + &oa->area_id); + ospf_area_update_fr_state(oa); + ospf_refresh_area_self_lsas(oa); + } + + return CMD_SUCCESS; +} + void ospf_vty_clear_init(void) { install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd); @@ -13154,6 +13342,12 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_lsa_refresh_timer_cmd); install_element(OSPF_NODE, &ospf_maxage_delay_timer_cmd); + /* Flood Reduction commands */ + install_element(OSPF_NODE, &flood_reduction_cmd); + install_element(OSPF_NODE, &no_flood_reduction_cmd); + install_element(OSPF_NODE, &flood_reduction_area_cmd); + install_element(OSPF_NODE, &no_flood_reduction_area_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); From ee09fbc0a57dcf323223e669db5e30b7e49bdded Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Mon, 21 Nov 2022 03:35:01 -0800 Subject: [PATCH 3/7] ospfd: ospf hello changes. Description: The changes involve setting DC bit on ospf hellos and addition of new DO_NOT_AGE flag. Signed-off-by: Manoj Naragund --- ospfd/ospf_lsa.h | 24 ++++++++++++++++++++++++ ospfd/ospf_packet.c | 11 +++++++++++ ospfd/ospf_route.c | 10 ++++++++++ 3 files changed, 45 insertions(+) diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 1caf8047e4..a5397b5fdb 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -60,6 +60,7 @@ /* OSPF LSA header. */ struct lsa_header { uint16_t ls_age; +#define DO_NOT_AGE 0x8000 uint8_t options; uint8_t type; struct in_addr id; @@ -233,6 +234,9 @@ enum lsid_status { LSID_AVAILABLE = 0, LSID_CHANGE, LSID_NOT_AVAILABLE }; || (type == OSPF_SUMMARY_LSA) || (type == OSPF_ASBR_SUMMARY_LSA) \ || (type == OSPF_AS_EXTERNAL_LSA) || (type == OSPF_AS_NSSA_LSA)) +#define OSPF_FR_CONFIG(o, a) \ + (o->fr_configured || ((a != NULL) ? a->fr_info.configured : 0)) + /* Prototypes. */ /* XXX: Eek, time functions, similar are in lib/thread.c */ extern struct timeval int2tv(int); @@ -358,4 +362,24 @@ extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); extern void ospf_maxage_lsa_remover(struct thread *thread); +extern bool ospf_check_dna_lsa(const struct ospf_lsa *lsa); +extern void ospf_refresh_area_self_lsas(struct ospf_area *area); + +/** @brief Check if the LSA is an indication LSA. + * @param lsa pointer. + * @return true or false based on lsa info. + */ +static inline bool ospf_check_indication_lsa(struct ospf_lsa *lsa) +{ + struct summary_lsa *sl = NULL; + + if (lsa->data->type == OSPF_ASBR_SUMMARY_LSA) { + sl = (struct summary_lsa *)lsa->data; + if ((GET_METRIC(sl->metric) == OSPF_LS_INFINITY) && + !CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + return true; + } + + return false; +} #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index b22fe5d99b..4680f496e1 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -43,6 +43,7 @@ #include "ospfd/ospf_network.h" #include "ospfd/ospf_interface.h" #include "ospfd/ospf_ism.h" +#include "ospfd/ospf_abr.h" #include "ospfd/ospf_asbr.h" #include "ospfd/ospf_lsa.h" #include "ospfd/ospf_lsdb.h" @@ -3332,6 +3333,14 @@ static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) else stream_putw(s, 0); /* hello-interval of 0 for fast-hellos */ + /* Check if flood-reduction is enabled, + * if yes set the DC bit in the options. + */ + if (OSPF_FR_CONFIG(oi->ospf, oi->area)) + SET_FLAG(OPTIONS(oi), OSPF_OPTION_DC); + else if (CHECK_FLAG(OPTIONS(oi), OSPF_OPTION_DC)) + UNSET_FLAG(OPTIONS(oi), OSPF_OPTION_DC); + if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: options: %x, int: %s", __func__, OPTIONS(oi), IF_NAME(oi)); @@ -3420,6 +3429,8 @@ static int ospf_make_db_desc(struct ospf_interface *oi, options = OPTIONS(oi); if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) SET_FLAG(options, OSPF_OPTION_O); + if (OSPF_FR_CONFIG(oi->ospf, oi->area)) + SET_FLAG(options, OSPF_OPTION_DC); stream_putc(s, options); /* DD flags */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 6360d8ec60..9b9b61bdb3 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -986,6 +986,16 @@ void ospf_prune_unreachable_routers(struct route_table *rtrs) &or->u.std.area_id); } + /* Unset the DNA flag on lsa, if the router + * which generated this lsa is no longer + * reachabele. + */ + (CHECK_FLAG(or->u.std.origin->ls_age, + DO_NOT_AGE)) + ? UNSET_FLAG(or->u.std.origin->ls_age, + DO_NOT_AGE) + : 0; + listnode_delete(paths, or); ospf_route_free(or); } From 516c4c665aaf8608b340259b8125c149136d89c9 Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Mon, 21 Nov 2022 04:50:16 -0800 Subject: [PATCH 4/7] ospfd: Core logic implementation. Description: Code changes involves. 1. Count the no.of router LSAs received with DC options bit set, supporting do not age(DNA). 2. If no of router LSAs received with DC bit set is equal to total no of LSAs in the router lsdb, then all the routers in the area support do not age processing. 3. Flood the self originated LSAs with DNA flag if all routers in the area supports the feature. 4. Stop aging of the LSAs recived with DO_NOT_AGE bit set from other routers. 5. Self originated DO_NOT_AGE lsas will still be aging in their own database. Signed-off-by: Manoj Naragund --- ospfd/ospf_flood.c | 137 +++++++++++++++++++++++++++++++++++++++++++++ ospfd/ospf_flood.h | 2 + ospfd/ospf_lsa.c | 88 +++++++++++++++++++++++++++-- ospfd/ospf_lsdb.c | 21 +++++++ 4 files changed, 244 insertions(+), 4 deletions(-) diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 4bbeee2d74..e3896b4535 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -50,6 +50,75 @@ extern struct zclient *zclient; +/** @brief Function to refresh type-5 and type-7 DNA + * LSAs when we receive an indication LSA. + * @param Ospf instance. + * @return Void. + */ +void ospf_refresh_dna_type5_and_type7_lsas(struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_lsa *lsa = NULL; + + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ospf_lsa_refresh(ospf, lsa); + + LSDB_LOOP (NSSA_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ospf_lsa_refresh(ospf, lsa); +} + +/** @brief Function to update area flood reduction states. + * @param area pointer. + * @return Void. + */ +void ospf_area_update_fr_state(struct ospf_area *area) +{ + unsigned int count_router_lsas = 0; + + if (area == NULL) + return; + + count_router_lsas = + (unsigned int)(ospf_lsdb_count(area->lsdb, OSPF_ROUTER_LSA) - + ospf_lsdb_count_self(area->lsdb, + OSPF_ROUTER_LSA)); + + if (count_router_lsas > + (unsigned int)area->fr_info.router_lsas_recv_dc_bit) { + area->fr_info.enabled = false; + area->fr_info.area_dc_clear = true; + return; + } else if (count_router_lsas < + (unsigned int)area->fr_info.router_lsas_recv_dc_bit) { + /* This can never happen, total number of router lsas received + * can never be less than router lsas received with dc bit set + */ + OSPF_LOG_ERR("%s: Counter mismatch for area %pI4", __func__, + &area->area_id); + OSPF_LOG_ERR( + "%s: router LSAs in lsdb %d router LSAs recvd with dc bit set %d", + __func__, count_router_lsas, + area->fr_info.router_lsas_recv_dc_bit); + return; + } + + area->fr_info.area_dc_clear = false; + + if (OSPF_FR_CONFIG(area->ospf, area)) { + if (!area->fr_info.enabled) { + area->fr_info.enabled = true; + area->fr_info.state_changed = true; + } + } else { + area->fr_info.enabled = false; + area->fr_info.area_dc_clear = true; + } +} + /* Do the LSA acking specified in table 19, Section 13.5, row 2 * This get called from ospf_flood_out_interface. Declared inline * for speed. */ @@ -428,6 +497,55 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, if (!(new = ospf_lsa_install(ospf, oi, new))) return -1; /* unknown LSA type or any other error condition */ + /* check if the installed LSA is an indication LSA */ + if (ospf_check_indication_lsa(new) && !IS_LSA_SELF(new) && + !IS_LSA_MAXAGE(new)) { + new->area->fr_info.area_ind_lsa_recvd = true; + /* check if there are already type 5 LSAs originated + * with DNA bit set, if yes reoriginate those LSAs. + */ + ospf_refresh_dna_type5_and_type7_lsas(ospf); + } + + /* Check if we recived an indication LSA flush on backbone + * network. + */ + ospf_recv_indication_lsa_flush(new); + + if (new->area && OSPF_FR_CONFIG(ospf, new->area)) { + struct lsa_header const *lsah = new->data; + + if (!CHECK_FLAG(lsah->options, OSPF_OPTION_DC) && + !ospf_check_indication_lsa(new)) { + + new->area->fr_info.area_dc_clear = true; + /* check of previously area supported flood reduction */ + if (new->area->fr_info.enabled) { + new->area->fr_info.enabled = false; + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "Flood Reduction STATE on -> off by %s LSA", + dump_lsa_key(new)); + /* if yes update all the lsa to the area the + * new LSAs will have DNA bit set to 0. + */ + ospf_refresh_area_self_lsas(new->area); + } + } else if (!new->area->fr_info.enabled) { + /* check again after installing new LSA that area + * supports flood reduction. + */ + ospf_area_update_fr_state(new->area); + if (new->area->fr_info.enabled) { + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "Flood Reduction STATE off -> on by %s LSA", + dump_lsa_key(new)); + ospf_refresh_area_self_lsas(new->area); + } + } + } + /* Acknowledge the receipt of the LSA by sending a Link State Acknowledgment packet back out the receiving interface. */ if (lsa_ack_flag) @@ -465,6 +583,25 @@ int ospf_flood_through_interface(struct ospf_interface *oi, if (!ospf_if_is_enable(oi)) return 0; + /* If flood reduction is configured, set the DC bit on the lsa. */ + if (IS_LSA_SELF(lsa)) { + if (OSPF_FR_CONFIG(oi->area->ospf, oi->area)) { + if (!ospf_check_indication_lsa(lsa)) { + SET_FLAG(lsa->data->options, OSPF_OPTION_DC); + ospf_lsa_checksum(lsa->data); + } + } else if (CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) { + UNSET_FLAG(lsa->data->options, OSPF_OPTION_DC); + ospf_lsa_checksum(lsa->data); + } + + /* If flood reduction is enabled then set DNA bit on the + * self lsas. + */ + if (oi->area->fr_info.enabled) + SET_FLAG(lsa->data->ls_age, DO_NOT_AGE); + } + /* Remember if new LSA is added to a retransmit list. */ retx_flag = 0; diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h index 95a5b358c9..dd8b6b7fcc 100644 --- a/ospfd/ospf_flood.h +++ b/ospfd/ospf_flood.h @@ -68,5 +68,7 @@ extern struct external_info *ospf_external_info_check(struct ospf *, struct ospf_lsa *); extern void ospf_lsdb_init(struct ospf_lsdb *); +extern void ospf_area_update_fr_state(struct ospf_area *area); +extern void ospf_refresh_dna_type5_and_type7_lsas(struct ospf *ospf); #endif /* _ZEBRA_OSPF_FLOOD_H */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 3e8b7b283d..b3d866f5eb 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -85,6 +85,16 @@ uint32_t get_metric(uint8_t *metric) return m; } +/** @brief The Function checks self generated DoNotAge. + * @param lsa pointer. + * @return true or false. + */ +bool ospf_check_dna_lsa(const struct ospf_lsa *lsa) +{ + return ((IS_LSA_SELF(lsa) && CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ? true + : false); +} struct timeval int2tv(int a) { @@ -136,6 +146,16 @@ int get_age(struct ospf_lsa *lsa) { struct timeval rel; + /* As per rfc4136, the self-originated LSAs in their + * own database keep aging, however rfc doesn't tell + * till how long the LSA should be aged, as of now + * we are capping it for OSPF_LSA_MAXAGE. + */ + + /* If LSA is marked as donotage */ + if (CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) && !IS_LSA_SELF(lsa)) + return ntohs(lsa->data->ls_age); + monotime_since(&lsa->tv_recv, &rel); return ntohs(lsa->data->ls_age) + rel.tv_sec; } @@ -1134,6 +1154,10 @@ static struct ospf_lsa *ospf_network_lsa_refresh(struct ospf_lsa *lsa) } return NULL; } + + if (oi->state != ISM_DR) + return NULL; + /* Delete LSA from neighbor retransmit-list. */ ospf_ls_retransmit_delete_nbr_area(area, lsa); @@ -1533,10 +1557,15 @@ static struct ospf_lsa *ospf_summary_asbr_lsa_refresh(struct ospf *ospf, struct ospf_lsa *new; struct summary_lsa *sl; struct prefix p; + bool ind_lsa = false; /* Sanity check. */ assert(lsa->data); + if (lsa->area->fr_info.indication_lsa_self && + (lsa->area->fr_info.indication_lsa_self == lsa)) + ind_lsa = true; + sl = (struct summary_lsa *)lsa->data; p.prefixlen = ip_masklen(sl->mask); new = ospf_summary_asbr_lsa_new(lsa->area, &p, GET_METRIC(sl->metric), @@ -1551,6 +1580,9 @@ static struct ospf_lsa *ospf_summary_asbr_lsa_refresh(struct ospf *ospf, /* Flood LSA through area. */ ospf_flood_through_area(new->area, NULL, new); + if (ind_lsa) + new->area->fr_info.indication_lsa_self = new; + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug("LSA[Type%d:%pI4]: summary-ASBR-LSA refresh", new->data->type, &new->data->id); @@ -3641,6 +3673,49 @@ void ospf_flush_self_originated_lsas_now(struct ospf *ospf) return; } +/** @brief Function to refresh all the self originated + * LSAs for area, when FR state change happens. + * @param area pointer. + * @return Void. + */ +void ospf_refresh_area_self_lsas(struct ospf_area *area) +{ + struct listnode *node2; + struct listnode *nnode2; + struct ospf_interface *oi; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (!area) + return; + + if (area->router_lsa_self) + ospf_lsa_refresh(area->ospf, area->router_lsa_self); + + for (ALL_LIST_ELEMENTS(area->oiflist, node2, nnode2, oi)) + if (oi->network_lsa_self) + ospf_lsa_refresh(oi->ospf, oi->network_lsa_self); + + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (EXTERNAL_LSDB(area->ospf), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB(area->ospf), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); +} + /* If there is self-originated LSA, then return 1, otherwise return 0. */ /* An interface-independent version of ospf_lsa_is_self_originated */ int ospf_lsa_is_self_originated(struct ospf *ospf, struct ospf_lsa *lsa) @@ -3976,6 +4051,7 @@ void ospf_lsa_refresh_walker(struct thread *t) struct ospf_lsa *lsa; int i; struct list *lsa_to_refresh = list_new(); + bool dna_lsa; if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) zlog_debug("LSA[Refresh]: %s: start", __func__); @@ -4034,10 +4110,14 @@ void ospf_lsa_refresh_walker(struct thread *t) ospf->lsa_refresher_started = monotime(NULL); for (ALL_LIST_ELEMENTS(lsa_to_refresh, node, nnode, lsa)) { - ospf_lsa_refresh(ospf, lsa); - assert(lsa->lock > 0); - ospf_lsa_unlock( - &lsa); /* lsa_refresh_queue & temp for lsa_to_refresh*/ + dna_lsa = ospf_check_dna_lsa(lsa); + if (!dna_lsa) { /* refresh only non-DNA LSAs */ + ospf_lsa_refresh(ospf, lsa); + assert(lsa->lock > 0); + ospf_lsa_unlock(&lsa); /* lsa_refresh_queue & temp for + * lsa_to_refresh. + */ + } } list_delete(&lsa_to_refresh); diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index f4fb858a5f..d55705b741 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -92,6 +92,21 @@ static void ospf_lsdb_delete_entry(struct ospf_lsdb *lsdb, lsdb->type[lsa->data->type].count--; lsdb->type[lsa->data->type].checksum -= ntohs(lsa->data->checksum); lsdb->total--; + + /* Decrement number of router LSAs received with DC bit set */ + if (lsa->area && (lsa->area->lsdb == lsdb) && !IS_LSA_SELF(lsa) && + (lsa->data->type == OSPF_ROUTER_LSA) && + CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + lsa->area->fr_info.router_lsas_recv_dc_bit--; + + /* + * If the LSA being deleted is indication LSA, then set the + * pointer to NULL. + */ + if (lsa->area && lsa->area->fr_info.indication_lsa_self && + (lsa->area->fr_info.indication_lsa_self == lsa)) + lsa->area->fr_info.indication_lsa_self = NULL; + rn->info = NULL; route_unlock_node(rn); #ifdef MONITOR_LSDB_CHANGE @@ -128,6 +143,12 @@ void ospf_lsdb_add(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) lsdb->type[lsa->data->type].count++; lsdb->total++; + /* Increment number of router LSAs received with DC bit set */ + if (lsa->area && (lsa->area->lsdb == lsdb) && !IS_LSA_SELF(lsa) && + (lsa->data->type == OSPF_ROUTER_LSA) && + CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + lsa->area->fr_info.router_lsas_recv_dc_bit++; + #ifdef MONITOR_LSDB_CHANGE if (lsdb->new_lsa_hook != NULL) (*lsdb->new_lsa_hook)(lsa); From 3d4e02e230a2ee29340ec569f73485566193dfc2 Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Mon, 21 Nov 2022 05:02:49 -0800 Subject: [PATCH 5/7] ospfd: generation of indication LSAs on area border routers. Description: code changes involve changes on abr routers to generate and flush indication LSAs, on backbone and non-backbone areas in different scenarios. Signed-off-by: Manoj Naragund --- ospfd/ospf_abr.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++- ospfd/ospf_abr.h | 21 ++++ 2 files changed, 290 insertions(+), 3 deletions(-) diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 4746b2fc66..eaf5d8eb54 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -1574,6 +1574,254 @@ static void ospf_abr_announce_stub_defaults(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } +/** @brief Function to check and generate indication + * LSA for area on which we received + * indication LSA flush. + * @param Ospf instance. + * @param Area on which indication lsa flush is to be generated. + * @return Void. + */ +void ospf_generate_indication_lsa(struct ospf *ospf, struct ospf_area *area) +{ + bool area_fr_not_supp = false; + + /* Check if you have any area which doesn't support + * flood reduction. + */ + + area_fr_not_supp = ospf_check_fr_enabled_all(ospf) ? false : true; + + /* If any one of the area doestn't support FR, generate + * indication LSA on behalf of that area. + */ + + if (area_fr_not_supp && !area->fr_info.area_ind_lsa_recvd && + !area->fr_info.indication_lsa_self && + !area->fr_info.area_dc_clear) { + + struct prefix_ipv4 p; + struct ospf_lsa *new; + + p.family = AF_INET; + p.prefix = ospf->router_id; + p.prefixlen = IPV4_MAX_BITLEN; + + new = ospf_summary_asbr_lsa_originate(&p, OSPF_LS_INFINITY, + area); + if (!new) { + zlog_debug("%s: Indication lsa originate failed", + __func__); + return; + } + /* save the indication lsa for that area */ + area->fr_info.indication_lsa_self = new; + } +} + +/** @brief Function to receive and process indication LSA + * flush from area. + * @param lsa being flushed. + * @return Void. + */ +void ospf_recv_indication_lsa_flush(struct ospf_lsa *lsa) +{ + if (!IS_LSA_SELF(lsa) && IS_LSA_MAXAGE(lsa) && + ospf_check_indication_lsa(lsa)) { + lsa->area->fr_info.area_ind_lsa_recvd = false; + + OSPF_LOG_INFO("%s: Received an ind lsa: %pI4 area %pI4", + __func__, &lsa->data->id, &lsa->area->area_id); + + if (!IS_OSPF_ABR(lsa->area->ospf)) + return; + + /* If the LSA received is a indication LSA with maxage on + * the network, then check and regenerate indication + * LSA if any of our areas don't support flood reduction. + */ + ospf_generate_indication_lsa(lsa->area->ospf, lsa->area); + } +} + +/** @brief Function to generate indication LSAs. + * @param Ospf instance. + * @param Area on behalf of which indication + * LSA is generated LSA. + * @return Void. + */ +void ospf_abr_generate_indication_lsa(struct ospf *ospf, + const struct ospf_area *area) +{ + struct ospf_lsa *new; + struct listnode *node; + struct ospf_area *o_area; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, o_area)) { + if (o_area == area) + continue; + + if (o_area->fr_info.indication_lsa_self || + o_area->fr_info.area_ind_lsa_recvd || + o_area->fr_info.area_dc_clear) { + /* if the area has already received an + * indication LSA or if area already has + * LSAs with DC bit 0 other than + * indication LSA then don't generate + * indication LSA in those areas. + */ + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Area %pI4 has LSAs with dc bit clear", + &o_area->area_id); + continue; + + } else { + + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = ospf->router_id; + p.prefixlen = IPV4_MAX_BITLEN; + + new = ospf_summary_asbr_lsa_originate( + &p, OSPF_LS_INFINITY, o_area); + if (!new) { + zlog_debug( + "%s: Indication lsa originate Failed", + __func__); + return; + } + /* save the indication lsa for that area */ + o_area->fr_info.indication_lsa_self = new; + } + } +} + +/** @brief Flush the indication LSA from all the areas + * of ospf instance. + * @param Ospf instance. + * @return Void. + */ +void ospf_flush_indication_lsas(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area->fr_info.indication_lsa_self) { + OSPF_LOG_INFO( + "Flushing ind lsa: %pI4 area %pI4", + &area->fr_info.indication_lsa_self->data->id, + &area->area_id); + ospf_schedule_lsa_flush_area( + area, area->fr_info.indication_lsa_self); + area->fr_info.indication_lsa_self = NULL; + } + } +} + +/** @brief Check if flood reduction is enabled on + * all the areas. + * @param Ospf instance. + * @return Void. + */ +bool ospf_check_fr_enabled_all(struct ospf *ospf) +{ + const struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (!ospf_check_area_fr_enabled(area)) + return false; + + return true; +} + +/** @brief Abr function to check conditions for generation + * of indication. LSAs/announcing non-DNA routers + * in the area. + * @param thread + * @return 0. + */ +static void ospf_abr_announce_non_dna_routers(struct thread *thread) +{ + struct ospf_area *area; + struct listnode *node; + struct ospf *ospf = THREAD_ARG(thread); + + THREAD_OFF(ospf->t_abr_fr); + + if (!IS_OSPF_ABR(ospf)) + return; + + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "%s: Area %pI4 FR enabled: %d", __func__, + &area->area_id, area->fr_info.enabled); + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "LSA with DC bit clear: %d Recived indication LSA: %d", + area->fr_info.area_dc_clear, + area->fr_info.area_ind_lsa_recvd); + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "FR state change: %d", + area->fr_info.state_changed); + if (!OSPF_IS_AREA_BACKBONE(area) && + area->fr_info.area_dc_clear) { + /* rfc4136 rfc1793: Suppose if the abr is connected to + * a regular non-backbone OSPF area, Furthermore if + * the area has LSAs with the DC-bit clear, other + * than indication-LSAs. Then originate indication-LSAs + * into all other directly-connected "regular" areas, + * including the backbone area. + */ + ospf_abr_generate_indication_lsa(ospf, area); + } + + if (OSPF_IS_AREA_BACKBONE(area) && + (area->fr_info.area_dc_clear || + area->fr_info.area_ind_lsa_recvd)) { + /* rfc4136 rfc1793: Suppose if the abr is connected to + * backbone OSPF area. Furthermore, if backbone has + * LSAs with the DC-bit clear that are either + * a) not indication-LSAs or indication-LSAs or + * b) indication-LSAs that have been originated by + * other routers, + * then originate indication-LSAs into all other + * directly-connected "regular" non-backbone areas. + */ + ospf_abr_generate_indication_lsa(ospf, area); + } + + if (area->fr_info.enabled && area->fr_info.state_changed && + area->fr_info.indication_lsa_self) { + /* Ospf area flood reduction state changed + * area now supports flood reduction. + * check if all other areas support flood reduction + * if yes then flush indication LSAs generated in + * all the areas. + */ + if (ospf_check_fr_enabled_all(ospf)) + ospf_flush_indication_lsas(ospf); + + area->fr_info.state_changed = false; + } + + /* If previously we had generated indication lsa + * but now area has lsas with dc bit set to 0 + * apart from indication lsa, we'll clear indication lsa + */ + if (area->fr_info.area_dc_clear && + area->fr_info.indication_lsa_self) { + ospf_schedule_lsa_flush_area( + area, area->fr_info.indication_lsa_self); + area->fr_info.indication_lsa_self = NULL; + } + } + + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__); +} + static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf, struct ospf_lsa *lsa) { @@ -1628,9 +1876,13 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) ospf_lsa_flush_area(lsa, area); LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) - if (ospf_lsa_is_self_originated(ospf, lsa)) - if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) - ospf_lsa_flush_area(lsa, area); + if (ospf_lsa_is_self_originated(ospf, lsa) && + !CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED) && + /* Do not remove indication LSAs while + * flushing unapproved summaries. + */ + !ospf_check_indication_lsa(lsa)) + ospf_lsa_flush_area(lsa, area); } if (IS_DEBUG_OSPF_EVENT) @@ -1793,6 +2045,20 @@ void ospf_abr_task(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: announce stub defaults", __func__); ospf_abr_announce_stub_defaults(ospf); + + if (ospf->fr_configured) { + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "%s(): announce non-DNArouters", + __func__); + /* + * Schedule indication lsa generation timer, + * giving time for route synchronization in + * all the routers. + */ + thread_add_timer( + master, ospf_abr_announce_non_dna_routers, ospf, + OSPF_ABR_DNA_TIMER, &ospf->t_abr_fr); + } } if (IS_DEBUG_OSPF_EVENT) diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h index c523638fb3..b541506e00 100644 --- a/ospfd/ospf_abr.h +++ b/ospfd/ospf_abr.h @@ -23,6 +23,11 @@ #define _ZEBRA_OSPF_ABR_H #define OSPF_ABR_TASK_DELAY 5 +#define OSPF_ABR_DNA_TIMER 10 +/* Delay in announceing Non-DNA routers + * so that LSAs are completely synced + * before generating indication LSAs. + */ #define OSPF_AREA_RANGE_ADVERTISE (1 << 0) #define OSPF_AREA_RANGE_SUBSTITUTE (1 << 1) @@ -84,4 +89,20 @@ extern void ospf_schedule_abr_task(struct ospf *); extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t, struct ospf_area *); extern void ospf_abr_nssa_check_status(struct ospf *ospf); +extern void ospf_abr_generate_indication_lsa(struct ospf *ospf, + const struct ospf_area *area); +extern void ospf_flush_indication_lsas(struct ospf *ospf); +extern void ospf_generate_indication_lsa(struct ospf *ospf, + struct ospf_area *area); +extern bool ospf_check_fr_enabled_all(struct ospf *ospf); +extern void ospf_recv_indication_lsa_flush(struct ospf_lsa *lsa); + +/** @brief Static inline functions. + * @param Area pointer. + * @return area Flood Reduction status. + */ +static inline bool ospf_check_area_fr_enabled(const struct ospf_area *area) +{ + return area->fr_info.enabled ? true : false; +} #endif /* _ZEBRA_OSPF_ABR_H */ From 701f4f616a6b43b35392a7e852dcbcc9d880165a Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Mon, 21 Nov 2022 05:04:46 -0800 Subject: [PATCH 6/7] ospfd: addition of OSPF_LOG. Description: Addition of OSPF_LOG for conditionally logging ospf messages, at different log levels. Signed-off-by: Manoj Naragund --- lib/log.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/log.h b/lib/log.h index 3011fa9a99..5fb7ee030b 100644 --- a/lib/log.h +++ b/lib/log.h @@ -129,6 +129,21 @@ extern int proto_redistnum(int afi, const char *s); extern const char *zserv_command_string(unsigned int command); +#define OSPF_LOG(level, cond, fmt, ...) \ + do { \ + if (cond) \ + zlog_##level(fmt, ##__VA_ARGS__); \ + } while (0) + +#define OSPF_LOG_ERR(fmt, ...) OSPF_LOG(err, true, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_WARN(fmt, ...) OSPF_LOG(warn, true, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_INFO(fmt, ...) OSPF_LOG(info, true, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_DEBUG(cond, fmt, ...) OSPF_LOG(debug, cond, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_NOTICE(fmt, ...) OSPF_LOG(notice, true, fmt, ##__VA_ARGS__) /* structure useful for avoiding repeated rendering of the same timestamp */ struct timestamp_control { From 044561b1bb14432f20e3e9d5077964f4e7486972 Mon Sep 17 00:00:00 2001 From: Manoj Naragund Date: Tue, 29 Nov 2022 21:11:21 -0800 Subject: [PATCH 7/7] tests: Added ospfv2 flood reduction topotest changes. Have added topotest to verify below scenarios. 1. Verify OSPF Flood reduction functionality with ospf enabled on process level. 2. Verify OSPF Flood reduction functionality with ospf enabled on area level. 3. Verify OSPF Flood reduction functionality between different area's Have sussessfully tested these in my local setup Signed-off-by: nguggarigoud --- tests/topotests/lib/ospf.py | 583 ++++++++- .../ospf_flood_reduction.json | 214 ++++ .../test_ospf_flood_reduction.py | 1066 +++++++++++++++++ 3 files changed, 1843 insertions(+), 20 deletions(-) create mode 100644 tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json create mode 100644 tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index e7ea7d32ba..0049cd127c 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -21,6 +21,7 @@ import ipaddress import sys from copy import deepcopy +from time import sleep # Import common_config to use commomnly used APIs from lib.common_config import ( @@ -201,6 +202,24 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): cmd = "no maximum-paths" config_data.append(cmd) + # Flood reduction. + flood_data = ospf_data.setdefault("flood-reduction", {}) + if flood_data: + cmd = "flood-reduction" + del_action = ospf_data.setdefault("del_flood_reduction", False) + if del_action: + cmd = "no flood-reduction" + config_data.append(cmd) + + # LSA refresh timer - A hidden command. + refresh_data = ospf_data.setdefault("lsa-refresh", {}) + if refresh_data: + cmd = "ospf lsa-refresh {}".format(refresh_data) + del_action = ospf_data.setdefault("del_lsa_refresh", False) + if del_action: + cmd = "no ospf lsa-refresh" + config_data.append(cmd) + # redistribute command redistribute_data = ospf_data.setdefault("redistribute", {}) if redistribute_data: @@ -233,6 +252,9 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): if "type" in area: cmd = cmd + " {}".format(area["type"]) + if "flood-reduction" in area: + cmd = cmd + " flood-reduction" + del_action = area.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) @@ -737,9 +759,6 @@ def verify_ospf_neighbor( return result -################################ -# Verification procs -################################ @retry(retry_timeout=50) def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False): """ @@ -1352,8 +1371,10 @@ def verify_ospf_interface( return result -@retry(retry_timeout=20) -def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): +@retry(retry_timeout=40) +def verify_ospf_database( + tgen, topo, dut, input_dict, vrf=None, lsatype=None, rid=None, expected=True +): """ This API is to verify ospf lsa's by running show ip ospf database command. @@ -1411,7 +1432,23 @@ def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): rnode = tgen.routers()[dut] logger.info("Verifying OSPF interface on router %s:", dut) - show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True) + + if not rid: + rid = "self-originate" + if lsatype: + if vrf is None: + command = "show ip ospf database {} {} json".format(lsatype, rid) + else: + command = "show ip ospf database {} {} vrf {} json".format( + lsatype, rid, vrf + ) + else: + if vrf is None: + command = "show ip ospf database json" + else: + command = "show ip ospf database vrf {} json".format(vrf) + + show_ospf_json = run_frr_cmd(rnode, command, isjson=True) # Verifying output dictionary show_ospf_json is empty or not if not bool(show_ospf_json): errormsg = "OSPF is not running" @@ -1420,26 +1457,40 @@ def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): # for inter and inter lsa's ospf_db_data = input_dict.setdefault("areas", None) ospf_external_lsa = input_dict.setdefault("AS External Link States", None) + # import pdb; pdb.set_trace() if ospf_db_data: for ospf_area, area_lsa in ospf_db_data.items(): - if ospf_area in show_ospf_json["areas"]: - if "Router Link States" in area_lsa: - for lsa in area_lsa["Router Link States"]: + if ospf_area in show_ospf_json["routerLinkStates"]["areas"]: + if "routerLinkStates" in area_lsa: + for lsa in area_lsa["routerLinkStates"]: + _advrtr = lsa.setdefault("advertisedRouter", None) + _options = lsa.setdefault("options", None) + if ( - lsa - in show_ospf_json["areas"][ospf_area]["Router Link States"] + _options + and lsa["lsaId"] + == show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["linkStateId"] + and lsa["options"] + == show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["options"] ): - logger.info( - "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s", - router, - ospf_area, - lsa, - ) result = True + break else: - errormsg = ( - "[DUT: {}] OSPF LSDB area {}: expected" - " Router LSA is {}".format(router, ospf_area, lsa) + errormsg = '[DUT: {}] OSPF LSA options: expected {}, Received Options are {} lsa["options"] {} OSPF LSAID: expected lsaid {}, Received lsaid {}'.format( + dut, + show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["options"], + _options, + lsa["options"], + show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["linkStateId"], + lsa["lsaId"], ) return errormsg if "Net Link States" in area_lsa: @@ -2489,3 +2540,495 @@ def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result + + +def get_ospf_database(tgen, topo, dut, input_dict, vrf=None, lsatype=None, rid=None): + """ + This API is to return ospf lsa's by running + show ip ospf database command. + + Parameters + ---------- + * `tgen` : Topogen object + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `topo` : next to be verified + * `vrf` : vrf to be checked + * `lsatype` : type of lsa to be checked + * `rid` : router id for lsa to be checked + Usage + ----- + input_dict = { + "areas": { + "0.0.0.0": { + "routerLinkStates": { + "100.1.1.0-100.1.1.0": { + "LSID": "100.1.1.0", + "Advertised router": "100.1.1.0", + "LSA Age": 130, + "Sequence Number": "80000006", + "Checksum": "a703", + "Router links": 3 + } + }, + "networkLinkStates": { + "10.0.0.2-100.1.1.1": { + "LSID": "10.0.0.2", + "Advertised router": "100.1.1.1", + "LSA Age": 137, + "Sequence Number": "80000001", + "Checksum": "9583" + } + }, + }, + } + } + result = get_ospf_database(tgen, topo, dut, input_dict) + + Returns + ------- + True or False (Error Message) + """ + + result = False + router = dut + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + sleep(10) + if "ospf" not in topo["routers"][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut) + return errormsg + + rnode = tgen.routers()[dut] + + logger.info("Verifying OSPF interface on router %s:", dut) + if not rid: + rid = "self-originate" + if lsatype: + if vrf is None: + command = "show ip ospf database {} {} json".format(lsatype, rid) + else: + command = "show ip ospf database {} {} vrf {} json".format( + lsatype, rid, vrf + ) + else: + if vrf is None: + command = "show ip ospf database json" + else: + command = "show ip ospf database vrf {} json".format(vrf) + + show_ospf_json = run_frr_cmd(rnode, command, isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # for inter and inter lsa's + ospf_db_data = input_dict.setdefault("areas", None) + ospf_external_lsa = input_dict.setdefault("asExternalLinkStates", None) + + if ospf_db_data: + for ospf_area, area_lsa in ospf_db_data.items(): + if "areas" in show_ospf_json and ospf_area in show_ospf_json["areas"]: + if "routerLinkStates" in area_lsa: + for lsa in area_lsa["routerLinkStates"]: + for rtrlsa in show_ospf_json["areas"][ospf_area][ + "routerLinkStates" + ]: + _advrtr = lsa.setdefault("advertisedRouter", None) + _options = lsa.setdefault("options", None) + if ( + _advrtr + and lsa["lsaId"] == rtrlsa["lsaId"] + and lsa["advertisedRouter"] + == rtrlsa["advertisedRouter"] + ): + result = True + break + if ( + _options + and lsa["lsaId"] == rtrlsa["lsaId"] + and lsa["options"] == rtrlsa["options"] + ): + result = True + break + + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Router LSA is {}\n found Router LSA: {}".format( + router, ospf_area, lsa, rtrlsa + ) + ) + return errormsg + + if "networkLinkStates" in area_lsa: + for lsa in area_lsa["networkLinkStates"]: + for netlsa in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ]: + if ( + lsa + in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ] + ): + if ( + lsa["lsaId"] == netlsa["lsaId"] + and lsa["advertisedRouter"] + == netlsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Network LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "summaryLinkStates" in area_lsa: + for lsa in area_lsa["summaryLinkStates"]: + for t3lsa in show_ospf_json["areas"][ospf_area][ + "summaryLinkStates" + ]: + if ( + lsa["lsaId"] == t3lsa["lsaId"] + and lsa["advertisedRouter"] == t3lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "nssaExternalLinkStates" in area_lsa: + for lsa in area_lsa["nssaExternalLinkStates"]: + for t7lsa in show_ospf_json["areas"][ospf_area][ + "nssaExternalLinkStates" + ]: + if ( + lsa["lsaId"] == t7lsa["lsaId"] + and lsa["advertisedRouter"] == t7lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Type7 LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "asbrSummaryLinkStates" in area_lsa: + for lsa in area_lsa["asbrSummaryLinkStates"]: + for t4lsa in show_ospf_json["areas"][ospf_area][ + "asbrSummaryLinkStates" + ]: + if ( + lsa["lsaId"] == t4lsa["lsaId"] + and lsa["advertisedRouter"] == t4lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + result = True + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " ASBR Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "linkLocalOpaqueLsa" in area_lsa: + for lsa in area_lsa["linkLocalOpaqueLsa"]: + try: + for lnklsa in show_ospf_json["areas"][ospf_area][ + "linkLocalOpaqueLsa" + ]: + if ( + lsa["lsaId"] in lnklsa["lsaId"] + and "linkLocalOpaqueLsa" + in show_ospf_json["areas"][ospf_area] + ): + logger.info( + ( + "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA" + "%s", + ospf_area, + lsa, + ) + ) + result = True + else: + errormsg = ( + "[DUT: FRR] OSPF LSDB area: {} " + "expected Opaque-LSA is {}, Found is {}".format( + ospf_area, lsa, show_ospf_json + ) + ) + raise ValueError(errormsg) + return errormsg + except KeyError: + errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present" + return errormsg + else: + if "routerLinkStates" in area_lsa: + for lsa in area_lsa["routerLinkStates"]: + for rtrlsa in show_ospf_json["routerLinkStates"]: + _advrtr = lsa.setdefault("advertisedRouter", None) + _options = lsa.setdefault("options", None) + _age = lsa.setdefault("lsaAge", None) + if ( + _options + and lsa["options"] + == show_ospf_json["routerLinkStates"][rtrlsa][ + ospf_area + ][0]["options"] + ): + result = True + break + if ( + _age is not "get" + and lsa["lsaAge"] + == show_ospf_json["routerLinkStates"][rtrlsa][ + ospf_area + ][0]["lsaAge"] + ): + result = True + break + + if _age == "get": + return "{}".format( + show_ospf_json["routerLinkStates"][rtrlsa][ + ospf_area + ][0]["lsaAge"] + ) + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Router LSA is {}\n found Router LSA: {}".format( + router, + ospf_area, + lsa, + show_ospf_json["routerLinkStates"], + ) + ) + return errormsg + + if "networkLinkStates" in area_lsa: + for lsa in area_lsa["networkLinkStates"]: + for netlsa in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ]: + if ( + lsa + in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ] + ): + if ( + lsa["lsaId"] == netlsa["lsaId"] + and lsa["advertisedRouter"] + == netlsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Network LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "summaryLinkStates" in area_lsa: + for lsa in area_lsa["summaryLinkStates"]: + for t3lsa in show_ospf_json["areas"][ospf_area][ + "summaryLinkStates" + ]: + if ( + lsa["lsaId"] == t3lsa["lsaId"] + and lsa["advertisedRouter"] == t3lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "nssaExternalLinkStates" in area_lsa: + for lsa in area_lsa["nssaExternalLinkStates"]: + for t7lsa in show_ospf_json["areas"][ospf_area][ + "nssaExternalLinkStates" + ]: + if ( + lsa["lsaId"] == t7lsa["lsaId"] + and lsa["advertisedRouter"] == t7lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Type7 LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "asbrSummaryLinkStates" in area_lsa: + for lsa in area_lsa["asbrSummaryLinkStates"]: + for t4lsa in show_ospf_json["areas"][ospf_area][ + "asbrSummaryLinkStates" + ]: + if ( + lsa["lsaId"] == t4lsa["lsaId"] + and lsa["advertisedRouter"] == t4lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + result = True + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " ASBR Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "linkLocalOpaqueLsa" in area_lsa: + for lsa in area_lsa["linkLocalOpaqueLsa"]: + try: + for lnklsa in show_ospf_json["areas"][ospf_area][ + "linkLocalOpaqueLsa" + ]: + if ( + lsa["lsaId"] in lnklsa["lsaId"] + and "linkLocalOpaqueLsa" + in show_ospf_json["areas"][ospf_area] + ): + logger.info( + ( + "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA" + "%s", + ospf_area, + lsa, + ) + ) + result = True + else: + errormsg = ( + "[DUT: FRR] OSPF LSDB area: {} " + "expected Opaque-LSA is {}, Found is {}".format( + ospf_area, lsa, show_ospf_json + ) + ) + raise ValueError(errormsg) + return errormsg + except KeyError: + errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present" + return errormsg + + if ospf_external_lsa: + for lsa in ospf_external_lsa: + try: + for t5lsa in show_ospf_json["asExternalLinkStates"]: + if ( + lsa["lsaId"] == t5lsa["lsaId"] + and lsa["advertisedRouter"] == t5lsa["advertisedRouter"] + ): + result = True + break + except KeyError: + result = False + if result: + logger.info("[DUT: %s] OSPF LSDB:External LSA %s", router, lsa) + result = True + else: + errormsg = ( + "[DUT: {}] OSPF LSDB : expected" + " External LSA is {}".format(router, lsa) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result diff --git a/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json b/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json new file mode 100644 index 0000000000..74443cdc7d --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json @@ -0,0 +1,214 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.1", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.1", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4, + "network": "broadcast" + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4, + "network": "broadcast" + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py b/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py new file mode 100644 index 0000000000..bd7db644d1 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py @@ -0,0 +1,1066 @@ +#!/usr/bin/python + +# +# Copyright (c) 2022 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 Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from time import sleep + +# 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/")) + +pytestmark = [pytest.mark.ospfd, pytest.mark.staticd] +# Global variables +topo = None + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topolog import logger +from lib.topojson import build_config_from_json + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_static_routes, + step, + topo_daemons, + shutdown_bringup_interface, + check_router_status, + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_static_routes, + step, + shutdown_bringup_interface, + check_router_status, + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + stop_router, + start_router, + step, + create_static_routes, + kill_router_daemons, + check_router_status, + start_router_daemons, +) +from lib.topolog import logger +from lib.topogen import Topogen, get_topogen + +from lib.topojson import build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + clear_ospf, + create_router_ospf, + verify_ospf_database, + get_ospf_database, +) + +# Global variables +topo = None +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", + ] +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. Verify OSPF Flood reduction functionality with ospf enabled on process level. +2. Verify OSPF Flood reduction functionality with ospf enabled on area level. +3. Verify OSPF Flood reduction functionality between different area's. +4. Verify OSPF Flood reduction functionality with ospf enabled on process level with default lsa refresh timer. +5. Chaos - Verify OSPF TTL GTSM and flood reduction functionality in chaos scenarios. +""" + + +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... + json_file = "{}/ospf_flood_reduction.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen) + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # 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_ospf_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.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": 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_ospf_flood_red_tc1_p0(request): + """Verify OSPF Flood reduction functionality with ospf enabled on process level.""" + 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(): + check_router_status(tgen) + + global topo + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + red_static("r0") + step("Base config should be up, verify using OSPF convergence on all the routers") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable flood reduction in process level on R0") + ospf_flood = {"r0": {"ospf": {"flood-reduction": True}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Verify that ospf lsa's are set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure the custom refresh timer") + ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Enable flood. reduction in all the routers of the topology.") + for rtr in topo["routers"].keys(): + ospf_flood = {rtr: {"ospf": {"lsa-refresh": 120, "flood-reduction": True}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lsa's are set with dc bit 1.") + for rtr in topo["routers"]: + dut = rtr + lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"]) + input_dict_db = { + "routerId": lsid, + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": lsid, + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + # this wait is put so that we wait for 5secs to check if lsa is refreshed. + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Disable flood reduction in R0.") + ospf_flood = { + "r0": {"ospf": {"flood-reduction": True, "del_flood_reduction": True}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + clear_ospf(tgen, "r0") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are not set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: OSPF LSA should not be set with DC bit in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + if result2 is not result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Enable flood reduction on each router with 10 secs delay of between each router." + ) + for rtr in topo["routers"].keys(): + ospf_flood = {rtr: {"ospf": {"lsa-refresh": 120, "flood-reduction": True}}} + sleep(10) + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify OSPF neighborship when OSPF flood reduction is configured and ospf process is restarted" + ) + + step("Kill OSPFd daemon on R0.") + kill_router_daemons(tgen, "r0", ["ospfd"]) + start_router_daemons(tgen, "r0", ["ospfd"]) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_flood_red_tc2_p0(request): + """Verify OSPF Flood reduction functionality with ospf enabled on area level.""" + 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(): + check_router_status(tgen) + + global topo + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + red_static("r0") + step("Base config should be up, verify using OSPF convergence on all the routers") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable flood reduction in area level on R0 in area 0") + ospf_flood = { + "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Verify that ospf lsa's are set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure the custom refresh timer") + ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Enable flood. reduction in all the routers of the topology.") + for rtr in topo["routers"].keys(): + ospf_flood = { + rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lsa's are set with dc bit 1.") + for rtr in topo["routers"]: + dut = rtr + lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"]) + input_dict_db = { + "routerId": lsid, + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": lsid, + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Disable flood reduction in R0.") + ospf_flood = { + "r0": { + "ospf": { + "area": [{"id": "0.0.0.0", "flood-reduction": True, "delete": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + clear_ospf(tgen, "r0") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are not set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: OSPF LSA should not be set with DC bit in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + if result2 is not result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Enable flood reduction on each router with 10 secs delay of between each router." + ) + for rtr in topo["routers"].keys(): + ospf_flood = { + rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + sleep(10) + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_flood_red_tc3_p0(request): + """Verify OSPF Flood reduction functionality between different area's""" + 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(): + check_router_status(tgen) + + global topo + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + red_static("r0") + step("Base config should be up, verify using OSPF convergence on all the routers") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable flood reduction in area level on R0 in area 0") + ospf_flood = { + "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Verify that ospf lsa's are set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure the custom refresh timer") + ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step( + "Enable flood. reduction in all the routers of the topology in area 0. Redistribute static route in area 0 R1 and area1 in R2." + ) + for rtr in topo["routers"].keys(): + ospf_flood = { + rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + sleep(10) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lsa's are set with dc bit 1.") + for rtr in topo["routers"]: + dut = rtr + lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"]) + input_dict_db = { + "routerId": lsid, + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": lsid, + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Enable flood reduction in area 1.") + + ospf_flood = { + "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r1": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True}, + {"id": "0.0.0.1", "flood-reduction": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r2": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True}, + {"id": "0.0.0.1", "flood-reduction": True}, + {"id": "0.0.0.2", "flood-reduction": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r3": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True}, + {"id": "0.0.0.2", "flood-reduction": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are set with dc bit 1.") + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.1", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.1", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.1", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.1", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1" + ) + + if result2 is result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Disable flood reduction in R0.") + + ospf_flood = { + "r0": { + "ospf": { + "area": [{"id": "0.0.0.0", "flood-reduction": True, "delete": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r1": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.1", "flood-reduction": True, "delete": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r2": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.1", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.2", "flood-reduction": True, "delete": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r3": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.2", "flood-reduction": True, "delete": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + clear_ospf(tgen, "r0") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are not set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False + ) + + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: OSPF LSA should not be set with DC bit in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + if result2 is not result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))