]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_rib.c
bgpd: bgpd-routemap-match-localpref.patch
[mirror_frr.git] / zebra / zebra_rib.c
index f61cbe319f16b36c5f30bcd69acad48dec6ff878..5cd81e3de45dc3f3f3ddb823e2a5ce3368bc93e5 100644 (file)
 #include "workqueue.h"
 #include "prefix.h"
 #include "routemap.h"
+#include "nexthop.h"
 
 #include "zebra/rib.h"
 #include "zebra/rt.h"
 #include "zebra/zserv.h"
 #include "zebra/redistribute.h"
 #include "zebra/debug.h"
+#include "zebra/zebra_fpm.h"
+#include "zebra/zebra_rnh.h"
 
 /* Default rtm_table for all clients */
 extern struct zebra_t zebrad;
@@ -51,26 +54,49 @@ extern struct zebra_t zebrad;
 int rib_process_hold_time = 10;
 
 /* Each route type's string and default distance value. */
-struct
+static const struct
 {  
   int key;
   int distance;
-} route_info[] =
-{
-  {ZEBRA_ROUTE_SYSTEM,    0},
-  {ZEBRA_ROUTE_KERNEL,    0},
-  {ZEBRA_ROUTE_CONNECT,   0},
-  {ZEBRA_ROUTE_STATIC,    1},
-  {ZEBRA_ROUTE_RIP,     120},
-  {ZEBRA_ROUTE_RIPNG,   120},
-  {ZEBRA_ROUTE_OSPF,    110},
-  {ZEBRA_ROUTE_OSPF6,   110},
-  {ZEBRA_ROUTE_ISIS,    115},
-  {ZEBRA_ROUTE_BGP,      20  /* IBGP is 200. */}
+} route_info[ZEBRA_ROUTE_MAX] =
+{
+  [ZEBRA_ROUTE_SYSTEM]  = {ZEBRA_ROUTE_SYSTEM,    0},
+  [ZEBRA_ROUTE_KERNEL]  = {ZEBRA_ROUTE_KERNEL,    0},
+  [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT,   0},
+  [ZEBRA_ROUTE_STATIC]  = {ZEBRA_ROUTE_STATIC,    1},
+  [ZEBRA_ROUTE_RIP]     = {ZEBRA_ROUTE_RIP,     120},
+  [ZEBRA_ROUTE_RIPNG]   = {ZEBRA_ROUTE_RIPNG,   120},
+  [ZEBRA_ROUTE_OSPF]    = {ZEBRA_ROUTE_OSPF,    110},
+  [ZEBRA_ROUTE_OSPF6]   = {ZEBRA_ROUTE_OSPF6,   110},
+  [ZEBRA_ROUTE_ISIS]    = {ZEBRA_ROUTE_ISIS,    115},
+  [ZEBRA_ROUTE_BGP]     = {ZEBRA_ROUTE_BGP,      20  /* IBGP is 200. */},
+  [ZEBRA_ROUTE_BABEL]   = {ZEBRA_ROUTE_BABEL,    95},
+  /* no entry/default: 150 */
 };
-\f
+
 /* Vector for routing table.  */
-vector vrf_vector;
+static vector vrf_vector;
+
+/*
+ * vrf_table_create
+ */
+static void
+vrf_table_create (struct vrf *vrf, afi_t afi, safi_t safi)
+{
+  rib_table_info_t *info;
+  struct route_table *table;
+
+  assert (!vrf->table[afi][safi]);
+
+  table = route_table_init ();
+  vrf->table[afi][safi] = table;
+
+  info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info));
+  info->vrf = vrf;
+  info->afi = afi;
+  info->safi = safi;
+  table->info = info;
+}
 
 /* Allocate new VRF.  */
 static struct vrf *
@@ -85,21 +111,19 @@ vrf_alloc (const char *name)
     vrf->name = XSTRDUP (MTYPE_VRF_NAME, name);
 
   /* Allocate routing table and static table.  */
-  vrf->table[AFI_IP][SAFI_UNICAST] = route_table_init ();
-  vrf->table[AFI_IP6][SAFI_UNICAST] = route_table_init ();
+  vrf_table_create (vrf, AFI_IP, SAFI_UNICAST);
+  vrf_table_create (vrf, AFI_IP6, SAFI_UNICAST);
   vrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init ();
   vrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init ();
+  vrf_table_create (vrf, AFI_IP, SAFI_MULTICAST);
+  vrf_table_create (vrf, AFI_IP6, SAFI_MULTICAST);
+  vrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init ();
+  vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init ();
 
-  return vrf;
-}
+  vrf->rnh_table[AFI_IP] = route_table_init();
+  vrf->rnh_table[AFI_IP6] = route_table_init();
 
-/* Free VRF.  */
-static void
-vrf_free (struct vrf *vrf)
-{
-  if (vrf->name)
-    XFREE (MTYPE_VRF_NAME, vrf->name);
-  XFREE (MTYPE_VRF, vrf);
+  return vrf;
 }
 
 /* Lookup VRF by identifier.  */
@@ -109,20 +133,6 @@ vrf_lookup (u_int32_t id)
   return vector_lookup (vrf_vector, id);
 }
 
-/* Lookup VRF by name.  */
-static struct vrf *
-vrf_lookup_by_name (char *name)
-{
-  unsigned int i;
-  struct vrf *vrf;
-
-  for (i = 0; i < vector_active (vrf_vector); i++)
-    if ((vrf = vector_slot (vrf_vector, i)) != NULL)
-      if (vrf->name && name && strcmp (vrf->name, name) == 0)
-       return vrf;
-  return NULL;
-}
-
 /* Initialize VRF.  */
 static void
 vrf_init (void)
@@ -149,6 +159,9 @@ vrf_table (afi_t afi, safi_t safi, u_int32_t id)
   if (! vrf)
     return NULL;
 
+  if( afi >= AFI_MAX  || safi >= SAFI_MAX )
+    return NULL;
+
   return vrf->table[afi][safi];
 }
 
@@ -162,23 +175,32 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id)
   if (! vrf)
     return NULL;
 
+  if( afi >= AFI_MAX  || safi >= SAFI_MAX )
+    return NULL;
+
   return vrf->stable[afi][safi];
 }
-\f
-/* Add nexthop to the end of the list.  */
+
+/* Add nexthop to the end of a nexthop list.  */
 static void
-nexthop_add (struct rib *rib, struct nexthop *nexthop)
+_nexthop_add (struct nexthop **target, struct nexthop *nexthop)
 {
   struct nexthop *last;
 
-  for (last = rib->nexthop; last && last->next; last = last->next)
+  for (last = *target; last && last->next; last = last->next)
     ;
   if (last)
     last->next = nexthop;
   else
-    rib->nexthop = nexthop;
+    *target = nexthop;
   nexthop->prev = last;
+}
 
+/* Add nexthop to the end of a rib node's nexthop list */
+void
+nexthop_add (struct rib *rib, struct nexthop *nexthop)
+{
+  _nexthop_add(&rib->nexthop, nexthop);
   rib->nexthop_num++;
 }
 
@@ -196,21 +218,35 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop)
 }
 
 /* Free nexthop. */
-static void
+void
 nexthop_free (struct nexthop *nexthop)
 {
   if (nexthop->ifname)
     XFREE (0, nexthop->ifname);
+  if (nexthop->resolved)
+    nexthops_free(nexthop->resolved);
   XFREE (MTYPE_NEXTHOP, nexthop);
 }
 
