ospf6d: improve ordered shutdown

Improve the _disable/_enable infrastructure so it gets into
a more usable shape and make 'no router ospf6' actually work.

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index b09d961..9a4e30e 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -193,18 +193,21 @@
 void
 ospf6_area_delete (struct ospf6_area *oa)
 {
-  struct listnode *n, *nnode;
+  struct listnode *n;
   struct ospf6_interface *oi;
 
   ospf6_route_table_delete (oa->range_table);
   ospf6_route_table_delete (oa->summary_prefix);
   ospf6_route_table_delete (oa->summary_router);
 
-  /* ospf6 interface list */
-  for (ALL_LIST_ELEMENTS (oa->if_list, n, nnode, oi))
-    {
-      ospf6_interface_delete (oi);
-    }
+  /* The ospf6_interface structs store configuration
+   * information which should not be lost/reset when
+   * deleting an area.
+   * So just detach the interface from the area and
+   * keep it around. */
+  for (ALL_LIST_ELEMENTS_RO (oa->if_list, n, oi))
+    oi->area = NULL;
+
   list_delete (oa->if_list);
 
   ospf6_lsdb_delete (oa->lsdb);
@@ -257,6 +260,7 @@
 
   for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
     ospf6_interface_enable (oi);
+  ospf6_abr_enable_area (oa);
 }
 
 void
@@ -269,6 +273,19 @@
 
   for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
     ospf6_interface_disable (oi);
+
+  ospf6_abr_disable_area (oa);
+  ospf6_lsdb_remove_all (oa->lsdb);
+  ospf6_lsdb_remove_all (oa->lsdb_self);
+
+  ospf6_spf_table_finish(oa->spf_table);
+  ospf6_route_remove_all(oa->route_table);
+
+  THREAD_OFF (oa->thread_spf_calculation);
+  THREAD_OFF (oa->thread_route_calculation);
+
+  THREAD_OFF (oa->thread_router_lsa);
+  THREAD_OFF (oa->thread_intra_prefix_lsa);
 }
 
 
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 60df6e6..3605e3f 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -409,6 +409,8 @@
       ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex,
                                       &route->prefix);
     }
+
+  ospf6_asbr_routemap_unset (type);
 }
 
 void
@@ -636,7 +638,6 @@
     return CMD_WARNING;
 
   ospf6_asbr_redistribute_unset (type);
-  ospf6_asbr_routemap_unset (type);
   ospf6_asbr_redistribute_set (type);
   return CMD_SUCCESS;
 }
@@ -677,7 +678,6 @@
     return CMD_WARNING;
 
   ospf6_asbr_redistribute_unset (type);
-  ospf6_asbr_routemap_unset (type);
 
   return CMD_SUCCESS;
 }
@@ -1313,6 +1313,20 @@
 }
 
 void
+ospf6_asbr_redistribute_reset (void)
+{
+  int type;
+
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    {
+      if (type == ZEBRA_ROUTE_OSPF6)
+        continue;
+      if (ospf6_zebra_is_redistribute (type))
+        ospf6_asbr_redistribute_unset(type);
+    }
+}
+
+void
 ospf6_asbr_terminate (void)
 {
   route_map_finish ();
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
index 72e4914..73770cc 100644
--- a/ospf6d/ospf6_asbr.h
+++ b/ospf6d/ospf6_asbr.h
@@ -89,6 +89,7 @@
 extern int ospf6_redistribute_config_write (struct vty *vty);
 
 extern void ospf6_asbr_init (void);
+extern void ospf6_asbr_redistribute_reset (void);
 extern void ospf6_asbr_terminate (void);
 
 extern int config_write_ospf6_debug_asbr (struct vty *vty);
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 86c0bf6..d9d2d03 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -219,31 +219,28 @@
 ospf6_interface_enable (struct ospf6_interface *oi)
 {
   UNSET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE);
-
-  oi->thread_send_hello =
-    thread_add_event (master, ospf6_hello_send, oi, 0);
+  ospf6_interface_state_update (oi->interface);
 }
 
 void
 ospf6_interface_disable (struct ospf6_interface *oi)
 {
-  struct listnode *node, *nnode;
-  struct ospf6_neighbor *on;
-
   SET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE);
 
