]> 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 9851cf443c648a368137e4e77cd435d05630b110..5cd81e3de45dc3f3f3ddb823e2a5ce3368bc93e5 100644 (file)
 #include "linklist.h"
 #include "thread.h"
 #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;
 
+/* Hold time for RIB process, should be very minimal.
+ * it is useful to able to set it otherwise for testing, hence exported
+ * as global here for test-rig code.
+ */
+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 */
 };
 
-struct zebra_queue_node_t
-{
-  struct route_node *node;
-  struct rib *del;
-};
-\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 *
@@ -83,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.  */
@@ -107,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)
@@ -147,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];
 }
 
@@ -160,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++;
 }
 
@@ -194,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;
 
@@ -222,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);
 
@@ -233,30 +270,32 @@ nexthop_ifname_add (struct rib *rib, char *ifname)
 }
 
 struct nexthop *
-nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4)
+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)
+    nexthop->src.ipv4 = *src;
 
   nexthop_add (rib, nexthop);
 
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, 
-                         unsigned int ifindex)
+                          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)
+    nexthop->src.ipv4 = *src;
   nexthop->ifindex = ifindex;
 
   nexthop_add (rib, nexthop);
@@ -270,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;
 
@@ -280,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);
@@ -297,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;
@@ -320,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);
 
@@ -330,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
@@ -340,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));
@@ -364,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. */
@@ -386,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. */
@@ -397,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))
@@ -404,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
            {
@@ -437,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));
@@ -461,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. */
@@ -483,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. */
@@ -495,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))
@@ -502,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
            {
@@ -534,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);
@@ -553,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. */
@@ -575,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;
@@ -591,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);
@@ -607,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;
@@ -618,13 +761,90 @@ 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;
 
   return NULL;
 }
 
+/*
+ * This clone function, unlike its original rib_lookup_ipv4(), checks
+ * if specified IPv4 route record (prefix/mask -> gate) exists in
+ * the whole RIB and has ZEBRA_FLAG_SELECTED set.
+ *
+ * Return values:
+ * -1: error
+ * 0: exact match found
+ * 1: a match was found with a different gate
+ * 2: connected route found
+ * 3: no matches found
+ */
+int
+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, *tnexthop;
+  int recursing;
+  int nexthops_active;
+
+  /* Lookup table.  */
+  table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+  if (! table)
+    return ZEBRA_RIB_LOOKUP_ERROR;
+
+  /* Scan the RIB table for exactly matching RIB entry. */
+  rn = route_node_lookup (table, (struct prefix *) p);
+
+  /* No route for this prefix. */
+  if (! rn)
+    return ZEBRA_RIB_NOTFOUND;
+
+  /* Unlock node. */
+  route_unlock_node (rn);
+
+  /* Find out if a "selected" RR for the discovered RIB entry exists ever. */
+  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)
+    return ZEBRA_RIB_NOTFOUND;
+
+  if (match->type == ZEBRA_ROUTE_CONNECT)
+    return ZEBRA_RIB_FOUND_CONNECTED;
+  
+  /* Ok, we have a cood candidate, let's check it's nexthop list... */
+  nexthops_active = 0;
+  for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
+    if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+      {
+        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], 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;
+}
+
 #ifdef HAVE_IPV6
 struct rib *
 rib_match_ipv6 (struct in6_addr *addr)
@@ -633,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);
@@ -652,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. */
@@ -674,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;
@@ -685,25 +910,45 @@ rib_match_ipv6 (struct in6_addr *addr)
 }
 #endif /* HAVE_IPV6 */
 
-static int
+#define RIB_SYSTEM_ROUTE(R) \
+        ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
+
+/* This function verifies reachability of one given nexthop, which can be
+ * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
+ * in nexthop->flags field. If the 4th parameter, 'set', is non-zero,
+ * nexthop->ifindex will be updated appropriately as well.
+ * An existing route map can turn (otherwise active) nexthop into inactive, but
+ * not vice versa.
+ *
+ * The return value is the final value of 'ACTIVE' flag.
+ */
+
+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];
+  struct route_map *rmap;
+  int family;
 
+  family = 0;
   switch (nexthop->type)
     {
     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);
       break;
-    case NEXTHOP_TYPE_IFNAME:
     case NEXTHOP_TYPE_IPV6_IFNAME:
