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

	* (general) SPF memory management cleanup and fix for rare
	  double-free bug.
	* ospf_spf.h: (struct vertex_parent) New struct to hold parent
	  specific data, eg the backlink and the parent vertex pointer,
	  and point to the appropriate general struct vertex_nexthop.
	  (struct vertex_nexthop) remove parent vertex pointer, so
	  this struct can be shared across vertices.
	  (struct vertex) rename list child to list children. Remove
	  list of nexthops, replace with list of vertex_parents.
	* ospf_spf.c: (update_stat) trivial, remove cast from void *.
	  (vertex_nexthop_new) remove init of parent - field is gone
          from struct vertex_nexthop.
          (ospf_canonical_nexthops_free) Remove the canonical
          vertex_nexthop memory objects. These are the vertex_nexthops
          attached to the first level of router vertices from the root.
          (vertex_parent_new) new function, create a vertex_parent.
          (vertex_parent_free) ditto, but free it.
          (ospf_vertex_new) Update to match changes to struct vertex.
          (ospf_vertex_free) Recursively free a struct vertex and its
          children. The parent list is used as a reference count.
          vertex_nexthops must be free seperately, if required.
          (ospf_vertex_dump) update to match struct vertex changes.
          Print out backlink of parents too.
          (ospf_vertex_add_parent) ditto.
          (ospf_lsa_has_link) update comment.
          (ospf_nexthop_add_unique) removed, not needed anymore.
          (ospf_nexthop_merge) ditto.
          (ospf_spf_consider_nexthop) renamed to ospf_spf_add_parent.
          Simplified to just create vertex_parent and add it.
          (ospf_spf_flush_parents) new function, flush out the parent
	  list.
	  (ospf_nexthop_calculation) Take the relevant route_lsa_link
	  as an argument, which simplifies things and removes the need
	  for the hack in ospf_nexthop_add_unique - ospf_spf_next
	  already knew exactly which link the cost calculated was for.
	  Update to match struct vertex changes too.
	  (ospf_spf_next) Don't create a vertex for W unnecessarily, if
          it's there's a vertex already created for W, use it, and
          hence there's no need to free it either.
          Update some manipulation/comparisons of distance to match.
          Flush the parent list if a lower cost path is found.
          (ospf_spf_route_free) unused, removed.
          (ospf_spf_dump) match the struct vertex changes, and dump the
          ifname if possible.
          (ospf_spf_calculate) At end of SPF, free the canonical nexthops
          and call ospf_vertex_free on the root vertex to free the
	  entire tree.
	* ospf_interface.c: (ospf_vl_set_params) match struct vertex
          changes.
        * ospf_route.c: (ospf_intra_route_add) ditto
          (ospf_route_copy_nexthops_from_vertex) ditto
	* memtypes.c: (memory_list_ospf) Add MTYPE_OSPF_VERTEX_PARENT.
diff --git a/ospfd/ChangeLog b/ospfd/ChangeLog
index c56f01b..299de23 100644
--- a/ospfd/ChangeLog
+++ b/ospfd/ChangeLog
@@ -1,3 +1,57 @@
+2005-10-18 Paul Jakma <paul.jakma@sun.com>
+
+	* (general) SPF memory management cleanup and fix for rare
+	  double-free bug.
+	* ospf_spf.h: (struct vertex_parent) New struct to hold parent
+	  specific data, eg the backlink and the parent vertex pointer,
+	  and point to the appropriate general struct vertex_nexthop.
+	  (struct vertex_nexthop) remove parent vertex pointer, so
+	  this struct can be shared across vertices.
+	  (struct vertex) rename list child to list children. Remove
+	  list of nexthops, replace with list of vertex_parents.
+	* ospf_spf.c: (update_stat) trivial, remove cast from void *.
+	  (vertex_nexthop_new) remove init of parent - field is gone
+          from struct vertex_nexthop.
+          (ospf_canonical_nexthops_free) Remove the canonical
+          vertex_nexthop memory objects. These are the vertex_nexthops
+          attached to the first level of router vertices from the root.
+          (vertex_parent_new) new function, create a vertex_parent.
+          (vertex_parent_free) ditto, but free it.
+          (ospf_vertex_new) Update to match changes to struct vertex.
+          (ospf_vertex_free) Recursively free a struct vertex and its
+          children. The parent list is used as a reference count.
+          vertex_nexthops must be free seperately, if required.
+          (ospf_vertex_dump) update to match struct vertex changes.
+          Print out backlink of parents too.
+          (ospf_vertex_add_parent) ditto.
+          (ospf_lsa_has_link) update comment.
+          (ospf_nexthop_add_unique) removed, not needed anymore.
+          (ospf_nexthop_merge) ditto.
+          (ospf_spf_consider_nexthop) renamed to ospf_spf_add_parent.
+          Simplified to just create vertex_parent and add it.
+          (ospf_spf_flush_parents) new function, flush out the parent
+	  list.
+	  (ospf_nexthop_calculation) Take the relevant route_lsa_link
+	  as an argument, which simplifies things and removes the need
+	  for the hack in ospf_nexthop_add_unique - ospf_spf_next
+	  already knew exactly which link the cost calculated was for.
+	  Update to match struct vertex changes too.
+	  (ospf_spf_next) Don't create a vertex for W unnecessarily, if
+          it's there's a vertex already created for W, use it, and
+          hence there's no need to free it either.
+          Update some manipulation/comparisons of distance to match.
+          Flush the parent list if a lower cost path is found.
+          (ospf_spf_route_free) unused, removed.
+          (ospf_spf_dump) match the struct vertex changes, and dump the
+          ifname if possible.
+          (ospf_spf_calculate) At end of SPF, free the canonical nexthops
+          and call ospf_vertex_free on the root vertex to free the
+	  entire tree.
+	* ospf_interface.c: (ospf_vl_set_params) match struct vertex
+          changes.
+        * ospf_route.c: (ospf_intra_route_add) ditto
+          (ospf_route_copy_nexthops_from_vertex) ditto
+          
 2005-10-11 Paul Jakma <paul.jakma@sun.com>
 
 	* ospf_api.c: sign warnings.
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 9d31b7a..fe32268 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -999,7 +999,7 @@
   int changed = 0;
   struct ospf_interface *voi;
   struct listnode *node;