-  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
-      ospf6_neighbor_delete (on);
-
-  list_delete_all_node (oi->neighbor_list);
+  thread_execute (master, interface_down, oi, 0);
 
   ospf6_lsdb_remove_all (oi->lsdb);
+  ospf6_lsdb_remove_all (oi->lsdb_self);
   ospf6_lsdb_remove_all (oi->lsupdate_list);
   ospf6_lsdb_remove_all (oi->lsack_list);
 
   THREAD_OFF (oi->thread_send_hello);
   THREAD_OFF (oi->thread_send_lsupdate);
   THREAD_OFF (oi->thread_send_lsack);
+
+  THREAD_OFF (oi->thread_network_lsa);
+  THREAD_OFF (oi->thread_link_lsa);
+  THREAD_OFF (oi->thread_intra_prefix_lsa);
 }
 
 static struct in6_addr *
@@ -327,6 +324,8 @@
     return;
   if (oi->area == NULL)
     return;
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE))
+    return;
 
   if (if_is_operative (ifp))
     thread_add_event (master, interface_up, oi, 0);
@@ -355,6 +354,9 @@
   if (oi->area == NULL)
     return;
 
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE))
+    return;
+
   /* update "route to advertise" interface route table */
   ospf6_route_remove_all (oi->route_connected);
 
diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h
index a25efa1..e909da2 100644
--- a/ospf6d/ospf6_intra.h
+++ b/ospf6d/ospf6_intra.h
@@ -156,32 +156,37 @@
 
 #define OSPF6_ROUTER_LSA_SCHEDULE(oa) \
   do { \
-    if (! (oa)->thread_router_lsa) \
+    if (! (oa)->thread_router_lsa \
+        && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \
       (oa)->thread_router_lsa = \
         thread_add_event (master, ospf6_router_lsa_originate, oa, 0); \
   } while (0)
 #define OSPF6_NETWORK_LSA_SCHEDULE(oi) \
   do { \
-    if (! (oi)->thread_network_lsa) \
+    if (! (oi)->thread_network_lsa \
+        && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
       (oi)->thread_network_lsa = \
         thread_add_event (master, ospf6_network_lsa_originate, oi, 0); \
   } while (0)
 #define OSPF6_LINK_LSA_SCHEDULE(oi) \
   do { \
-    if (! (oi)->thread_link_lsa) \
+    if (! (oi)->thread_link_lsa \
+        && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
       (oi)->thread_link_lsa = \
         thread_add_event (master, ospf6_link_lsa_originate, oi, 0); \
   } while (0)
 #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa) \
   do { \
-    if (! (oa)->thread_intra_prefix_lsa) \
+    if (! (oa)->thread_intra_prefix_lsa \
+        && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \
       (oa)->thread_intra_prefix_lsa = \
         thread_add_event (master, ospf6_intra_prefix_lsa_originate_stub, \
                           oa, 0); \
   } while (0)
 #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi) \
   do { \
-    if (! (oi)->thread_intra_prefix_lsa) \
+    if (! (oi)->thread_intra_prefix_lsa \
+        && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
       (oi)->thread_intra_prefix_lsa = \
         thread_add_event (master, ospf6_intra_prefix_lsa_originate_transit, \
                           oi, 0); \
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
index 17d7654..e991971 100644
--- a/ospf6d/ospf6_main.c
+++ b/ospf6d/ospf6_main.c
@@ -41,6 +41,8 @@
 #include "ospf6_message.h"
 #include "ospf6_asbr.h"
 #include "ospf6_lsa.h"
+#include "ospf6_interface.h"
+#include "ospf6_zebra.h"
 
 /* Default configuration file name for ospf6d. */
 #define OSPF6_DEFAULT_CONFIG       "ospf6d.conf"
@@ -134,12 +136,16 @@
 static void __attribute__ ((noreturn))
 ospf6_exit (int status)
 {
-  extern struct ospf6 *ospf6;
-  extern struct zclient *zclient;
+  struct listnode *node;
+  struct interface *ifp;
 
   if (ospf6)
     ospf6_delete (ospf6);
 
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+    if (ifp->info != NULL)
+      ospf6_interface_delete(ifp->info);
+
   ospf6_message_terminate ();
   ospf6_asbr_terminate ();
   ospf6_lsa_terminate ();
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index caebf5d..5fb5a21 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -1543,7 +1543,7 @@
     }
 
   oi = ospf6_interface_lookup_by_ifindex (ifindex);
-  if (oi == NULL || oi->area == NULL)
+  if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE))
     {
       zlog_debug ("Message received on disabled interface");
       return 0;
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index f83e6ab..7c0922a 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -161,6 +161,8 @@
 
   for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa))
     ospf6_area_delete (oa);
+
+
   list_delete (o->area_list);
 
   ospf6_lsdb_delete (o->lsdb);
@@ -202,9 +204,16 @@
       for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa))
         ospf6_area_disable (oa);
 
+      /* XXX: This also changes persistent settings */
+      ospf6_asbr_redistribute_reset();
+
       ospf6_lsdb_remove_all (o->lsdb);
       ospf6_route_remove_all (o->route_table);
       ospf6_route_remove_all (o->brouter_table);
+
+      THREAD_OFF(o->maxage_remover);
+      THREAD_OFF(o->t_spf_calc);
+      THREAD_OFF(o->t_ase_calc);
     }
 }
 