+      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;
@@ -718,6 +963,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
       break;
     case NEXTHOP_TYPE_IPV4:
     case NEXTHOP_TYPE_IPV4_IFINDEX:
+      family = AFI_IP;
       if (nexthop_active_ipv4 (rib, nexthop, set, rn))
        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
       else
@@ -725,16 +971,18 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
       break;
 #ifdef HAVE_IPV6
     case NEXTHOP_TYPE_IPV6:
+      family = AFI_IP6;
       if (nexthop_active_ipv6 (rib, nexthop, set, rn))
        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
       else
        UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
       break;
     case NEXTHOP_TYPE_IPV6_IFINDEX:
+      family = AFI_IP6;
       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);
@@ -754,74 +1002,85 @@ nexthop_active_check (struct route_node *rn, struct rib *rib,
     default:
       break;
     }
+  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])
+    rmap = route_map_lookup_by_name (proto_rm[family][rib->type]);
+  if (!rmap && proto_rm[family][ZEBRA_ROUTE_MAX])
+    rmap = route_map_lookup_by_name (proto_rm[family][ZEBRA_ROUTE_MAX]);
+  if (rmap) {
+      ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, nexthop);
+  }
+
+  if (ret == RMAP_DENYMATCH)
+    UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
   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
-#define RIB_SYSTEM_ROUTE(R) \
-        ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
-
-static struct rib *
-rib_lock (struct rib *rib)
-{
-  assert (rib->lock >= 0);
-  
-  rib->lock++;
-  return rib;
-}
-
-static struct rib *
-rib_unlock (struct rib *rib)
-{
-  struct nexthop *nexthop;
-  struct nexthop *next;
-  
-  assert (rib->lock > 0);
-  rib->lock--;
 
-  if (rib->lock == 0)
-    {
-      for (nexthop = rib->nexthop; nexthop; nexthop = next)
-        {
-          next = nexthop->next;
-          nexthop_free (nexthop);
-        }
-      XFREE (MTYPE_RIB, rib);
-      return NULL;
-    }
-  return rib;
-}
 
 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:
@@ -834,9 +1093,10 @@ rib_install_kernel (struct route_node *rn, struct rib *rib)
 #endif /* HAVE_IPV6 */
     }
 
+  /* 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);
     }
 }
@@ -846,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))
     {
@@ -860,7 +1127,7 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib)
 #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;
@@ -872,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);
@@ -879,37 +1148,116 @@ 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 zebra_queue_node_t *qnode = data;
   struct rib *rib;
   struct rib *next;
   struct rib *fib = NULL;
   struct rib *select = NULL;
-  struct rib *del = qnode->del;
-  struct route_node *rn = qnode->node;
+  struct rib *del = NULL;
   int installed = 0;
-  struct nexthop *nexthop = NULL;
+  struct nexthop *nexthop = NULL, *tnexthop;
+  int recursing;
+  char buf[INET6_ADDRSTRLEN];
   
   assert (rn);
   
-  /* possibly should lock and unlock rib on each iteration. however, for
-   * now, we assume called functions are synchronous and dont delete RIBs
-   * (as the work-queue deconstructor for this function is supposed to be
-   * the canonical 'delete' path for RIBs). Further if called functions
-   * below were to made asynchronous they should themselves acquire any
-   * locks/refcounts as needed and not depend on this caller to do it for
-   * them
-   */
-  for (rib = rn->info; rib; rib = next)
+  if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q)
+    inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
+
+  RNODE_FOREACH_RIB_SAFE (rn, rib, next)
     {
-      next = rib->next;
-      
       /* Currently installed rib. */
       if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
-        fib = rib;
+        {
+          assert (fib == NULL);
+          fib = rib;
+        }
+      
+      /* Unlock removed routes, so they'll be freed, bar the FIB entry,
+       * which we need to do do further work with below.
+       */
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        {
+          if (rib != fib)
+            {
+              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);
+            }
+          else
+            del = rib;
+          
+          continue;
+        }
       
       /* Skip unreachable nexthop. */
       if (! nexthop_active_update (rn, rib, 0))
@@ -960,23 +1308,25 @@ rib_process (struct work_queue *wq, void *data)
       /* metric tie-breaks equal distance */
       if (rib->metric <= select->metric)
         select = rib;
