zebra: add structure to hold per-prefix state in RIB

Add the rib_dest_t structure to hold per-prefix state in the routing
information base. This gives us an appropriate place to maintain the
queueing state of a route_node. Queuing state was previously being
stored on the first rib in the list of ribs hanging off the
route_node.

  * zebra/rib.h

    - Add new structure rib_dest_t.

    - Remove the rn_status field from 'struct rib', it is no longer
      required.

    - Add macros (RNODE_FOREACH_RIB, RNODE_FOREACH_RIB_SAFE) for
      walking all 'struct ribs' corresponding to a route_node. These
      hide the fact that there is an intermediate rib_dest_t
      structure.

    - Add a few utility inlines to go between a rib_dest_t and
      associated structures.

  * zebra/zebra_rib.c

    - rib_link()/rib_unlink()

      Tweak for new behavior, where the 'info' pointer of a route_node
      points to a rib_dest_t. The list of ribs for a prefix now hangs
      off of the dest.

      Change the way we ref count route_nodes. We now hold a single
      ref count on a route_node if there is a corresponding
      rib_dest_t.

    - Maintain the queuing state of a route_node on the flags field of
      the rib_dest_t.

    - Add the rib_gc_dest() function, which deletes a rib_dest_t if it
      is no longer required. A rib_dest_t can be deleted iff there are
      no struct ribs hanging off of it.

    - Call rib_gc_dest() any time we unlink a rib from the
      rib_dest_t. Currently we only need to call it once, just before
      we return from rib_process().

  * zebra/{redistribute,zebra_rib,zebra_snmp,zebra_vty}.c

    Use new macros to walk over route_node ribs.

  * lib/memtypes.c

    Add memory type for rib_dest_t.

Signed-off-by: Avneesh Sachdev <avneesh@opensourcerouting.org>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/lib/memtypes.c b/lib/memtypes.c
index bbf9692..76dece2 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -82,6 +82,7 @@
   { MTYPE_RIB_QUEUE,		"RIB process work queue"	},
   { MTYPE_STATIC_IPV4,		"Static IPv4 route"		},
   { MTYPE_STATIC_IPV6,		"Static IPv6 route"		},
+  { MTYPE_RIB_DEST,		"RIB destination"		},
   { -1, NULL },
 };
 
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 4276f1d..4765824 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -107,7 +107,7 @@
       rn = route_node_lookup (table, (struct prefix *)&p);
       if (rn)
 	{
-	  for (newrib = rn->info; newrib; newrib = newrib->next)
+	  RNODE_FOREACH_RIB (rn, newrib)
 	    if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
 		&& newrib->distance != DISTANCE_INFINITY)
 	      zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib);
@@ -127,7 +127,7 @@
       rn = route_node_lookup (table, (struct prefix *)&p6);
       if (rn)
 	{
-	  for (newrib = rn->info; newrib; newrib = newrib->next)
+	  RNODE_FOREACH_RIB (rn, newrib)
 	    if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
 		&& newrib->distance != DISTANCE_INFINITY)
 	      zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib);
@@ -148,7 +148,7 @@
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      for (newrib = rn->info; newrib; newrib = newrib->next)
+      RNODE_FOREACH_RIB (rn, newrib)
 	if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) 
 	    && newrib->type == type 
 	    && newrib->distance != DISTANCE_INFINITY
@@ -159,7 +159,7 @@
   table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      for (newrib = rn->info; newrib; newrib = newrib->next)
+      RNODE_FOREACH_RIB (rn, newrib)
 	if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
 	    && newrib->type == type 
 	    && newrib->distance != DISTANCE_INFINITY
diff --git a/zebra/rib.h b/zebra/rib.h
index 1b85c81..084f351 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -24,6 +24,7 @@
 #define _ZEBRA_RIB_H
 
 #include "prefix.h"
+#include "table.h"
 
 #define DISTANCE_INFINITY  255
 