-  struct vertex_nexthop *nh;
+  struct vertex_parent *vp = NULL;
   int i;
   struct router_lsa *rl;
 
@@ -1012,9 +1012,9 @@
       changed = 1;
     }
 
-  for (ALL_LIST_ELEMENTS_RO (v->nexthop, node, nh))
+  for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
     {
-      vl_data->out_oi = (struct ospf_interface *) nh->oi;
+      vl_data->out_oi = vp->nexthop->oi;
       
       if (!IPV4_ADDR_SAME(&voi->address->u.prefix4,
                           &vl_data->out_oi->address->u.prefix4))
@@ -1031,12 +1031,12 @@
   /* use SPF determined backlink index in struct vertex
    * for virtual link destination address
    */
-  if (v->backlink >= 0)
+  if (vp && vp->backlink >= 0)
     {
       if (!IPV4_ADDR_SAME (&vl_data->peer_addr,
-                           &rl->link[v->backlink].link_data))
+                           &rl->link[vp->backlink].link_data))
         changed = 1;
-      vl_data->peer_addr = rl->link[v->backlink].link_data;
+      vl_data->peer_addr = rl->link[vp->backlink].link_data;
     }
   else
     {
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
index 00733d8..bdbdd03 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -278,7 +278,7 @@
   struct ospf_route *or;
   struct prefix_ipv4 p;
   struct ospf_path *path;
-  struct vertex_nexthop *nexthop;
+  struct vertex_parent *parent;
   struct listnode *node, *nnode;
 
   p.family = AF_INET;
@@ -306,10 +306,10 @@
     {
       or->type = OSPF_DESTINATION_NETWORK;
 
-      for (ALL_LIST_ELEMENTS (v->nexthop, node, nnode, nexthop))
+      for (ALL_LIST_ELEMENTS (v->parents, node, nnode, parent))
         {
           path = ospf_path_new ();
-          path->nexthop = nexthop->router;
+          path->nexthop = parent->nexthop->router;
           listnode_add (or->paths, path);
         }
     }
@@ -799,11 +799,14 @@
   struct listnode *node;
   struct ospf_path *path;
   struct vertex_nexthop *nexthop;
+  struct vertex_parent *vp;
 
   assert (to->paths);
 
-  for (ALL_LIST_ELEMENTS_RO (v->nexthop, node, nexthop))
+  for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
     {
+      nexthop = vp->nexthop;
+      
       if (nexthop->oi != NULL) 
 	{
 	  if (! ospf_path_exist (to->paths, nexthop->router, nexthop->oi))
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index 1aba5d9..b05117d 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -60,23 +60,18 @@
 }
 
 static void
-update_stat (void * node , int position)
+update_stat (void *node , int position)
 {
-  struct vertex * v = (struct vertex *) node;
+  struct vertex *v = node;
+
   /* Set the status of the vertex, when its position changes. */
   *(v->stat) = position;
 }
-/* End of the heap related functions. */
-
+
 static struct vertex_nexthop *
-vertex_nexthop_new (struct vertex *parent)
+vertex_nexthop_new (void)
 {
-  struct vertex_nexthop *new;
-
-  new = XCALLOC (MTYPE_OSPF_NEXTHOP, sizeof (struct vertex_nexthop));
-  new->parent = parent;
-
-  return new;
+  return XCALLOC (MTYPE_OSPF_NEXTHOP, sizeof (struct vertex_nexthop));
 }
 
 static void
@@ -85,27 +80,56 @@
   XFREE (MTYPE_OSPF_NEXTHOP, nh);
 }
 
-static struct vertex_nexthop *
-vertex_nexthop_dup (struct vertex_nexthop *nh)
+/* Free the canonical nexthop objects for an area, ie the nexthop objects
+ * attached to the first-hop router vertices.
+ */
+static void
+ospf_canonical_nexthops_free (struct vertex *root)
 {
-  struct vertex_nexthop *new;
-
-  new = vertex_nexthop_new (nh->parent);
-
-  new->oi = nh->oi;
-  new->router = nh->router;
-
+  struct listnode *node, *nnode;
+  struct vertex *child;
+  
+  for (ALL_LIST_ELEMENTS (root->children, node, nnode, child))
+    {
+      struct listnode *n2, *nn2;
+      struct vertex_parent *vp;
+      
+      if (child->type == OSPF_VERTEX_NETWORK)
+        ospf_canonical_nexthops_free (child);
+      
+      for (ALL_LIST_ELEMENTS (child->parents, n2, nn2, vp))
+        vertex_nexthop_free (vp->nexthop);
+    }
+}      
+
+static struct vertex_parent *
+vertex_parent_new (struct vertex *v, int backlink, struct vertex_nexthop *hop)
+{
+  struct vertex_parent *new;
+  
+  new = XMALLOC (MTYPE_OSPF_VERTEX_PARENT, sizeof (struct vertex_parent));
+  
+  if (new == NULL)
+    return NULL;
+  
+  new->parent = v;
+  new->backlink = backlink;
+  new->nexthop = hop;
   return new;
 }
-
 
+static void
+vertex_parent_free (struct vertex_parent *p)
+{
+  XFREE (MTYPE_OSPF_VERTEX_PARENT, p);
+}
+
 static struct vertex *
 ospf_vertex_new (struct ospf_lsa *lsa)
 {
   struct vertex *new;
 
-  new = XMALLOC (MTYPE_OSPF_VERTEX, sizeof (struct vertex));
-  memset (new, 0, sizeof (struct vertex));
+  new = XCALLOC (MTYPE_OSPF_VERTEX, sizeof (struct vertex));
 
   new->flags = 0;
   new->stat = &(lsa->stat);
@@ -113,66 +137,86 @@
   new->id = lsa->data->id;
   new->lsa = lsa->data;
   new->distance = 0;
-  new->child = list_new ();
-  new->nexthop = list_new ();
-  new->backlink = -1;
-
+  new->children = list_new ();
+  new->parents = list_new ();
+  
   return new;
 }
 
+/* Recursively free the given vertex and all its children 
+ * and associated parent and nexthop information
+ * Parent should indicate which parent is trying to free this child,
+ * NULL parent is only valid for the root vertex.
+ */
 static void
-ospf_vertex_free (struct vertex *v)
+ospf_vertex_free (struct vertex *v, const struct ospf_area *area)
 {
   struct listnode *node, *nnode;
-  struct vertex_nexthop *nh;
-
-  list_delete (v->child);
-  v->child = NULL;
+  struct vertex *vc;
   
-  if (listcount (v->nexthop) > 0)
-    for (ALL_LIST_ELEMENTS (v->nexthop, node, nnode, nh))
-      vertex_nexthop_free (nh);
+  assert (*(v->stat) == LSA_SPF_IN_SPFTREE);
+  
+  /* recurse down the tree, freeing furthest children first */
+  if (v->children)
+    {
+      for (ALL_LIST_ELEMENTS (v->children, node, nnode, vc))
+        ospf_vertex_free (vc, area);
+      
+      list_delete (v->children);
+      v->children = NULL;
+    }
+  
+  /* The vertex itself can only be freed when the last remaining parent
+   * with a reference to this vertex frees this vertex. Until then, we
+   * just cleanup /one/ parent association (doesnt matter which) -
+   * essentially using the parents list as a refcount.
+   */
+  if (listcount (v->parents) > 0)
+    {
+      vertex_parent_free (listgetdata (listhead (v->parents)));
+      list_delete_node (v->parents, listhead (v->parents));
 
-  list_delete (v->nexthop);
-  v->nexthop = NULL;
+      if (listcount (v->parents) > 0)
+        return; /* there are still parent references left */
+    }
+  
+  list_delete (v->parents);
+  v->parents = NULL;
+  
+  v->lsa = NULL;
   
   XFREE (MTYPE_OSPF_VERTEX, v);
 }
 
 static void
 ospf_vertex_dump(const char *msg, struct vertex *v,
-		 int print_nexthops, int print_children)
+		 int print_parents, int print_children)
 {
   if ( ! IS_DEBUG_OSPF_EVENT)
     return;
 
-  zlog_debug("%s %s vertex %s  distance %u backlink %d flags %u",
+  zlog_debug("%s %s vertex %s  distance %u flags %u",
             msg,
 	    v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network",
 	    inet_ntoa(v->lsa->id),
 	    v->distance,
-	    v->backlink,
 	    (unsigned int)v->flags);
 
-  if (print_nexthops)
+  if (print_parents)
     {
       struct listnode *node;
-      struct vertex_nexthop *nexthop;
+      struct vertex_parent *vp;
       
-      for (ALL_LIST_ELEMENTS_RO (v->nexthop, node, nexthop))
+      for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
         {
 	  char buf1[BUFSIZ];
-	  char buf2[BUFSIZ];
-
-	  if (nexthop)
+	  
+	  if (vp)
 	    {
-	      zlog_debug (" nexthop %s  interface %s  parent %s",
-			 inet_ntop(AF_INET, &nexthop->router, buf1, BUFSIZ),
-			 nexthop->oi ? IF_NAME(nexthop->oi) : "NULL",
-			 nexthop->parent ? inet_ntop(AF_INET, 
-						     &nexthop->parent->id,
-						     buf2, BUFSIZ)
-			                 : "NULL");
+	      zlog_debug ("parent %s backlink %d nexthop %s  interface %s",
+	                 inet_ntoa(vp->parent->lsa->id), vp->backlink,
+			 inet_ntop(AF_INET, &vp->nexthop->router, buf1, BUFSIZ),
+			 vp->nexthop->oi ? IF_NAME(vp->nexthop->oi) : "NULL");
 	    }
 	}
     }
@@ -182,7 +226,7 @@
       struct listnode *cnode;
       struct vertex *cv;
       
-      for (ALL_LIST_ELEMENTS_RO (v->child, cnode, cv))
+      for (ALL_LIST_ELEMENTS_RO (v->children, cnode, cv))
         ospf_vertex_dump(" child:", cv, 0, 0);
     }
 }