+/* Frees a list of nexthops */
+void
+nexthops_free (struct nexthop *nexthop)
+{
+  struct nexthop *nh, *next;
+
+  for (nh = nexthop; nh; nh = next)
+    {
+      next = nh->next;
+      nexthop_free (nh);
+    }
+}
+
 struct nexthop *
 nexthop_ifindex_add (struct rib *rib, unsigned int ifindex)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IFINDEX;
   nexthop->ifindex = ifindex;
 
@@ -224,8 +260,7 @@ nexthop_ifname_add (struct rib *rib, char *ifname)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IFNAME;
   nexthop->ifname = XSTRDUP (0, ifname);
 
@@ -239,8 +274,7 @@ nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IPV4;
   nexthop->gate.ipv4 = *ipv4;
   if (src)
@@ -251,14 +285,13 @@ nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, 
                           struct in_addr *src, unsigned int ifindex)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
   nexthop->gate.ipv4 = *ipv4;
   if (src)
@@ -276,8 +309,7 @@ nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IPV6;
   nexthop->gate.ipv6 = *ipv6;
 
@@ -286,14 +318,13 @@ nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
                         char *ifname)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME;
   nexthop->gate.ipv6 = *ipv6;
   nexthop->ifname = XSTRDUP (0, ifname);
@@ -303,14 +334,13 @@ nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
                          unsigned int ifindex)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
   nexthop->gate.ipv6 = *ipv6;
   nexthop->ifindex = ifindex;
@@ -326,8 +356,7 @@ nexthop_blackhole_add (struct rib *rib)
 {
   struct nexthop *nexthop;
 
-  nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
-  memset (nexthop, 0, sizeof (struct nexthop));
+  nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
   nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
   SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE);
 
@@ -336,6 +365,24 @@ nexthop_blackhole_add (struct rib *rib)
   return nexthop;
 }
 
+/* This method checks whether a recursive nexthop has at
+ * least one resolved nexthop in the fib.
+ */
+int
+nexthop_has_fib_child(struct nexthop *nexthop)
+{
+  struct nexthop *nh;
+
+  if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+    return 0;
+
+  for (nh = nexthop->resolved; nh; nh = nh->next)
+    if (CHECK_FLAG (nh->flags, NEXTHOP_FLAG_FIB))
+      return 1;
+
+  return 0;
+}
+
 /* If force flag is not set, do not modify falgs at all for uninstall
    the route from FIB. */
 static int
@@ -346,13 +393,19 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
   struct route_table *table;
   struct route_node *rn;
   struct rib *match;
+  int resolved;
   struct nexthop *newhop;
+  struct nexthop *resolved_hop;
 
   if (nexthop->type == NEXTHOP_TYPE_IPV4)
     nexthop->ifindex = 0;
 
   if (set)
-    UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+    {
+      UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+      nexthops_free(nexthop->resolved);
+      nexthop->resolved = NULL;
+    }
 
   /* Make lookup prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv4));
@@ -370,14 +423,18 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
     {
       route_unlock_node (rn);
       
-      /* If lookup self prefix return immidiately. */
+      /* If lookup self prefix return immediately. */
       if (rn == top)
        return 0;
 
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
-       if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
-         break;
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+           break;
+       }
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
@@ -392,6 +449,12 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
        }
       else
        {
+         /* If the longest prefix match for the nexthop yields
+          * a blackhole, mark it as inactive. */
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE)
+             || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT))
+           return 0;
+
          if (match->type == ZEBRA_ROUTE_CONNECT)
            {
              /* Directly point connected route. */
@@ -403,6 +466,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
            }
          else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
            {
+             resolved = 0;
              for (newhop = match->nexthop; newhop; newhop = newhop->next)
                if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
                    && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -410,18 +474,46 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
                    if (set)
                      {
                        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
-                       nexthop->rtype = newhop->type;
-                       if (newhop->type == NEXTHOP_TYPE_IPV4 ||
-                           newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-                         nexthop->rgate.ipv4 = newhop->gate.ipv4;
+
+                       resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
+                       SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+                       /* If the resolving route specifies a gateway, use it */
+                       if (newhop->type == NEXTHOP_TYPE_IPV4
+                           || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME)
+                         {
+                           resolved_hop->type = newhop->type;
+                           resolved_hop->gate.ipv4 = newhop->gate.ipv4;
+
+                           if (newhop->ifindex)
+                             {
+                               resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+                               resolved_hop->ifindex = newhop->ifindex;
+                             }
+                         }
+
+                       /* If the resolving route is an interface route,
+                        * it means the gateway we are looking up is connected
+                        * to that interface. (The actual network is _not_ onlink).
+                        * Therefore, the resolved route should have the original
+                        * gateway as nexthop as it is directly connected.
+                        *
+                        * On Linux, we have to set the onlink netlink flag because
+                        * otherwise, the kernel won't accept the route. */
                        if (newhop->type == NEXTHOP_TYPE_IFINDEX
-                           || newhop->type == NEXTHOP_TYPE_IFNAME
-                           || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-                         nexthop->rifindex = newhop->ifindex;
+                           || newhop->type == NEXTHOP_TYPE_IFNAME)
+                         {
+                           resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+                           resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+                           resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
+                           resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       _nexthop_add(&nexthop->resolved, resolved_hop);
                      }
-                   return 1;
+                   resolved = 1;
                  }
-             return 0;
+             return resolved;
            }
          else
            {
@@ -443,13 +535,19 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
   struct route_table *table;
   struct route_node *rn;
   struct rib *match;
+  int resolved;
   struct nexthop *newhop;
+  struct nexthop *resolved_hop;
 
   if (nexthop->type == NEXTHOP_TYPE_IPV6)
     nexthop->ifindex = 0;
 
   if (set)
-    UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+    {
+      UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+      nexthops_free(nexthop->resolved);
+      nexthop->resolved = NULL;
+    }
 
   /* Make lookup prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv6));
@@ -467,14 +565,18 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
     {
       route_unlock_node (rn);
       
-      /* If lookup self prefix return immidiately. */
+      /* If lookup self prefix return immediately. */
       if (rn == top)
        return 0;
 
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
-       if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
-         break;
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+           break;
+       }
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
@@ -489,6 +591,12 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
        }
       else
        {
+         /* If the longest prefix match for the nexthop yields
+          * a blackhole, mark it as inactive. */
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE)
+             || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT))
+           return 0;
+
          if (match->type == ZEBRA_ROUTE_CONNECT)
            {
              /* Directly point connected route. */
@@ -501,6 +609,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
            }
          else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
            {
+             resolved = 0;
              for (newhop = match->nexthop; newhop; newhop = newhop->next)
                if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
                    && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -508,20 +617,39 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
                    if (set)
                      {
                        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
-                       nexthop->rtype = newhop->type;
+
+                       resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
+                       SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+                       /* See nexthop_active_ipv4 for a description how the
+                        * resolved nexthop is constructed. */
                        if (newhop->type == NEXTHOP_TYPE_IPV6
                            || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
                            || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
-                         nexthop->rgate.ipv6 = newhop->gate.ipv6;
+                         {
+                           resolved_hop->type = newhop->type;
+                           resolved_hop->gate.ipv6 = newhop->gate.ipv6;
+
+                           if (newhop->ifindex)
+                             {
+                               resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                               resolved_hop->ifindex = newhop->ifindex;
+                             }
+                         }
+
                        if (newhop->type == NEXTHOP_TYPE_IFINDEX
-                           || newhop->type == NEXTHOP_TYPE_IFNAME
-                           || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
-                           || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
-                         nexthop->rifindex = newhop->ifindex;
+                           || newhop->type == NEXTHOP_TYPE_IFNAME)
+                         {
+                               resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+                               resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                               resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
+                               resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       _nexthop_add(&nexthop->resolved, resolved_hop);
                      }
-                   return 1;
+                   resolved = 1;
                  }
-             return 0;
+             return resolved;
            }
          else
            {
@@ -540,7 +668,8 @@ rib_match_ipv4 (struct in_addr addr)
   struct route_table *table;
   struct route_node *rn;
   struct rib *match;
-  struct nexthop *newhop;
+  struct nexthop *newhop, *tnewhop;
+  int recursing;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
@@ -559,9 +688,13 @@ rib_match_ipv4 (struct in_addr addr)
       route_unlock_node (rn);
       
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
-       if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
-         break;
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+           break;
+       }
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
@@ -581,7 +714,7 @@ rib_match_ipv4 (struct in_addr addr)
            return match;
          else
            {
-             for (newhop = match->nexthop; newhop; newhop = newhop->next)
+             for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
                if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
                  return match;
              return NULL;
@@ -597,7 +730,8 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p)
   struct route_table *table;
   struct route_node *rn;
   struct rib *match;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
@@ -613,10 +747,13 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p)
   /* Unlock node. */
   route_unlock_node (rn);
 
