ospfd: Fast OSPF convergence

When considering small networks that have extreme requirements on
availability and thus convergence delay, the timers given in the OSPF RFC
seem a little “conservative”, i.e., the delay between accepted LSAs and the
rate at which LSAs are sent.  Cisco introduced two commands 'timers throttle
lsa all’ and 'timers lsa arrival’, which allow operators to tune these
parameters.

I have been writing a patch to also support 'timers lsa arrival’ fully and
‘timers throttle lsa all’ (without the throttling part) also in quagga.
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 0e42ff5..df19adf 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -266,7 +266,7 @@
           ; /* Accept this LSA for quick LSDB resynchronization. */
         }
       else if (tv_cmp (tv_sub (recent_relative_time (), current->tv_recv),
-	               int2tv (OSPF_MIN_LS_ARRIVAL)) < 0)
+	               msec2tv (ospf->min_ls_arrival)) < 0)
         {
           if (IS_DEBUG_OSPF_EVENT)
 	    zlog_debug ("LSA[Flooding]: LSA is received recently.");
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index f032601..e62a4e7 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -108,6 +108,17 @@
 }
 
 struct timeval
+msec2tv (int a)
+{
+  struct timeval ret;
+
+  ret.tv_sec = 0;
+  ret.tv_usec = a * 1000;
+
+  return tv_adjust (ret);
+}
+
+struct timeval
 tv_add (struct timeval a, struct timeval b)
 {
   struct timeval ret;
@@ -145,9 +156,9 @@
   quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
   delta = tv_sub (now, lsa->tv_orig);
 
-  if (tv_cmp (delta, int2tv (OSPF_MIN_LS_INTERVAL)) < 0)
+  if (tv_cmp (delta, msec2tv (OSPF_MIN_LS_INTERVAL)) < 0)
     {
-      delay = tv_ceil (tv_sub (int2tv (OSPF_MIN_LS_INTERVAL), delta));
+      delay = tv_ceil (tv_sub (msec2tv (OSPF_MIN_LS_INTERVAL), delta));
 
       if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
         zlog_debug ("LSA[Type%d:%s]: Refresh timer delay %d seconds",
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
index c71877d..dd3b91a 100644
--- a/ospfd/ospf_lsa.h
+++ b/ospfd/ospf_lsa.h
@@ -237,6 +237,7 @@
 extern int tv_ceil (struct timeval);
 extern int tv_floor (struct timeval);
 extern struct timeval int2tv (int);
+extern struct timeval msec2tv (int);
 extern struct timeval tv_add (struct timeval, struct timeval);
 extern struct timeval tv_sub (struct timeval, struct timeval);
 extern int tv_cmp (struct timeval, struct timeval);
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index f584fc7..6428480 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -1331,10 +1331,10 @@
   &&    oi->t_opaque_lsa_self == NULL)
     {
       if (IS_DEBUG_OSPF_EVENT)
-        zlog_debug ("Schedule Type-9 Opaque-LSA origination in %d sec later.", delay);
+        zlog_debug ("Schedule Type-9 Opaque-LSA origination in %d ms later.", delay);
       oi->t_opaque_lsa_self =
-	thread_add_timer (master, ospf_opaque_type9_lsa_originate, oi, delay);
-      delay += OSPF_MIN_LS_INTERVAL;
+	thread_add_timer_msec (master, ospf_opaque_type9_lsa_originate, oi, delay);
+      delay += top->min_ls_interval;
     }
 
   if (! list_isempty (ospf_opaque_type10_funclist)
@@ -1347,11 +1347,11 @@
        * again and again.
        */
       if (IS_DEBUG_OSPF_EVENT)
-        zlog_debug ("Schedule Type-10 Opaque-LSA origination in %d sec later.", delay);
+        zlog_debug ("Schedule Type-10 Opaque-LSA origination in %d ms later.", delay);
       area->t_opaque_lsa_self =
-        thread_add_timer (master, ospf_opaque_type10_lsa_originate,
+        thread_add_timer_msec (master, ospf_opaque_type10_lsa_originate,
                           area, delay);
-      delay += OSPF_MIN_LS_INTERVAL;
+      delay += top->min_ls_interval;
     }
 
   if (! list_isempty (ospf_opaque_type11_funclist)
@@ -1364,11 +1364,11 @@
        * again and again.
        */
       if (IS_DEBUG_OSPF_EVENT)
-        zlog_debug ("Schedule Type-11 Opaque-LSA origination in %d sec later.", delay);
+        zlog_debug ("Schedule Type-11 Opaque-LSA origination in %d ms later.", delay);
       top->t_opaque_lsa_self =
-        thread_add_timer (master, ospf_opaque_type11_lsa_originate,
+        thread_add_timer_msec (master, ospf_opaque_type11_lsa_originate,
                           top, delay);
-      delay += OSPF_MIN_LS_INTERVAL;
+      delay += top->min_ls_interval;
     }
 
   /*
@@ -1646,7 +1646,7 @@
 
 #define OSPF_OPAQUE_TIMER_ON(T,F,L,V) \
       if (!(T)) \
-        (T) = thread_add_timer (master, (F), (L), (V))
+        (T) = thread_add_timer_msec (master, (F), (L), (V))
 
 static struct ospf_lsa *pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area, u_char lsa_type, u_char opaque_type);
 static int ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t);
@@ -1794,11 +1794,11 @@
    * it is highly possible that these conditions might not be satisfied
    * at the time of re-origination function is to be called.
    */
-  delay = OSPF_MIN_LS_INTERVAL; /* XXX */
+  delay = top->min_ls_interval; /* XXX */
 
   if (IS_DEBUG_OSPF_EVENT)
     zlog_debug ("Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d"
-               " sec later: [opaque-type=%u]", 
+               " ms later: [opaque-type=%u]",
                lsa_type, delay, 
                GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)));
 
@@ -2024,7 +2024,7 @@
     zlog_debug ("Schedule Type-%u Opaque-LSA to REFRESH in %d sec later: [opaque-type=%u, opaque-id=%x]", lsa->data->type, delay, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)));
 
   OSPF_OPAQUE_TIMER_ON (oipi->t_opaque_lsa_self,
-                        ospf_opaque_lsa_refresh_timer, oipi, delay);
+                        ospf_opaque_lsa_refresh_timer, oipi, delay * 1000);
 out:
   return;
 }
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index c6cd018..73111e8 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -1703,7 +1703,7 @@
 
 /* OSPF Link State Update message read -- RFC2328 Section 13. */
 static void
-ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh,
+ospf_ls_upd (struct ospf *ospf, struct ip *iph, struct ospf_header *ospfh,
 	     struct stream *s, struct ospf_interface *oi, u_int16_t size)
 {
   struct ospf_neighbor *nbr;
@@ -2046,7 +2046,7 @@
 	      quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
 	      
 	      if (tv_cmp (tv_sub (now, current->tv_orig), 
-			  int2tv (OSPF_MIN_LS_ARRIVAL)) >= 0)
+			  msec2tv (ospf->min_ls_arrival)) >= 0)
 		/* Trap NSSA type later.*/
 		ospf_ls_upd_send_lsa (nbr, current, OSPF_SEND_PACKET_DIRECT);
 	      DISCARD_LSA (lsa, 8);
@@ -2932,7 +2932,7 @@
       ospf_ls_req (iph, ospfh, ibuf, oi, length);
       break;
     case OSPF_MSG_LS_UPD:
-      ospf_ls_upd (iph, ospfh, ibuf, oi, length);
+      ospf_ls_upd (ospf, iph, ospfh, ibuf, oi, length);
       break;
     case OSPF_MSG_LS_ACK:
       ospf_ls_ack (iph, ospfh, ibuf, oi, length);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 9d04892..e5e5631 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -2233,6 +2233,83 @@
   return CMD_SUCCESS;
 }
 
