2005-10-29 Paul Jakma <paul.jakma@sun.com>

	* (general) RFC3137 stub-router support
	* ospfd.h: Add OSPF_OUTPUT_COST_INFINITE define.
	  (struct ospf_master) Add a OSPF_MASTER_SHUTDOWN flag for
	  options, to allow shutdown to distinguish between complete
	  shutdown and shutdown of a subset of ospf instances.
	  (struct ospf)
	  Add stub_router_{startup,shutdown_}time, configuration of startup
	  and shutdown time for stub-router.
	  Add t_graceful_shutdown struct thread, timer for graceful
	  shutdown, if needed.
	  (struct ospf_area) Add stub_router_state - run time state of
	  stub-router for an area. Add flags for ADMIN, IS and WAS
	  states.
	  Add t_stub_router, timer thread to resend router-lsa for an
	  area.
	* ospf_lsa.c: (ospf_link_cost) new simple function to spit out
	  either the given lnks cost or infinite cost if stub-router is
	  in effect.
	  (lsa_link_{ptop,broadcast,virtuallink,ptomp}_set) use
	  previous function for transit-links.
	  (ospf_stub_router_timer) timer thread for end of startup stub
	  router. Change state as required for the area and setup
	  re-origination of router-lsa.
	  (ospf_stub_router_check) Check/do whether stub-router should be
	  enabled, and whether it requires timer to be setup.
	  (ospf_router_lsa_new) call previous function at top.
	  (ospf_router_lsa_originate) no external callers, made static.
	* ospf_lsa.h: (ospf_router_lsa_originate) removed.
	* ospf_main.c: (sigint) make static.
	  remove call to exit, as ospf_terminate now deals with
	  exiting.
	* ospf_route.c: (ospf_terminate) removed, now in ospfd.c.
	* ospf_vty.c: (show_ip_ospf_area) print out state of
	  stub-router, if active.
	  (show_ip_ospf) print out configuration of stub-router
	  support, and details of graceful-shutdown if the timer is
	  active.
	  ((no)?ospf_max_metric_router_lsa_{admin,startup,shutdown}) new
	  commands to (de-)?configure stub-router support.
	  (config_write_stub_router) write out config of stub-router.
	  (ospf_config_write) call previous.
	  (ospf_vty_init) install the new stub-router commands.
	* ospfd.c: various functions made static.
	  (ospf_new) Set defaults for stub-router. Graceful shutdown
	  is made to default on, just to be adventerous.
	  (ospf_graceful_shutdown_finish) new function, final part of
	  shutdown.
	  (ospf_graceful_shutdown_timer) timer thread wrapper for
	  graceful-shutdown.
	  (ospf_graceful_shutdown_check) check whether to setup timer
	  for shutdown or proceed directly to final shutdown.
	  (ospf_terminate) moved here from ospf_route.c, call
	  ospf_finish for each instance.
	  (ospf_finish) renamed to ospf_finish_final and made static.
	  (ospf_finish) new function, exported wrapper around
	  ospf_graceful_shutdown_check.
	  (ospf_finish_final) complete shutdown of an instance.
	  Add missing TIMER_OFF's of two timer threads.
	  (ospf_area_free) opaque self lsa timer should be turned off.
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index bf8ca4d..69f091d 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -64,13 +64,14 @@
 extern struct in_addr router_id_zebra;
 
 
-void ospf_remove_vls_through_area (struct ospf *, struct ospf_area *);
-void ospf_network_free (struct ospf *, struct ospf_network *);
-void ospf_area_free (struct ospf_area *);
-void ospf_network_run (struct ospf *, struct prefix *, struct ospf_area *);
+static void ospf_remove_vls_through_area (struct ospf *, struct ospf_area *);
+static void ospf_network_free (struct ospf *, struct ospf_network *);
+static void ospf_area_free (struct ospf_area *);
+static void ospf_network_run (struct ospf *, struct prefix *, struct ospf_area *);
+static void ospf_finish_final (struct ospf *);
 
 #define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1