-  /* Pick up selected route. */
-  for (match = rn->info; match; match = match->next)
-    if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
-      break;
+  RNODE_FOREACH_RIB (rn, match)
+    {
+      if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+       continue;
+      if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+       break;
+    }
 
   if (! match || match->type == ZEBRA_ROUTE_BGP)
     return NULL;
@@ -624,7 +761,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p)
   if (match->type == ZEBRA_ROUTE_CONNECT)
     return match;
   
-  for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next)
+  for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
     if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
       return match;
 
@@ -649,7 +786,9 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate)
   struct route_table *table;
   struct route_node *rn;
   struct rib *match;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  int nexthops_active;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
@@ -667,13 +806,13 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate)
   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)
-  {
-    if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
-      continue;
-    if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
-      break;
-  }
+  RNODE_FOREACH_RIB (rn, match)
+    {
+      if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+       continue;
+      if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+       break;
+    }
 
   /* None such found :( */
   if (!match)
@@ -683,26 +822,25 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate)
     return ZEBRA_RIB_FOUND_CONNECTED;
   
   /* Ok, we have a cood candidate, let's check it's nexthop list... */
-  for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next)
+  nexthops_active = 0;
+  for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
     if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
-    {
-      /* We are happy with either direct or recursive hexthop */
-      if (nexthop->gate.ipv4.s_addr == qgate->sin.sin_addr.s_addr ||
-          nexthop->rgate.ipv4.s_addr == qgate->sin.sin_addr.s_addr)
-        return ZEBRA_RIB_FOUND_EXACT;
-      else
       {
+        nexthops_active = 1;
+        if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate))
+          return ZEBRA_RIB_FOUND_EXACT;
         if (IS_ZEBRA_DEBUG_RIB)
-        {
-          char gate_buf[INET_ADDRSTRLEN], rgate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN];
-          inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN);
-          inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, rgate_buf, INET_ADDRSTRLEN);
-          inet_ntop (AF_INET, &qgate->sin.sin_addr.s_addr, qgate_buf, INET_ADDRSTRLEN);
-          zlog_debug ("%s: qgate == %s, gate == %s, rgate == %s", __func__, qgate_buf, gate_buf, rgate_buf);
-        }
-        return ZEBRA_RIB_FOUND_NOGATE;
+          {
+            char gate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN];
+            inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN);
+            inet_ntop (AF_INET, &sockunion2ip(qgate), qgate_buf, INET_ADDRSTRLEN);
+            zlog_debug ("%s: qgate == %s, %s == %s", __func__,
+                        qgate_buf, recursing ? "rgate" : "gate", gate_buf);
+          }
       }
-    }
+
+  if (nexthops_active)
+    return ZEBRA_RIB_FOUND_NOGATE;
 
   return ZEBRA_RIB_NOTFOUND;
 }
@@ -715,7 +853,8 @@ rib_match_ipv6 (struct in6_addr *addr)
   struct route_table *table;
   struct route_node *rn;
   struct rib *match;
-  struct nexthop *newhop;
+  struct nexthop *newhop, *tnewhop;
+  int recursing;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
@@ -734,9 +873,13 @@ rib_match_ipv6 (struct in6_addr *addr)
       route_unlock_node (rn);
       
       /* Pick up selected route. */
-      for (match = rn->info; match; match = match->next)
-       if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
-         break;
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+           break;
+       }
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
@@ -756,7 +899,7 @@ rib_match_ipv6 (struct in6_addr *addr)
            return match;
          else
            {
-             for (newhop = match->nexthop; newhop; newhop = newhop->next)
+             for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
                if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
                  return match;
              return NULL;
@@ -780,10 +923,11 @@ rib_match_ipv6 (struct in6_addr *addr)
  * The return value is the final value of 'ACTIVE' flag.
  */
 
-static int
+static unsigned
 nexthop_active_check (struct route_node *rn, struct rib *rib,
                      struct nexthop *nexthop, int set)
 {
+  rib_table_info_t *info = rn->table->info;
   struct interface *ifp;
   route_map_result_t ret = RMAP_MATCH;
   extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1];
@@ -795,7 +939,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
     {
     case NEXTHOP_TYPE_IFINDEX:
       ifp = if_lookup_by_index (nexthop->ifindex);
-      if (ifp && if_is_up (ifp))
+      if (ifp && if_is_operative(ifp))
        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
       else
        UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -804,7 +948,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
       family = AFI_IP6;
     case NEXTHOP_TYPE_IFNAME:
       ifp = if_lookup_by_name (nexthop->ifname);
-      if (ifp && if_is_up (ifp))
+      if (ifp && if_is_operative(ifp))
        {
          if (set)
            nexthop->ifindex = ifp->ifindex;
@@ -838,7 +982,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
       if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6))
        {
          ifp = if_lookup_by_index (nexthop->ifindex);
-         if (ifp && if_is_up (ifp))
+         if (ifp && if_is_operative(ifp))
            SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
          else
            UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -861,11 +1005,22 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
   if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
     return 0;
 
+  /* XXX: What exactly do those checks do? Do we support
+   * e.g. IPv4 routes with IPv6 nexthops or vice versa? */
   if (RIB_SYSTEM_ROUTE(rib) ||
       (family == AFI_IP && rn->p.family != AF_INET) ||
       (family == AFI_IP6 && rn->p.family != AF_INET6))
     return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
 
+  /* The original code didn't determine the family correctly
+   * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
+   * from the rib_table_info in those cases.
+   * Possibly it may be better to use only the rib_table_info
+   * in every case.
+   */
+  if (!family)
+    family = info->afi;
+
   rmap = 0;
   if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX &&
                proto_rm[family][rib->type])
@@ -881,38 +1036,51 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
   return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
 }
 