@@ -192,18 +236,18 @@
 static void
 ospf_vertex_add_parent (struct vertex *v)
 {
-  struct vertex_nexthop *nh;
+  struct vertex_parent *vp;
   struct listnode *node;
   
-  assert (v->nexthop && v->child);
+  assert (v && v->children);
   
-  for (ALL_LIST_ELEMENTS_RO (v->nexthop, node, nh))
+  for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
     {
-      assert (nh->parent && nh->parent->child);
+      assert (vp->parent && vp->parent->children);
       
       /* No need to add two links from the same parent. */
-      if (listnode_lookup (nh->parent->child, v) == NULL)
-        listnode_add (nh->parent->child, v);
+      if (listnode_lookup (vp->parent->children, v) == NULL)
+        listnode_add (vp->parent->children, v);
     }
 }
 
@@ -276,7 +320,7 @@
                 }
               break;
             case LSA_LINK_TYPE_STUB:
-              /* Not take into count? */
+              /* Stub can't lead anywhere, carry on */
               continue;
             default:
               break;
@@ -286,51 +330,6 @@
   return -1;
 }
 
-/* Add the nexthop to the list, only if it is unique.
- * If it's not unique, free the nexthop entry.
- */
-static void
-ospf_nexthop_add_unique (struct vertex_nexthop *new, struct list *nexthop)
-{
-  struct vertex_nexthop *nh;
-  struct listnode *node;
-  int match;
-
-  match = 0;
-
-  for (ALL_LIST_ELEMENTS_RO (nexthop, node, nh))
-    {
-      /* Compare the two entries. */
-      /* XXX
-       * Comparing the parent preserves the shortest path tree
-       * structure even when the nexthops are identical.
-       */
-      if (nh->oi == new->oi &&
-          IPV4_ADDR_SAME (&nh->router, &new->router) &&
-          nh->parent == new->parent)
-        {
-          match = 1;
-          break;
-        }
-    }
-
-  if (!match)
-    listnode_add (nexthop, new);
-  else
-    vertex_nexthop_free (new);
-}
-
-/* Merge entries in list b into list a. */
-static void
-ospf_nexthop_merge (struct list *a, struct list *b)
-{
-  struct listnode *node, *nnode;
-  struct vertex_nexthop *nh;
-
-  for (ALL_LIST_ELEMENTS (b, node, nnode, nh))
-    ospf_nexthop_add_unique (nh, a);
-}
-
 #define ROUTER_LSA_MIN_SIZE 12
 #define ROUTER_LSA_TOS_SIZE 4
 
