summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuss White <russ@riw.us>2023-02-21 14:03:06 +0100
committerGitHub <noreply@github.com>2023-02-21 14:03:06 +0100
commit62bd2580e3e7934d3512b6a88aaf93528a1d2f26 (patch)
treea41517de9fb95bd4e1620dc16c0be7e64f9b6de3
parentMerge pull request #12248 from pguibert6WIND/bgpasdot (diff)
parenttests: Added ospfv2 flood reduction topotest changes. (diff)
downloadfrr-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.h15
-rw-r--r--ospfd/ospf_abr.c272
-rw-r--r--ospfd/ospf_abr.h21
-rw-r--r--ospfd/ospf_flood.c137
-rw-r--r--ospfd/ospf_flood.h2
-rw-r--r--ospfd/ospf_lsa.c88
-rw-r--r--ospfd/ospf_lsa.h24
-rw-r--r--ospfd/ospf_lsdb.c21
-rw-r--r--ospfd/ospf_packet.c11
-rw-r--r--ospfd/ospf_route.c10
-rw-r--r--ospfd/ospf_vty.c396
-rw-r--r--ospfd/ospfd.c12
-rw-r--r--ospfd/ospfd.h26
-rw-r--r--tests/topotests/lib/ospf.py583
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json214
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py1066
16 files changed, 2769 insertions, 129 deletions
diff --git a/lib/log.h b/lib/log.h
index 8e94d3fd8..b8452ac21 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -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))