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>
This commit is contained in:
Acee Lindem 2024-05-31 14:08:04 +00:00
parent a1b21f526a
commit c494702929
24 changed files with 621 additions and 112 deletions

View file

@ -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.

View file

@ -8,6 +8,7 @@ OSPFD
:maxdepth: 2 :maxdepth: 2
ospf-api ospf-api
ospf-ls-retrans
ospf-sr ospf-sr
cspf cspf

View file

@ -738,7 +738,17 @@ Interfaces
retransmitting Database Description and Link State Request packets. The retransmitting Database Description and Link State Request packets. The
default value is 5 seconds. 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 Set number of seconds for InfTransDelay value. LSAs' age should be

View file

@ -58,6 +58,7 @@ extern "C" {
#define OSPF_HELLO_DELAY_DEFAULT 10 #define OSPF_HELLO_DELAY_DEFAULT 10
#define OSPF_ROUTER_PRIORITY_DEFAULT 1 #define OSPF_ROUTER_PRIORITY_DEFAULT 1
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 #define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
#define OSPF_RETRANSMIT_WINDOW_DEFAULT 50 /* milliseconds */
#define OSPF_TRANSMIT_DELAY_DEFAULT 1 #define OSPF_TRANSMIT_DELAY_DEFAULT 1
#define OSPF_DEFAULT_BANDWIDTH 10000 /* Mbps */ #define OSPF_DEFAULT_BANDWIDTH 10000 /* Mbps */

View file

@ -1015,7 +1015,7 @@ void ospf_ls_request_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
ospf_lsdb_delete(&nbr->ls_req, 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) void ospf_ls_request_delete_all(struct ospf_neighbor *nbr)
{ {
ospf_lsa_unlock(&nbr->ls_req_last); 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. */ /* Add LSA to be retransmitted to neighbor's ls-retransmit list. */
void ospf_ls_retransmit_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) 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 (ospf_lsa_more_recent(old, lsa) < 0) {
if (old) { if (old) {
old->retransmit_counter--; 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)) 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), ospf_ls_retransmit_count(nbr),
&nbr->router_id, &nbr->router_id,
ospf_get_name(nbr->oi->ospf), ospf_get_name(nbr->oi->ospf),
dump_lsa_key(old)); dump_lsa_key(lsa));
ospf_lsdb_delete(&nbr->ls_rxmt, old); ospf_lsa_unlock(&old);
} }
lsa->retransmit_counter++; lsa->retransmit_counter++;
ls_rxmt_list_entry = XCALLOC(MTYPE_OSPF_LSA_LIST,
sizeof(struct ospf_lsa_list_entry));
/* /*
* We cannot make use of the newly introduced callback function * Set the LSA retransmission time for the neighbor;
* "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.
*/ */
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) monotime(&ls_rxmt_list_entry->list_entry_time);
zlog_debug("RXmtL(%lu)++, NBR(%pI4(%s)), LSA[%s]", ls_rxmt_list_entry->list_entry_time.tv_sec += nbr->v_ls_rxmt;
ospf_ls_retransmit_count(nbr),
&nbr->router_id, /*
ospf_get_name(nbr->oi->ospf), * Add the LSA to the neighbor retransmission list.
dump_lsa_key(lsa)); */
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); ospf_lsdb_add(&nbr->ls_rxmt, lsa);
/*
* 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)) 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),
(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. */ /* Remove LSA from neibghbor's ls-retransmit list. */
void ospf_ls_retransmit_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) 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--; lsa->retransmit_counter--;
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) /* -- endo. */ ospf_lsa_list_del(&nbr->ls_rxmt_list,
zlog_debug("RXmtL(%lu)--, NBR(%pI4(%s)), LSA[%s]", ls_rxmt_node->lsa_list_entry);
ospf_ls_retransmit_count(nbr), XFREE(MTYPE_OSPF_LSA_LIST, ls_rxmt_node->lsa_list_entry);
&nbr->router_id,
ospf_get_name(nbr->oi->ospf),
dump_lsa_key(lsa));
ospf_lsdb_delete(&nbr->ls_rxmt, lsa); 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),
dump_lsa_key(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. */ /* Clear neighbor's ls-retransmit list. */
void ospf_ls_retransmit_clear(struct ospf_neighbor *nbr) void ospf_ls_retransmit_clear(struct ospf_neighbor *nbr)
{ {
struct ospf_lsa_list_entry *ls_rxmt_list_entry;
struct ospf_lsdb *lsdb; struct ospf_lsdb *lsdb;
int i; int i;
@ -1128,10 +1184,54 @@ void ospf_ls_retransmit_clear(struct ospf_neighbor *nbr)
ospf_ls_retransmit_delete(nbr, lsa); 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); ospf_lsa_unlock(&nbr->ls_req_last);
nbr->ls_req_last = NULL; 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. */ /* Lookup LSA from neighbor's ls-retransmit list. */
struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *nbr, struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *nbr,
struct ospf_lsa *lsa) struct ospf_lsa *lsa)