@@ -395,51 +394,49 @@
  * cost path to a candidate vertex in SPF, maybe.
  */
 static void
-ospf_spf_consider_nexthop (struct list *nexthops,
-                           struct vertex_nexthop *newhop)
+ospf_spf_add_parent (struct vertex *v, struct vertex *w,
+                     struct vertex_nexthop *newhop)
 {
-  struct vertex_nexthop *hop;
-  struct listnode *ln, *nn;
-
-  /* nexthop list should contain only the set of nexthops that have the lowest
-   * equal cost
-   */
-  if (nexthops->head != NULL)
-    {
-      hop = listgetdata (nexthops->head);
-      
-      /* weed out hops with higher cost than the newhop */
-      if (hop->oi->output_cost > newhop->oi->output_cost)
-        {
-          /* delete the existing nexthops */
-          for (ALL_LIST_ELEMENTS (nexthops, ln, nn, hop))
-            {
-              listnode_delete (nexthops, hop);
-              vertex_nexthop_free (hop);
-            }
-        }
-      else if (hop->oi->output_cost < newhop->oi->output_cost)
-        return;
-    }
-
-  /* new hop is <= existing hops, add it */
-  listnode_add (nexthops, newhop);
+  struct vertex_parent *vp;
+    
+  /* we must have a newhop.. */
+  assert (v && w && newhop);
+  
+  /* new parent is <= existing parents, add it */
+  vp = vertex_parent_new (v, ospf_lsa_has_link (w->lsa, v->lsa), newhop);
+  listnode_add (w->parents, vp);
 
   return;
 }
 