-    }
-  
-  /* Deleted route check. */
-  if (del && CHECK_FLAG (del->flags, ZEBRA_FLAG_SELECTED))
-    fib = del;
-  
-  /* We possibly should lock fib and select here However, all functions
-   * below are 'inline' and not asynchronous And if any were to be
-   * converted, they should manage references themselves really..  See
-   * previous comment above.
+    } /* 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
+   * fib    --- the SELECTED RIB entry, if any, otherwise NULL
+   * del    --- equal to fib, if fib is queued for deletion, NULL otherwise
+   * rib    --- NULL
    */
-  
-  /* Same route is selected. */
+
+  /* Same RIB entry is selected. Update FIB and finish. */
   if (select && select == fib)
     {
+      if (IS_ZEBRA_DEBUG_RIB)
+        zlog_debug ("%s: %s/%d: Updating existing route, select %p, fib %p",
+                     __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);
@@ -997,20 +1347,30 @@ 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);
         }
-      return WQ_SUCCESS;
+      goto end;
     }
 
-  /* Uninstall old rib from forwarding table. */
+  /* At this point we either haven't found the best RIB entry or it is
+   * different from what we currently intend to flag with SELECTED. In both
+   * cases, if a RIB block is present in FIB, it should be withdrawn.
+   */
   if (fib)
     {
+      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);
@@ -1020,9 +1380,18 @@ rib_process (struct work_queue *wq, void *data)
       nexthop_active_update (rn, fib, 1);
     }
 
-  /* Install new rib into forwarding table. */
+  /* Regardless of some RIB entry being SELECTED or not before, now we can
+   * tell, that if a new winner exists, FIB is still not updated with this
+   * data, but ready to be.
+   */
   if (select)
     {
+      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);
 
@@ -1032,73 +1401,211 @@ rib_process (struct work_queue *wq, void *data)
       redistribute_add (&rn->p, select);
     }
 
-  return WQ_SUCCESS;
+  /* FIB route was removed, should be deleted */
+  if (del)
+    {
+      if (IS_ZEBRA_DEBUG_RIB)
+        zlog_debug ("%s: %s/%d: Deleting fib %p, rn %p", __func__, buf,
+          rn->p.prefixlen, del, rn);
+      rib_unlink (rn, del);
+    }
+
+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);
 }
 
-/* Add work queue item to work queue and schedule processing */
-static void
-rib_queue_add_qnode (struct zebra_t *zebra, struct zebra_queue_node_t *qnode)
+/* 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)
 {
-  route_lock_node (qnode->node);
-  
-  if (IS_ZEBRA_DEBUG_EVENT)
-    zlog_info ("rib_queue_add_qnode: work queue added");
+  struct listnode *lnode  = listhead (subq);
+  struct route_node *rnode;
 
-  assert (zebra && qnode && qnode->node);
+  if (!lnode)
+    return 0;
 
-  if (qnode->del)
-    rib_lock (qnode->del);
-  
-  if (zebra->ribq == NULL)
+  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_err ("rib_queue_add_qnode: ribq work_queue does not exist!");
-      route_unlock_node (qnode->node);
-      return;
+      zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
+                  __func__, rnode, rnode->lock);
+      zlog_backtrace(LOG_DEBUG);
     }
-  
-  work_queue_add (zebra->ribq, qnode);
+#endif
+  route_unlock_node (rnode);
+  list_delete_node (subq, lnode);
+  return 1;
+}
 
-  return;
+/*
+ * 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;
 }
 
-/* Add route node and rib to work queue and schedule processing */
+/*
+ * 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_queue_add (struct zebra_t *zebra, struct route_node *rn, struct rib *del)
+rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
 {
- struct zebra_queue_node_t *qnode;
+  struct rib *rib;
+  char buf[INET6_ADDRSTRLEN];
 
- assert (zebra && rn);
- qnode = (struct zebra_queue_node_t *) 
-          XCALLOC (MTYPE_RIB_QUEUE, sizeof (struct zebra_queue_node_t));
- if (qnode == NULL)
-   {
-     zlog_err ("rib_queue_add: failed to allocate queue node memory, %s",
-               strerror (errno));
-     return;
-   }
-
- qnode->node = rn;
qnode->del = del;
- rib_queue_add_qnode (zebra, qnode);
+  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;
+       }
 
- return;
+      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);
+    }
 }
 
-/* free zebra_queue_node_t */
+/* Add route_node to work queue and schedule processing */
 static void