+DEFUN (ospf_timers_min_ls_interval,
+       ospf_timers_min_ls_interval_cmd,
+       "timers throttle lsa all <0-5000>",
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "LSA delay between transmissions\n"
+       NO_STR
+       "Delay (msec) between sending LSAs\n")
+{
+  struct ospf *ospf = vty->index;
+  unsigned int interval;
+
+  if (argc != 1)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER ("LSA interval", interval, argv[0]);
+
+  ospf->min_ls_interval = interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_timers_min_ls_interval,
+       no_ospf_timers_min_ls_interval_cmd,
+       "no timers throttle lsa all",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "LSA delay between transmissions\n")
+{
+  struct ospf *ospf = vty->index;
+  ospf->min_ls_interval = OSPF_MIN_LS_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_timers_min_ls_arrival,
+       ospf_timers_min_ls_arrival_cmd,
+       "timers lsa arrival <0-1000>",
+       "Adjust routing timers\n"
+       "Throttling link state advertisement delays\n"
+       "OSPF minimum arrival interval delay\n"
+       "Delay (msec) between accepted LSAs\n")
+{
+  struct ospf *ospf = vty->index;
+  unsigned int arrival;
+
+  if (argc != 1)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE ("minimum LSA inter-arrival time", arrival, argv[0], 0, 1000);
+
+  ospf->min_ls_arrival = arrival;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_timers_min_ls_arrival,
+       no_ospf_timers_min_ls_arrival_cmd,
+       "no timers lsa arrival",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling link state advertisement delays\n"
+       "OSPF minimum arrival interval delay\n")
+{
+  struct ospf *ospf = vty->index;
+  ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL;
+
+  return CMD_SUCCESS;
+}
+
 DEFUN (ospf_timers_throttle_spf,
        ospf_timers_throttle_spf_cmd,
        "timers throttle spf <0-600000> <0-600000> <0-600000>",
@@ -7289,6 +7366,14 @@
 		   ospf->ref_bandwidth / 1000, VTY_NEWLINE);
         }
 
+      /* LSA timers */
+      if (ospf->min_ls_interval != OSPF_MIN_LS_INTERVAL)
+  vty_out (vty, " timers throttle lsa all %d%s",
+     ospf->min_ls_interval, VTY_NEWLINE);
+      if (ospf->min_ls_arrival != OSPF_MIN_LS_ARRIVAL)
+  vty_out (vty, " timers lsa arrival %d%s",
+     ospf->min_ls_arrival, VTY_NEWLINE);
+
       /* SPF timers print. */
       if (ospf->spf_delay != OSPF_SPF_DELAY_DEFAULT ||
 	  ospf->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT ||
@@ -7700,6 +7785,12 @@
   install_element (OSPF_NODE, &ospf_area_import_list_cmd);
   install_element (OSPF_NODE, &no_ospf_area_import_list_cmd);
   
+  /* LSA timer commands */
+  install_element (OSPF_NODE, &ospf_timers_min_ls_interval_cmd);
+  install_element (OSPF_NODE, &no_ospf_timers_min_ls_interval_cmd);
+  install_element (OSPF_NODE, &ospf_timers_min_ls_arrival_cmd);
+  install_element (OSPF_NODE, &no_ospf_timers_min_ls_arrival_cmd);
+
   /* SPF timer commands */
   install_element (OSPF_NODE, &ospf_timers_spf_cmd);
   install_element (OSPF_NODE, &no_ospf_timers_spf_cmd);
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 2704100..588f0fb 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -1007,8 +1007,6 @@
   return 0;
 }
 
-#define OSPF_DISTRIBUTE_UPDATE_DELAY 5
-
 /* Update distribute-list and set timer to apply access-list. */
 void
 ospf_distribute_list_update (struct ospf *ospf, uintptr_t type)
@@ -1025,8 +1023,8 @@
 
   /* Set timer. */
   ospf->t_distribute_update =
-    thread_add_timer (master, ospf_distribute_list_update_timer,
-                      (void *) type, OSPF_DISTRIBUTE_UPDATE_DELAY);
+    thread_add_timer_msec (master, ospf_distribute_list_update_timer,
+                      (void *) type, ospf->min_ls_interval);
 }
 
 /* If access-list is updated, apply some check. */
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 1a549c3..8c7d1c2 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -199,6 +199,10 @@
   new->default_metric = -1;
   new->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH;
 
+  /* LSA timers */
+  new->min_ls_interval = OSPF_MIN_LS_INTERVAL;
+  new->min_ls_arrival = OSPF_MIN_LS_ARRIVAL;
+
   /* SPF timer value init. */
   new->spf_delay = OSPF_SPF_DELAY_DEFAULT;
   new->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT;
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 06841b8..c50e615 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -147,6 +147,10 @@
 
 #define OSPF_STUB_MAX_METRIC_SUMMARY_COST	0x00ff0000
 
+  /* LSA timers */
+  unsigned int min_ls_interval; /* minimum delay between LSAs (in msec) */
+  unsigned int min_ls_arrival; /* minimum interarrival time between LSAs (in msec) */
+
   /* SPF parameters */
   unsigned int spf_delay;		/* SPF delay time. */
   unsigned int spf_holdtime;		/* SPF hold time. */