+/* Iterate over all nexthops of the given RIB entry and refresh their
+ * ACTIVE flag. rib->nexthop_active_num is updated accordingly. If any
+ * nexthop is found to toggle the ACTIVE flag, the whole rib structure
+ * is flagged with ZEBRA_FLAG_CHANGED. The 4th 'set' argument is
+ * transparently passed to nexthop_active_check().
+ *
+ * Return value is the new number of active nexthops.
+ */
+
 static int
 nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
 {
   struct nexthop *nexthop;
-  int active;
+  unsigned int prev_active, prev_index, new_active;
 
   rib->nexthop_active_num = 0;
   UNSET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
 
   for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-    {
-      active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-
-      nexthop_active_check (rn, rib, nexthop, set);
-      if ((MULTIPATH_NUM == 0 || rib->nexthop_active_num < MULTIPATH_NUM)
-          && active != CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-        SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
-
-      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-        rib->nexthop_active_num++;
-    }
+  {
+    prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+    prev_index = nexthop->ifindex;
+    if ((new_active = nexthop_active_check (rn, rib, nexthop, set)))
+      rib->nexthop_active_num++;
+    if (prev_active != new_active ||
+       prev_index != nexthop->ifindex)
+      SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
+  }
   return rib->nexthop_active_num;
 }
 
-\f
+
 
 static void
 rib_install_kernel (struct route_node *rn, struct rib *rib)
 {
   int ret = 0;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
 
+  /*
+   * Make sure we update the FPM any time we send new information to
+   * the kernel.
+   */
+  zfpm_trigger_update (rn, "installing in kernel");
   switch (PREFIX_FAMILY (&rn->p))
     {
     case AF_INET:
@@ -928,7 +1096,7 @@ rib_install_kernel (struct route_node *rn, struct rib *rib)
   /* This condition is never met, if we are using rt_socket.c */
   if (ret < 0)
     {
-      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
        UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
     }
 }
@@ -938,7 +1106,14 @@ static int
 rib_uninstall_kernel (struct route_node *rn, struct rib *rib)
 {
   int ret = 0;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+
+  /*
+   * Make sure we update the FPM any time we send new information to
+   * the kernel.
+   */
+  zfpm_trigger_update (rn, "uninstalling from kernel");
 
   switch (PREFIX_FAMILY (&rn->p))
     {
@@ -947,14 +1122,12 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib)
       break;
 #ifdef HAVE_IPV6
     case AF_INET6:
-      if (IS_ZEBRA_DEBUG_RIB)
-        zlog_debug ("%s: calling kernel_delete_ipv4 (%p, %p)", __func__, rn, rib);
       ret = kernel_delete_ipv6 (&rn->p, rib);
       break;
 #endif /* HAVE_IPV6 */
     }
 
-  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
 
   return ret;
@@ -966,6 +1139,8 @@ rib_uninstall (struct route_node *rn, struct rib *rib)
 {
   if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
     {
+      zfpm_trigger_update (rn, "rib_uninstall");
+
       redistribute_delete (&rn->p, rib);
       if (! RIB_SYSTEM_ROUTE (rib))
        rib_uninstall_kernel (rn, rib);
@@ -975,32 +1150,90 @@ rib_uninstall (struct route_node *rn, struct rib *rib)
 
 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;
+    }
+
+  /*
+   * Don't delete the dest if we have to update the FPM about this
+   * prefix.
+   */
+  if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM) ||
+      CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM))
+    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 wq_item_status
-rib_process (struct work_queue *wq, void *data)
+static void
+rib_process (struct route_node *rn)
 {
   struct rib *rib;
   struct rib *next;
   struct rib *fib = NULL;
   struct rib *select = NULL;
   struct rib *del = NULL;
-  struct route_node *rn = data;
   int installed = 0;
-  struct nexthop *nexthop = NULL;
-  char buf[INET_ADDRSTRLEN];
+  struct nexthop *nexthop = NULL, *tnexthop;
+  int recursing;
+  char buf[INET6_ADDRSTRLEN];
   
   assert (rn);
   
   if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q)
-    inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+    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))
         {
@@ -1018,7 +1251,7 @@ rib_process (struct work_queue *wq, void *data)
               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;
@@ -1075,7 +1308,7 @@ rib_process (struct work_queue *wq, void *data)
       /* 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
@@ -1092,6 +1325,8 @@ rib_process (struct work_queue *wq, void *data)
                      __func__, buf, rn->p.prefixlen, select, fib);
       if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED))
         {
+         zfpm_trigger_update (rn, "updating existing route");
+
           redistribute_delete (&rn->p, select);
           if (! RIB_SYSTEM_ROUTE (select))
             rib_uninstall_kernel (rn, select);
@@ -1112,10 +1347,11 @@ rib_process (struct work_queue *wq, void *data)
              This makes sure the routes are IN the kernel.
            */
 
-          for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next)
+          for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing))
+            if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
             {
-              if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
-                installed = 1;
+              installed = 1;
+              break;
             }
           if (! installed) 
             rib_install_kernel (rn, select);
@@ -1132,6 +1368,9 @@ rib_process (struct work_queue *wq, void *data)
       if (IS_ZEBRA_DEBUG_RIB)
         zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__,
           buf, rn->p.prefixlen, fib);
+
+      zfpm_trigger_update (rn, "removing existing route");
+
       redistribute_delete (&rn->p, fib);
       if (! RIB_SYSTEM_ROUTE (fib))
        rib_uninstall_kernel (rn, fib);
@@ -1150,6 +1389,9 @@ rib_process (struct work_queue *wq, void *data)
       if (IS_ZEBRA_DEBUG_RIB)
         zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf,
           rn->p.prefixlen, select);
+
+      zfpm_trigger_update (rn, "new route selected");
+
       /* Set real nexthop. */
       nexthop_active_update (rn, select, 1);
 
@@ -1171,10 +1413,129 @@ rib_process (struct work_queue *wq, void *data)
 end:
   if (IS_ZEBRA_DEBUG_RIB_Q)
     zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn);
