diff options
author | Russ White <russ@riw.us> | 2023-02-21 14:03:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-21 14:03:06 +0100 |
commit | 62bd2580e3e7934d3512b6a88aaf93528a1d2f26 (patch) | |
tree | a41517de9fb95bd4e1620dc16c0be7e64f9b6de3 | |
parent | Merge pull request #12248 from pguibert6WIND/bgpasdot (diff) | |
parent | tests: Added ospfv2 flood reduction topotest changes. (diff) | |
download | frr-62bd2580e3e7934d3512b6a88aaf93528a1d2f26.tar.xz frr-62bd2580e3e7934d3512b6a88aaf93528a1d2f26.zip |
Merge pull request #12366 from manojvn/ospfv2-flood-reduction
ospfd: Support OSPF Refresh and Flooding Reduction RFC4136.
-rw-r--r-- | lib/log.h | 15 | ||||
-rw-r--r-- | ospfd/ospf_abr.c | 272 | ||||
-rw-r--r-- | ospfd/ospf_abr.h | 21 | ||||
-rw-r--r-- | ospfd/ospf_flood.c | 137 | ||||
-rw-r--r-- | ospfd/ospf_flood.h | 2 | ||||
-rw-r--r-- | ospfd/ospf_lsa.c | 88 | ||||
-rw-r--r-- | ospfd/ospf_lsa.h | 24 | ||||
-rw-r--r-- | ospfd/ospf_lsdb.c | 21 | ||||
-rw-r--r-- | ospfd/ospf_packet.c | 11 | ||||
-rw-r--r-- | ospfd/ospf_route.c | 10 | ||||
-rw-r--r-- | ospfd/ospf_vty.c | 396 | ||||
-rw-r--r-- | ospfd/ospfd.c | 12 | ||||
-rw-r--r-- | ospfd/ospfd.h | 26 | ||||
-rw-r--r-- | tests/topotests/lib/ospf.py | 583 | ||||
-rw-r--r-- | tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json | 214 | ||||
-rw-r--r-- | tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py | 1066 |
16 files changed, 2769 insertions, 129 deletions
@@ -114,6 +114,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 { diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 55cb8b183..8f177cbce 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -1559,6 +1559,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) { @@ -1613,9 +1861,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) @@ -1778,6 +2030,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 21e87669f..19d444b12 100644 --- a/ospfd/ospf_abr.h +++ b/ospfd/ospf_abr.h @@ -8,6 +8,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) @@ -69,4 +74,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 */ diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index debbcd964..d0453bbc4 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -35,6 +35,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. */ @@ -413,6 +482,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) @@ -450,6 +568,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 6dce6937b..3757400d0 100644 --- a/ospfd/ospf_flood.h +++ b/ospfd/ospf_flood.h @@ -53,5 +53,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 ffbfc2af2..49e342d72 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -70,6 +70,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) { @@ -121,6 +131,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; } @@ -1119,6 +1139,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); @@ -1518,10 +1542,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), @@ -1536,6 +1565,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); @@ -3626,6 +3658,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) @@ -3961,6 +4036,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__); @@ -4019,10 +4095,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_lsa.h b/ospfd/ospf_lsa.h index 6fa6399a9..3c7ea3fda 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -45,6 +45,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; @@ -218,6 +219,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); @@ -343,4 +347,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_lsdb.c b/ospfd/ospf_lsdb.c index 0d18e9f77..0111c4924 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -77,6 +77,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 @@ -113,6 +128,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); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 4c2f5d72b..5268c9896 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -28,6 +28,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" @@ -3317,6 +3318,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)); @@ -3405,6 +3414,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 25cd32b6f..5f18bff1c 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -980,6 +980,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); } diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index b3e543d22..29bddb119 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3038,6 +3038,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, @@ -3273,6 +3310,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) @@ -5949,118 +5994,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]; - - 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 self option is set, check LSA self flag. */ + if (self == 0 || IS_LSA_SELF(lsa)) { - /* LSA specific part show. */ - switch (lsa->data->type) { - case OSPF_ROUTER_LSA: - rl = (struct router_lsa *)lsa->data; + 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]; + + 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); + } + + /* 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; + 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); + 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, " %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, "\n"); + 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; } - return 1; + if (!json_lsa) + vty_out(vty, "\n"); } - return 0; + return 1; } static const char *const show_database_desc[] = { @@ -6129,7 +6165,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, @@ -12188,6 +12233,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; @@ -12565,6 +12613,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); @@ -12947,6 +12998,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 <A.B.C.D|(0-4294967295)> 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 <A.B.C.D|(0-4294967295)> 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); @@ -13107,6 +13295,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(); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 1c8ebccea..0296d9d9f 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -452,6 +452,8 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) */ ospf_gr_nvm_read(new); + new->fr_configured = false; + return new; } @@ -802,6 +804,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); @@ -947,6 +950,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 cb26ef5fb..4df65ea75 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -101,7 +101,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 */ @@ -240,6 +258,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. */ @@ -391,6 +410,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); @@ -576,6 +598,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. */ diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index f1937f45b..7609c7f89 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -8,6 +8,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 ( @@ -188,6 +189,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: @@ -220,6 +239,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) @@ -724,9 +746,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): """ @@ -1339,8 +1358,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. @@ -1398,7 +1419,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" @@ -1407,26 +1444,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: @@ -2476,3 +2527,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 000000000..74443cdc7 --- /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 000000000..bd7db644d --- /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)) |