@@ -38,10 +39,6 @@
 
 struct rib
 {
-  /* Status Flags for the *route_node*, but kept in the head RIB.. */
-  u_char rn_status;
-#define RIB_ROUTE_QUEUED(x)	(1 << (x))
-
   /* Link list. */
   struct rib *next;
   struct rib *prev;
@@ -97,6 +94,57 @@
   u_int32_t size; /* sum of lengths of all subqueues */
 };
 
+/*
+ * Structure that represents a single destination (prefix).
+ */
+typedef struct rib_dest_t_
+{
+
+  /*
+   * Back pointer to the route node for this destination. This helps
+   * us get to the prefix that this structure is for.
+   */
+  struct route_node *rnode;
+
+  /*
+   * Doubly-linked list of routes for this prefix.
+   */
+  struct rib *routes;
+
+  /*
+   * Flags, see below.
+   */
+  u_int32_t flags;
+
+} rib_dest_t;
+
+#define RIB_ROUTE_QUEUED(x)	(1 << (x))
+
+/*
+ * The maximum qindex that can be used.
+ */
+#define ZEBRA_MAX_QINDEX        (MQ_SIZE - 1)
+
+/*
+ * Macro to iterate over each route for a destination (prefix).
+ */
+#define RIB_DEST_FOREACH_ROUTE(dest, rib)				\
+  for ((rib) = (dest) ? (dest)->routes : NULL; (rib); (rib) = (rib)->next)
+
+/*
+ * Same as above, but allows the current node to be unlinked.
+ */
+#define RIB_DEST_FOREACH_ROUTE_SAFE(dest, rib, next)	\
+  for ((rib) = (dest) ? (dest)->routes : NULL;		\
+       (rib) && ((next) = (rib)->next, 1);		\
+       (rib) = (next))
+
+#define RNODE_FOREACH_RIB(rn, rib)				\
+  RIB_DEST_FOREACH_ROUTE (rib_dest_from_rnode (rn), rib)
+
+#define RNODE_FOREACH_RIB_SAFE(rn, rib, next)				\
+  RIB_DEST_FOREACH_ROUTE_SAFE (rib_dest_from_rnode (rn), rib, next)
+
 /* Static route information. */
 struct static_ipv4
 {
@@ -307,4 +355,66 @@
 
 #endif /* HAVE_IPV6 */
 
+extern int rib_gc_dest (struct route_node *rn);
+
+/*
+ * Inline functions.
+ */
+
+/*
+ * rib_dest_from_rnode
+ */
+static inline rib_dest_t *
+rib_dest_from_rnode (struct route_node *rn)
+{
+  return (rib_dest_t *) rn->info;
+}
+
+/*
+ * rnode_to_ribs
+ *
+ * Returns a pointer to the list of routes corresponding to the given
+ * route_node.
+ */
+static inline struct rib *
+rnode_to_ribs (struct route_node *rn)
+{
+  rib_dest_t *dest;
+
+  dest = rib_dest_from_rnode (rn);
+  if (!dest)
+    return NULL;
+
+  return dest->routes;
+}
+
+/*
+ * rib_dest_prefix
+ */
+static inline struct prefix *
+rib_dest_prefix (rib_dest_t *dest)
+{
+  return &dest->rnode->p;
+}
+
+/*
+ * rib_dest_af
+ *
+ * Returns the address family that the destination is for.
+ */
+static inline u_char
+rib_dest_af (rib_dest_t *dest)
+{
+  return dest->rnode->p.family;
+}
+
+/*
+ * rib_dest_table
+ */
+static inline struct route_table *
+rib_dest_table (rib_dest_t *dest)
+{
+  return dest->rnode->table;
+}
+
 #endif /*_ZEBRA_RIB_H */
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 0e29e61..f0d5c9d 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -351,7 +351,7 @@
 	return 0;
 
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
+      RNODE_FOREACH_RIB (rn, match)
 	{
 	  if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
 	    continue;
@@ -452,7 +452,7 @@
 	return 0;
 
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
+      RNODE_FOREACH_RIB (rn, match)
 	{
 	  if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
 	    continue;
@@ -543,7 +543,7 @@
       route_unlock_node (rn);
       
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
+      RNODE_FOREACH_RIB (rn, match)
 	{
 	  if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
 	    continue;
@@ -601,7 +601,7 @@
   /* Unlock node. */
   route_unlock_node (rn);
 
-  for (match = rn->info; match; match = match->next)
+  RNODE_FOREACH_RIB (rn, match)
     {
       if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
 	continue;
@@ -658,7 +658,7 @@
   route_unlock_node (rn);
 
   /* Find out if a "selected" RR for the discovered RIB entry exists ever. */
-  for (match = rn->info; match; match = match->next)
+  RNODE_FOREACH_RIB (rn, match)
     {
       if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
 	continue;
@@ -725,7 +725,7 @@
       route_unlock_node (rn);
       
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
+      RNODE_FOREACH_RIB (rn, match)
 	{
 	  if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
 	    continue;
@@ -975,6 +975,61 @@
 
 static void rib_unlink (struct route_node *, struct rib *);
 
+/*
+ * rib_can_delete_dest
+ *
+ * Returns TRUE if the given dest can be deleted from the table.
+ */
+static int
+rib_can_delete_dest (rib_dest_t *dest)
+{
+  if (dest->routes)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/*
+ * rib_gc_dest
+ *
+ * Garbage collect the rib dest corresponding to the given route node
+ * if appropriate.
+ *
+ * Returns TRUE if the dest was deleted, FALSE otherwise.
+ */
+int
+rib_gc_dest (struct route_node *rn)
+{
+  rib_dest_t *dest;
+  char buf[INET6_ADDRSTRLEN];
+
+  dest = rib_dest_from_rnode (rn);
+  if (!dest)
+    return 0;
+
+  if (!rib_can_delete_dest (dest))
+    return 0;
+
+  if (IS_ZEBRA_DEBUG_RIB)
+    {
+      inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf));
+      zlog_debug ("%s: %s/%d: removing dest from table", __func__,
+		  buf, rn->p.prefixlen);
+    }
+
+  dest->rnode = NULL;
+  XFREE (MTYPE_RIB_DEST, dest);
+  rn->info = NULL;
+
+  /*
+   * Release the one reference that we keep on the route node.
+   */
+  route_unlock_node (rn);
+  return 1;
+}
+
 /* Core function for processing routing information base. */
 static void
 rib_process (struct route_node *rn)
@@ -993,13 +1048,8 @@
   if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q)
     inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
 
-  for (rib = rn->info; rib; rib = next)
+  RNODE_FOREACH_RIB_SAFE (rn, rib, next)
     {
-      /* The next pointer is saved, because current pointer
-       * may be passed to rib_unlink() in the middle of iteration.
-       */
-      next = rib->next;
-      
       /* Currently installed rib. */
       if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
         {
@@ -1017,7 +1067,7 @@
               if (IS_ZEBRA_DEBUG_RIB)
                 zlog_debug ("%s: %s/%d: rn %p, removing rib %p", __func__,
                   buf, rn->p.prefixlen, rn, rib);
-                rib_unlink (rn, rib);
+	      rib_unlink (rn, rib);
             }
           else
             del = rib;
@@ -1074,7 +1124,7 @@
       /* metric tie-breaks equal distance */
       if (rib->metric <= select->metric)
         select = rib;
-    } /* for (rib = rn->info; rib; rib = next) */
+    } /* RNODE_FOREACH_RIB_SAFE */
 
   /* After the cycle is finished, the following pointers will be set:
    * select --- the winner RIB entry, if any was found, otherwise NULL
@@ -1171,6 +1221,11 @@
 end:
   if (IS_ZEBRA_DEBUG_RIB_Q)
     zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn);
+
+  /*
+   * Check if the dest can be deleted now.
+   */
+  rib_gc_dest (rn);
 }
 
 /* Take a list of route_node structs and return 1, if there was a record
@@ -1189,8 +1244,9 @@
   rnode = listgetdata (lnode);
   rib_process (rnode);
 
-  if (rnode->info) /* The first RIB record is holding the flags bitmask. */
-    UNSET_FLAG (((struct rib *)rnode->info)->rn_status, RIB_ROUTE_QUEUED(qindex));
+  if (rnode->info)
+    UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, RIB_ROUTE_QUEUED (qindex));
+
 #if 0
   else
     {
@@ -1223,7 +1279,9 @@
   return mq->size ? WQ_REQUEUE : WQ_SUCCESS;
 }
 
-/* Map from rib types to queue type (priority) in meta queue */
+/*
+ * Map from rib types to queue type (priority) in meta queue
+ */
 static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = {
   [ZEBRA_ROUTE_SYSTEM]  = 4,
   [ZEBRA_ROUTE_KERNEL]  = 0,
@@ -1251,12 +1309,13 @@
   if (IS_ZEBRA_DEBUG_RIB_Q)
     inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
 
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       u_char qindex = meta_queue_map[rib->type];
 
       /* Invariant: at this point we always have rn->info set. */
-      if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex)))
+      if (CHECK_FLAG (rib_dest_from_rnode (rn)->flags,
+		      RIB_ROUTE_QUEUED (qindex)))
 	{
 	  if (IS_ZEBRA_DEBUG_RIB_Q)
 	    zlog_debug ("%s: %s/%d: rn %p is already queued in sub-queue %u",
@@ -1264,7 +1323,7 @@
 	  continue;
 	}
 
-      SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex));
+      SET_FLAG (rib_dest_from_rnode (rn)->flags, RIB_ROUTE_QUEUED (qindex));
       listnode_add (mq->subq[qindex], rn);
       route_lock_node (rn);
       mq->size++;