-
+
 void
 ospf_router_id_update (struct ospf *ospf)
 {
@@ -172,7 +173,10 @@
   new->new_external_route = route_table_init ();
   new->old_external_route = route_table_init ();
   new->external_lsas = route_table_init ();
-
+  
+  new->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED;
+  new->stub_router_shutdown_time = OSPF_STUB_ROUTER_SHUTDOWN_DEFAULT;
+  
   /* Distribute parameter init. */
   for (i = 0; i <= ZEBRA_ROUTE_MAX; i++)
     {
@@ -263,10 +267,117 @@
 
   return ospf;
 }
+
+/* Handle the second half of graceful shutdown. This is called either
+ * from the graceful-shutdown timer thread, or directly through
+ * ospf_graceful_shutdown_check.
+ *
+ * Function is to cleanup G-R state, if required then call ospf_finish_final
+ * to complete shutdown of this ospf instance. Possibly exit if the
+ * whole process is being shutdown and this was the last OSPF instance.
+ */
+static void
+ospf_graceful_shutdown_finish (struct ospf *ospf)
+{
+  ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED;  
+  OSPF_TIMER_OFF (ospf->t_graceful_shutdown);
+  
+  ospf_finish_final (ospf);
+  
+  /* *ospf is now invalid */
+  
+  /* ospfd being shut-down? If so, was this the last ospf instance? */
+  if (CHECK_FLAG (om->options, OSPF_MASTER_SHUTDOWN)
+      && (listcount (om->ospf) == 0))
+    exit (0);
+
+  return;
+}
+
+/* Timer thread for G-R */
+static int
+ospf_graceful_shutdown_timer (struct thread *t)
+{
+  struct ospf *ospf = THREAD_ARG(t);
+  
+  ospf_graceful_shutdown_finish (ospf);
+  
+  return 0;
+}
+
+/* Check whether graceful-shutdown must be scheduled, otherwise call
+ * down directly into second-half of instance shutdown.
+ */
+static void
+ospf_graceful_shutdown_check (struct ospf *ospf)
+{
+  unsigned long timeout;
+  struct listnode *ln;
+  struct ospf_area *area;
+  
+  /* graceful shutdown already running? */
+  if (ospf->t_graceful_shutdown)
+    return;
+  
+  /* Should we try push out max-metric LSAs? */
+  if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+    {
+      for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area))
+        {
+          SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED);
+          
+          if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+              ospf_router_lsa_timer_add (area);
+        }
+      timeout = ospf->stub_router_shutdown_time;
+    }
+  else
+    /* No timer needed */
+    return ospf_graceful_shutdown_finish (ospf);
+  
+  OSPF_TIMER_ON (ospf->t_graceful_shutdown, ospf_graceful_shutdown_timer,
+                 timeout);
+  return;
+}
+
+/* Shut down the entire process */
+void
+ospf_terminate (void)
+{
+  struct ospf *ospf;
+  struct listnode *node, *nnode;
+  
+  /* shutdown already in progress */
+  if (CHECK_FLAG (om->options, OSPF_MASTER_SHUTDOWN))
+    return;
+  
+  SET_FLAG (om->options, OSPF_MASTER_SHUTDOWN);
+
+  for (ALL_LIST_ELEMENTS (om->ospf, node, nnode, ospf))
+    ospf_finish (ospf);
+
+  /* Deliberately go back up, hopefully to thread scheduler, as
+   * One or more ospf_finish()'s may have deferred shutdown to a timer
+   * thread
+   */
+}
 
 void
 ospf_finish (struct ospf *ospf)
 {
+  /* let graceful shutdown decide */
+  return ospf_graceful_shutdown_check (ospf);
+      
+  /* if ospf_graceful_shutdown returns, then ospf_finish_final is
+   * deferred to expiry of G-S timer thread. Return back up, hopefully
+   * to thread scheduler.
+   */
+}
+
+/* Final cleanup of ospf instance */
+static void
+ospf_finish_final (struct ospf *ospf)
+{
   struct route_node *rn;
   struct ospf_nbr_nbma *nbr_nbma;
   struct ospf_lsa *lsa;
@@ -279,8 +390,11 @@
 #ifdef HAVE_OPAQUE_LSA
   ospf_opaque_type11_lsa_term (ospf);
 #endif /* HAVE_OPAQUE_LSA */
-
-  /* Unredister redistribution */
+  
+  /* be nice if this worked, but it doesn't */
+  /*ospf_flush_self_originated_lsas_now (ospf);*/
+  
+  /* Unregister redistribution */
   for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
     ospf_redistribute_unset (ospf, i);
 
@@ -347,10 +461,12 @@
   OSPF_TIMER_OFF (ospf->t_maxage);
   OSPF_TIMER_OFF (ospf->t_maxage_walker);
   OSPF_TIMER_OFF (ospf->t_abr_task);
+  OSPF_TIMER_OFF (ospf->t_asbr_check);
   OSPF_TIMER_OFF (ospf->t_distribute_update);
   OSPF_TIMER_OFF (ospf->t_lsa_refresher);
   OSPF_TIMER_OFF (ospf->t_read);
   OSPF_TIMER_OFF (ospf->t_write);
+  OSPF_TIMER_OFF (ospf->t_opaque_lsa_self);
 
   close (ospf->fd);
   stream_free(ospf->ibuf);
@@ -435,7 +551,7 @@
   new->external_routing = OSPF_AREA_DEFAULT;
   new->default_cost = 1;
   new->auth_type = OSPF_AUTH_NULL;
-
+  
   /* New LSDB init. */
   new->lsdb = ospf_lsdb_new ();
 
@@ -496,7 +612,11 @@
 
   /* Cancel timer. */
   OSPF_TIMER_OFF (area->t_router_lsa_self);
-
+  OSPF_TIMER_OFF (area->t_stub_router);
+#ifdef HAVE_OPAQUE_LSA
+  OSPF_TIMER_OFF (area->t_opaque_lsa_self);
+#endif /* HAVE_OPAQUE_LSA */
+  
   if (OSPF_IS_AREA_BACKBONE (area))
     area->ospf->backbone = NULL;