-rib_queue_qnode_del (struct work_queue *wq, void *data)
+rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
 {
-  struct zebra_queue_node_t *qnode = data;
-  route_unlock_node (qnode->node);
+  char buf[INET_ADDRSTRLEN];
+  assert (zebra && rn);
   
-  if (qnode->del)
-    rib_unlock (qnode->del);
-  
-  XFREE (MTYPE_RIB_QUEUE, qnode);
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    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 (!rnode_to_ribs (rn))
+    {
+      zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
+                  __func__, rn, rn->lock);
+      zlog_backtrace(LOG_DEBUG);
+      return;
+    }
+
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen);
+
+  assert (zebra);
+
+  if (zebra->ribq == NULL)
+    {
+      zlog_err ("%s: work_queue does not exist!", __func__);
+      return;
+    }
+
+  /*
+   * 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);
+
+  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 */
@@ -1108,60 +1615,189 @@ rib_queue_init (struct zebra_t *zebra)
   assert (zebra);
   
   if (! (zebra->ribq = work_queue_new (zebra->master, 
-                                       "zebra_rib_work_queue")))
+                                       "route_node processing")))
     {
-      zlog_err ("rib_queue_init: could not initialise work queue!");
+      zlog_err ("%s: could not initialise work queue!", __func__);
       return;
     }
 
   /* 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.del_item_data = &rib_queue_qnode_del;
+  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;
 }
 
+/* RIB updates are processed via a queue of pointers to route_nodes.
+ *
+ * The queue length is bounded by the maximal size of the routing table,
+ * as a route_node will not be requeued, if already queued.
+ *
+ * RIBs are submitted via rib_addnode or rib_delnode which set minimal
+ * state, or static_install_ipv{4,6} (when an existing RIB is updated)
+ * and then submit route_node to queue for best-path selection later.
+ * Order of add/delete state changes are preserved for any given RIB.
+ *
+ * Deleted RIBs are reaped during best-path selection.
+ *
+ * rib_addnode
+ * |-> rib_link or unset RIB_ENTRY_REMOVE        |->Update kernel with
+ *       |-------->|                             |  best RIB, if required
+ *                 |                             |
+ * static_install->|->rib_addqueue...... -> rib_process
+ *                 |                             |
+ *       |-------->|                             |-> rib_unlink
+ * |-> set RIB_ENTRY_REMOVE                           |
+ * rib_delnode                                  (RIB freed)
+ *
+ * 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:
+ *   - dest attached to route_node:
+ *     - managed by: rib_link/rib_gc_dest
+ *   - route_node processing queue
+ *     - managed by: rib_addqueue, rib_process.
+ *
+ */
 /* Add RIB to head of the route node. */
 static void