@@ -1286,7 +1345,7 @@
     inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
 
   /* Pointless to queue a route_node with no RIB entries to add or remove */
-  if (!rn->info)
+  if (!rnode_to_ribs (rn))
     {
       zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
                   __func__, rn, rn->lock);
@@ -1395,17 +1454,16 @@
  * |-> set RIB_ENTRY_REMOVE                           |
  * rib_delnode                                  (RIB freed)
  *
- *
- * Queueing state for a route_node is kept in the head RIB entry, this
- * state must be preserved as and when the head RIB entry of a
- * route_node is changed by rib_unlink / rib_link. A small complication,
- * but saves having to allocate a dedicated object for this.
+ * The 'info' pointer of a route_node points to a rib_dest_t
+ * ('dest'). Queueing state for a route_node is kept on the dest. The
+ * dest is created on-demand by rib_link() and is kept around at least
+ * as long as there are ribs hanging off it (@see rib_gc_dest()).
  * 
  * Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
  *
  * - route_nodes: refcounted by:
- *   - RIBs attached to route_node:
- *     - managed by: rib_link/unlink
+ *   - dest attached to route_node:
+ *     - managed by: rib_link/rib_gc_dest
  *   - route_node processing queue
  *     - managed by: rib_addqueue, rib_process.
  *