@@ -282,8 +291,6 @@
 {
   if (ospf6 == NULL)
     ospf6 = ospf6_create ();
-  if (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED))
-    ospf6_enable (ospf6);
 
   /* set current ospf point. */
   vty->node = OSPF6_NODE;
@@ -299,10 +306,13 @@
        NO_STR
        OSPF6_ROUTER_STR)
 {
-  if (ospf6 == NULL || CHECK_FLAG (ospf6->flag, OSPF6_DISABLED))
-    vty_out (vty, "OSPFv3 is not running%s", VNL);
+  if (ospf6 == NULL)
+    vty_out (vty, "OSPFv3 is not configured%s", VNL);
   else
-    ospf6_disable (ospf6);
+    {
+      ospf6_delete (ospf6);
+      ospf6 = NULL;
+    }
 
   /* return to config node . */
   vty->node = CONFIG_NODE;
@@ -435,8 +445,12 @@
 
   SET_FLAG (oa->flag, OSPF6_AREA_ENABLE);
 
+  /* ospf6 process is currently disabled, not much more to do */
+  if (CHECK_FLAG (o->flag, OSPF6_DISABLED))
+    return CMD_SUCCESS;
+
   /* start up */
-  thread_add_event (master, interface_up, oi, 0);
+  ospf6_interface_enable (oi);
 
   /* If the router is ABR, originate summary routes */
   if (ospf6_is_router_abr (o))
@@ -861,8 +875,6 @@
   /* OSPFv6 configuration. */
   if (ospf6 == NULL)
     return CMD_SUCCESS;
-  if (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED))
-    return CMD_SUCCESS;
 
   inet_ntop (AF_INET, &ospf6->router_id_static, router_id, sizeof (router_id));
   vty_out (vty, "router ospf6%s", VNL);
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index f09e9d2..50ecc17 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -117,7 +117,9 @@
 		ifp->name, ifp->ifindex, ifp->mtu6);
 
 #if 0
-  /* Why is this commented out? */
+  /* XXX: ospf6_interface_if_del is not the right way to handle this,
+   * because among other thinkable issues, it will also clear all
+   * settings as they are contained in the struct ospf6_interface. */
   ospf6_interface_if_del (ifp);
 #endif /*0*/