View file

@ -7,6 +7,26 @@
#ifndef _ZEBRA_OSPF_FLOOD_H #ifndef _ZEBRA_OSPF_FLOOD_H
#define _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 *, extern int ospf_flood(struct ospf *, struct ospf_neighbor *, struct ospf_lsa *,
struct ospf_lsa *); struct ospf_lsa *);
extern int ospf_flood_through(struct ospf *, struct ospf_neighbor *, 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 *, extern void ospf_ls_retransmit_delete(struct ospf_neighbor *,
struct ospf_lsa *); struct ospf_lsa *);
extern void ospf_ls_retransmit_clear(struct ospf_neighbor *); 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 *, extern struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *,
struct ospf_lsa *); struct ospf_lsa *);
extern void ospf_ls_retransmit_delete_nbr_area(struct ospf_area *, extern void ospf_ls_retransmit_delete_nbr_area(struct ospf_area *,

View file

@ -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, output_cost_cmd);
UNSET_IF_PARAM(oip, transmit_delay); UNSET_IF_PARAM(oip, transmit_delay);
UNSET_IF_PARAM(oip, retransmit_interval); UNSET_IF_PARAM(oip, retransmit_interval);
UNSET_IF_PARAM(oip, retransmit_window);
UNSET_IF_PARAM(oip, passive_interface); UNSET_IF_PARAM(oip, passive_interface);
UNSET_IF_PARAM(oip, v_hello); UNSET_IF_PARAM(oip, v_hello);
UNSET_IF_PARAM(oip, fast_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) && if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) &&
!OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) && !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) &&
!OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) && !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, passive_interface) &&
!OSPF_IF_PARAM_CONFIGURED(oip, v_hello) && !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) &&
!OSPF_IF_PARAM_CONFIGURED(oip, fast_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 = IF_DEF_PARAMS(ifp)->retransmit_interval =
OSPF_RETRANSMIT_INTERVAL_DEFAULT; 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); SET_IF_PARAM(IF_DEF_PARAMS(ifp), priority);
IF_DEF_PARAMS(ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT; IF_DEF_PARAMS(ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT;

View file

@ -47,6 +47,8 @@ struct ospf_if_params {
output_cost_cmd); /* Command Interface Output Cost */ output_cost_cmd); /* Command Interface Output Cost */
DECLARE_IF_PARAM(uint32_t, DECLARE_IF_PARAM(uint32_t,
retransmit_interval); /* Retransmission Interval */ retransmit_interval); /* Retransmission Interval */
DECLARE_IF_PARAM(uint32_t,
retransmit_window); /* Retransmission Window */
DECLARE_IF_PARAM(uint8_t, passive_interface); /* OSPF Interface is DECLARE_IF_PARAM(uint8_t, passive_interface); /* OSPF Interface is
passive: no sending or passive: no sending or
receiving (no need to receiving (no need to
@ -296,6 +298,7 @@ struct ospf_interface {
uint32_t ls_ack_out; /* LS Ack message output count. */ uint32_t ls_ack_out; /* LS Ack message output count. */
uint32_t discarded; /* discarded input count by error. */ uint32_t discarded; /* discarded input count by error. */
uint32_t state_change; /* Number of status change. */ uint32_t state_change; /* Number of status change. */
uint32_t ls_rxmt_lsa; /* Number of LSAs retransmitted. */
uint32_t full_nbrs; uint32_t full_nbrs;

View file

@ -34,6 +34,59 @@ void ospf_lsdb_init(struct ospf_lsdb *lsdb)
lsdb->type[i].db = route_table_init(); 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) void ospf_lsdb_free(struct ospf_lsdb *lsdb)
{ {
ospf_lsdb_cleanup(lsdb); ospf_lsdb_cleanup(lsdb);

View file

@ -7,6 +7,9 @@
#ifndef _ZEBRA_OSPF_LSDB_H #ifndef _ZEBRA_OSPF_LSDB_H
#define _ZEBRA_OSPF_LSDB_H #define _ZEBRA_OSPF_LSDB_H
#include "prefix.h"
#include "table.h"
/* OSPF LSDB structure. */ /* OSPF LSDB structure. */
struct ospf_lsdb { struct ospf_lsdb {
struct { struct {
@ -43,9 +46,29 @@ struct ospf_lsdb {
#define AREA_LSDB(A,T) ((A)->lsdb->type[(T)].db) #define AREA_LSDB(A,T) ((A)->lsdb->type[(T)].db)
#define AS_LSDB(O,T) ((O)->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. */ /* OSPF LSDB related functions. */
extern struct ospf_lsdb *ospf_lsdb_new(void); extern struct ospf_lsdb *ospf_lsdb_new(void);
extern void ospf_lsdb_init(struct ospf_lsdb *); 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_free(struct ospf_lsdb *);
extern void ospf_lsdb_cleanup(struct ospf_lsdb *); extern void ospf_lsdb_cleanup(struct ospf_lsdb *);
extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa); extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa);

View file

@ -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_EXTERNAL_RT_AGGR, "OSPF External Route Summarisation");
DEFINE_MTYPE(OSPFD, OSPF_P_SPACE, "OSPF TI-LFA P-Space"); 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_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");

View file

@ -44,5 +44,7 @@ DECLARE_MTYPE(OSPF_GR_HELPER);
DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR); DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR);
DECLARE_MTYPE(OSPF_P_SPACE); DECLARE_MTYPE(OSPF_P_SPACE);
DECLARE_MTYPE(OSPF_Q_SPACE); DECLARE_MTYPE(OSPF_Q_SPACE);
DECLARE_MTYPE(OSPF_LSA_LIST);
DECLARE_MTYPE(OSPF_LSDB_NODE);
#endif /* _QUAGGA_OSPF_MEMORY_H */ #endif /* _QUAGGA_OSPF_MEMORY_H */

View file

@ -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_inactivity = OSPF_IF_PARAM(oi, v_wait);
nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval); nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval);
nbr->v_ls_req = 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; nbr->priority = -1;
/* DD flags. */ /* DD flags. */
@ -80,8 +80,10 @@ struct ospf_neighbor *ospf_nbr_new(struct ospf_interface *oi)
nbr->nbr_nbma = NULL; nbr->nbr_nbma = NULL;
ospf_lsdb_init(&nbr->db_sum); 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_lsdb_init(&nbr->ls_req);
ospf_lsa_list_init(&nbr->ls_rxmt_list);
nbr->crypt_seqnum = 0; 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_inactivity);
EVENT_OFF(nbr->t_db_desc); EVENT_OFF(nbr->t_db_desc);
EVENT_OFF(nbr->t_ls_req); 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. */ /* Cancel all events. */ /* Thread lookup cost would be negligible. */
event_cancel_event(master, nbr); event_cancel_event(master, nbr);