@@ -1416,12 +1474,11 @@
 rib_link (struct route_node *rn, struct rib *rib)
 {
   struct rib *head;
+  rib_dest_t *dest;
   char buf[INET6_ADDRSTRLEN];
-  
+
   assert (rib && rn);
   
-  route_lock_node (rn); /* rn route table reference */
-
   if (IS_ZEBRA_DEBUG_RIB)
   {
     inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
@@ -1429,18 +1486,28 @@
       buf, rn->p.prefixlen, rn, rib);
   }
 
-  head = rn->info;
-  if (head)
+  dest = rib_dest_from_rnode (rn);
+  if (!dest)
     {
       if (IS_ZEBRA_DEBUG_RIB)
-        zlog_debug ("%s: %s/%d: new head, rn_status copied over", __func__,
-          buf, rn->p.prefixlen);
+	{
+	  zlog_debug ("%s: %s/%d: adding dest to table", __func__,
+		      buf, rn->p.prefixlen);
+	}
+
+      dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t));
+      route_lock_node (rn); /* rn route table reference */
+      rn->info = dest;
+      dest->rnode = rn;
+    }
+
+  head = dest->routes;
+  if (head)
+    {
       head->prev = rib;
-      /* Transfer the rn status flags to the new head RIB */
-      rib->rn_status = head->rn_status;
     }
   rib->next = head;
