summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/developer/ospf-ls-retrans.rst69
-rw-r--r--doc/developer/ospf.rst1
-rw-r--r--doc/user/ospfd.rst12
-rw-r--r--lib/libospf.h1
-rw-r--r--ospfd/ospf_flood.c146
-rw-r--r--ospfd/ospf_flood.h22
-rw-r--r--ospfd/ospf_interface.c5
-rw-r--r--ospfd/ospf_interface.h3
-rw-r--r--ospfd/ospf_lsdb.c53
-rw-r--r--ospfd/ospf_lsdb.h23
-rw-r--r--ospfd/ospf_memory.c2
-rw-r--r--ospfd/ospf_memory.h2
-rw-r--r--ospfd/ospf_neighbor.c8
-rw-r--r--ospfd/ospf_neighbor.h7
-rw-r--r--ospfd/ospf_nsm.c6
-rw-r--r--ospfd/ospf_packet.c91
-rw-r--r--ospfd/ospf_packet.h2
-rw-r--r--ospfd/ospf_vty.c140
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref2
-rw-r--r--tests/topotests/ospf_p2mp/r1/frr-p2mp.conf6
-rw-r--r--tests/topotests/ospf_p2mp/r2/frr-p2mp.conf6
-rw-r--r--tests/topotests/ospf_p2mp/r3/frr-p2mp.conf6
-rw-r--r--tests/topotests/ospf_p2mp/r4/frr-p2mp.conf6
-rw-r--r--tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py112
24 files changed, 620 insertions, 111 deletions
diff --git a/doc/developer/ospf-ls-retrans.rst b/doc/developer/ospf-ls-retrans.rst
new file mode 100644
index 0000000000..230d7a1c5d
--- /dev/null
+++ b/doc/developer/ospf-ls-retrans.rst
@@ -0,0 +1,69 @@
+OSPF Neighor Retransmission List
+================================
+
+Overview
+--------
+
+OSPF neighbor link-state retransmission lists are implemented using
+both a sparse Link State Database (LSDB) and a doubly-linked list.
+Rather than previous per-neighbor periodic timer, a per-neighbor
+timer is set to the expiration time of the next scheduled LSA
+retransmission.
+
+Sparse Link State Database (LSDB)
+---------------------------------
+
+When an explicit or implied acknowledgment is recieved from a
+neighbor in 2-way state or higher, the acknowledge LSA must be
+removed from the neighbor's link state retransmission list. In order
+to do this efficiently, a sparse LSDB is utilized. LSDB entries also
+include a pointer to the corresponding list entry so that it may be
+efficiently removed from the doubly-linked list.
+
+The sparse LSDB is implemented using the OSPF functions is
+ospf_lsdb.[c,h]. OSPF LSDBs are implemented as an array of route
+tables (lib/table.[c,h]). What is unique of the LS Retransmission
+list LSDB is that each entry also has a pointer into the doubly-linked
+list to facilitate fast deletions.
+
+Doubly-Linked List
+------------------
+
+In addition to the sparse LSDB, LSAs on a neighbor LS retransmission
+list are also maintained in a linked-list order chronologically
+with the LSA scheduled for the next retransmission at the head of
+the list.
+
+The doubly-link list is implemented using the dlist macros in
+lib/typesafe.h.
+
+LSA LS Retransmission List Addition
+------------------------------------
+
+When an LSA is added to a neighbor retransmission list, it is
+added to both the sparse LSDB and the doubly-linked list with a pointer
+in the LSDB route-table node to the list entry. The LSA is added to
+the tail of the list with the expiration time set to the current time
+with the retransmission interval added. If the neighbor retransmission
+timer is not set, it is set to expire at the time of the newly added
+LSA.
+
+LSA LS Retransmission List Deletion
+-----------------------------------
+
+When an LSA is deleted from a neighbor retransmission list, it is
+deleted from eboth the sparse LSDB and the doubly-linked list with the
+pointer the LSDB route-table node used to efficiently delete the entry
+from the list. If the LSA at the head of the list was removed, then
+the neighbor retransmission timer is reset to the expiration of the
+LSA at the head of the list or canceled if the list is empty.
+
+Neighbor LS Retransmission List Expiration
+------------------------------------------
+
+When the neighbor retransmission timer expires, the LSA at the top of
+list and any in a configured window (e.g., 50 milliseconds) are
+retransmitted. The LSAs that have been retransmitted are removed from
+the list and readded to the tail of the list with a new expiration time
+which is retransmit-interval seconds in the future.
+
diff --git a/doc/developer/ospf.rst b/doc/developer/ospf.rst
index 837a0bd185..da4802533c 100644
--- a/doc/developer/ospf.rst
+++ b/doc/developer/ospf.rst
@@ -8,6 +8,7 @@ OSPFD
:maxdepth: 2
ospf-api
+ ospf-ls-retrans
ospf-sr
cspf
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 70c15e73de..b80adba7f0 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -738,7 +738,17 @@ Interfaces
retransmitting Database Description and Link State Request packets. The
default value is 5 seconds.
-.. clicmd:: ip ospf transmit-delay (1-65535) [A.B.C.D]
+.. clicmd:: ip ospf retransmit-window (20-1000)
+
+
+ Set number of milliseconds in the window for neighbor LSA retransmission.
+ When a neighbor Link State (LS) retransmission timer expires, LSAs scheduled
+ to be retransmitted within the number of milliseconds configured are
+ retransmitted to the neighbor. Any expiring after the window will be
+ retransmitted the next time the neighbor LS retransmission timer expires.
+ The default is 50 milliseconds.
+
+ .. clicmd:: ip ospf transmit-delay (1-65535) [A.B.C.D]
Set number of seconds for InfTransDelay value. LSAs' age should be
diff --git a/lib/libospf.h b/lib/libospf.h
index 0ac490a00e..f2dc5d61d9 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -58,6 +58,7 @@ extern "C" {
#define OSPF_HELLO_DELAY_DEFAULT 10
#define OSPF_ROUTER_PRIORITY_DEFAULT 1
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
+#define OSPF_RETRANSMIT_WINDOW_DEFAULT 50 /* milliseconds */
#define OSPF_TRANSMIT_DELAY_DEFAULT 1
#define OSPF_DEFAULT_BANDWIDTH 10000 /* Mbps */
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index e15871ac81..e9797ce935 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -1015,7 +1015,7 @@ void ospf_ls_request_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
ospf_lsdb_delete(&nbr->ls_req, lsa);
}
-/* Remove all LSA from neighbor's ls-requenst list. */
+/* Remove all LSAs from neighbor's ls-request list. */
void ospf_ls_request_delete_all(struct ospf_neighbor *nbr)
{
ospf_lsa_unlock(&nbr->ls_req_last);
@@ -1061,58 +1061,114 @@ int ospf_ls_retransmit_isempty(struct ospf_neighbor *nbr)
/* Add LSA to be retransmitted to neighbor's ls-retransmit list. */
void ospf_ls_retransmit_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
{
- struct ospf_lsa *old;
+ struct ospf_lsdb_linked_node *ls_rxmt_node;
+ struct ospf_lsa_list_entry *ls_rxmt_list_entry;
+ struct ospf_lsa *old = NULL;
+ bool rxmt_head_replaced = false;
- old = ospf_ls_retransmit_lookup(nbr, lsa);
+ ls_rxmt_node = ospf_lsdb_linked_lookup(&nbr->ls_rxmt, lsa);
+ if (ls_rxmt_node)
+ old = ls_rxmt_node->info;
if (ospf_lsa_more_recent(old, lsa) < 0) {
if (old) {
old->retransmit_counter--;
+ if (ls_rxmt_node->lsa_list_entry ==
+ ospf_lsa_list_first(&nbr->ls_rxmt_list))
+ rxmt_head_replaced = true;
+ ospf_lsa_list_del(&nbr->ls_rxmt_list,
+ ls_rxmt_node->lsa_list_entry);
+ XFREE(MTYPE_OSPF_LSA_LIST, ls_rxmt_node->lsa_list_entry);
+ ospf_lsdb_delete(&nbr->ls_rxmt, old);
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
- zlog_debug("RXmtL(%lu)--, NBR(%pI4(%s)), LSA[%s]",
+ zlog_debug("RXmtL(%lu) NBR(%pI4(%s)) Old Delete LSA[%s] on Add",
ospf_ls_retransmit_count(nbr),
&nbr->router_id,
ospf_get_name(nbr->oi->ospf),
- dump_lsa_key(old));
- ospf_lsdb_delete(&nbr->ls_rxmt, old);
+ dump_lsa_key(lsa));
+ ospf_lsa_unlock(&old);
}
lsa->retransmit_counter++;
+ ls_rxmt_list_entry = XCALLOC(MTYPE_OSPF_LSA_LIST,
+ sizeof(struct ospf_lsa_list_entry));
+ /*
+ * Set the LSA retransmission time for the neighbor;
+ */
+ monotime(&ls_rxmt_list_entry->list_entry_time);
+ ls_rxmt_list_entry->list_entry_time.tv_sec += nbr->v_ls_rxmt;
+
+ /*
+ * Add the LSA to the neighbor retransmission list.
+ */
+ ls_rxmt_list_entry->lsa = ospf_lsa_lock(lsa);
+ ospf_lsa_list_add_tail(&nbr->ls_rxmt_list, ls_rxmt_list_entry);
+ ospf_lsdb_add(&nbr->ls_rxmt, lsa);
+
/*
- * We cannot make use of the newly introduced callback function
- * "lsdb->new_lsa_hook" to replace debug output below, just
- * because
- * it seems no simple and smart way to pass neighbor information
- * to
- * the common function "ospf_lsdb_add()" -- endo.
+ * Look up the newly added node and set the list pointer.
*/
+ ls_rxmt_node = ospf_lsdb_linked_lookup(&nbr->ls_rxmt, lsa);
+ ls_rxmt_node->lsa_list_entry = ls_rxmt_list_entry;
+
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
- zlog_debug("RXmtL(%lu)++, NBR(%pI4(%s)), LSA[%s]",
+ zlog_debug("RXmtL(%lu) NBR(%pI4(%s)) Add LSA[%s] retrans at (%ld/%ld)",
ospf_ls_retransmit_count(nbr),
- &nbr->router_id,
- ospf_get_name(nbr->oi->ospf),
- dump_lsa_key(lsa));
- ospf_lsdb_add(&nbr->ls_rxmt, lsa);
+ &nbr->router_id, ospf_get_name(nbr->oi->ospf),
+ dump_lsa_key(lsa),
+ (long)ls_rxmt_list_entry->list_entry_time
+ .tv_sec,
+ (long)ls_rxmt_list_entry->list_entry_time
+ .tv_usec);
+ /*
+ * Reset the neighbor LSA retransmission timer if isn't currently
+ * running or the LSA at the head of the list was updated.
+ */
+ if (!nbr->t_ls_rxmt || rxmt_head_replaced)
+ ospf_ls_retransmit_set_timer(nbr);
}
}
/* Remove LSA from neibghbor's ls-retransmit list. */
void ospf_ls_retransmit_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
{
- if (ospf_ls_retransmit_lookup(nbr, lsa)) {
+ struct ospf_lsdb_linked_node *ls_rxmt_node;
+
+ ls_rxmt_node = ospf_lsdb_linked_lookup(&nbr->ls_rxmt, lsa);
+
+ if (ls_rxmt_node) {
+ bool rxmt_timer_reset;
+
+ if (ls_rxmt_node->lsa_list_entry ==
+ ospf_lsa_list_first(&nbr->ls_rxmt_list))
+ rxmt_timer_reset = true;
+ else
+ rxmt_timer_reset = false;
+
lsa->retransmit_counter--;
- if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) /* -- endo. */
- zlog_debug("RXmtL(%lu)--, NBR(%pI4(%s)), LSA[%s]",
+ ospf_lsa_list_del(&nbr->ls_rxmt_list,
+ ls_rxmt_node->lsa_list_entry);
+ XFREE(MTYPE_OSPF_LSA_LIST, ls_rxmt_node->lsa_list_entry);
+ ospf_lsdb_delete(&nbr->ls_rxmt, lsa);
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
+ zlog_debug("RXmtL(%lu) NBR(%pI4(%s)) Delete LSA[%s]",
ospf_ls_retransmit_count(nbr),
- &nbr->router_id,
- ospf_get_name(nbr->oi->ospf),
+ &nbr->router_id, ospf_get_name(nbr->oi->ospf),
dump_lsa_key(lsa));
- ospf_lsdb_delete(&nbr->ls_rxmt, lsa);
+ ospf_lsa_unlock(&lsa);
+
+ /*
+ * If the LS retransmission entry at the head of the list was
+ * deleted, reset the timer.
+ */
+ if (rxmt_timer_reset)
+ ospf_ls_retransmit_set_timer(nbr);
}
}
/* Clear neighbor's ls-retransmit list. */
void ospf_ls_retransmit_clear(struct ospf_neighbor *nbr)
{
+ struct ospf_lsa_list_entry *ls_rxmt_list_entry;
struct ospf_lsdb *lsdb;
int i;
@@ -1128,10 +1184,54 @@ void ospf_ls_retransmit_clear(struct ospf_neighbor *nbr)
ospf_ls_retransmit_delete(nbr, lsa);
}
+ frr_each_safe (ospf_lsa_list, &nbr->ls_rxmt_list, ls_rxmt_list_entry) {
+ ospf_lsa_list_del(&nbr->ls_rxmt_list, ls_rxmt_list_entry);
+ ospf_lsa_unlock(&ls_rxmt_list_entry->lsa);
+ XFREE(MTYPE_OSPF_LSA_LIST, ls_rxmt_list_entry);
+ }
+
ospf_lsa_unlock(&nbr->ls_req_last);
nbr->ls_req_last = NULL;
}
+/*
+ * Set the neighbor's ls-retransmit timer based on the next
+ * LSA retransmit time.
+ */
+void ospf_ls_retransmit_set_timer(struct ospf_neighbor *nbr)
+{
+ struct ospf_lsa_list_entry *ls_rxmt_list_entry;
+
+ if (nbr->t_ls_rxmt)
+ EVENT_OFF(nbr->t_ls_rxmt);
+
+ ls_rxmt_list_entry = ospf_lsa_list_first(&nbr->ls_rxmt_list);
+ if (ls_rxmt_list_entry) {
+ struct timeval current_time, delay;
+ unsigned long delay_milliseconds;
+
+ monotime(&current_time);
+ if (timercmp(&current_time,
+ &ls_rxmt_list_entry->list_entry_time, >=))
+ delay_milliseconds = 10;
+ else {
+ timersub(&ls_rxmt_list_entry->list_entry_time,
+ &current_time, &delay);
+ delay_milliseconds = (delay.tv_sec * 1000) +
+ (delay.tv_usec / 1000);
+ }
+
+ event_add_timer_msec(master, ospf_ls_rxmt_timer, nbr,
+ delay_milliseconds, &nbr->t_ls_rxmt);
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
+ zlog_debug("RXmtL(%lu) NBR(%pI4(%s)) retrans timer set in %ld msecs - Head LSA(%s)",
+ ospf_ls_retransmit_count(nbr),
+ &nbr->router_id, ospf_get_name(nbr->oi->ospf),
+ delay_milliseconds,
+ dump_lsa_key(ls_rxmt_list_entry->lsa));
+ }
+}
+
/* Lookup LSA from neighbor's ls-retransmit list. */
struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *nbr,
struct ospf_lsa *lsa)
diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h
index 3757400d0c..d9d9537351 100644
--- a/ospfd/ospf_flood.h
+++ b/ospfd/ospf_flood.h
@@ -7,6 +7,26 @@
#ifndef _ZEBRA_OSPF_FLOOD_H
#define _ZEBRA_OSPF_FLOOD_H
+/*
+ * OSPF Temporal LSA List
+ */
+PREDECL_DLIST(ospf_lsa_list);
+
+struct ospf_lsa_list_entry {
+ /* Linkage for LSA List */
+ struct ospf_lsa_list_item list_linkage;
+
+ /*
+ * Time associated with the list entry. For example, for a neigbhor
+ * link retransmission list, this is the retransmission time.
+ */
+ struct timeval list_entry_time;
+
+ struct ospf_lsa *lsa;
+};
+
+DECLARE_DLIST(ospf_lsa_list, struct ospf_lsa_list_entry, list_linkage);
+
extern int ospf_flood(struct ospf *, struct ospf_neighbor *, struct ospf_lsa *,
struct ospf_lsa *);
extern int ospf_flood_through(struct ospf *, struct ospf_neighbor *,
@@ -36,6 +56,8 @@ extern void ospf_ls_retransmit_add(struct ospf_neighbor *, struct ospf_lsa *);
extern void ospf_ls_retransmit_delete(struct ospf_neighbor *,
struct ospf_lsa *);
extern void ospf_ls_retransmit_clear(struct ospf_neighbor *);
+extern void ospf_ls_retransmit_set_timer(struct ospf_neighbor *nbr);
+
extern struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *,
struct ospf_lsa *);
extern void ospf_ls_retransmit_delete_nbr_area(struct ospf_area *,
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 11ac7af7c9..803c36861d 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -542,6 +542,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
UNSET_IF_PARAM(oip, output_cost_cmd);
UNSET_IF_PARAM(oip, transmit_delay);
UNSET_IF_PARAM(oip, retransmit_interval);
+ UNSET_IF_PARAM(oip, retransmit_window);
UNSET_IF_PARAM(oip, passive_interface);
UNSET_IF_PARAM(oip, v_hello);
UNSET_IF_PARAM(oip, fast_hello);
@@ -599,6 +600,7 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr)
if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) &&
!OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) &&
!OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_window) &&
!OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) &&
!OSPF_IF_PARAM_CONFIGURED(oip, v_hello) &&
!OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) &&
@@ -695,6 +697,9 @@ int ospf_if_new_hook(struct interface *ifp)
IF_DEF_PARAMS(ifp)->retransmit_interval =
OSPF_RETRANSMIT_INTERVAL_DEFAULT;
+ SET_IF_PARAM(IF_DEF_PARAMS(ifp), retransmit_window);
+ IF_DEF_PARAMS(ifp)->retransmit_window = OSPF_RETRANSMIT_WINDOW_DEFAULT;
+
SET_IF_PARAM(IF_DEF_PARAMS(ifp), priority);
IF_DEF_PARAMS(ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT;
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index 45d0b7943a..a944847b5d 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -47,6 +47,8 @@ struct ospf_if_params {
output_cost_cmd); /* Command Interface Output Cost */
DECLARE_IF_PARAM(uint32_t,
retransmit_interval); /* Retransmission Interval */
+ DECLARE_IF_PARAM(uint32_t,
+ retransmit_window); /* Retransmission Window */
DECLARE_IF_PARAM(uint8_t, passive_interface); /* OSPF Interface is
passive: no sending or
receiving (no need to
@@ -296,6 +298,7 @@ struct ospf_interface {
uint32_t ls_ack_out; /* LS Ack message output count. */
uint32_t discarded; /* discarded input count by error. */
uint32_t state_change; /* Number of status change. */
+ uint32_t ls_rxmt_lsa; /* Number of LSAs retransmitted. */
uint32_t full_nbrs;
diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c
index 0111c4924e..d1b3eb0d35 100644
--- a/ospfd/ospf_lsdb.c
+++ b/ospfd/ospf_lsdb.c
@@ -34,6 +34,59 @@ void ospf_lsdb_init(struct ospf_lsdb *lsdb)
lsdb->type[i].db = route_table_init();
}
+static struct route_node *
+ospf_lsdb_linked_node_create(route_table_delegate_t *delegate,
+ struct route_table *table)
+{
+ struct ospf_lsdb_linked_node *node;
+
+ node = XCALLOC(MTYPE_OSPF_LSDB_NODE,
+ sizeof(struct ospf_lsdb_linked_node));
+
+ return (struct route_node *)node;
+}
+
+static void ospf_lsdb_linked_node_destroy(route_table_delegate_t *delegate,
+ struct route_table *table,
+ struct route_node *node)
+{
+ struct ospf_lsdb_linked_node *lsdb_linked_node =
+ (struct ospf_lsdb_linked_node *)node;
+
+ XFREE(MTYPE_OSPF_LSDB_NODE, lsdb_linked_node);
+}
+
+static route_table_delegate_t ospf_lsdb_linked_table_delegate = {
+ .create_node = ospf_lsdb_linked_node_create,
+ .destroy_node = ospf_lsdb_linked_node_destroy,
+};
+
+void ospf_lsdb_linked_init(struct ospf_lsdb *lsdb)
+{
+ int i;
+
+ for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+ lsdb->type[i].db = route_table_init_with_delegate(
+ &ospf_lsdb_linked_table_delegate);
+}
+
+struct ospf_lsdb_linked_node *ospf_lsdb_linked_lookup(struct ospf_lsdb *lsdb,
+ struct ospf_lsa *lsa)
+{
+ struct ospf_lsdb_linked_node *lsdb_linked_node;
+ struct route_table *table;
+ struct prefix_ls lp;
+
+ table = lsdb->type[lsa->data->type].db;
+ ls_prefix_set(&lp, lsa);
+ lsdb_linked_node = (struct ospf_lsdb_linked_node *)
+ route_node_lookup(table, (struct prefix *)&lp);
+ if (lsdb_linked_node)
+ route_unlock_node((struct route_node *)lsdb_linked_node);
+
+ return lsdb_linked_node;
+}
+
void ospf_lsdb_free(struct ospf_lsdb *lsdb)
{
ospf_lsdb_cleanup(lsdb);
diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h
index bf295ca830..e5e3be8baa 100644
--- a/ospfd/ospf_lsdb.h
+++ b/ospfd/ospf_lsdb.h
@@ -7,6 +7,9 @@
#ifndef _ZEBRA_OSPF_LSDB_H
#define _ZEBRA_OSPF_LSDB_H
+#include "prefix.h"
+#include "table.h"
+
/* OSPF LSDB structure. */
struct ospf_lsdb {
struct {
@@ -43,9 +46,29 @@ struct ospf_lsdb {
#define AREA_LSDB(A,T) ((A)->lsdb->type[(T)].db)
#define AS_LSDB(O,T) ((O)->lsdb->type[(T)].db)
+/*
+ * Alternate route node structure for LSDB nodes linked to
+ * list elements.
+ */
+struct ospf_lsdb_linked_node {
+ /*
+ * Caution these must be the very first fields
+ */
+ ROUTE_NODE_FIELDS
+
+ /*
+ * List entry on an LSA list, e.g., a neighbor
+ * retransmission list.
+ */
+ struct ospf_lsa_list_entry *lsa_list_entry;
+};
+
/* OSPF LSDB related functions. */
extern struct ospf_lsdb *ospf_lsdb_new(void);
extern void ospf_lsdb_init(struct ospf_lsdb *);
+extern void ospf_lsdb_linked_init(struct ospf_lsdb *lsdb);
+extern struct ospf_lsdb_linked_node *
+ospf_lsdb_linked_lookup(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa);
extern void ospf_lsdb_free(struct ospf_lsdb *);
extern void ospf_lsdb_cleanup(struct ospf_lsdb *);
extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa);
diff --git a/ospfd/ospf_memory.c b/ospfd/ospf_memory.c
index 9854c8cae8..478af323d3 100644
--- a/ospfd/ospf_memory.c
+++ b/ospfd/ospf_memory.c
@@ -45,3 +45,5 @@ DEFINE_MTYPE(OSPFD, OSPF_GR_HELPER, "OSPF Graceful Restart Helper");
DEFINE_MTYPE(OSPFD, OSPF_EXTERNAL_RT_AGGR, "OSPF External Route Summarisation");
DEFINE_MTYPE(OSPFD, OSPF_P_SPACE, "OSPF TI-LFA P-Space");
DEFINE_MTYPE(OSPFD, OSPF_Q_SPACE, "OSPF TI-LFA Q-Space");
+DEFINE_MTYPE(OSPFD, OSPF_LSA_LIST, "OSPF LSA List");
+DEFINE_MTYPE(OSPFD, OSPF_LSDB_NODE, "OSPF LSDB Linked Node");
diff --git a/ospfd/ospf_memory.h b/ospfd/ospf_memory.h
index d11b69abb0..e2139b517b 100644
--- a/ospfd/ospf_memory.h
+++ b/ospfd/ospf_memory.h
@@ -44,5 +44,7 @@ DECLARE_MTYPE(OSPF_GR_HELPER);
DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR);
DECLARE_MTYPE(OSPF_P_SPACE);
DECLARE_MTYPE(OSPF_Q_SPACE);
+DECLARE_MTYPE(OSPF_LSA_LIST);
+DECLARE_MTYPE(OSPF_LSDB_NODE);
#endif /* _QUAGGA_OSPF_MEMORY_H */
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
index d47d581605..2514fc0ab3 100644
--- a/ospfd/ospf_neighbor.c
+++ b/ospfd/ospf_neighbor.c
@@ -68,7 +68,7 @@ struct ospf_neighbor *ospf_nbr_new(struct ospf_interface *oi)
nbr->v_inactivity = OSPF_IF_PARAM(oi, v_wait);
nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval);
nbr->v_ls_req = OSPF_IF_PARAM(oi, retransmit_interval);
- nbr->v_ls_upd = OSPF_IF_PARAM(oi, retransmit_interval);
+ nbr->v_ls_rxmt = OSPF_IF_PARAM(oi, retransmit_interval);
nbr->priority = -1;
/* DD flags. */
@@ -80,8 +80,10 @@ struct ospf_neighbor *ospf_nbr_new(struct ospf_interface *oi)
nbr->nbr_nbma = NULL;
ospf_lsdb_init(&nbr->db_sum);
- ospf_lsdb_init(&nbr->ls_rxmt);
+
+ ospf_lsdb_linked_init(&nbr->ls_rxmt);
ospf_lsdb_init(&nbr->ls_req);
+ ospf_lsa_list_init(&nbr->ls_rxmt_list);
nbr->crypt_seqnum = 0;
@@ -128,7 +130,7 @@ void ospf_nbr_free(struct ospf_neighbor *nbr)
EVENT_OFF(nbr->t_inactivity);
EVENT_OFF(nbr->t_db_desc);
EVENT_OFF(nbr->t_ls_req);
- EVENT_OFF(nbr->t_ls_upd);
+ EVENT_OFF(nbr->t_ls_rxmt);
/* Cancel all events. */ /* Thread lookup cost would be negligible. */
event_cancel_event(master, nbr);
diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h
index 07d095f03d..0e041f9e6d 100644
--- a/ospfd/ospf_neighbor.h
+++ b/ospfd/ospf_neighbor.h
@@ -9,6 +9,7 @@
#include <ospfd/ospf_gr.h>
#include <ospfd/ospf_packet.h>
+#include <ospfd/ospf_flood.h>
/* Neighbor Data Structure */
struct ospf_neighbor {
@@ -44,6 +45,7 @@ struct ospf_neighbor {
/* LSA data. */
struct ospf_lsdb ls_rxmt;
+ struct ospf_lsa_list_head ls_rxmt_list;
struct ospf_lsdb db_sum;
struct ospf_lsdb ls_req;
struct ospf_lsa *ls_req_last;
@@ -54,13 +56,13 @@ struct ospf_neighbor {
uint32_t v_inactivity;
uint32_t v_db_desc;
uint32_t v_ls_req;
- uint32_t v_ls_upd;
+ uint32_t v_ls_rxmt;
/* Threads. */
struct event *t_inactivity;
struct event *t_db_desc;
struct event *t_ls_req;
- struct event *t_ls_upd;
+ struct event *t_ls_rxmt;
struct event *t_hello_reply;
/* NBMA configured neighbour */
@@ -71,6 +73,7 @@ struct ospf_neighbor {
struct timeval ts_last_regress; /* last regressive NSM change */
const char *last_regress_str; /* Event which last regressed NSM */
uint32_t state_change; /* NSM state change counter */
+ uint32_t ls_rxmt_lsa; /* Number of LSAs retransmited. */
/* BFD information */
struct bfd_session_params *bfd_session;
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index c466ddcc6f..079a1fa55e 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -112,18 +112,16 @@ static void nsm_timer_set(struct ospf_neighbor *nbr)
case NSM_Init:
case NSM_TwoWay:
EVENT_OFF(nbr->t_db_desc);
- EVENT_OFF(nbr->t_ls_upd);
+ EVENT_OFF(nbr->t_ls_rxmt);
EVENT_OFF(nbr->t_ls_req);
break;
case NSM_ExStart:
OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer,
nbr->v_db_desc);
- EVENT_OFF(nbr->t_ls_upd);
+ EVENT_OFF(nbr->t_ls_rxmt);
EVENT_OFF(nbr->t_ls_req);
break;
case NSM_Exchange:
- OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer,
- nbr->v_ls_upd);
if (!IS_SET_DD_MS(nbr->dd_flags))
EVENT_OFF(nbr->t_db_desc);
break;
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 87aaccad92..86f877b621 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -292,54 +292,66 @@ void ospf_ls_req_event(struct ospf_neighbor *nbr)
event_add_event(master, ospf_ls_req_timer, nbr, 0, &nbr->t_ls_req);
}
-/* Cyclic timer function. Fist registered in ospf_nbr_new () in
- ospf_neighbor.c */
-void ospf_ls_upd_timer(struct event *thread)
+/*
+ * OSPF neighbor link state retransmission timer handler. Unicast
+ * unacknowledged LSAs to the neigbhors.
+ */
+void ospf_ls_rxmt_timer(struct event *thread)
{
struct ospf_neighbor *nbr;
+ int retransmit_interval, retransmit_window, rxmt_lsa_count = 0;
nbr = EVENT_ARG(thread);
- nbr->t_ls_upd = NULL;
+ nbr->t_ls_rxmt = NULL;
+ retransmit_interval = nbr->v_ls_rxmt;
+ retransmit_window = OSPF_IF_PARAM(nbr->oi, retransmit_window);
/* Send Link State Update. */
if (ospf_ls_retransmit_count(nbr) > 0) {
+ struct ospf_lsa_list_entry *ls_rxmt_list_entry;
+ struct timeval current_time, latest_rxmt_time, next_rxmt_time;
+ struct timeval rxmt_interval = { retransmit_interval, 0 };
+ struct timeval rxmt_window;
struct list *update;
- struct ospf_lsdb *lsdb;
- int i;
- int retransmit_interval;
- retransmit_interval =
- OSPF_IF_PARAM(nbr->oi, retransmit_interval);
+ /*
+ * Set the retransmission window based on the configured value
+ * in milliseconds.
+ */
+ rxmt_window.tv_sec = retransmit_window / 1000;
+ rxmt_window.tv_usec = (retransmit_window % 1000) * 1000;
+
+ /*
+ * Calculate the latest retransmit time for LSAs transmited in
+ * this timer pass by adding the retransmission window to the
+ * current time. Calculate the next retransmission time by adding
+ * the retransmit interval to the current time.
+ */
+ monotime(&current_time);
+ timeradd(&current_time, &rxmt_window, &latest_rxmt_time);
+ timeradd(&current_time, &rxmt_interval, &next_rxmt_time);
- lsdb = &nbr->ls_rxmt;
update = list_new();
+ while ((ls_rxmt_list_entry =
+ ospf_lsa_list_first(&nbr->ls_rxmt_list))) {
+ if (timercmp(&ls_rxmt_list_entry->list_entry_time,
+ &latest_rxmt_time, >))
+ break;
- for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) {
- struct route_table *table = lsdb->type[i].db;
- struct route_node *rn;
-
- for (rn = route_top(table); rn; rn = route_next(rn)) {
- struct ospf_lsa *lsa;
-
- if ((lsa = rn->info) != NULL) {
- /* Don't retransmit an LSA if we
- received it within
- the last RxmtInterval seconds - this
- is to allow the
- neighbour a chance to acknowledge the
- LSA as it may
- have ben just received before the
- retransmit timer
- fired. This is a small tweak to what
- is in the RFC,
- but it will cut out out a lot of
- retransmit traffic
- - MAG */
- if (monotime_since(&lsa->tv_recv, NULL)
- >= retransmit_interval * 1000000LL)
- listnode_add(update, rn->info);
- }
- }
+ listnode_add(update, ls_rxmt_list_entry->lsa);
+ rxmt_lsa_count++;
+
+ /*
+ * Set the next retransmit time for the LSA and move it
+ * to the end of the neighbor's retransmission list.
+ */
+ ls_rxmt_list_entry->list_entry_time = next_rxmt_time;
+ ospf_lsa_list_del(&nbr->ls_rxmt_list,
+ ls_rxmt_list_entry);
+ ospf_lsa_list_add_tail(&nbr->ls_rxmt_list,
+ ls_rxmt_list_entry);
+ nbr->ls_rxmt_lsa++;
+ nbr->oi->ls_rxmt_lsa++;
}
if (listcount(update) > 0)
@@ -348,8 +360,13 @@ void ospf_ls_upd_timer(struct event *thread)
list_delete(&update);
}
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("RXmtL(%lu) NBR(%pI4(%s)) timer event - sent %u LSAs",
+ ospf_ls_retransmit_count(nbr), &nbr->router_id,
+ ospf_get_name(nbr->oi->ospf), rxmt_lsa_count);
+
/* Set LS Update retransmission timer. */
- OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd);
+ ospf_ls_retransmit_set_timer(nbr);
}
void ospf_ls_ack_timer(struct event *thread)
diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h
index 234738979e..2c9dba6c88 100644
--- a/ospfd/ospf_packet.h
+++ b/ospfd/ospf_packet.h
@@ -140,7 +140,7 @@ extern void ospf_ls_ack_send_delayed(struct ospf_interface *);
extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *);
extern void ospf_ls_req_event(struct ospf_neighbor *);
-extern void ospf_ls_upd_timer(struct event *thread);
+extern void ospf_ls_rxmt_timer(struct event *thread);
extern void ospf_ls_ack_timer(struct event *thread);
extern void ospf_poll_timer(struct event *thread);
extern void ospf_hello_reply_timer(struct event *thread);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 3a11b21232..7a7a684dd6 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -815,6 +815,7 @@ struct ospf_vl_config_data {
int del_keychain;
int hello_interval; /* Obvious what these are... */
int retransmit_interval;
+ int retransmit_window;
int transmit_delay;
int dead_interval;
};
@@ -957,6 +958,12 @@ static int ospf_vl_set_timers(struct ospf_vl_data *vl_data,
vl_config->retransmit_interval;
}
+ if (vl_config->retransmit_window) {
+ SET_IF_PARAM(IF_DEF_PARAMS(ifp), retransmit_window);
+ IF_DEF_PARAMS(ifp)->retransmit_window =
+ vl_config->retransmit_window;
+ }
+
if (vl_config->transmit_delay) {
SET_IF_PARAM(IF_DEF_PARAMS(ifp), transmit_delay);
IF_DEF_PARAMS(ifp)->transmit_delay = vl_config->transmit_delay;
@@ -1012,14 +1019,16 @@ static int ospf_vl_set(struct ospf *ospf, struct ospf_vl_config_data *vl_config)
"Use null authentication\n" \
"Use message-digest authentication\n"
-#define VLINK_HELPSTR_TIME_PARAM \
- "Time between HELLO packets\n" \
- "Seconds\n" \
- "Time between retransmitting lost link state advertisements\n" \
- "Seconds\n" \
- "Link state transmit delay\n" \
- "Seconds\n" \
- "Interval time after which a neighbor is declared down\n" \
+#define VLINK_HELPSTR_TIME_PARAM \
+ "Time between HELLO packets\n" \
+ "Seconds\n" \
+ "Time between retransmitting lost link state advertisements\n" \
+ "Seconds\n" \
+ "Window for LSA retransmit - Retransmit LSAs expiring in this window\n" \
+ "Milliseconds\n" \
+ "Link state transmit delay\n" \
+ "Seconds\n" \
+ "Interval time after which a neighbor is declared down\n" \
"Seconds\n"
#define VLINK_HELPSTR_AUTH_SIMPLE \
@@ -1204,7 +1213,7 @@ DEFUN (no_ospf_area_vlink,
DEFUN (ospf_area_vlink_intervals,
ospf_area_vlink_intervals_cmd,
- "area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|transmit-delay (1-65535)|dead-interval (1-65535)}",
+ "area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|retransmit-window (20-10000)|transmit-delay (1-65535)|dead-interval (1-65535)}",
VLINK_HELPSTR_IPADDR
VLINK_HELPSTR_TIME_PARAM)
{
@@ -1236,6 +1245,9 @@ DEFUN (ospf_area_vlink_intervals,
else if (strmatch(argv[idx]->text, "retransmit-interval"))
vl_config.retransmit_interval =
strtol(argv[++idx]->arg, NULL, 10);
+ else if (strmatch(argv[idx]->text, "retransmit-window"))
+ vl_config.retransmit_window = strtol(argv[++idx]->arg,
+ NULL, 10);
else if (strmatch(argv[idx]->text, "transmit-delay"))
vl_config.transmit_delay =
strtol(argv[++idx]->arg, NULL, 10);
@@ -1250,7 +1262,7 @@ DEFUN (ospf_area_vlink_intervals,
DEFUN (no_ospf_area_vlink_intervals,
no_ospf_area_vlink_intervals_cmd,
- "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|transmit-delay (1-65535)|dead-interval (1-65535)}",
+ "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|retransmit-window (20-1000)|transmit-delay (1-65535)|dead-interval (1-65535)}",
NO_STR
VLINK_HELPSTR_IPADDR
VLINK_HELPSTR_TIME_PARAM)
@@ -1282,6 +1294,9 @@ DEFUN (no_ospf_area_vlink_intervals,
else if (strmatch(argv[idx]->text, "retransmit-interval"))
vl_config.retransmit_interval =
OSPF_RETRANSMIT_INTERVAL_DEFAULT;
+ else if (strmatch(argv[idx]->text, "retransmit-window"))
+ vl_config.retransmit_window =
+ OSPF_RETRANSMIT_WINDOW_DEFAULT;
else if (strmatch(argv[idx]->text, "transmit-delay"))
vl_config.transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT;
else if (strmatch(argv[idx]->text, "dead-interval"))
@@ -3846,6 +3861,10 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
json_object_int_add(
json_interface_sub, "timerRetransmitSecs",
OSPF_IF_PARAM(oi, retransmit_interval));
+ json_object_int_add(json_interface_sub,
+ "timerRetransmitWindowMsecs",
+ OSPF_IF_PARAM(oi,
+ retransmit_window));
} else {
vty_out(vty, " Timer intervals configured,");
vty_out(vty, " Hello ");
@@ -3964,6 +3983,16 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
"nbrFilterPrefixList",
"N/A");
}
+
+ /* Non-Traffic interface counters
+ */
+ if (use_json)
+ json_object_int_add(json_interface_sub,
+ "lsaRetransmissions",
+ oi->ls_rxmt_lsa);
+ else
+ vty_out(vty, " LSA retransmissions: %u\n",
+ oi->ls_rxmt_lsa);
}
}
@@ -5177,12 +5206,20 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
lookup_msg(ospf_ism_state_msg, ospf_nbr_ism_state(nbr),
NULL));
}
+
/* Show state changes. */
if (use_json)
json_object_int_add(json_neigh, "stateChangeCounter",
nbr->state_change);
else
- vty_out(vty, " %d state changes\n", nbr->state_change);
+ vty_out(vty, " %d state changes\n", nbr->state_change);
+
+ /* Show LSA retransmissions. */
+ if (use_json)
+ json_object_int_add(json_neigh, "lsaRetransmissions",
+ nbr->ls_rxmt_lsa);
+ else
+ vty_out(vty, " %u LSA retransmissions\n", nbr->ls_rxmt_lsa);
if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec) {
struct timeval res;
@@ -5231,7 +5268,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
if (DR(oi).s_addr == INADDR_ANY) {
if (!use_json)
vty_out(vty,
- " No designated router on this network\n");
+ " No designated router on this network\n");
} else {
nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi));
if (nbr_dr) {
@@ -5250,14 +5287,14 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
if (nbr_bdr == NULL) {
if (!use_json)
vty_out(vty,
- " No backup designated router on this network\n");
+ " No backup designated router on this network\n");
} else {
if (use_json)
json_object_string_addf(json_neigh,
"routerDesignatedBackupId",
"%pI4", &nbr_bdr->router_id);
else
- vty_out(vty, " BDR is %pI4\n", &nbr_bdr->router_id);
+ vty_out(vty, " BDR is %pI4\n", &nbr_bdr->router_id);
}
/* Show options. */
@@ -5347,7 +5384,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
/* Show Link State Update Retransmission thread. */
if (use_json) {
- if (nbr->t_ls_upd != NULL)
+ if (nbr->t_ls_rxmt != NULL)
json_object_string_add(
json_neigh,
"threadLinkStateUpdateRetransmission",
@@ -5355,7 +5392,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
} else
vty_out(vty,
" Thread Link State Update Retransmission %s\n\n",
- nbr->t_ls_upd != NULL ? "on" : "off");
+ nbr->t_ls_rxmt != NULL ? "on" : "off");
if (!use_json) {
vty_out(vty, " Graceful restart Helper info:\n");
@@ -7993,7 +8030,7 @@ static void ospf_nbr_timer_update(struct ospf_interface *oi)
nbr->v_inactivity = OSPF_IF_PARAM(oi, v_wait);
nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval);
nbr->v_ls_req = OSPF_IF_PARAM(oi, retransmit_interval);
- nbr->v_ls_upd = OSPF_IF_PARAM(oi, retransmit_interval);
+ nbr->v_ls_rxmt = OSPF_IF_PARAM(oi, retransmit_interval);
}
}
@@ -8728,6 +8765,40 @@ DEFUN_HIDDEN (no_ospf_retransmit_interval,
return no_ip_ospf_retransmit_interval(self, vty, argc, argv);
}
+DEFPY(ip_ospf_retransmit_window, ip_ospf_retransmit_window_addr_cmd,
+ "[no] ip ospf retransmit-window ![(20-1000)]$retransmit-window [A.B.C.D]$ip_addr", NO_STR
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Window for LSA retransmit - Retransmit LSAs expiring in this window\n"
+ "Milliseconds\n"
+ "Address of interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf_if_params *params;
+
+ params = IF_DEF_PARAMS(ifp);
+
+ if (ip_addr.s_addr != INADDR_ANY) {
+ params = ospf_get_if_params(ifp, ip_addr);
+ ospf_if_update_params(ifp, ip_addr);
+ }
+
+ if (no) {
+ UNSET_IF_PARAM(params, retransmit_window);
+ params->retransmit_window = OSPF_RETRANSMIT_WINDOW_DEFAULT;
+ } else {
+ SET_IF_PARAM(params, retransmit_window);
+ params->retransmit_window = retransmit_window;
+ }
+
+ /*
+ * There is nothing to do when the retransmit-window changes, any
+ * change will take effect the next time the interface LSA retransmision
+ * timer expires.
+ */
+ return CMD_SUCCESS;
+}
+
DEFPY (ip_ospf_gr_hdelay,
ip_ospf_gr_hdelay_cmd,
"ip ospf graceful-restart hello-delay (1-1800)",
@@ -12210,6 +12281,17 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
vty_out(vty, "\n");
}
+ /* Retransmit Window print. */
+ if (OSPF_IF_PARAM_CONFIGURED(params, retransmit_window) &&
+ params->retransmit_window !=
+ OSPF_RETRANSMIT_WINDOW_DEFAULT) {
+ vty_out(vty, " ip ospf retransmit-window %u",
+ params->retransmit_window);
+ if (params != IF_DEF_PARAMS(ifp) && rn)
+ vty_out(vty, " %pI4", &rn->p.u.prefix4);
+ vty_out(vty, "\n");
+ }
+
/* Transmit Delay print. */
if (OSPF_IF_PARAM_CONFIGURED(params, transmit_delay)
&& params->transmit_delay
@@ -12567,19 +12649,22 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf)
oi = vl_data->vl_oi;
/* timers */
- if (OSPF_IF_PARAM(oi, v_hello)
- != OSPF_HELLO_INTERVAL_DEFAULT
- || OSPF_IF_PARAM(oi, v_wait)
- != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT
- || OSPF_IF_PARAM(oi, retransmit_interval)
- != OSPF_RETRANSMIT_INTERVAL_DEFAULT
- || OSPF_IF_PARAM(oi, transmit_delay)
- != OSPF_TRANSMIT_DELAY_DEFAULT)
+ if (OSPF_IF_PARAM(oi, v_hello) !=
+ OSPF_HELLO_INTERVAL_DEFAULT ||
+ OSPF_IF_PARAM(oi, v_wait) !=
+ OSPF_ROUTER_DEAD_INTERVAL_DEFAULT ||
+ OSPF_IF_PARAM(oi, retransmit_interval) !=
+ OSPF_RETRANSMIT_INTERVAL_DEFAULT ||
+ OSPF_IF_PARAM(oi, retransmit_window) !=
+ OSPF_RETRANSMIT_WINDOW_DEFAULT ||
+ OSPF_IF_PARAM(oi, transmit_delay) !=
+ OSPF_TRANSMIT_DELAY_DEFAULT)
vty_out(vty,
- " area %s virtual-link %pI4 hello-interval %d retransmit-interval %d transmit-delay %d dead-interval %d\n",
+ " area %s virtual-link %pI4 hello-interval %d retransmit-interval %d retransmit-window %d transmit-delay %d dead-interval %d\n",
buf, &vl_data->vl_peer,
OSPF_IF_PARAM(oi, v_hello),
OSPF_IF_PARAM(oi, retransmit_interval),
+ OSPF_IF_PARAM(oi, retransmit_window),
OSPF_IF_PARAM(oi, transmit_delay),
OSPF_IF_PARAM(oi, v_wait));
else
@@ -13112,6 +13197,9 @@ static void ospf_vty_if_init(void)
install_element(INTERFACE_NODE,
&no_ip_ospf_retransmit_interval_addr_cmd);
+ /* "ip ospf retransmit-window" commands. */
+ install_element(INTERFACE_NODE, &ip_ospf_retransmit_window_addr_cmd);
+
/* "ip ospf transmit-delay" commands. */
install_element(INTERFACE_NODE, &ip_ospf_transmit_delay_addr_cmd);
install_element(INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd);
diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
index f52b51d9d8..e4e3290111 100644
--- a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
@@ -11,6 +11,7 @@ r1-eth0 is up
Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0
Graceful Restart hello delay: 10s
+ LSA retransmissions: 0
r1-eth3 is up
ifindex X, MTU 1500 bytes, BW XX Mbit <UP,LOWER_UP,BROADCAST,RUNNING,MULTICAST>
Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
@@ -24,3 +25,4 @@ r1-eth3 is up
Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0
Graceful Restart hello delay: 10s
+ LSA retransmissions: 0
diff --git a/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf
index cb4538c0e3..89f255bb44 100644
--- a/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf
+++ b/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf
@@ -1,4 +1,10 @@
!
+!log file ospfd.log debug
+! debug ospf event
+! debug ospf client
+! debug ospf lsa
+! debug ospf packet all
+
hostname r1
password zebra
log file /tmp/r1-frr.log
diff --git a/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf
index 0ca8aec3bf..429330987e 100644
--- a/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf
+++ b/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf
@@ -1,4 +1,10 @@
!
+!log file ospfd.log debug
+! debug ospf event
+! debug ospf client
+! debug ospf lsa
+! debug ospf packet all
+!
hostname r2
password zebra
log file /tmp/r1-frr.log
diff --git a/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf
index 41ea70d443..eada78450e 100644
--- a/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf
+++ b/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf
@@ -1,4 +1,10 @@
!
+!log file ospfd.log debug
+! debug ospf event
+! debug ospf client
+! debug ospf lsa
+! debug ospf packet all
+!
hostname r3
password zebra
log file /tmp/r1-frr.log
diff --git a/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf
index 21fa9c72f9..3146ea0957 100644
--- a/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf
+++ b/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf
@@ -1,4 +1,10 @@
!
+!log file ospfd.log debug
+! debug ospf event
+! debug ospf client
+! debug ospf lsa
+! debug ospf packet all
+!
hostname r4
password zebra
log file /tmp/r1-frr.log
diff --git a/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py b/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py
index d52c8147fe..455c737f0d 100644
--- a/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py
+++ b/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py
@@ -9,6 +9,7 @@
import os
import sys
+from time import sleep
from functools import partial
import pytest
@@ -113,7 +114,9 @@ def teardown_module():
tgen.stop_topology()
-def verify_p2mp_interface(tgen, router, nbr_cnt, nbr_adj_cnt, nbr_filter):
+def verify_p2mp_interface(
+ tgen, router, nbr_cnt, nbr_adj_cnt, delay_reflood, nbr_filter
+):
"Verify the P2MP Configuration and interface settings"
topo_router = tgen.gears[router]
@@ -147,7 +150,7 @@ def verify_p2mp_interface(tgen, router, nbr_cnt, nbr_adj_cnt, nbr_filter):
"nbrCount": nbr_cnt,
"nbrAdjacentCount": nbr_adj_cnt,
"prefixSuppression": False,
- "p2mpDelayReflood": False,
+ "p2mpDelayReflood": delay_reflood,
"nbrFilterPrefixList": nbr_filter,
}
}
@@ -280,7 +283,7 @@ def test_p2mp_broadcast_interface():
pytest.skip("Skipped because of router(s) failure")
step("Verify router r1 interface r1-eth0 p2mp configuration")
- verify_p2mp_interface(tgen, "r1", 3, 3, "N/A")
+ verify_p2mp_interface(tgen, "r1", 3, 3, False, "N/A")
step("Verify router r1 p2mp interface r1-eth0 neighbors")
verify_p2mp_neighbor(
@@ -305,7 +308,7 @@ def test_p2mp_broadcast_interface():
step("Verify router r1 interface r1-eth0 p2mp configuration application")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf network point-to-multipoint")
- verify_p2mp_interface(tgen, "r1", 3, 3, "N/A")
+ verify_p2mp_interface(tgen, "r1", 3, 3, False, "N/A")
step("Verify restablishment of r1-eth0 p2mp neighbors")
verify_p2mp_neighbor(
@@ -324,14 +327,14 @@ def test_p2mp_broadcast_interface():
verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
-def test_p2mp_broadcast_neighbor_filter():
+def p2mp_broadcast_neighbor_filter_common(delay_reflood):
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip("Skipped because of router(s) failure")
step("Verify router r1 interface r1-eth0 p2mp configuration")
- verify_p2mp_interface(tgen, "r1", 3, 3, "N/A")
+ verify_p2mp_interface(tgen, "r1", 3, 3, delay_reflood, "N/A")
step("Verify router r1 p2mp interface r1-eth0 neighbors")
verify_p2mp_neighbor(
@@ -362,7 +365,7 @@ def test_p2mp_broadcast_neighbor_filter():
assert neighbor_filter_cfg == " ip ospf neighbor-filter nbr-filter", assertmsg
step("Verify non-existent neighbor-filter is not applied to r1 interfaces")
- verify_p2mp_interface(tgen, "r1", 3, 3, "N/A")
+ verify_p2mp_interface(tgen, "r1", 3, 3, delay_reflood, "N/A")
step("Add nbr-filter prefix-list configuration to r1")
r1.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 200 permit any")
@@ -370,7 +373,7 @@ def test_p2mp_broadcast_neighbor_filter():
step(
"Verify neighbor-filter is now applied to r1 interface and neighbors still adjacent"
)
- verify_p2mp_interface(tgen, "r1", 3, 3, "nbr-filter")
+ verify_p2mp_interface(tgen, "r1", 3, 3, delay_reflood, "nbr-filter")
step("Add nbr-filter prefix-list configuration to block r4")
r1.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 10 deny 10.1.0.4/32")
@@ -378,7 +381,7 @@ def test_p2mp_broadcast_neighbor_filter():
step(
"Verify neighbor-filter is now applied to r1 interface and r4 is no longer adjacent"
)
- verify_p2mp_interface(tgen, "r1", 2, 2, "nbr-filter")
+ verify_p2mp_interface(tgen, "r1", 2, 2, delay_reflood, "nbr-filter")
verify_p2mp_neighbor_missing(tgen, "r1", "4.4.4.4")
step("Verify route to r4 subnet is now through r2")
@@ -390,7 +393,7 @@ def test_p2mp_broadcast_neighbor_filter():
step(
"Verify neighbor-filter is now applied to r1 interface and r2 is no longer adjacent"
)
- verify_p2mp_interface(tgen, "r1", 1, 1, "nbr-filter")
+ verify_p2mp_interface(tgen, "r1", 1, 1, delay_reflood, "nbr-filter")
verify_p2mp_neighbor_missing(tgen, "r1", "2.2.2.2")
step("Verify route to r4 and r2 subnet are now through r3")
@@ -406,24 +409,105 @@ def test_p2mp_broadcast_neighbor_filter():
assert rc, assertmsg
step("Verify interface neighbor-filter is removed and neighbors present")
- verify_p2mp_interface(tgen, "r1", 3, 3, "N/A")
+ verify_p2mp_interface(tgen, "r1", 3, 3, delay_reflood, "N/A")
step("Add neighbor filter configuration and verify neighbors are filtered")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf neighbor-filter nbr-filter")
- verify_p2mp_interface(tgen, "r1", 1, 1, "nbr-filter")
+ verify_p2mp_interface(tgen, "r1", 1, 1, delay_reflood, "nbr-filter")
verify_p2mp_neighbor_missing(tgen, "r1", "2.2.2.2")
verify_p2mp_neighbor_missing(tgen, "r1", "4.4.4.4")
step("Remove nbr-filter prefix-list configuration to block r2 and verify neighbor")
r1.vtysh_cmd("conf t\nno ip prefix-list nbr-filter seq 20")
- verify_p2mp_interface(tgen, "r1", 2, 2, "nbr-filter")
+ verify_p2mp_interface(tgen, "r1", 2, 2, delay_reflood, "nbr-filter")
verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
)
step("Delete nbr-filter prefix-list and verify neighbors are present")
r1.vtysh_cmd("conf t\nno ip prefix-list nbr-filter")
- verify_p2mp_interface(tgen, "r1", 3, 3, "N/A")
+ verify_p2mp_interface(tgen, "r1", 3, 3, delay_reflood, "N/A")
+
+
+def test_p2mp_broadcast_neighbor_filter():
+ p2mp_broadcast_neighbor_filter_common(False)
+
+
+def test_p2mp_broadcast_neighbor_filter_delay_reflood():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step("Modify router r1 interface r1-eth0 p2mp delay-reflood configuration")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd(
+ "conf t\ninterface r1-eth0\nip ospf network point-to-multipoint delay-reflood"
+ )
+ verify_p2mp_interface(tgen, "r1", 3, 3, True, "N/A")
+
+ step("Modify router r2 interface r2-eth0 p2mp delay-reflood configuration")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd(
+ "conf t\ninterface r2-eth0\nip ospf network point-to-multipoint delay-reflood"
+ )
+
+ step("Modify router r3 interface r3-eth0 p2mp delay-reflood configuration")
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ "conf t\ninterface r3-eth0\nip ospf network point-to-multipoint delay-reflood"
+ )
+
+ step("Modify router r4 interface r4-eth0 p2mp delay-reflood configuration")
+ r4 = tgen.gears["r4"]
+ r4.vtysh_cmd(
+ "conf t\ninterface r4-eth0\nip ospf network point-to-multipoint delay-reflood"
+ )
+
+ p2mp_broadcast_neighbor_filter_common(True)
+
+ step("Recreate a partial P2MP mesh with neighbor filters")
+ step("Add nbr-filter prefix-list configuration to block r4")
+ r1.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 30 permit any")
+ r1.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 10 deny 10.1.0.3/32")
+ r1.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 20 deny 10.1.0.4/32")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf neighbor-filter nbr-filter")
+
+ r2.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 30 permit any")
+ r2.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 10 deny 10.1.0.4/32")
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nip ospf neighbor-filter nbr-filter")
+
+ r3.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 30 permit any")
+ r3.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 10 deny 10.1.0.1/32")
+ r3.vtysh_cmd("conf t\ninterface r3-eth0\nip ospf neighbor-filter nbr-filter")
+
+ r4.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 30 permit any")
+ r4.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 10 deny 10.1.0.1/32")
+ r4.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 20 deny 10.1.0.2/32")
+ r4.vtysh_cmd("conf t\ninterface r4-eth0\nip ospf neighbor-filter nbr-filter")
+
+ step(
+ "Add redistribution and spaced static routes to r1 to test delay flood retransmission"
+ )
+ r1.vtysh_cmd("conf t\nrouter ospf\nredistribute static")
+ r1.vtysh_cmd("conf t\nip route 20.1.1.1/32 null0")
+ sleep(1)
+ r1.vtysh_cmd("conf t\nip route 20.1.1.2/32 null0")
+ sleep(1)
+ r1.vtysh_cmd("conf t\nip route 20.1.1.3/32 null0")
+ sleep(1)
+ r1.vtysh_cmd("conf t\nip route 20.1.1.4/32 null0")
+ sleep(1)
+ r1.vtysh_cmd("conf t\nip route 20.1.1.5/32 null0")
+ sleep(1)
+
+ step(
+ "Verify the routes are installed on r1 with delay-reflood in P2MP partial mesh"
+ )
+ verify_p2mp_route(tgen, "r4", "20.1.1.1/32", 32, "10.1.0.3", "r4-eth0")
+ verify_p2mp_route(tgen, "r4", "20.1.1.2/32", 32, "10.1.0.3", "r4-eth0")
+ verify_p2mp_route(tgen, "r4", "20.1.1.3/32", 32, "10.1.0.3", "r4-eth0")
+ verify_p2mp_route(tgen, "r4", "20.1.1.4/32", 32, "10.1.0.3", "r4-eth0")
def test_memory_leak():