summaryrefslogtreecommitdiff
path: root/ospfd
diff options
context:
space:
mode:
authorAcee Lindem <acee@lindem.com>2024-05-31 14:08:04 +0000
committerAcee Lindem <acee@lindem.com>2024-06-20 15:31:07 +0000
commitc494702929d15141e8f3fd4a1d9f85095076a4b7 (patch)
treee8d790d16fef1d0a0cf83975b04c1a2aa5b7f208 /ospfd
parenta1b21f526ad5d6807b39d3569845b0cb25b4d1bf (diff)
ospfd: Improve OSPF neighbor retransmission list granularity and precision
The current OSPF neighbor retransmission operates on a single per-neighbor periodic timer that sends all LSAs on the list when it expires. Additionally, since it skips the first retransmission of received LSAs so that at least the retransmission interval (resulting in a delay of between the retransmission interval and twice the interval. In environments where the links are lossy on P2MP networks with "delay-reflood" configured (which relies on neighbor retransmission in partial meshs), the implementation is sub-optimal (to say the least). This commit reimplements OSPF neighbor retransmission as follows: 1. A new data structure making use the application managed typesafe.h doubly linked list implements an OSPF LSA list where each node includes a timestamp. 2. The existing neighbor LS retransmission LSDB data structure is augmented with a pointer to the list node on the LSA list to faciliate O(1) removal when the LSA is acknowledged. 3. The neighbor LS retransmission timer is set to the expiration timer of the LSA at the top of the list. 4. When the timer expires, LSAs are retransmitted that within the window of the current time and a small delta (50 milli-secs default). The LSAs that are retransmited are given an updated retransmission time and moved to the end of the LSA list. 5. Configuration is added to set the "retransmission-window" to a value other than 50 milliseconds. 6. Neighbor and interface LSA retransmission counters are added to provide insight into the lossiness of the links. However, these will increment quickly on non-fully meshed P2MP networks with "delay-reflood" configured. 7. Added a topotest to exercise the implementation on a non-fully meshed P2MP network with "delay-reflood" configured. The alternative was to use existing mechanisms to instroduce loss but these seem less determistic in a topotest. Signed-off-by: Acee Lindem <acee@lindem.com>
Diffstat (limited to 'ospfd')
-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
14 files changed, 414 insertions, 96 deletions
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);