-rib_addnode (struct route_node *rn, struct rib *rib)
+rib_link (struct route_node *rn, struct rib *rib)
 {
   struct rib *head;
-  
+  rib_dest_t *dest;
+  char buf[INET6_ADDRSTRLEN];
+
   assert (rib && rn);
   
-  rib_lock (rib);
-  route_lock_node (rn);
-  
-  head = rn->info;
+  if (IS_ZEBRA_DEBUG_RIB)
+  {
+    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 (!dest)
+    {
+      if (IS_ZEBRA_DEBUG_RIB)
+       {
+         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;
+    {
+      head->prev = rib;
+    }
   rib->next = head;
-  rn->info = rib;
+  dest->routes = rib;
+  rib_queue_add (&zebrad, rn);
 }
 
 static void
-rib_delnode (struct route_node *rn, struct rib *rib)
+rib_addnode (struct route_node *rn, struct rib *rib)
+{
+  /* RIB node has been un-removed before route-node is processed. 
+   * route_node must hence already be on the queue for processing.. 
+   */
+  if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+    {
+      if (IS_ZEBRA_DEBUG_RIB)
+      {
+        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);
+      }
+      UNSET_FLAG (rib->status, RIB_ENTRY_REMOVED);
+      return;
+    }
+  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)
 {
+  char buf[INET6_ADDRSTRLEN];
+  rib_dest_t *dest;
+
   assert (rn && rib);
-  
+
+  if (IS_ZEBRA_DEBUG_RIB)
+  {
+    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;
+
   if (rib->prev)
     rib->prev->next = rib->next;
   else
-    rn->info = rib->next;
-  
-  rib_unlock (rib);
-  route_unlock_node (rn);
+    {
+      dest->routes = rib->next;
+    }
+
+  /* free RIB and nexthops */
+  nexthops_free(rib->nexthop);
+  XFREE (MTYPE_RIB, rib);
+
+}
+
+static void
+rib_delnode (struct route_node *rn, struct rib *rib)
+{
+  if (IS_ZEBRA_DEBUG_RIB)
+  {
+    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);
+  }
+  SET_FLAG (rib->status, RIB_ENTRY_REMOVED);
+  rib_queue_add (&zebrad, rn);
 }
 
 int
 rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, 
-             struct in_addr *gate, unsigned int ifindex, u_int32_t vrf_id,
-             u_int32_t metric, u_char distance)
+             struct in_addr *gate, struct in_addr *src,
+             unsigned int ifindex, u_int32_t vrf_id,
+             u_int32_t metric, u_char distance, safi_t safi)
 {
   struct rib *rib;
   struct rib *same = NULL;
@@ -1170,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;
 
@@ -1180,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))
@@ -1192,8 +1831,11 @@ 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;
+      
       if (rib->type != type)
        continue;
       if (rib->type != ZEBRA_ROUTE_CONNECT)
@@ -1204,7 +1846,8 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
       /* Duplicate connected route comes in. */
       else if ((nexthop = rib->nexthop) &&
               nexthop->type == NEXTHOP_TYPE_IFINDEX &&
-              nexthop->ifindex == ifindex)
+              nexthop->ifindex == ifindex &&
+              !CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
        {
          rib->refcnt++;
          return 0 ;
@@ -1221,38 +1864,201 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
   rib->nexthop_num = 0;
   rib->uptime = time (NULL);
 
-  /* Nexthop settings. */
-  if (gate)
-    {
-      if (ifindex)
-       nexthop_ipv4_ifindex_add (rib, gate, ifindex);
-      else
-       nexthop_ipv4_add (rib, gate);
-    }
-  else
-    nexthop_ifindex_add (rib, ifindex);
+  /* Nexthop settings. */
+  if (gate)
+    {
+      if (ifindex)
+       nexthop_ipv4_ifindex_add (rib, gate, src, ifindex);
+      else
+       nexthop_ipv4_add (rib, gate, src);
+    }
+  else
+    nexthop_ifindex_add (rib, ifindex);
+
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+
+  /* Link new rib to node.*/
+  if (IS_ZEBRA_DEBUG_RIB)
+    zlog_debug ("%s: calling rib_addnode (%p, %p)", __func__, rn, rib);
+  rib_addnode (rn, rib);
+  
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+      zlog_debug ("%s: calling rib_delnode (%p, %p)", __func__, rn, rib);
+    rib_delnode (rn, same);
+  }
+  
+  route_unlock_node (rn);
+  return 0;
+}
+
+/* This function dumps the contents of a given RIB entry into
+ * standard debug log. Calling function name and IP prefix in
+ * question are passed as 1st and 2nd arguments.
+ */
+
+void _rib_dump (const char * func,
+               union prefix46constptr pp, const struct rib * rib)
+{
+  const struct prefix *p = pp.p;
+  char straddr[INET6_ADDRSTRLEN];
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+
+  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 == %lu, type == %u, table == %d",
+    func,
+    rib->refcnt,
+    (unsigned long) rib->uptime,
+    rib->type,
+    rib->table
+  );
+  zlog_debug
+  (
+    "%s: metric == %u, distance == %u, flags == %u, status == %u",
+    func,
+    rib->metric,
+    rib->distance,
+    rib->flags,
+    rib->status
+  );
+  zlog_debug
+  (
+    "%s: nexthop_num == %u, nexthop_active_num == %u, nexthop_fib_num == %u",
+    func,
+    rib->nexthop_num,
+    rib->nexthop_active_num,
+    rib->nexthop_fib_num
+  );
+
+  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);
+}
+
+/* This is an exported helper to rtm_read() to dump the strange
+ * RIB entry found by rib_lookup_ipv4_route()
+ */
+
+void rib_lookup_and_dump (struct prefix_ipv4 * p)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  char prefix_buf[INET_ADDRSTRLEN];
+
+  /* Lookup table.  */
+  table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+  if (! table)
+  {
+    zlog_err ("%s: vrf_table() returned NULL", __func__);
+    return;
+  }
+
+  inet_ntop (AF_INET, &p->prefix.s_addr, prefix_buf, INET_ADDRSTRLEN);
+  /* Scan the RIB table for exactly matching RIB entry. */
+  rn = route_node_lookup (table, (struct prefix *) p);
+
+  /* No route for this prefix. */
+  if (! rn)
+  {
+    zlog_debug ("%s: lookup failed for %s/%d", __func__, prefix_buf, p->prefixlen);
+    return;
+  }
+
+  /* Unlock node. */
+  route_unlock_node (rn);
+
+  /* let's go */
+  RNODE_FOREACH_RIB (rn, rib)
+  {
+    zlog_debug
+    (
+      "%s: rn %p, rib %p: %s, %s",
+      __func__,
+      rn,
+      rib,
+      (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"),
+      (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected")
+    );
+    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 this route is kernel route, set FIB flag to the route. */
-  if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
-    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+  if (NULL == (table = vrf_table (AFI_IP, SAFI_UNICAST, 0)))
+  {
+    zlog_err ("%s: vrf_table() returned NULL", __func__);
+    return;
+  }
 
-  /* Link new rib to node.*/
-  rib_addnode (rn, rib);
-  
-  /* Process this route node. */
-  rib_queue_add (&zebrad, rn, same);
-  
-  /* Free implicit route.*/
-  if (same)
-    rib_delnode (rn, same);
-  
+  /* No matches would be the simplest case. */
+  if (NULL == (rn = route_node_lookup (table, (struct prefix *) p)))
+    return;
+
+  /* Unlock node. */
   route_unlock_node (rn);
-  return 0;
+
+  /* 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;
@@ -1260,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);
 
@@ -1282,8 +2089,11 @@ 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;
+      
       if (same->type == rib->type && same->table == rib->table
          && same->type != ZEBRA_ROUTE_CONNECT)
         break;
@@ -1296,13 +2106,24 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
 
   /* Link new rib to node.*/
   rib_addnode (rn, rib);
-
-  /* Process this route node. */
-  rib_queue_add (&zebrad, rn, same);
+  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;
@@ -1311,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);
@@ -1345,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);
        }