-  rn->info = rib;
+  dest->routes = rib;
   rib_queue_add (&zebrad, rn);
 }
 
@@ -1465,11 +1532,21 @@
   rib_link (rn, rib);
 }
 
+/*
+ * rib_unlink
+ *
+ * Detach a rib structure from a route_node.
+ *
+ * Note that a call to rib_unlink() should be followed by a call to
+ * rib_gc_dest() at some point. This allows a rib_dest_t that is no
+ * longer required to be deleted.
+ */
 static void
 rib_unlink (struct route_node *rn, struct rib *rib)
 {
   struct nexthop *nexthop, *next;
   char buf[INET6_ADDRSTRLEN];
+  rib_dest_t *dest;
 
   assert (rn && rib);
 
@@ -1480,6 +1557,8 @@
                 __func__, buf, rn->p.prefixlen, rn, rib);
   }
 
+  dest = rib_dest_from_rnode (rn);
+
   if (rib->next)
     rib->next->prev = rib->prev;
 
@@ -1487,15 +1566,7 @@
     rib->prev->next = rib->next;
   else
     {
-      rn->info = rib->next;
-      
-      if (rn->info)
-        {
-          if (IS_ZEBRA_DEBUG_RIB)
-            zlog_debug ("%s: %s/%d: rn %p, rib %p, new head copy",
-                        __func__, buf, rn->p.prefixlen, rn, rib);
-          rib->next->rn_status = rib->rn_status;
-        }
+      dest->routes = rib->next;
     }
 
   /* free RIB and nexthops */
@@ -1506,7 +1577,6 @@
     }
   XFREE (MTYPE_RIB, rib);
 
-  route_unlock_node (rn); /* rn route table reference */
 }
 
 static void
@@ -1561,7 +1631,7 @@
 
   /* If same type of route are installed, treat it as a implicit
      withdraw. */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -1717,7 +1787,7 @@
   route_unlock_node (rn);
 
   /* let's go */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
   {
     zlog_debug
     (
@@ -1764,7 +1834,7 @@
    * RIBQ record already on head. This is necessary for proper revalidation
    * of the rest of the RIB.
    */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
   {
     if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) &&
       ! RIB_SYSTEM_ROUTE (rib))
@@ -1816,7 +1886,7 @@
 
   /* If same type of route are installed, treat it as a implicit
      withdraw. */
-  for (same = rn->info; same; same = same->next)
+  RNODE_FOREACH_RIB (rn, same)
     {
       if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED))
         continue;
@@ -1907,7 +1977,7 @@
     }
 
   /* Lookup same type route. */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -2000,7 +2070,7 @@
 
   /* Lookup existing route */
   rn = route_node_get (table, p);
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
        if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
          continue;
@@ -2096,7 +2166,7 @@
   if (! rn)
     return;
 
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -2355,7 +2425,7 @@
 
   /* If same type of route are installed, treat it as a implicit
      withdraw. */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -2458,7 +2528,7 @@
     }
 
   /* Lookup same type route. */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -2551,7 +2621,7 @@
 
   /* Lookup existing route */
   rn = route_node_get (table, p);
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -2648,7 +2718,7 @@
   if (! rn)
     return;
 
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
         continue;
@@ -2844,13 +2914,13 @@
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      if (rn->info)
+      if (rnode_to_ribs (rn))
         rib_queue_add (&zebrad, rn);
 
   table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      if (rn->info)
+      if (rnode_to_ribs (rn))
         rib_queue_add (&zebrad, rn);
 }
 