+static void
+ospf_spf_flush_parents (struct vertex *w)
+{
+  struct vertex_parent *vp;
+  struct listnode *ln, *nn;
+  
+  /* delete the existing nexthops */
+  for (ALL_LIST_ELEMENTS (w->parents, ln, nn, vp))
+    {
+      list_delete_node (w->parents, ln);
+      vertex_parent_free (vp);
+    }
+}
+
 /* 16.1.1.  Calculate nexthop from root through V (parent) to
  * vertex W (destination).
+ *
+ * The link must be supplied if V is the root vertex. In all other cases
+ * it may be NULL.
  */
 static void
-ospf_nexthop_calculation (struct ospf_area *area,
-                          struct vertex *v, struct vertex *w)
+ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v,
+                          struct vertex *w, struct router_lsa_link *l)
 {
   struct listnode *node, *nnode;
-  struct vertex_nexthop *nh, *x;
+  struct vertex_nexthop *nh;
+  struct vertex_parent *vp;
   struct ospf_interface *oi = NULL;
-  struct router_lsa_link *l = NULL;
-
 
   if (IS_DEBUG_OSPF_EVENT)
     {
@@ -459,128 +456,124 @@
 
       if (w->type == OSPF_VERTEX_ROUTER)
         {
-          while ((l = ospf_get_next_link (v, w, l)))
+          /* l  is a link from v to w
+           * l2 will be link from w to v
+           */
+          struct router_lsa_link *l2 = NULL;
+          
+          /* we *must* be supplied with the link data */
+          assert (l != NULL);
+          
+          if (IS_DEBUG_OSPF_EVENT)
             {
-	      /* l  is a link from v to w
-	       * l2 will be link from w to v
-	       */
-              struct router_lsa_link *l2 = NULL;
+              char buf1[BUFSIZ];
+              char buf2[BUFSIZ];
+              
+              zlog_debug("ospf_nexthop_calculation(): considering link "
+                        "type %d link_id %s link_data %s",
+                        l->m[0].type,
+                        inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
+                        inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
+            }
 
-	      if (IS_DEBUG_OSPF_EVENT)
-	        {
-		  char buf1[BUFSIZ];
-		  char buf2[BUFSIZ];
-		  
-		  zlog_debug("ospf_nexthop_calculation(): considering link "
-			    "type %d link_id %s link_data %s",
-			    l->m[0].type,
-			    inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
-			    inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
-		}
+          if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT)
+            {
+              /* If the destination is a router which connects to
+                 the calculating router via a Point-to-MultiPoint
+                 network, the destination's next hop IP address(es)
+                 can be determined by examining the destination's
+                 router-LSA: each link pointing back to the
+                 calculating router and having a Link Data field
+                 belonging to the Point-to-MultiPoint network
+                 provides an IP address of the next hop router.
 
-              if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT)
+                 At this point l is a link from V to W, and V is the
+                 root ("us").  Find the local interface associated 
+                 with l (its address is in l->link_data).  If it
+                 is a point-to-multipoint interface, then look through
+                 the links in the opposite direction (W to V).  If
+                 any of them have an address that lands within the
+                 subnet declared by the PtMP link, then that link
+                 is a constituent of the PtMP link, and its address is 
+                 a nexthop address for V.
+              */
+              oi = ospf_if_is_configured (area->ospf, &l->link_data);
+              if (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
                 {
-		  /* If the destination is a router which connects to
-		     the calculating router via a Point-to-MultiPoint
-		     network, the destination's next hop IP address(es)
-		     can be determined by examining the destination's
-		     router-LSA: each link pointing back to the
-		     calculating router and having a Link Data field
-		     belonging to the Point-to-MultiPoint network
-		     provides an IP address of the next hop router.
+                  struct prefix_ipv4 la;
 
-		     At this point l is a link from V to W, and V is the
-		     root ("us").  Find the local interface associated 
-		     with l (its address is in l->link_data).  If it
-		     is a point-to-multipoint interface, then look through
-		     the links in the opposite direction (W to V).  If
-		     any of them have an address that lands within the
-		     subnet declared by the PtMP link, then that link
-		     is a constituent of the PtMP link, and its address is 
-		     a nexthop address for V.
-		  */
-                  oi = ospf_if_is_configured (area->ospf, &l->link_data);
-                  if (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+                  la.family = AF_INET;
+                  la.prefixlen = oi->address->prefixlen;
+
+                  /* V links to W on PtMP interface
+                     - find the interface address on W */
+                  while ((l2 = ospf_get_next_link (w, v, l2)))
                     {
-                      struct prefix_ipv4 la;
+                      la.prefix = l2->link_data;
 
-		      la.family = AF_INET;
-                      la.prefixlen = oi->address->prefixlen;
-
-                      /* V links to W on PtMP interface
-                         - find the interface address on W */
-                      while ((l2 = ospf_get_next_link (w, v, l2)))
-                        {
-                          la.prefix = l2->link_data;
-
-                          if (prefix_cmp ((struct prefix *) &la,
-                                          oi->address) == 0)
-                            /* link_data is on our PtMP network */
-                            break;
-                        }
-                    } /* end l is on point-to-multipoint link */
-                  else
-                    {
-		      /* l is a regular point-to-point link.
-			 Look for a link from W to V.
-		       */
-                      while ((l2 = ospf_get_next_link (w, v, l2)))
-                        {
-                          oi = ospf_if_is_configured (area->ospf,
-                                                      &(l2->link_data));
-
-                          if (oi == NULL)
-                            continue;
-
-                          if (!IPV4_ADDR_SAME (&oi->address->u.prefix4,
-                                               &l->link_data))
-                            continue;
-
-                          break;
-                        }
+                      if (prefix_cmp ((struct prefix *) &la,
+                                      oi->address) == 0)
+                        /* link_data is on our PtMP network */
+                        break;
                     }
-
-                  if (oi && l2)
+                } /* end l is on point-to-multipoint link */
+              else
+                {
+                  /* l is a regular point-to-point link.
+                     Look for a link from W to V.
+                   */
+                  while ((l2 = ospf_get_next_link (w, v, l2)))
                     {
-		      /* found all necessary info to build nexthop */
-                      nh = vertex_nexthop_new (v);
-                      nh->oi = oi;
-                      nh->router = l2->link_data;
-                      ospf_spf_consider_nexthop (w->nexthop, nh);
+                      oi = ospf_if_is_configured (area->ospf,
+                                                  &(l2->link_data));
+
+                      if (oi == NULL)
+                        continue;
+
+                      if (!IPV4_ADDR_SAME (&oi->address->u.prefix4,
+                                           &l->link_data))
+                        continue;
+
+                      break;
                     }
-		  else
-		    {
-		      zlog_info("ospf_nexthop_calculation(): "
-				"could not determine nexthop for link");
-		    }
-                } /* end point-to-point link from V to W */
-            } /* end iterate over links in W */
+                }
+
+              if (oi && l2)
+                {
+                  /* found all necessary info to build nexthop */
+                  nh = vertex_nexthop_new ();
+                  nh->oi = oi;
+                  nh->router = l2->link_data;
+                  ospf_spf_add_parent (v, w, nh);
+                }
+              else
+                {
+                  zlog_info("ospf_nexthop_calculation(): "
+                            "could not determine nexthop for link");
+                }
+            } /* end point-to-point link from V to W */
         } /* end W is a Router vertex */
       else
         {
-	  assert(w->type == OSPF_VERTEX_NETWORK);
-          while ((l = ospf_get_next_link (v, w, l)))
+          assert(w->type == OSPF_VERTEX_NETWORK);
+          oi = ospf_if_is_configured (area->ospf, &(l->link_data));
+          if (oi)
             {
-              oi = ospf_if_is_configured (area->ospf, &(l->link_data));
-              if (oi)
-                {
-                  nh = vertex_nexthop_new (v);
-                  nh->oi = oi;
-                  nh->router.s_addr = 0;
-                  ospf_spf_consider_nexthop (w->nexthop, nh);
-                }
+              nh = vertex_nexthop_new ();
+              nh->oi = oi;
+              nh->router.s_addr = 0;
+              ospf_spf_add_parent (v, w, nh);
             }
         }
       return;
     } /* end V is the root */
-
   /* Check if W's parent is a network connected to root. */
   else if (v->type == OSPF_VERTEX_NETWORK)
     {
       /* See if any of V's parents are the root. */
-      for (ALL_LIST_ELEMENTS (v->nexthop, node, nnode, x))
+      for (ALL_LIST_ELEMENTS (v->parents, node, nnode, vp))
         {
-          if (x->parent == area->spf) /* connects to root? */
+          if (vp->parent == area->spf) /* connects to root? */
 	    {
 	      /* 16.1.1 para 5. ...the parent vertex is a network that
 	       * directly connects the calculating router to the destination
@@ -597,10 +590,10 @@
 		   * use can then be derived from the next hop IP address (or 
 		   * it can be inherited from the parent network).
 		   */
-                  nh = vertex_nexthop_new (v);
-                  nh->oi = x->oi;
-                  nh->router = l->link_data;
-                  ospf_spf_consider_nexthop (w->nexthop, nh);
+		  nh = vertex_nexthop_new ();
+		  nh->oi = vp->nexthop->oi;
+		  nh->router = l->link_data;
+                  ospf_spf_add_parent (v, w, nh);
                 }
               return;
             }
@@ -612,11 +605,8 @@
    * destination simply inherits the set of next hops from the
    * parent.
    */
-  for (ALL_LIST_ELEMENTS (v->nexthop, node, nnode, nh))
-    {
-      nh->parent = v;
-      ospf_nexthop_add_unique (nh, w->nexthop);
-    }
+  for (ALL_LIST_ELEMENTS (v->parents, node, nnode, vp))
+    ospf_spf_add_parent (v, w, vp->nexthop);
 }
 
 /* RFC2328 Section 16.1 (2).
@@ -648,9 +638,8 @@
 
   while (p < lim)
     {
-      struct vertex *w, *cw;
-      
-      int link = -1; /* link index for w's back link */
+      struct vertex *w;
+      unsigned int distance;
       
       /* In case of V is Router-LSA. */
       if (v->lsa->type == OSPF_ROUTER_LSA)
@@ -723,7 +712,7 @@
       if (IS_LSA_MAXAGE (w_lsa))
         continue;
 
-      if ( (link = ospf_lsa_has_link (w_lsa->data, v->lsa)) < 0 )
+      if (ospf_lsa_has_link (w_lsa->data, v->lsa) < 0 )
         {
           if (IS_DEBUG_OSPF_EVENT)
             zlog_debug ("The LSA doesn't have a link back");
@@ -745,54 +734,52 @@
          vertex V and the advertised cost of the link between vertices
          V and W.  If D is: */
 
-      /* prepare vertex W. */
-      w = ospf_vertex_new (w_lsa);
-
-      /* Save W's back link index number, for use by virtual links */
-      w->backlink = link;
-
       /* calculate link cost D. */
       if (v->lsa->type == OSPF_ROUTER_LSA)
-	w->distance = v->distance + ntohs (l->m[0].metric);
+	distance = v->distance + ntohs (l->m[0].metric);
       else /* v is not a Router-LSA */
-	w->distance = v->distance;
+	distance = v->distance;
 
       /* Is there already vertex W in candidate list? */
       if (w_lsa->stat == LSA_SPF_NOT_EXPLORED)
 	{
+          /* prepare vertex W. */
+          w = ospf_vertex_new (w_lsa);
+
           /* Calculate nexthop to W. */
-	  ospf_nexthop_calculation (area, v, w);
+          w->distance = distance;
+	  ospf_nexthop_calculation (area, v, w, l);
 	  pqueue_enqueue (w, candidate);
 	}
       else if (w_lsa->stat >= 0)
 	{
 	  /* Get the vertex from candidates. */
-	  cw = (struct vertex *) candidate->array[w_lsa->stat];
+	  w = candidate->array[w_lsa->stat];
 
 	  /* if D is greater than. */  
-	  if (cw->distance < w->distance)
+	  if (w->distance < distance)
             {
-              ospf_vertex_free (w);
               continue;
             }
           /* equal to. */
-	  else if (cw->distance == w->distance)
+	  else if (w->distance == distance)
             {
-	      /* Found an equal-cost path to W.  Calculate nexthop to W. */
-              ospf_nexthop_calculation (area, v, w);
-              ospf_nexthop_merge (cw->nexthop, w->nexthop);
-              list_delete_all_node (w->nexthop);
-              ospf_vertex_free (w);
+	      /* Found an equal-cost path to W.  
+               * Calculate nexthop of to W from V. */
+              ospf_nexthop_calculation (area, v, w, l);
             }
            /* less than. */
 	  else
             {
-	      /* Found a lower-cost path to W.  Calculate nexthop to W. */
-              ospf_nexthop_calculation (area, v, w);
+	      /* Found a lower-cost path to W. */
+	      w->distance = distance;
+	      
+	      /* Flush existing parent list from W */
+	      ospf_spf_flush_parents (w);
+	      
+	      /* Calculate new nexthop(s) to W. */
+              ospf_nexthop_calculation (area, v, w, l);
 
-              /* Remove old vertex from candidate list. */
-              ospf_vertex_free (cw);
-	      candidate->array[w_lsa->stat] = w;
 	      /* Decrease the key of the node in the heap, re-sort the heap. */
 	      trickle_down (w_lsa->stat, candidate);
             }
@@ -801,31 +788,11 @@
 }
 
 static void
-ospf_spf_route_free (struct route_table *table)
-{
-  struct route_node *rn;
-  struct vertex *v;
-
-  for (rn = route_top (table); rn; rn = route_next (rn))
-    {
-      if ((v = rn->info))
-        {
-          ospf_vertex_free (v);
-          rn->info = NULL;
-        }
-
-      route_unlock_node (rn);
-    }
-
-  route_table_finish (table);
-}
-
-static void
 ospf_spf_dump (struct vertex *v, int i)
 {
   struct listnode *cnode;
   struct listnode *nnode;
-  struct vertex_nexthop *nexthop;
+  struct vertex_parent *parent;
 
   if (v->type == OSPF_VERTEX_ROUTER)
     {
@@ -841,12 +808,18 @@
     }
 
   if (IS_DEBUG_OSPF_EVENT)
-    for (ALL_LIST_ELEMENTS_RO (v->nexthop, nnode, nexthop))
-      zlog_debug (" nexthop %s", inet_ntoa (nexthop->router));
+    for (ALL_LIST_ELEMENTS_RO (v->parents, nnode, parent))
+      {
+        zlog_debug (" nexthop %p %s %s", 
+                    parent->nexthop,
+                    inet_ntoa (parent->nexthop->router),
+                    parent->nexthop->oi ? IF_NAME(parent->nexthop->oi)
+                                        : "NULL");
+      }
 
   i++;
 
-  for (ALL_LIST_ELEMENTS_RO (v->child, cnode, v))
+  for (ALL_LIST_ELEMENTS_RO (v->children, cnode, v))
     ospf_spf_dump (v, i);
 }
 
@@ -894,7 +867,7 @@
 
   ospf_vertex_dump("ospf_process_stubs(): after examining links: ", v, 1, 1);
 
-  for (ALL_LIST_ELEMENTS (v->child, cnode, cnnode, child))
+  for (ALL_LIST_ELEMENTS (v->children, cnode, cnnode, child))
     {
       if (CHECK_FLAG (child->flags, OSPF_VERTEX_PROCESSED))
         continue;
@@ -997,7 +970,7 @@
 {
   struct pqueue *candidate;
   struct vertex *v;
-
+  
   if (IS_DEBUG_OSPF_EVENT)
     {
       zlog_debug ("ospf_spf_calculate: Start");
@@ -1038,7 +1011,7 @@
   /* Set Area A's TransitCapability to FALSE. */
   area->transit = OSPF_TRANSIT_FALSE;
   area->shortcut_capability = 1;
-
+  
   for (;;)
     {
       /* RFC2328 16.1. (2). */
@@ -1059,6 +1032,7 @@
       v = (struct vertex *) pqueue_dequeue (candidate);
       /* Update stat field in vertex. */
       *(v->stat) = LSA_SPF_IN_SPFTREE;
+
       ospf_vertex_add_parent (v);
 
       /* Note that when there is a choice of vertices closest to the
@@ -1087,9 +1061,19 @@
   /* Second stage of SPF calculation procedure's  */
   ospf_spf_process_stubs (area, area->spf, new_table);
 
-  /* Free candidates. */
+  /* Free candidate queue. */
   pqueue_delete (candidate);
-
+  
+  ospf_vertex_dump (__func__, area->spf, 0, 1);
+  /* Free nexthop information, canonical versions of which are attached
+   * the first level of router vertices attached to the root vertex, see
+   * ospf_nexthop_calculation.
+   */
+  ospf_canonical_nexthops_free (area->spf);
+  
+  /* Free SPF vertices */
+  ospf_vertex_free (area->spf, area);
+  
   /* Increment SPF Calculation Counter. */
   area->spf_calculation++;
 
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
index 1aa871a..50e590d 100644
--- a/ospfd/ospf_spf.h
+++ b/ospfd/ospf_spf.h
@@ -36,11 +36,10 @@
   u_char type;		/* copied from LSA header */
   struct in_addr id;	/* copied from LSA header */
   struct lsa_header *lsa; /* Router or Network LSA */
-  int * stat;		/* Link to LSA status. */
-  u_int32_t distance;	/* from root to this vertex */
-  int backlink;        /* link index of back-link */
-  struct list *child;		/* list of vertex: children in SPF tree*/
-  struct list *nexthop;		/* list of vertex_nexthop from root to this vertex */
+  int *stat;		/* Link to LSA status. */
+  u_int32_t distance;	/* from root to this vertex */  
+  struct list *parents;		/* list of parents in SPF tree */
+  struct list *children;	/* list of children in SPF tree*/
 };
 
 /* A nexthop taken on the root node to get to this (parent) vertex */
@@ -48,7 +47,13 @@
 {
   struct ospf_interface *oi;	/* output intf on root node */
   struct in_addr router;	/* router address to send to */
-  struct vertex *parent;	/* parent in SPF tree */
+};
+
+struct vertex_parent
+{
+  struct vertex_nexthop *nexthop; /* link to nexthop info for this parent */
+  struct vertex *parent;	/* parent vertex */
+  int backlink;			/* index back to parent for router-lsa's */
 };
 
 extern void ospf_spf_calculate_schedule (struct ospf *);