-  if (rn->info)
-    UNSET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED);  
-  route_unlock_node (rn); /* rib queue lock */
-  return WQ_SUCCESS;
+
+  /*
+   * 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
+ * picked from it and processed by rib_process(). Don't process more, 
+ * than one RN record; operate only in the specified sub-queue.
+ */
+static unsigned int
+process_subq (struct list * subq, u_char qindex)
+{
+  struct listnode *lnode  = listhead (subq);
+  struct route_node *rnode;
+
+  if (!lnode)
+    return 0;
+
+  rnode = listgetdata (lnode);
+  rib_process (rnode);
+
+  if (rnode->info)
+    UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, RIB_ROUTE_QUEUED (qindex));
+
+#if 0
+  else
+    {
+      zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
+                  __func__, rnode, rnode->lock);
+      zlog_backtrace(LOG_DEBUG);
+    }
+#endif
+  route_unlock_node (rnode);
+  list_delete_node (subq, lnode);
+  return 1;
+}
+
+/*
+ * All meta queues have been processed. Trigger next-hop evaluation.
+ */
+static void
+meta_queue_process_complete (struct work_queue *dummy)
+{
+  zebra_evaluate_rnh_table(0, AF_INET);
+#ifdef HAVE_IPV6
+  zebra_evaluate_rnh_table(0, AF_INET6);
+#endif /* HAVE_IPV6 */
+}
+
+/* Dispatch the meta queue by picking, processing and unlocking the next RN from
+ * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data
+ * is pointed to the meta queue structure.
+ */
+static wq_item_status
+meta_queue_process (struct work_queue *dummy, void *data)
+{
+  struct meta_queue * mq = data;
+  unsigned i;
+
+  for (i = 0; i < MQ_SIZE; i++)
+    if (process_subq (mq->subq[i], i))
+      {
+       mq->size--;
+       break;
+      }
+  return mq->size ? WQ_REQUEUE : WQ_SUCCESS;
+}
+
+/*
+ * 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,
+  [ZEBRA_ROUTE_CONNECT] = 0,
+  [ZEBRA_ROUTE_STATIC]  = 1,
+  [ZEBRA_ROUTE_RIP]     = 2,
+  [ZEBRA_ROUTE_RIPNG]   = 2,
+  [ZEBRA_ROUTE_OSPF]    = 2,
+  [ZEBRA_ROUTE_OSPF6]   = 2,
+  [ZEBRA_ROUTE_ISIS]    = 2,
+  [ZEBRA_ROUTE_BGP]     = 3,
+  [ZEBRA_ROUTE_HSLS]    = 4,
+  [ZEBRA_ROUTE_BABEL]   = 2,
+};
+
+/* Look into the RN and queue it into one or more priority queues,
+ * increasing the size for each data push done.
+ */
+static void
+rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
+{
+  struct rib *rib;
+  char buf[INET6_ADDRSTRLEN];
+
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
+
+  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 (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",
+                       __func__, buf, rn->p.prefixlen, rn, qindex);
+         continue;
+       }
+
+      SET_FLAG (rib_dest_from_rnode (rn)->flags, RIB_ROUTE_QUEUED (qindex));
+      listnode_add (mq->subq[qindex], rn);
+      route_lock_node (rn);
+      mq->size++;
+
+      if (IS_ZEBRA_DEBUG_RIB_Q)
+       zlog_debug ("%s: %s/%d: queued rn %p into sub-queue %u",
+                   __func__, buf, rn->p.prefixlen, rn, qindex);
+    }
 }
 
 /* Add route_node to work queue and schedule processing */
@@ -1188,7 +1549,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
     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);
@@ -1196,17 +1557,6 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
       return;
     }
 
-  /* Route-table node already queued, so nothing to do */
-  if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED))
-    {
-      if (IS_ZEBRA_DEBUG_RIB_Q)
-        zlog_debug ("%s: %s/%d: rn %p already queued", __func__, buf,
-          rn->p.prefixlen, rn);
-      return;
-    }
-
-  route_lock_node (rn); /* rib queue lock */
-
   if (IS_ZEBRA_DEBUG_RIB_Q)
     zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen);
 
@@ -1215,13 +1565,21 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
   if (zebra->ribq == NULL)
     {
       zlog_err ("%s: work_queue does not exist!", __func__);
-      route_unlock_node (rn);
       return;
     }
-  
-  work_queue_add (zebra->ribq, rn);
 
-  SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED);
+  /*
+   * The RIB queue should normally be either empty or holding the only
+   * work_queue_item element. In the latter case this element would
+   * hold a pointer to the meta queue structure, which must be used to
+   * actually queue the route nodes to process. So create the MQ
+   * holder, if necessary, then push the work into it in any case.
+   * This semantics was introduced after 0.99.9 release.
+   */
+  if (!zebra->ribq->items->count)
+    work_queue_add (zebra->ribq, zebra->mq);
+
+  rib_meta_queue_add (zebra->mq, rn);
 
   if (IS_ZEBRA_DEBUG_RIB_Q)
     zlog_debug ("%s: %s/%d: rn %p queued", __func__, buf, rn->p.prefixlen, rn);
@@ -1229,6 +1587,27 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
   return;
 }
 
+/* Create new meta queue.
+   A destructor function doesn't seem to be necessary here.
+ */
+static struct meta_queue *
+meta_queue_new (void)
+{
+  struct meta_queue *new;
+  unsigned i;
+
+  new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct meta_queue));
+  assert(new);
+
+  for (i = 0; i < MQ_SIZE; i++)
+    {
+      new->subq[i] = list_new ();
+      assert(new->subq[i]);
+    }
+
+  return new;
+}
+
 /* initialise zebra rib work queue */
 static void
 rib_queue_init (struct zebra_t *zebra)
@@ -1243,12 +1622,18 @@ rib_queue_init (struct zebra_t *zebra)
     }
 
   /* fill in the work queue spec */
-  zebra->ribq->spec.workfunc = &rib_process;
+  zebra->ribq->spec.workfunc = &meta_queue_process;
   zebra->ribq->spec.errorfunc = NULL;
+  zebra->ribq->spec.completion_func = &meta_queue_process_complete;
   /* XXX: TODO: These should be runtime configurable via vty */
   zebra->ribq->spec.max_retries = 3;
   zebra->ribq->spec.hold = rib_process_hold_time;
   
+  if (!(zebra->mq = meta_queue_new ()))
+  {
+    zlog_err ("%s: could not initialise meta queue!", __func__);
+    return;
+  }
   return;
 }
 
@@ -1274,17 +1659,16 @@ rib_queue_init (struct zebra_t *zebra)
  * |-> 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.
  *
@@ -1295,31 +1679,40 @@ static void
 rib_link (struct route_node *rn, struct rib *rib)
 {
   struct rib *head;
-  char buf[INET_ADDRSTRLEN];
-  
+  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 (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+    inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
     zlog_debug ("%s: %s/%d: rn %p, rib %p", __func__,
       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);
 }
 
@@ -1333,8 +1726,8 @@ rib_addnode (struct route_node *rn, struct rib *rib)
     {
       if (IS_ZEBRA_DEBUG_RIB)
       {
-        char buf[INET_ADDRSTRLEN];
-        inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+        char buf[INET6_ADDRSTRLEN];
+        inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
         zlog_debug ("%s: %s/%d: rn %p, un-removed rib %p",
                     __func__, buf, rn->p.prefixlen, rn, rib);
       }
@@ -1344,21 +1737,32 @@ rib_addnode (struct route_node *rn, struct rib *rib)
   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[INET_ADDRSTRLEN];
+  char buf[INET6_ADDRSTRLEN];
+  rib_dest_t *dest;
 
   assert (rn && rib);
 
   if (IS_ZEBRA_DEBUG_RIB)
   {
-    inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+    inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
     zlog_debug ("%s: %s/%d: rn %p, rib %p",
                 __func__, buf, rn->p.prefixlen, rn, rib);
   }
 
+  dest = rib_dest_from_rnode (rn);
+
   if (rib->next)
     rib->next->prev = rib->prev;
 
@@ -1366,26 +1770,13 @@ rib_unlink (struct route_node *rn, struct rib *rib)
     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 */
-  for (nexthop = rib->nexthop; nexthop; nexthop = next)
-    {
-      next = nexthop->next;
-      nexthop_free (nexthop);
-    }
+  nexthops_free(rib->nexthop);
   XFREE (MTYPE_RIB, rib);
 
-  route_unlock_node (rn); /* rn route table reference */
 }
 
 static void
@@ -1393,8 +1784,8 @@ rib_delnode (struct route_node *rn, struct rib *rib)
 {
   if (IS_ZEBRA_DEBUG_RIB)
   {
-    char buf[INET_ADDRSTRLEN];
-    inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+    char buf[INET6_ADDRSTRLEN];
+    inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
     zlog_debug ("%s: %s/%d: rn %p, rib %p, removing", __func__,
       buf, rn->p.prefixlen, rn, rib);
   }
@@ -1406,7 +1797,7 @@ int
 rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, 
              struct in_addr *gate, struct in_addr *src,
              unsigned int ifindex, u_int32_t vrf_id,
-             u_int32_t metric, u_char distance)
+             u_int32_t metric, u_char distance, safi_t safi)
 {
   struct rib *rib;
   struct rib *same = NULL;
@@ -1415,7 +1806,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
   struct nexthop *nexthop;
 
   /* Lookup table.  */
-  table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+  table = vrf_table (AFI_IP, safi, 0);
   if (! table)
     return 0;
 
@@ -1425,7 +1816,10 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
   /* Set default distance by route type. */
   if (distance == 0)
     {
-      distance = route_info[type].distance;
+      if ((unsigned)type >= array_size(route_info))
+       distance = 150;
+      else
+        distance = route_info[type].distance;
 
       /* iBGP distance is 200. */
       if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
@@ -1437,7 +1831,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
 
   /* 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;
@@ -1508,19 +1902,22 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
  * question are passed as 1st and 2nd arguments.
  */
 
-void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib * rib)
+void _rib_dump (const char * func,
+               union prefix46constptr pp, const struct rib * rib)
 {
-  char straddr1[INET_ADDRSTRLEN], straddr2[INET_ADDRSTRLEN];
-  struct nexthop *nexthop;
+  const struct prefix *p = pp.p;
+  char straddr[INET6_ADDRSTRLEN];
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
 
-  inet_ntop (AF_INET, &p->prefix, straddr1, INET_ADDRSTRLEN);
-  zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr1, p->prefixlen);
+  inet_ntop (p->family, &p->u.prefix, straddr, INET6_ADDRSTRLEN);
+  zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr, p->prefixlen);
   zlog_debug
   (
-    "%s: refcnt == %lu, uptime == %u, type == %u, table == %d",
+    "%s: refcnt == %lu, uptime == %lu, type == %u, table == %d",
     func,
     rib->refcnt,
-    rib->uptime,
+    (unsigned long) rib->uptime,
     rib->type,
     rib->table
   );