@@ -1359,16 +2189,21 @@ 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;
+
       if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
        fib = rib;
 
       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--;
@@ -1380,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)
@@ -1408,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);
@@ -1425,16 +2267,13 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
        }
     }
   
-  /* Process changes. */
-  rib_queue_add (&zebrad, rn, same);
-
   if (same)
     rib_delnode (rn, same);
   
   route_unlock_node (rn);
   return 0;
 }
-\f
+
 /* Install static route into rib. */
 static void
 static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
@@ -1450,9 +2289,14 @@ 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)
-    if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
-      break;
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+         continue;
+        
+       if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
+         break;
+    }
 
   if (rib)
     {
@@ -1462,7 +2306,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
       switch (si->type)
         {
           case STATIC_IPV4_GATEWAY:
-            nexthop_ipv4_add (rib, &si->gate.ipv4);
+            nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
             break;
           case STATIC_IPV4_IFNAME:
             nexthop_ifname_add (rib, si->gate.ifname);
@@ -1471,7 +2315,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
             nexthop_blackhole_add (rib);
             break;
         }
-      rib_queue_add (&zebrad, rn, NULL);
+      rib_queue_add (&zebrad, rn);
     }
   else
     {
@@ -1481,12 +2325,13 @@ 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)
         {
           case STATIC_IPV4_GATEWAY:
-            nexthop_ipv4_add (rib, &si->gate.ipv4);
+            nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
             break;
           case STATIC_IPV4_IFNAME:
             nexthop_ifname_add (rib, si->gate.ifname);
@@ -1501,9 +2346,6 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
 
       /* Link this rib to the tree. */
       rib_addnode (rn, rib);
-
-      /* Process this prefix. */
-      rib_queue_add (&zebrad, rn, NULL);
     }
 }
 
@@ -1543,9 +2385,14 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
   if (! rn)
     return;
 