@@ -2865,10 +2935,8 @@
 
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      for (rib = rn->info; rib; rib = next)
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
 	{
-	  next = rib->next;
-
 	  if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
 	    continue;
 
@@ -2897,10 +2965,8 @@
 
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      for (rib = rn->info; rib; rib = next)
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
 	{
-	  next = rib->next;
-
 	  if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
 	    continue;
 
@@ -2933,9 +2999,8 @@
 
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      for (rib = rn->info; rib; rib = next)
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
         {
-          next = rib->next;
           if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
             continue;
           if (rib->type == proto)
@@ -2965,11 +3030,13 @@
 
   if (table)
     for (rn = route_top (table); rn; rn = route_next (rn))
-      for (rib = rn->info; rib; rib = rib->next)
+      RNODE_FOREACH_RIB (rn, rib)
         {
-          if (! RIB_SYSTEM_ROUTE (rib)
-	      && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
-            rib_uninstall_kernel (rn, rib);
+          if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+	    continue;
+
+	  if (! RIB_SYSTEM_ROUTE (rib))
+	    rib_uninstall_kernel (rn, rib);
         }
 }
 
diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c
index f52bbcb..e06e144 100644
--- a/zebra/zebra_snmp.c
+++ b/zebra/zebra_snmp.c
@@ -150,7 +150,7 @@
   /* Return number of routing entries. */
   result = 0;
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       result++;
 
   return (u_char *)&result;
@@ -175,7 +175,7 @@
   /* Return number of routing entries. */
   result = 0;
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       result++;
 
   return (u_char *)&result;
@@ -369,7 +369,7 @@
 	{
 	  if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest))
 	    {
-	      for (*rib = (*np)->info; *rib; *rib = (*rib)->next)
+	      RNODE_FOREACH_RIB (*np, *rib)
 	        {
 		  if (!in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4,
 				   (u_char *)&nexthop))
@@ -388,12 +388,12 @@
 
       /* Check destination first */
       if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) > 0)
-        for (rib2 = np2->info; rib2; rib2 = rib2->next)
+	RNODE_FOREACH_RIB (np2, rib2)
 	  check_replace(np2, rib2, np, rib);
 
       if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) == 0)
         { /* have to look at each rib individually */
-          for (rib2 = np2->info; rib2; rib2 = rib2->next)
+	  RNODE_FOREACH_RIB (np2, rib2)
 	    {
 	      int proto2, policy2;
 
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 743c13f..d07b09c 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -535,7 +535,7 @@
   struct rib *rib;
   struct nexthop *nexthop;
 
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       vty_out (vty, "Routing entry for %s/%d%s", 
 	       inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
@@ -822,7 +822,7 @@
 
   /* Show all IPv4 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       {
 	if (first)
 	  {
@@ -863,7 +863,7 @@
 
   /* Show matched type IPv4 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       if (prefix_match (&p, &rn->p))
 	{
 	  if (first)
@@ -896,7 +896,7 @@
 
   /* Show matched type IPv4 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       {
 	addr = ntohl (rn->p.u.prefix4.s_addr);
 
@@ -942,7 +942,7 @@
 
   /* Show matched type IPv4 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       if (rib->type == type)
 	{
 	  if (first)
@@ -1046,7 +1046,7 @@
   memset (&rib_cnt, 0, sizeof(rib_cnt));
   memset (&fib_cnt, 0, sizeof(fib_cnt));
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
         {
 	  rib_cnt[ZEBRA_ROUTE_TOTAL]++;
@@ -1219,7 +1219,7 @@
 
   /* Show all IPv4 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       {
        if (first)
          {
@@ -1546,7 +1546,7 @@
   struct nexthop *nexthop;
   char buf[BUFSIZ];
 
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
     {
       vty_out (vty, "Routing entry for %s/%d%s", 
 	       inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ),
@@ -1795,7 +1795,7 @@
 
   /* Show all IPv6 route. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       {
 	if (first)
 	  {
@@ -1836,7 +1836,7 @@
 
   /* Show matched type IPv6 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       if (prefix_match (&p, &rn->p))
 	{
 	  if (first)
@@ -1876,7 +1876,7 @@
 
   /* Show matched type IPv6 routes. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       if (rib->type == type)
 	{
 	  if (first)
@@ -2008,7 +2008,7 @@
 
   /* Show all IPv6 route. */
   for (rn = route_top (table); rn; rn = route_next (rn))
-    for (rib = rn->info; rib; rib = rib->next)
+    RNODE_FOREACH_RIB (rn, rib)
       {
        if (first)
          {