@@ -1541,21 +1938,21 @@ void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib
     rib->nexthop_active_num,
     rib->nexthop_fib_num
   );
-  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-  {
-    inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr1, INET_ADDRSTRLEN);
-    inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, straddr2, INET_ADDRSTRLEN);
-    zlog_debug
-    (
-      "%s: NH %s (%s) with flags %s%s%s",
-      func,
-      straddr1,
-      straddr2,
-      (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""),
-      (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""),
-      (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "")
-    );
-  }
+
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      inet_ntop (p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN);
+      zlog_debug
+      (
+        "%s: %s %s with flags %s%s%s",
+        func,
+        (recursing ? "  NH" : "NH"),
+        straddr,
+        (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""),
+        (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""),
+        (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "")
+      );
+    }
   zlog_debug ("%s: dump complete", func);
 }
 
@@ -1593,7 +1990,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p)
   route_unlock_node (rn);
 
   /* let's go */
-  for (rib = rn->info; rib; rib = rib->next)
+  RNODE_FOREACH_RIB (rn, rib)
   {
     zlog_debug
     (
@@ -1604,12 +2001,64 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p)
       (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"),
       (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected")
     );
-    rib_dump (__func__, p, rib);
+    rib_dump (p, rib);
+  }
+}
+
+/* Check if requested address assignment will fail due to another
+ * route being installed by zebra in FIB already. Take necessary
+ * actions, if needed: remove such a route from FIB and deSELECT
+ * corresponding RIB entry. Then put affected RN into RIBQ head.
+ */
+void rib_lookup_and_pushup (struct prefix_ipv4 * p)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  unsigned changed = 0;
+
+  if (NULL == (table = vrf_table (AFI_IP, SAFI_UNICAST, 0)))
+  {
+    zlog_err ("%s: vrf_table() returned NULL", __func__);
+    return;
+  }
+
+  /* No matches would be the simplest case. */
+  if (NULL == (rn = route_node_lookup (table, (struct prefix *) p)))
+    return;
+
+  /* Unlock node. */
+  route_unlock_node (rn);
+
+  /* Check all RIB entries. In case any changes have to be done, requeue
+   * the RN into RIBQ head. If the routing message about the new connected
+   * route (generated by the IP address we are going to assign very soon)
+   * comes before the RIBQ is processed, the new RIB entry will join
+   * RIBQ record already on head. This is necessary for proper revalidation
+   * of the rest of the RIB.
+   */
+  RNODE_FOREACH_RIB (rn, rib)
+  {
+    if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) &&
+      ! RIB_SYSTEM_ROUTE (rib))
+    {
+      changed = 1;
+      if (IS_ZEBRA_DEBUG_RIB)
+      {
+        char buf[INET_ADDRSTRLEN];
+        inet_ntop (rn->p.family, &p->prefix, buf, INET_ADDRSTRLEN);
+        zlog_debug ("%s: freeing way for connected prefix %s/%d", __func__, buf, p->prefixlen);
+        rib_dump (&rn->p, rib);
+      }
+      rib_uninstall (rn, rib);
+    }
   }
+  if (changed)
+    rib_queue_add (&zebrad, rn);
 }
 
 int
-rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
+rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
 {
   struct route_table *table;
   struct route_node *rn;
@@ -1617,9 +2066,10 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
   struct nexthop *nexthop;
   
   /* Lookup table.  */
-  table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+  table = vrf_table (AFI_IP, safi, 0);
   if (! table)
     return 0;
+
   /* Make it sure prefixlen is applied to the prefix. */
   apply_mask_ipv4 (p);
 
@@ -1639,7 +2089,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
 
   /* 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;
@@ -1660,7 +2110,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
   {
     zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
       __func__, rn, rib);
-    rib_dump (__func__, p, rib);
+    rib_dump (p, rib);
   }
 
   /* Free implicit route.*/
@@ -1670,7 +2120,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
     {
       zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
         __func__, rn, same);
-      rib_dump (__func__, p, same);
+      rib_dump (p, same);
     }
     rib_delnode (rn, same);
   }
@@ -1682,31 +2132,40 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
 /* XXX factor with rib_delete_ipv6 */
 int
 rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
-                struct in_addr *gate, unsigned int ifindex, u_int32_t vrf_id)
+                struct in_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi)
 {
   struct route_table *table;
   struct route_node *rn;
   struct rib *rib;
   struct rib *fib = NULL;
   struct rib *same = NULL;
-  struct nexthop *nexthop;
-  char buf1[BUFSIZ];
-  char buf2[BUFSIZ];
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  char buf1[INET_ADDRSTRLEN];
+  char buf2[INET_ADDRSTRLEN];
 
   /* Lookup table.  */
-  table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+  table = vrf_table (AFI_IP, safi, 0);
   if (! table)
     return 0;
 
   /* Apply mask. */
   apply_mask_ipv4 (p);
 
-  if (IS_ZEBRA_DEBUG_KERNEL && gate)
-    zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d",
-                      inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ),
-                      p->prefixlen, 
-                      inet_ntoa (*gate), 
-                      ifindex);
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      if (gate)
+       zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d",
+                   inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
+                   p->prefixlen,
+                   inet_ntoa (*gate),
+                   ifindex);
+      else
+       zlog_debug ("rib_delete_ipv4(): route delete %s/%d ifindex %d",
+                   inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
+                   p->prefixlen,
+                   ifindex);
+    }
 
   /* Lookup route node. */
   rn = route_node_lookup (table, (struct prefix *) p);