-  for (rib = rn->info; rib; rib = rib->next)
-    if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
-      break;
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+
+      if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
+        break;
+    }
 
   if (! rib)
     {
@@ -1567,17 +2414,14 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
   
   /* Check nexthop. */
   if (rib->nexthop_num == 1)
-    {
-      rib_queue_add (&zebrad, rn, rib);
-      rib_delnode (rn, rib);
-    }
+    rib_delnode (rn, rib);
   else
     {
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
         rib_uninstall (rn, rib);
       nexthop_delete (rib, nexthop);
       nexthop_free (nexthop);
-      rib_queue_add (&zebrad, rn, NULL);
+      rib_queue_add (&zebrad, rn);
     }
   /* Unlock node. */
   route_unlock_node (rn);
@@ -1629,13 +2473,12 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
        }
     }
 
-  /* Distance chaged.  */
+  /* Distance changed.  */
   if (update)
     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;
@@ -1743,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)
 {
@@ -1769,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;
@@ -1778,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;
 
@@ -1801,8 +2644,11 @@ 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;
+
       if (rib->type != type)
        continue;
       if (rib->type != ZEBRA_ROUTE_CONNECT)
@@ -1848,37 +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);
+  }
 
-  /* Process this route node. */
-  rib_queue_add (&zebrad, rn, same);
-  
   /* 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;
   
@@ -1890,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);
        }
@@ -1904,16 +2852,21 @@ 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;
+
       if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
        fib = rib;
 
       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--;
@@ -1925,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
@@ -1953,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);
@@ -1970,16 +2931,13 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
        }
     }
 
-  /* Process changes. */
-  rib_queue_add (&zebrad, rn, same);
-
   if (same)
     rib_delnode (rn, same);
   
   route_unlock_node (rn);
   return 0;
 }
-\f
+
 /* Install static route into rib. */
 static void
 static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
@@ -1995,9 +2953,14 @@ 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)
-    if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
-      break;
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
+        continue;
+
+      if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
+        break;
+    }
 
   if (rib)
     {
@@ -2017,7 +2980,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
          nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname);
          break;
        }
-      rib_queue_add (&zebrad, rn, NULL);
+      rib_queue_add (&zebrad, rn);
     }
   else
     {
@@ -2027,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)
@@ -2047,9 +3011,6 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
 
       /* Link this rib to the tree. */
       rib_addnode (rn, rib);
-
-      /* Process this prefix. */
-      rib_queue_add (&zebrad, rn, NULL);
     }
 }
 
@@ -2090,9 +3051,15 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
   if (! rn)
     return;
 
-  for (rib = rn->info; rib; rib = rib->next)
-    if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
-      break;
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+    
+      if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
+        break;
+    }
+
   if (! rib)
     {
       route_unlock_node (rn);
@@ -2115,7 +3082,6 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
   if (rib->nexthop_num == 1)
     {
       rib_delnode (rn, rib);
-      rib_queue_add (&zebrad, rn, rib);
     }
   else
     {
@@ -2123,7 +3089,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
         rib_uninstall (rn, rib);
       nexthop_delete (rib, nexthop);
       nexthop_free (nexthop);
-      rib_queue_add (&zebrad, rn, NULL);
+      rib_queue_add (&zebrad, rn);
     }
   /* Unlock node. */
   route_unlock_node (rn);
@@ -2171,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;
@@ -2271,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)
@@ -2282,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)
-        rib_queue_add (&zebrad, rn, NULL);
+      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)
-        rib_queue_add (&zebrad, rn, NULL);
+      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)
@@ -2316,9 +3268,10 @@ 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;
 
          if (rib->table != zebrad.rtm_table_default &&
              rib->table != RT_TABLE_MAIN)
@@ -2333,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)
@@ -2345,9 +3298,10 @@ 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;
 
          if (rib->type == ZEBRA_ROUTE_KERNEL && 
              CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELFROUTE))
@@ -2366,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)
@@ -2376,10 +3363,16 @@ 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)
-       if (! RIB_SYSTEM_ROUTE (rib)
-           && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
-         rib_uninstall_kernel (rn, rib);
+      RNODE_FOREACH_RIB (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);
+        }
 }
 
 /* Close all RIB tables.  */
@@ -2389,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)
@@ -2398,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;
+}