View file

@ -9,6 +9,7 @@
#include <ospfd/ospf_gr.h> #include <ospfd/ospf_gr.h>
#include <ospfd/ospf_packet.h> #include <ospfd/ospf_packet.h>
#include <ospfd/ospf_flood.h>
/* Neighbor Data Structure */ /* Neighbor Data Structure */
struct ospf_neighbor { struct ospf_neighbor {
@ -44,6 +45,7 @@ struct ospf_neighbor {
/* LSA data. */ /* LSA data. */
struct ospf_lsdb ls_rxmt; struct ospf_lsdb ls_rxmt;
struct ospf_lsa_list_head ls_rxmt_list;
struct ospf_lsdb db_sum; struct ospf_lsdb db_sum;
struct ospf_lsdb ls_req; struct ospf_lsdb ls_req;
struct ospf_lsa *ls_req_last; struct ospf_lsa *ls_req_last;
@ -54,13 +56,13 @@ struct ospf_neighbor {
uint32_t v_inactivity; uint32_t v_inactivity;
uint32_t v_db_desc; uint32_t v_db_desc;
uint32_t v_ls_req; uint32_t v_ls_req;
uint32_t v_ls_upd; uint32_t v_ls_rxmt;
/* Threads. */ /* Threads. */
struct event *t_inactivity; struct event *t_inactivity;
struct event *t_db_desc; struct event *t_db_desc;
struct event *t_ls_req; struct event *t_ls_req;
struct event *t_ls_upd; struct event *t_ls_rxmt;
struct event *t_hello_reply; struct event *t_hello_reply;
/* NBMA configured neighbour */ /* NBMA configured neighbour */
@ -71,6 +73,7 @@ struct ospf_neighbor {
struct timeval ts_last_regress; /* last regressive NSM change */ struct timeval ts_last_regress; /* last regressive NSM change */
const char *last_regress_str; /* Event which last regressed NSM */ const char *last_regress_str; /* Event which last regressed NSM */
uint32_t state_change; /* NSM state change counter */ uint32_t state_change; /* NSM state change counter */
uint32_t ls_rxmt_lsa; /* Number of LSAs retransmited. */
/* BFD information */ /* BFD information */
struct bfd_session_params *bfd_session; struct bfd_session_params *bfd_session;

View file

@ -112,18 +112,16 @@ static void nsm_timer_set(struct ospf_neighbor *nbr)
case NSM_Init: case NSM_Init:
case NSM_TwoWay: case NSM_TwoWay:
EVENT_OFF(nbr->t_db_desc); EVENT_OFF(nbr->t_db_desc);
EVENT_OFF(nbr->t_ls_upd); EVENT_OFF(nbr->t_ls_rxmt);
EVENT_OFF(nbr->t_ls_req); EVENT_OFF(nbr->t_ls_req);
break; break;
case NSM_ExStart: case NSM_ExStart:
OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer,
nbr->v_db_desc); nbr->v_db_desc);
EVENT_OFF(nbr->t_ls_upd); EVENT_OFF(nbr->t_ls_rxmt);
EVENT_OFF(nbr->t_ls_req); EVENT_OFF(nbr->t_ls_req);
break; break;
case NSM_Exchange: 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)) if (!IS_SET_DD_MS(nbr->dd_flags))
EVENT_OFF(nbr->t_db_desc); EVENT_OFF(nbr->t_db_desc);
break; break;

View file

@ -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); 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 */ * OSPF neighbor link state retransmission timer handler. Unicast
void ospf_ls_upd_timer(struct event *thread) * unacknowledged LSAs to the neigbhors.
*/
void ospf_ls_rxmt_timer(struct event *thread)
{ {
struct ospf_neighbor *nbr; struct ospf_neighbor *nbr;
int retransmit_interval, retransmit_window, rxmt_lsa_count = 0;
nbr = EVENT_ARG(thread); 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. */ /* Send Link State Update. */
if (ospf_ls_retransmit_count(nbr) > 0) { 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 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(); 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++) { listnode_add(update, ls_rxmt_list_entry->lsa);
struct route_table *table = lsdb->type[i].db; rxmt_lsa_count++;
struct route_node *rn;
for (rn = route_top(table); rn; rn = route_next(rn)) { /*
struct ospf_lsa *lsa; * Set the next retransmit time for the LSA and move it
* to the end of the neighbor's retransmission list.
if ((lsa = rn->info) != NULL) { */
/* Don't retransmit an LSA if we ls_rxmt_list_entry->list_entry_time = next_rxmt_time;
received it within ospf_lsa_list_del(&nbr->ls_rxmt_list,
the last RxmtInterval seconds - this ls_rxmt_list_entry);
is to allow the ospf_lsa_list_add_tail(&nbr->ls_rxmt_list,
neighbour a chance to acknowledge the ls_rxmt_list_entry);
LSA as it may nbr->ls_rxmt_lsa++;
have ben just received before the nbr->oi->ls_rxmt_lsa++;
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);
}
}
} }
if (listcount(update) > 0) if (listcount(update) > 0)
@ -348,8 +360,13 @@ void ospf_ls_upd_timer(struct event *thread)
list_delete(&update); 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. */ /* 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) void ospf_ls_ack_timer(struct event *thread)

View file

@ -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_retransmit(struct ospf_interface *, struct ospf_lsa *);
extern void ospf_ls_req_event(struct ospf_neighbor *); 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_ls_ack_timer(struct event *thread);
extern void ospf_poll_timer(struct event *thread); extern void ospf_poll_timer(struct event *thread);
extern void ospf_hello_reply_timer(struct event *thread); extern void ospf_hello_reply_timer(struct event *thread);

View file

@ -815,6 +815,7 @@ struct ospf_vl_config_data {
int del_keychain; int del_keychain;
int hello_interval; /* Obvious what these are... */ int hello_interval; /* Obvious what these are... */
int retransmit_interval; int retransmit_interval;
int retransmit_window;
int transmit_delay; int transmit_delay;
int dead_interval; int dead_interval;
}; };
@ -957,6 +958,12 @@ static int ospf_vl_set_timers(struct ospf_vl_data *vl_data,
vl_config->retransmit_interval; 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) { if (vl_config->transmit_delay) {
SET_IF_PARAM(IF_DEF_PARAMS(ifp), transmit_delay); SET_IF_PARAM(IF_DEF_PARAMS(ifp), transmit_delay);
IF_DEF_PARAMS(ifp)->transmit_delay = vl_config->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 null authentication\n" \
"Use message-digest authentication\n" "Use message-digest authentication\n"
#define VLINK_HELPSTR_TIME_PARAM \ #define VLINK_HELPSTR_TIME_PARAM \
"Time between HELLO packets\n" \ "Time between HELLO packets\n" \
"Seconds\n" \ "Seconds\n" \
"Time between retransmitting lost link state advertisements\n" \ "Time between retransmitting lost link state advertisements\n" \
"Seconds\n" \ "Seconds\n" \
"Link state transmit delay\n" \ "Window for LSA retransmit - Retransmit LSAs expiring in this window\n" \
"Seconds\n" \ "Milliseconds\n" \
"Interval time after which a neighbor is declared down\n" \ "Link state transmit delay\n" \
"Seconds\n" \
"Interval time after which a neighbor is declared down\n" \
"Seconds\n" "Seconds\n"
#define VLINK_HELPSTR_AUTH_SIMPLE \ #define VLINK_HELPSTR_AUTH_SIMPLE \
@ -1204,7 +1213,7 @@ DEFUN (no_ospf_area_vlink,
DEFUN (ospf_area_vlink_intervals, DEFUN (ospf_area_vlink_intervals,
ospf_area_vlink_intervals_cmd, 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_IPADDR
VLINK_HELPSTR_TIME_PARAM) VLINK_HELPSTR_TIME_PARAM)
{ {
@ -1236,6 +1245,9 @@ DEFUN (ospf_area_vlink_intervals,
else if (strmatch(argv[idx]->text, "retransmit-interval")) else if (strmatch(argv[idx]->text, "retransmit-interval"))
vl_config.retransmit_interval = vl_config.retransmit_interval =
strtol(argv[++idx]->arg, NULL, 10); 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")) else if (strmatch(argv[idx]->text, "transmit-delay"))
vl_config.transmit_delay = vl_config.transmit_delay =
strtol(argv[++idx]->arg, NULL, 10); strtol(argv[++idx]->arg, NULL, 10);
@ -1250,7 +1262,7 @@ DEFUN (ospf_area_vlink_intervals,
DEFUN (no_ospf_area_vlink_intervals, DEFUN (no_ospf_area_vlink_intervals,
no_ospf_area_vlink_intervals_cmd, 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 NO_STR
VLINK_HELPSTR_IPADDR VLINK_HELPSTR_IPADDR
VLINK_HELPSTR_TIME_PARAM) VLINK_HELPSTR_TIME_PARAM)
@ -1282,6 +1294,9 @@ DEFUN (no_ospf_area_vlink_intervals,
else if (strmatch(argv[idx]->text, "retransmit-interval")) else if (strmatch(argv[idx]->text, "retransmit-interval"))
vl_config.retransmit_interval = vl_config.retransmit_interval =
OSPF_RETRANSMIT_INTERVAL_DEFAULT; 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")) else if (strmatch(argv[idx]->text, "transmit-delay"))
vl_config.transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; vl_config.transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT;
else if (strmatch(argv[idx]->text, "dead-interval")) 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_object_int_add(
json_interface_sub, "timerRetransmitSecs", json_interface_sub, "timerRetransmitSecs",
OSPF_IF_PARAM(oi, retransmit_interval)); OSPF_IF_PARAM(oi, retransmit_interval));
json_object_int_add(json_interface_sub,
"timerRetransmitWindowMsecs",
OSPF_IF_PARAM(oi,
retransmit_window));
} else { } else {
vty_out(vty, " Timer intervals configured,"); vty_out(vty, " Timer intervals configured,");
vty_out(vty, " Hello "); vty_out(vty, " Hello ");
@ -3964,6 +3983,16 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
"nbrFilterPrefixList", "nbrFilterPrefixList",
"N/A"); "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), lookup_msg(ospf_ism_state_msg, ospf_nbr_ism_state(nbr),
NULL)); NULL));
} }
/* Show state changes. */ /* Show state changes. */
if (use_json) if (use_json)
json_object_int_add(json_neigh, "stateChangeCounter", json_object_int_add(json_neigh, "stateChangeCounter",
nbr->state_change); nbr->state_change);
else 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) { if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec) {
struct timeval res; 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 (DR(oi).s_addr == INADDR_ANY) {
if (!use_json) if (!use_json)
vty_out(vty, vty_out(vty,
" No designated router on this network\n"); " No designated router on this network\n");
} else { } else {
nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi)); nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi));
if (nbr_dr) { if (nbr_dr) {
@ -5250,14 +5287,14 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
if (nbr_bdr == NULL) { if (nbr_bdr == NULL) {
if (!use_json) if (!use_json)
vty_out(vty, vty_out(vty,
" No backup designated router on this network\n"); " No backup designated router on this network\n");
} else { } else {
if (use_json) if (use_json)
json_object_string_addf(json_neigh, json_object_string_addf(json_neigh,
"routerDesignatedBackupId", "routerDesignatedBackupId",
"%pI4", &nbr_bdr->router_id); "%pI4", &nbr_bdr->router_id);
else 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. */ /* Show options. */
@ -5347,7 +5384,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
/* Show Link State Update Retransmission thread. */ /* Show Link State Update Retransmission thread. */
if (use_json) { if (use_json) {
if (nbr->t_ls_upd != NULL) if (nbr->t_ls_rxmt != NULL)
json_object_string_add( json_object_string_add(
json_neigh, json_neigh,
"threadLinkStateUpdateRetransmission", "threadLinkStateUpdateRetransmission",
@ -5355,7 +5392,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
} else } else
vty_out(vty, vty_out(vty,
" Thread Link State Update Retransmission %s\n\n", " 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) { if (!use_json) {
vty_out(vty, " Graceful restart Helper info:\n"); 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_inactivity = OSPF_IF_PARAM(oi, v_wait);
nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval); nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval);
nbr->v_ls_req = 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); 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, DEFPY (ip_ospf_gr_hdelay,
ip_ospf_gr_hdelay_cmd, ip_ospf_gr_hdelay_cmd,
"ip ospf graceful-restart hello-delay (1-1800)", "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"); 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. */ /* Transmit Delay print. */
if (OSPF_IF_PARAM_CONFIGURED(params, transmit_delay) if (OSPF_IF_PARAM_CONFIGURED(params, transmit_delay)
&& 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; oi = vl_data->vl_oi;
/* timers */ /* timers */
if (OSPF_IF_PARAM(oi, v_hello) if (OSPF_IF_PARAM(oi, v_hello) !=
!= OSPF_HELLO_INTERVAL_DEFAULT OSPF_HELLO_INTERVAL_DEFAULT ||
|| OSPF_IF_PARAM(oi, v_wait) OSPF_IF_PARAM(oi, v_wait) !=
!= OSPF_ROUTER_DEAD_INTERVAL_DEFAULT OSPF_ROUTER_DEAD_INTERVAL_DEFAULT ||
|| OSPF_IF_PARAM(oi, retransmit_interval) OSPF_IF_PARAM(oi, retransmit_interval) !=
!= OSPF_RETRANSMIT_INTERVAL_DEFAULT OSPF_RETRANSMIT_INTERVAL_DEFAULT ||
|| OSPF_IF_PARAM(oi, transmit_delay) OSPF_IF_PARAM(oi, retransmit_window) !=
!= OSPF_TRANSMIT_DELAY_DEFAULT) OSPF_RETRANSMIT_WINDOW_DEFAULT ||
OSPF_IF_PARAM(oi, transmit_delay) !=
OSPF_TRANSMIT_DELAY_DEFAULT)
vty_out(vty, 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, buf, &vl_data->vl_peer,
OSPF_IF_PARAM(oi, v_hello), OSPF_IF_PARAM(oi, v_hello),
OSPF_IF_PARAM(oi, retransmit_interval), OSPF_IF_PARAM(oi, retransmit_interval),
OSPF_IF_PARAM(oi, retransmit_window),
OSPF_IF_PARAM(oi, transmit_delay), OSPF_IF_PARAM(oi, transmit_delay),
OSPF_IF_PARAM(oi, v_wait)); OSPF_IF_PARAM(oi, v_wait));
else else
@ -13112,6 +13197,9 @@ static void ospf_vty_if_init(void)
install_element(INTERFACE_NODE, install_element(INTERFACE_NODE,
&no_ip_ospf_retransmit_interval_addr_cmd); &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. */ /* "ip ospf transmit-delay" commands. */
install_element(INTERFACE_NODE, &ip_ospf_transmit_delay_addr_cmd); install_element(INTERFACE_NODE, &ip_ospf_transmit_delay_addr_cmd);
install_element(INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd); install_element(INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd);

View file

@ -11,6 +11,7 @@ r1-eth0 is up
Hello due in XX.XXXs Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0 Neighbor Count is 0, Adjacent neighbor count is 0
Graceful Restart hello delay: 10s Graceful Restart hello delay: 10s
LSA retransmissions: 0
r1-eth3 is up r1-eth3 is up
ifindex X, MTU 1500 bytes, BW XX Mbit <UP,LOWER_UP,BROADCAST,RUNNING,MULTICAST> 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 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 Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0 Neighbor Count is 0, Adjacent neighbor count is 0
Graceful Restart hello delay: 10s Graceful Restart hello delay: 10s
LSA retransmissions: 0

View file

@ -1,4 +1,10 @@
! !
!log file ospfd.log debug
! debug ospf event
! debug ospf client
! debug ospf lsa
! debug ospf packet all
hostname r1 hostname r1
password zebra password zebra
log file /tmp/r1-frr.log log file /tmp/r1-frr.log

View file

@ -1,4 +1,10 @@
! !
!log file ospfd.log debug
! debug ospf event
! debug ospf client
! debug ospf lsa
! debug ospf packet all
!
hostname r2 hostname r2
password zebra password zebra
log file /tmp/r1-frr.log log file /tmp/r1-frr.log

View file

@ -1,4 +1,10 @@
! !
!log file ospfd.log debug
! debug ospf event
! debug ospf client
! debug ospf lsa
! debug ospf packet all
!
hostname r3 hostname r3
password zebra password zebra
log file /tmp/r1-frr.log log file /tmp/r1-frr.log

View file

@ -1,4 +1,10 @@
! !
!log file ospfd.log debug
! debug ospf event
! debug ospf client
! debug ospf lsa
! debug ospf packet all
!
hostname r4 hostname r4
password zebra password zebra
log file /tmp/r1-frr.log log file /tmp/r1-frr.log

View file

@ -9,6 +9,7 @@
import os import os
import sys import sys
from time import sleep
from functools import partial from functools import partial
import pytest import pytest
@ -113,7 +114,9 @@ def teardown_module():
tgen.stop_topology() 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" "Verify the P2MP Configuration and interface settings"
topo_router = tgen.gears[router] 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, "nbrCount": nbr_cnt,
"nbrAdjacentCount": nbr_adj_cnt, "nbrAdjacentCount": nbr_adj_cnt,
"prefixSuppression": False, "prefixSuppression": False,
"p2mpDelayReflood": False, "p2mpDelayReflood": delay_reflood,
"nbrFilterPrefixList": nbr_filter, "nbrFilterPrefixList": nbr_filter,
} }
} }
@ -280,7 +283,7 @@ def test_p2mp_broadcast_interface():
pytest.skip("Skipped because of router(s) failure") pytest.skip("Skipped because of router(s) failure")
step("Verify router r1 interface r1-eth0 p2mp configuration") 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") step("Verify router r1 p2mp interface r1-eth0 neighbors")
verify_p2mp_neighbor( verify_p2mp_neighbor(
@ -305,7 +308,7 @@ def test_p2mp_broadcast_interface():
step("Verify router r1 interface r1-eth0 p2mp configuration application") step("Verify router r1 interface r1-eth0 p2mp configuration application")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf network point-to-multipoint") 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") step("Verify restablishment of r1-eth0 p2mp neighbors")
verify_p2mp_neighbor( 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") 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() tgen = get_topogen()
if tgen.routers_have_failure(): if tgen.routers_have_failure():
pytest.skip("Skipped because of router(s) failure") pytest.skip("Skipped because of router(s) failure")
step("Verify router r1 interface r1-eth0 p2mp configuration") 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") step("Verify router r1 p2mp interface r1-eth0 neighbors")
verify_p2mp_neighbor( verify_p2mp_neighbor(
@ -362,7 +365,7 @@ def test_p2mp_broadcast_neighbor_filter():
assert neighbor_filter_cfg == " ip ospf neighbor-filter nbr-filter", assertmsg assert neighbor_filter_cfg == " ip ospf neighbor-filter nbr-filter", assertmsg
step("Verify non-existent neighbor-filter is not applied to r1 interfaces") 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") step("Add nbr-filter prefix-list configuration to r1")
r1.vtysh_cmd("conf t\nip prefix-list nbr-filter seq 200 permit any") 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( step(
"Verify neighbor-filter is now applied to r1 interface and neighbors still adjacent" "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") 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") 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( step(
"Verify neighbor-filter is now applied to r1 interface and r4 is no longer adjacent" "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") verify_p2mp_neighbor_missing(tgen, "r1", "4.4.4.4")
step("Verify route to r4 subnet is now through r2") step("Verify route to r4 subnet is now through r2")
@ -390,7 +393,7 @@ def test_p2mp_broadcast_neighbor_filter():
step( step(
"Verify neighbor-filter is now applied to r1 interface and r2 is no longer adjacent" "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") verify_p2mp_neighbor_missing(tgen, "r1", "2.2.2.2")
step("Verify route to r4 and r2 subnet are now through r3") 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 assert rc, assertmsg
step("Verify interface neighbor-filter is removed and neighbors present") 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") step("Add neighbor filter configuration and verify neighbors are filtered")
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf neighbor-filter nbr-filter") 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", "2.2.2.2")
verify_p2mp_neighbor_missing(tgen, "r1", "4.4.4.4") verify_p2mp_neighbor_missing(tgen, "r1", "4.4.4.4")
step("Remove nbr-filter prefix-list configuration to block r2 and verify neighbor") 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") 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( verify_p2mp_neighbor(
tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1" 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") step("Delete nbr-filter prefix-list and verify neighbors are present")
r1.vtysh_cmd("conf t\nno ip prefix-list nbr-filter") 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(): def test_memory_leak():