@@ -1716,13 +2175,13 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
        {
          if (gate)
            zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib",
-                      inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ),
+                      inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
                       p->prefixlen,
-                      inet_ntop (AF_INET, gate, buf2, BUFSIZ),
+                      inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN),
                       ifindex);
          else
            zlog_debug ("route %s/%d ifindex %d doesn't exist in rib",
-                      inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ),
+                      inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
                       p->prefixlen,
                       ifindex);
        }
@@ -1730,7 +2189,7 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
     }
 
   /* 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;
@@ -1741,8 +2200,10 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
       if (rib->type != type)
        continue;
       if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
-         nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex)
+         nexthop->type == NEXTHOP_TYPE_IFINDEX)
        {
+         if (nexthop->ifindex != ifindex)
+           continue;
          if (rib->refcnt)
            {
              rib->refcnt--;
@@ -1754,16 +2215,23 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
          break;
        }
       /* Make sure that the route found has the same gateway. */
-      else if (gate == NULL ||
-              ((nexthop = rib->nexthop) &&
-               (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate) ||
-                IPV4_ADDR_SAME (&nexthop->rgate.ipv4, gate)))) 
+      else
         {
-         same = rib;
-         break;
-       }
+          if (gate == NULL)
+            {
+              same = rib;
+              break;
+            }
+          for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+            if (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate))
+              {
+                same = rib;
+                break;
+              }
+          if (same)
+            break;
+        }
     }
-
   /* If same type of route can't be found and this message is from
      kernel. */
   if (! same)
@@ -1782,14 +2250,14 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
            {
              if (gate)
                zlog_debug ("route %s/%d via %s ifindex %d type %d doesn't exist in rib",
-                          inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ),
+                          inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
                           p->prefixlen,
-                          inet_ntop (AF_INET, gate, buf2, BUFSIZ),
+                          inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN),
                           ifindex,
                           type);
              else
                zlog_debug ("route %s/%d ifindex %d type %d doesn't exist in rib",
-                          inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ),
+                          inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
                           p->prefixlen,
                           ifindex,
                           type);
@@ -1805,7 +2273,7 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
   route_unlock_node (rn);
   return 0;
 }
-\f
+
 /* Install static route into rib. */
 static void
 static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
@@ -1821,7 +2289,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
 
   /* 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;
@@ -1857,6 +2325,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
       rib->type = ZEBRA_ROUTE_STATIC;
       rib->distance = si->distance;
       rib->metric = 0;
+      rib->table = zebrad.rtm_table_default;
       rib->nexthop_num = 0;
 
       switch (si->type)
@@ -1916,7 +2385,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
   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;
@@ -2009,8 +2478,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
     static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id);
 
   /* Make new static route structure. */
-  si = XMALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4));
-  memset (si, 0, sizeof (struct static_ipv4));
+  si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4));
 
   si->type = type;
   si->distance = distance;
@@ -2118,9 +2586,9 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
   return 1;
 }
 
-\f
+
 #ifdef HAVE_IPV6
-static int
+int
 rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
                struct in6_addr *gate, unsigned int ifindex, int table)
 {
@@ -2144,7 +2612,7 @@ rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
 int
 rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
              struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id,
-             u_int32_t metric, u_char distance)
+             u_int32_t metric, u_char distance, safi_t safi)
 {
   struct rib *rib;
   struct rib *same = NULL;
@@ -2153,7 +2621,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
   struct nexthop *nexthop;
 
   /* Lookup table.  */
-  table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
+  table = vrf_table (AFI_IP6, safi, 0);
   if (! table)
     return 0;
 
@@ -2176,7 +2644,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
 
   /* 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;
@@ -2226,34 +2694,139 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
 
   /* Link new rib to node.*/
   rib_addnode (rn, rib);
+  if (IS_ZEBRA_DEBUG_RIB)
+  {
+    zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
+      __func__, rn, rib);
+    rib_dump (p, rib);
+  }
 
   /* Free implicit route.*/
   if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
+        __func__, rn, same);
+      rib_dump (p, same);
+    }
     rib_delnode (rn, same);
+  }
   
   route_unlock_node (rn);
   return 0;
 }
 
+int
+rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi,
+                       unsigned long ifindex)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *same = NULL;
+  struct nexthop *nexthop;
+  int ret = 0;
+  int table_id = 0;
+
+  if (rib)
+    table_id = rib->table;
+  else
+    return 0;                  /* why are we getting called with NULL rib */
+
+  /* Lookup table.  */
+  table = vrf_table (AFI_IP6, safi, 0);
+
+  if (! table)
+    return 0;
+
+  /* Make sure mask is applied. */
+  apply_mask_ipv6 (p);
+
+  /* Set default distance by route type. */
+  if (rib->distance == 0)
+    {
+      rib->distance = route_info[rib->type].distance;
+
+      /* iBGP distance is 200. */
+      if (rib->type == ZEBRA_ROUTE_BGP
+         && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
+       rib->distance = 200;
+    }
+
+  /* Lookup route node.*/
+  rn = route_node_get (table, (struct prefix *) p);
+
+  /* If same type of route are installed, treat it as a implicit
+     withdraw. */
+  RNODE_FOREACH_RIB (rn, same) {
+     if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) {
+       continue;
+     }
+     if (same->type != rib->type) {
+       continue;
+     }
+
+     if (same->table != rib->table) {
+       continue;
+     }
+     if (same->type != ZEBRA_ROUTE_CONNECT) {
+       break;
+     }
+     else if ((nexthop = same->nexthop) &&
+              nexthop->type == NEXTHOP_TYPE_IFINDEX &&
+              nexthop->ifindex == ifindex) {
+           same->refcnt++;
+           return 0;
+     }
+  }
+
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT) {
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) {
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+    }
+  }
+
+  /* Link new rib to node.*/
+  rib_addnode (rn, rib);
+  ret = 1;
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
+        __func__, rn, same);
+      rib_dump ((struct prefix *)p, same);
+    }
+    rib_delnode (rn, same);
+    ret = -1;
+  }
+
+  route_unlock_node (rn);
+  return ret;
+}
+
 /* XXX factor with rib_delete_ipv6 */
 int
 rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
-                struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id)
+                struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi)
 {
   struct route_table *table;
   struct route_node *rn;
   struct rib *rib;
   struct rib *fib = NULL;
   struct rib *same = NULL;
-  struct nexthop *nexthop;
-  char buf1[BUFSIZ];
-  char buf2[BUFSIZ];
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  char buf1[INET6_ADDRSTRLEN];
+  char buf2[INET6_ADDRSTRLEN];
 
   /* Apply mask. */
   apply_mask_ipv6 (p);
 
   /* Lookup table.  */
-  table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
+  table = vrf_table (AFI_IP6, safi, 0);
   if (! table)
     return 0;
   
@@ -2265,13 +2838,13 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
        {
          if (gate)
            zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib",
-                      inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ),
+                      inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
                       p->prefixlen,
-                      inet_ntop (AF_INET6, gate, buf2, BUFSIZ),
+                      inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN),
                       ifindex);
          else
            zlog_debug ("route %s/%d ifindex %d doesn't exist in rib",
-                      inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ),
+                      inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
                       p->prefixlen,
                       ifindex);
        }
@@ -2279,7 +2852,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
     }
 
   /* 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;
@@ -2290,8 +2863,10 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
       if (rib->type != type)
         continue;
       if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
-         nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex)
+         nexthop->type == NEXTHOP_TYPE_IFINDEX)
        {
+         if (nexthop->ifindex != ifindex)
+           continue;
          if (rib->refcnt)
            {
              rib->refcnt--;
@@ -2303,14 +2878,22 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
          break;
        }
       /* Make sure that the route found has the same gateway. */
-      else if (gate == NULL ||
-              ((nexthop = rib->nexthop) &&
-               (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate) ||
-                IPV6_ADDR_SAME (&nexthop->rgate.ipv6, gate))))
-       {
-         same = rib;
-         break;
-       }
+      else
+        {
+          if (gate == NULL)
+            {
+              same = rib;
+              break;
+            }
+          for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+            if (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate))
+              {
+                same = rib;
+                break;
+              }
+          if (same)
+            break;
+        }
     }
 
   /* If same type of route can't be found and this message is from
@@ -2331,14 +2914,14 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
            {
              if (gate)
                zlog_debug ("route %s/%d via %s ifindex %d type %d doesn't exist in rib",
-                          inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ),
+                          inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
                           p->prefixlen,
-                          inet_ntop (AF_INET6, gate, buf2, BUFSIZ),
+                          inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN),
                           ifindex,
                           type);
              else
                zlog_debug ("route %s/%d ifindex %d type %d doesn't exist in rib",
-                          inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ),
+                          inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
                           p->prefixlen,
                           ifindex,
                           type);
@@ -2354,7 +2937,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
   route_unlock_node (rn);
   return 0;
 }
-\f
+
 /* Install static route into rib. */
 static void
 static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
@@ -2370,7 +2953,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
 
   /* 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;
@@ -2407,6 +2990,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
       rib->type = ZEBRA_ROUTE_STATIC;
       rib->distance = si->distance;
       rib->metric = 0;
+      rib->table = zebrad.rtm_table_default;
       rib->nexthop_num = 0;
 
       switch (si->type)
@@ -2467,7 +3051,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
   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;
@@ -2553,8 +3137,7 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
     }
 
   /* Make new static route structure. */
-  si = XMALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6));
-  memset (si, 0, sizeof (struct static_ipv6));
+  si = XCALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6));
 
   si->type = type;
   si->distance = distance;
@@ -2653,7 +3236,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
   return 1;
 }
 #endif /* HAVE_IPV6 */
-\f
+
 /* RIB update function. */
 void
 rib_update (void)
@@ -2664,30 +3247,17 @@ rib_update (void)
   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);
 }
 
-/* Interface goes up. */
-static void
-rib_if_up (struct interface *ifp)
-{
-  rib_update ();
-}
 
-/* Interface goes down. */
-static void
-rib_if_down (struct interface *ifp)
-{
-  rib_update ();
-}
-\f
 /* Remove all routes which comes from non main table.  */
 static void
 rib_weed_table (struct route_table *table)
@@ -2698,10 +3268,8 @@ rib_weed_table (struct route_table *table)
 
   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;
 
@@ -2718,7 +3286,7 @@ rib_weed_tables (void)
   rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
   rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
 }
-\f
+
 /* Delete self installed routes after zebra is relaunched.  */
 static void
 rib_sweep_table (struct route_table *table)
@@ -2730,10 +3298,8 @@ rib_sweep_table (struct route_table *table)
 
   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;
 
@@ -2754,7 +3320,40 @@ rib_sweep_route (void)
   rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
   rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
 }
-\f
+
+/* Remove specific by protocol routes from 'table'. */
+static unsigned long
+rib_score_proto_table (u_char proto, struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *next;
+  unsigned long n = 0;
+
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
+        {
+          if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+            continue;
+          if (rib->type == proto)
+            {
+              rib_delnode (rn, rib);
+              n++;
+            }
+        }
+
+  return n;
+}
+
+/* Remove specific by protocol routes. */
+unsigned long
+rib_score_proto (u_char proto)
+{
+  return  rib_score_proto_table (proto, vrf_table (AFI_IP,  SAFI_UNICAST, 0))
+         +rib_score_proto_table (proto, vrf_table (AFI_IP6, SAFI_UNICAST, 0));
+}
+
 /* Close RIB and clean up kernel routes. */
 static void
 rib_close_table (struct route_table *table)
@@ -2764,11 +3363,15 @@ rib_close_table (struct route_table *table)
 
   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;
+
+         zfpm_trigger_update (rn, NULL);
+
+         if (! RIB_SYSTEM_ROUTE (rib))
+           rib_uninstall_kernel (rn, rib);
         }
 }
 
@@ -2779,7 +3382,7 @@ rib_close (void)
   rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
   rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
 }
-\f
+
 /* Routing information base initialize. */
 void
 rib_init (void)
@@ -2788,3 +3391,106 @@ rib_init (void)
   /* VRF initialization.  */
   vrf_init ();
 }
+
+/*
+ * vrf_id_get_next
+ *
+ * Get the first vrf id that is greater than the given vrf id if any.
+ *
+ * Returns TRUE if a vrf id was found, FALSE otherwise.
+ */
+static inline int
+vrf_id_get_next (uint32_t id, uint32_t *next_id_p)
+{
+  while (++id < vector_active (vrf_vector))
+    {
+      if (vrf_lookup (id))
+       {
+         *next_id_p = id;
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+/*
+ * rib_tables_iter_next
+ *
+ * Returns the next table in the iteration.
+ */
+struct route_table *
+rib_tables_iter_next (rib_tables_iter_t *iter)
+{
+  struct route_table *table;
+
+  /*
+   * Array that helps us go over all AFI/SAFI combinations via one
+   * index.
+   */
+  static struct {
+    afi_t afi;
+    safi_t safi;
+  } afi_safis[] = {
+    { AFI_IP, SAFI_UNICAST },
+    { AFI_IP, SAFI_MULTICAST },
+    { AFI_IP6, SAFI_UNICAST },
+    { AFI_IP6, SAFI_MULTICAST },
+  };
+
+  table = NULL;
+
+  switch (iter->state)
+    {
+
+    case RIB_TABLES_ITER_S_INIT:
+      iter->vrf_id = 0;
+      iter->afi_safi_ix = -1;
+
+      /* Fall through */
+
+    case RIB_TABLES_ITER_S_ITERATING:
+      iter->afi_safi_ix++;
+      while (1)
+       {
+
+         while (iter->afi_safi_ix < (int) ZEBRA_NUM_OF (afi_safis))
+           {
+             table = vrf_table (afi_safis[iter->afi_safi_ix].afi,
+                                afi_safis[iter->afi_safi_ix].safi,
+                                iter->vrf_id);
+             if (table)
+               break;
+
+             iter->afi_safi_ix++;
+           }
+
+         /*
+          * Found another table in this vrf.
+          */
+         if (table)
+           break;
+
+         /*
+          * Done with all tables in the current vrf, go to the next
+          * one.
+          */
+         if (!vrf_id_get_next (iter->vrf_id, &iter->vrf_id))
+           break;
+
+         iter->afi_safi_ix = 0;
+       }
+
+      break;
+
+    case RIB_TABLES_ITER_S_DONE:
+      return NULL;
+    }
+
+  if (table)
+    iter->state = RIB_TABLES_ITER_S_ITERATING;
+  else
+    iter->state = RIB_TABLES_ITER_S_DONE;
+
+  return table;
+}