A Protocol Independent Hierarchical FIB (VPP-352)
[vpp.git] / vnet / vnet / lisp-gpe / ip_forward.c
index bd9951a..8a24ec0 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/**
- *  @file
- *  @brief LISP-GPE overlay IP forwarding logic and lookup data structures.
- *
- *  Provides an implementation of a Source/Dest (SD) IP FIB that leverages the
- *  existing destination only FIB. Lookups are done in two stages, first the
- *  destination FIB looks up a packet's destination address and then if a
- *  an SD entry is hit, the destination adjacency will point to the second
- *  stage, the source FIB, where the packet's source is looked up. Note that a
- *  miss in the source FIB does not result in an overall SD lookup retry with
- *  a less specific entry from the destination FIB.
- */
-#include <vnet/lisp-gpe/lisp_gpe.h>
-
-/** Sets adj index for destination address in IP4 FIB. Similar to the function
- * in ip4_forward but this one avoids calling route callbacks */
-static void
-ip4_sd_fib_set_adj_index (lisp_gpe_main_t * lgm, ip4_fib_t * fib, u32 flags,
-                         u32 dst_address_u32, u32 dst_address_length,
-                         u32 adj_index)
-{
-  ip_lookup_main_t *lm = lgm->lm4;
-  uword *hash;
-
-  if (vec_bytes (fib->old_hash_values))
-    memset (fib->old_hash_values, ~0, vec_bytes (fib->old_hash_values));
-  if (vec_bytes (fib->new_hash_values))
-    memset (fib->new_hash_values, ~0, vec_bytes (fib->new_hash_values));
-  fib->new_hash_values[0] = adj_index;
-
-  /* Make sure adj index is valid. */
-  if (CLIB_DEBUG > 0)
-    (void) ip_get_adjacency (lm, adj_index);
-
-  hash = fib->adj_index_by_dst_address[dst_address_length];
-
-  hash = _hash_set3 (hash, dst_address_u32,
-                    fib->new_hash_values, fib->old_hash_values);
-
-  fib->adj_index_by_dst_address[dst_address_length] = hash;
-}
-
-/** Initialize the adjacency index by destination address vector for IP4 FIB.
- * Copied from ip4_forward since it's static */
-static void
-ip4_fib_init_adj_index_by_dst_address (ip_lookup_main_t * lm,
-                                      ip4_fib_t * fib, u32 address_length)
-{
-  hash_t *h;
-  uword max_index;
-
-  ASSERT (lm->fib_result_n_bytes >= sizeof (uword));
-  lm->fib_result_n_words = round_pow2 (lm->fib_result_n_bytes, sizeof (uword))
-    / sizeof (uword);
-
-  fib->adj_index_by_dst_address[address_length] =
-    hash_create (32 /* elts */ , lm->fib_result_n_words * sizeof (uword));
-
-  hash_set_flags (fib->adj_index_by_dst_address[address_length],
-                 HASH_FLAG_NO_AUTO_SHRINK);
-
-  h = hash_header (fib->adj_index_by_dst_address[address_length]);
-  max_index = (hash_value_bytes (h) / sizeof (fib->new_hash_values[0])) - 1;
-
-  /* Initialize new/old hash value vectors. */
-  vec_validate_init_empty (fib->new_hash_values, max_index, ~0);
-  vec_validate_init_empty (fib->old_hash_values, max_index, ~0);
-}
-
-/** Add/del src route to IP4 SD FIB. */
-static void
-ip4_sd_fib_add_del_src_route (lisp_gpe_main_t * lgm,
-                             ip4_add_del_route_args_t * a)
-{
-  ip_lookup_main_t *lm = lgm->lm4;
-  ip4_fib_t *fib;
-  u32 dst_address, dst_address_length, adj_index, old_adj_index;
-  uword *hash, is_del;
-
-  /* Either create new adjacency or use given one depending on arguments. */
-  if (a->n_add_adj > 0)
-    ip_add_adjacency (lm, a->add_adj, a->n_add_adj, &adj_index);
-  else
-    adj_index = a->adj_index;
-
-  dst_address = a->dst_address.data_u32;
-  dst_address_length = a->dst_address_length;
-
-  fib = pool_elt_at_index (lgm->ip4_src_fibs, a->table_index_or_table_id);
-
-  if (!fib->adj_index_by_dst_address[dst_address_length])
-    ip4_fib_init_adj_index_by_dst_address (lm, fib, dst_address_length);
-
-  hash = fib->adj_index_by_dst_address[dst_address_length];
-
-  is_del = (a->flags & IP4_ROUTE_FLAG_DEL) != 0;
-
-  if (is_del)
-    {
-      fib->old_hash_values[0] = ~0;
-      hash = _hash_unset (hash, dst_address, fib->old_hash_values);
-      fib->adj_index_by_dst_address[dst_address_length] = hash;
-    }
-  else
-    ip4_sd_fib_set_adj_index (lgm, fib, a->flags, dst_address,
-                             dst_address_length, adj_index);
-
-  old_adj_index = fib->old_hash_values[0];
-
-  /* Avoid spurious reference count increments */
-  if (old_adj_index == adj_index
-      && adj_index != ~0 && !(a->flags & IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY))
-    {
-      ip_adjacency_t *adj = ip_get_adjacency (lm, adj_index);
-      if (adj->share_count > 0)
-       adj->share_count--;
-    }
-
-  ip4_fib_mtrie_add_del_route (fib, a->dst_address, dst_address_length,
-                              is_del ? old_adj_index : adj_index, is_del);
-
-  /* Delete old adjacency index if present and changed. */
-  if (!(a->flags & IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY)
-      && old_adj_index != ~0 && old_adj_index != adj_index)
-    ip_del_adjacency (lm, old_adj_index);
-}
-
-/** Get src route from IP4 SD FIB. */
-static void *
-ip4_sd_get_src_route (lisp_gpe_main_t * lgm, u32 src_fib_index,
-                     ip4_address_t * src, u32 address_length)
-{
-  ip4_fib_t *fib = pool_elt_at_index (lgm->ip4_src_fibs, src_fib_index);
-  uword *hash, *p;
 
-  hash = fib->adj_index_by_dst_address[address_length];
-  p = hash_get (hash, src->as_u32);
-  return (void *) p;
-}
-
-/* *INDENT-OFF* */
-typedef CLIB_PACKED (struct ip4_route {
-  ip4_address_t address;
-  u32 address_length : 6;
-  u32 index : 26;
-}) ip4_route_t;
-/* *INDENT-ON* */
-
-/** Remove all routes from src IP4 FIB */
-void
-ip4_sd_fib_clear_src_fib (lisp_gpe_main_t * lgm, ip4_fib_t * fib)
-{
-  ip4_route_t *routes = 0, *r;
-  u32 i;
-
-  vec_reset_length (routes);
-
-  for (i = 0; i < ARRAY_LEN (fib->adj_index_by_dst_address); i++)
-    {
-      uword *hash = fib->adj_index_by_dst_address[i];
-      hash_pair_t *p;
-      ip4_route_t x;
-
-      x.address_length = i;
-      x.index = 0;             /* shut up coverity */
-
-      /* *INDENT-OFF* */
-      hash_foreach_pair (p, hash,
-      ({
-          x.address.data_u32 = p->key;
-          vec_add1 (routes, x);
-      }));
-      /* *INDENT-ON* */
-    }
-
-  vec_foreach (r, routes)
-  {
-    ip4_add_del_route_args_t a;
-
-    memset (&a, 0, sizeof (a));
-    a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
-    a.table_index_or_table_id = fib - lgm->ip4_src_fibs;
-    a.dst_address = r->address;
-    a.dst_address_length = r->address_length;
-    a.adj_index = ~0;
-
-    ip4_sd_fib_add_del_src_route (lgm, &a);
-  }
-}
-
-/** Test if IP4 FIB is empty */
-static u8
-ip4_fib_is_empty (ip4_fib_t * fib)
-{
-  u8 fib_is_empty;
-  int i;
-
-  fib_is_empty = 1;
-  for (i = ARRAY_LEN (fib->adj_index_by_dst_address) - 1; i >= 0; i--)
-    {
-      uword *hash = fib->adj_index_by_dst_address[i];
-      uword n_elts = hash_elts (hash);
-      if (n_elts)
-       {
-         fib_is_empty = 0;
-         break;
-       }
-    }
-  return fib_is_empty;
-}
+#include <vnet/lisp-gpe/lisp_gpe_adjacency.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/dpo/lookup_dpo.h>
+#include <vnet/dpo/load_balance.h>
 
 /**
- * @brief Add/del route to IP4 SD FIB.
- *
- * Adds/remove routes to both destination and source FIBs. Entries added
- * to destination FIB are associated to adjacencies that point to the source
- * FIB and store the index of the particular source FIB associated to the
- * destination. Source FIBs are locally managed (see @ref lgm->ip4_src_fibs
- * and @ref lgm->ip6_src_fibs), but the adjacencies are allocated out of the
- * global adjacency pool.
+ * @brief Add route to IP4 or IP6 Destination FIB.
  *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  dst_prefix      Destination IP4 prefix.
- * @param[in]   src_prefix      Source IP4 prefix.
- * @param[in]   table_id        Table id.
- * @param[in]   add_adj         Pointer to the adjacency to be added.
- * @param[in]   is_add          Add/del flag.
+ * Add a route to the destination FIB that results in the lookup
+ * in the SRC FIB. The SRC FIB is created is it does not yet exist.
  *
- * @return 0 on success.
+ * @param[in]   dst_table_id    Destination FIB Table-ID
+ * @param[in]   dst_prefix      Destination IP prefix.
+ * @param[out]  src_fib_index   The index/ID of the SRC FIB created.
  */
-static int
-ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
-                         ip_prefix_t * src_prefix, u32 table_id,
-                         ip_adjacency_t * add_adj, u8 is_add)
-{
-  uword *p;
-  ip4_add_del_route_args_t a;
-  ip_adjacency_t *dst_adjp, dst_adj;
-  ip4_address_t dst = ip_prefix_v4 (dst_prefix), src;
-  u32 dst_address_length = ip_prefix_len (dst_prefix), src_address_length = 0;
-  ip4_fib_t *src_fib;
-
-  if (src_prefix)
-    {
-      src = ip_prefix_v4 (src_prefix);
-      src_address_length = ip_prefix_len (src_prefix);
-    }
-  else
-    memset (&src, 0, sizeof (src));
-
-  /* lookup dst adj */
-  p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8, dst_address_length);
-
-  if (is_add)
-    {
-      /* insert dst prefix to ip4 fib, if it's not in yet */
-      if (p == 0)
-       {
-         /* allocate and init src ip4 fib */
-         pool_get (lgm->ip4_src_fibs, src_fib);
-         ip4_mtrie_init (&src_fib->mtrie);
-
-         /* configure adjacency */
-         memset (&dst_adj, 0, sizeof (dst_adj));
-
-         /* reuse rewrite header to store pointer to src fib */
-         dst_adj.rewrite_header.sw_if_index = src_fib - lgm->ip4_src_fibs;
-
-         /* dst adj should point to lisp gpe lookup */
-         dst_adj.lookup_next_index = lgm->ip4_lookup_next_lgpe_ip4_lookup;
-
-         /* explicit_fib_index is used in IP6 FIB lookup, don't reuse it */
-         dst_adj.explicit_fib_index = ~0;
-         dst_adj.n_adj = 1;
-
-         /* make sure we have different signatures for adj in different tables
-          * but with the same lookup_next_index and for adj in the same table
-          * but associated to different destinations */
-         dst_adj.if_address_index = table_id;
-         dst_adj.indirect.next_hop.ip4 = dst;
-
-         memset (&a, 0, sizeof (a));
-         a.flags = IP4_ROUTE_FLAG_TABLE_ID;
-         a.table_index_or_table_id = table_id; /* vrf */
-         a.adj_index = ~0;
-         a.dst_address_length = dst_address_length;
-         a.dst_address = dst;
-         a.flags |= IP4_ROUTE_FLAG_ADD;
-         a.add_adj = &dst_adj;
-         a.n_add_adj = 1;
-
-         ip4_add_del_route (lgm->im4, &a);
-
-         /* lookup dst adj to obtain the adj index */
-         p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8,
-                            dst_address_length);
-
-         /* make sure insertion succeeded */
-         if (CLIB_DEBUG)
-           {
-             ASSERT (p != 0);
-             dst_adjp = ip_get_adjacency (lgm->lm4, p[0]);
-             ASSERT (dst_adjp->rewrite_header.sw_if_index
-                     == dst_adj.rewrite_header.sw_if_index);
-           }
-       }
-    }
-  else
-    {
-      if (p == 0)
-       {
-         clib_warning
-           ("Trying to delete inexistent dst route for %U. Aborting",
-            format_ip4_address_and_length, dst.as_u8, dst_address_length);
-         return -1;
-       }
-    }
-
-  dst_adjp = ip_get_adjacency (lgm->lm4, p[0]);
-
-  /* add/del src prefix to src fib */
-  memset (&a, 0, sizeof (a));
-  a.flags = IP4_ROUTE_FLAG_TABLE_ID;
-  a.table_index_or_table_id = dst_adjp->rewrite_header.sw_if_index;
-  a.adj_index = ~0;
-  a.flags |= is_add ? IP4_ROUTE_FLAG_ADD : IP4_ROUTE_FLAG_DEL;
-  a.add_adj = add_adj;
-  a.n_add_adj = is_add ? 1 : 0;
-  /* if src prefix is null, add 0/0 */
-  a.dst_address_length = src_address_length;
-  a.dst_address = src;
-  ip4_sd_fib_add_del_src_route (lgm, &a);
-
-  /* make sure insertion succeeded */
-  if (CLIB_DEBUG && is_add)
-    {
-      uword *sai;
-      ip_adjacency_t *src_adjp;
-      sai = ip4_sd_get_src_route (lgm, dst_adjp->rewrite_header.sw_if_index,
-                                 &src, src_address_length);
-      ASSERT (sai != 0);
-      src_adjp = ip_get_adjacency (lgm->lm4, sai[0]);
-      ASSERT (src_adjp->if_address_index == add_adj->if_address_index);
-    }
-
-  /* if a delete, check if there are elements left in the src fib */
-  if (!is_add)
-    {
-      src_fib = pool_elt_at_index (lgm->ip4_src_fibs,
-                                  dst_adjp->rewrite_header.sw_if_index);
-      if (!src_fib)
-       return 0;
-
-      /* if there's nothing left */
-      if (ip4_fib_is_empty (src_fib))
-       {
-         /* remove the src fib ..  */
-         pool_put (lgm->ip4_src_fibs, src_fib);
-
-         /* .. and remove dst route */
-         memset (&a, 0, sizeof (a));
-         a.flags = IP4_ROUTE_FLAG_TABLE_ID;
-         a.table_index_or_table_id = table_id; /* vrf */
-         a.adj_index = ~0;
-         a.dst_address_length = dst_address_length;
-         a.dst_address = dst;
-         a.flags |= IP4_ROUTE_FLAG_DEL;
-
-         ip4_add_del_route (lgm->im4, &a);
-       }
-    }
-
-  return 0;
-}
-
-/**
- * @brief Retrieve IP4 SD FIB entry.
- *
- * Looks up SD IP4 route by first looking up the destination in VPP's main FIB
- * and subsequently the source in the src FIB. The index of the source FIB is
- * stored in the dst adjacency's rewrite_header.sw_if_index. If source is 0
- * do search with 0/0 src.
- *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  dst_prefix      Destination IP4 prefix.
- * @param[in]   src_prefix      Source IP4 prefix.
- * @param[in]   table_id        Table id.
- *
- * @return pointer to the adjacency if route found.
- */
-static void *
-ip4_sd_fib_get_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
-                     ip_prefix_t * src_prefix, u32 table_id)
-{
-  uword *p;
-  ip4_address_t dst = ip_prefix_v4 (dst_prefix), src;
-  u32 dst_address_length = ip_prefix_len (dst_prefix), src_address_length = 0;
-  ip_adjacency_t *dst_adj;
-
-  if (src_prefix)
-    {
-      src = ip_prefix_v4 (src_prefix);
-      src_address_length = ip_prefix_len (src_prefix);
-    }
-  else
-    memset (&src, 0, sizeof (src));
-
-  /* lookup dst adj */
-  p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8, dst_address_length);
-  if (p == 0)
-    return p;
-
-  dst_adj = ip_get_adjacency (lgm->lm4, p[0]);
-  return ip4_sd_get_src_route (lgm, dst_adj->rewrite_header.sw_if_index, &src,
-                              src_address_length);
-}
-
-/** Get src route from IP6 SD FIB. */
-static u32
-ip6_sd_get_src_route (lisp_gpe_main_t * lgm, u32 src_fib_index,
-                     ip6_address_t * src, u32 address_length)
-{
-  int rv;
-  BVT (clib_bihash_kv) kv, value;
-  ip6_src_fib_t *fib = pool_elt_at_index (lgm->ip6_src_fibs, src_fib_index);
-
-  ip6_address_t *mask;
-
-  ASSERT (address_length <= 128);
-
-  mask = &fib->fib_masks[address_length];
-
-  kv.key[0] = src->as_u64[0] & mask->as_u64[0];
-  kv.key[1] = src->as_u64[1] & mask->as_u64[1];
-  kv.key[2] = address_length;
-
-  rv = BV (clib_bihash_search_inline_2) (&fib->ip6_lookup_table, &kv, &value);
-  if (rv == 0)
-    return value.value;
-
-  return 0;
-}
-
-static void
-compute_prefix_lengths_in_search_order (ip6_src_fib_t * fib)
-{
-  int i;
-  vec_reset_length (fib->prefix_lengths_in_search_order);
-  /* Note: bitmap reversed so this is in fact a longest prefix match */
-
-  /* *INDENT-OFF* */
-  clib_bitmap_foreach(i, fib->non_empty_dst_address_length_bitmap, ({
-    int dst_address_length = 128 - i;
-    vec_add1 (fib->prefix_lengths_in_search_order, dst_address_length);
-  }));
-  /* *INDENT-ON* */
-}
-
-/** Add/del src route to IP6 SD FIB. Rewrite of ip6_add_del_route() because
- * it uses im6 to find the FIB .*/
-static void
-ip6_sd_fib_add_del_src_route (lisp_gpe_main_t * lgm,
-                             ip6_add_del_route_args_t * a)
+u32
+ip_dst_fib_add_route (u32 dst_fib_index, const ip_prefix_t * dst_prefix)
 {
-  ip_lookup_main_t *lm = lgm->lm6;
-  ip6_src_fib_t *fib;
-  ip6_address_t dst_address;
-  u32 dst_address_length, adj_index;
-  uword is_del;
-  u32 old_adj_index = ~0;
-  BVT (clib_bihash_kv) kv, value;
-
-  vlib_smp_unsafe_warning ();
-
-  is_del = (a->flags & IP6_ROUTE_FLAG_DEL) != 0;
+  fib_node_index_t src_fib_index;
+  fib_prefix_t dst_fib_prefix;
+  fib_node_index_t dst_fei;
 
-  /* Either create new adjacency or use given one depending on arguments. */
-  if (a->n_add_adj > 0)
-    {
-      ip_add_adjacency (lm, a->add_adj, a->n_add_adj, &adj_index);
-    }
-  else
-    adj_index = a->adj_index;
+  ASSERT (NULL != dst_prefix);
 
-  dst_address = a->dst_address;
-  dst_address_length = a->dst_address_length;
-  fib = pool_elt_at_index (lgm->ip6_src_fibs, a->table_index_or_table_id);
+  ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix);
 
-  ASSERT (dst_address_length < ARRAY_LEN (fib->fib_masks));
-  ip6_address_mask (&dst_address, &fib->fib_masks[dst_address_length]);
+  /*
+   * lookup the destination prefix in the VRF table and retrieve the
+   * LISP associated data
+   */
+  dst_fei = fib_table_lookup_exact_match (dst_fib_index, &dst_fib_prefix);
 
-  /* refcount accounting */
-  if (is_del)
+  /*
+   * If the FIB entry is not present, or not LISP sourced, add it
+   */
+  if (dst_fei == FIB_NODE_INDEX_INVALID ||
+      NULL == fib_entry_get_source_data (dst_fei, FIB_SOURCE_LISP))
     {
-      ASSERT (fib->dst_address_length_refcounts[dst_address_length] > 0);
-      if (--fib->dst_address_length_refcounts[dst_address_length] == 0)
-       {
-         fib->non_empty_dst_address_length_bitmap =
-           clib_bitmap_set (fib->non_empty_dst_address_length_bitmap,
-                            128 - dst_address_length, 0);
-         compute_prefix_lengths_in_search_order (fib);
-       }
+      dpo_id_t src_lkup_dpo = DPO_NULL;
+
+      /* create a new src FIB.  */
+      src_fib_index =
+       fib_table_create_and_lock (dst_fib_prefix.fp_proto,
+                                  "LISP-src for [%d,%U]",
+                                  dst_fib_index,
+                                  format_fib_prefix, &dst_fib_prefix);
+
+      /*
+       * create a data-path object to perform the source address lookup
+       * in the SRC FIB
+       */
+      lookup_dpo_add_or_lock_w_fib_index (src_fib_index,
+                                         (ip_prefix_version (dst_prefix) ==
+                                          IP6 ? DPO_PROTO_IP6 :
+                                          DPO_PROTO_IP4),
+                                         LOOKUP_INPUT_SRC_ADDR,
+                                         LOOKUP_TABLE_FROM_CONFIG,
+                                         &src_lkup_dpo);
+
+      /*
+       * add the entry to the destination FIB that uses the lookup DPO
+       */
+      dst_fei = fib_table_entry_special_dpo_add (dst_fib_index,
+                                                &dst_fib_prefix,
+                                                FIB_SOURCE_LISP,
+                                                FIB_ENTRY_FLAG_EXCLUSIVE,
+                                                &src_lkup_dpo);
+
+      /*
+       * the DPO is locked by the FIB entry, and we have no further
+       * need for it.
+       */
+      dpo_unlock (&src_lkup_dpo);
+
+      /*
+       * save the SRC FIB index on the entry so we can retrieve it for
+       * subsequent routes.
+       */
+      fib_entry_set_source_data (dst_fei, FIB_SOURCE_LISP, &src_fib_index);
     }
   else
     {
-      fib->dst_address_length_refcounts[dst_address_length]++;
-
-      fib->non_empty_dst_address_length_bitmap =
-       clib_bitmap_set (fib->non_empty_dst_address_length_bitmap,
-                        128 - dst_address_length, 1);
-      compute_prefix_lengths_in_search_order (fib);
-    }
-
-  kv.key[0] = dst_address.as_u64[0];
-  kv.key[1] = dst_address.as_u64[1];
-  kv.key[2] = dst_address_length;
-
-  if (BV (clib_bihash_search) (&fib->ip6_lookup_table, &kv, &value) == 0)
-    old_adj_index = value.value;
-
-  if (is_del)
-    BV (clib_bihash_add_del) (&fib->ip6_lookup_table, &kv, 0 /* is_add */ );
-  else
-    {
-      /* Make sure adj index is valid. */
-      if (CLIB_DEBUG > 0)
-       (void) ip_get_adjacency (lm, adj_index);
-
-      kv.value = adj_index;
-
-      BV (clib_bihash_add_del) (&fib->ip6_lookup_table, &kv, 1 /* is_add */ );
+      /*
+       * destination FIB entry already present
+       */
+      src_fib_index = *(u32 *) fib_entry_get_source_data (dst_fei,
+                                                         FIB_SOURCE_LISP);
     }
 
-  /* Avoid spurious reference count increments */
-  if (old_adj_index == adj_index
-      && !(a->flags & IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY))
-    {
-      ip_adjacency_t *adj = ip_get_adjacency (lm, adj_index);
-      if (adj->share_count > 0)
-       adj->share_count--;
-    }
-
-  /* Delete old adjacency index if present and changed. */
-  {
-    if (!(a->flags & IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY)
-       && old_adj_index != ~0 && old_adj_index != adj_index)
-      ip_del_adjacency (lm, old_adj_index);
-  }
-}
-
-static void
-ip6_src_fib_init (ip6_src_fib_t * fib)
-{
-  uword i;
-
-  for (i = 0; i < ARRAY_LEN (fib->fib_masks); i++)
-    {
-      u32 j, i0, i1;
-
-      i0 = i / 32;
-      i1 = i % 32;
-
-      for (j = 0; j < i0; j++)
-       fib->fib_masks[i].as_u32[j] = ~0;
-
-      if (i1)
-       fib->fib_masks[i].as_u32[i0] =
-         clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
-    }
-
-  if (fib->lookup_table_nbuckets == 0)
-    fib->lookup_table_nbuckets = IP6_FIB_DEFAULT_HASH_NUM_BUCKETS;
-
-  fib->lookup_table_nbuckets = 1 << max_log2 (fib->lookup_table_nbuckets);
-
-  if (fib->lookup_table_size == 0)
-    fib->lookup_table_size = IP6_FIB_DEFAULT_HASH_MEMORY_SIZE;
-
-  BV (clib_bihash_init) (&fib->ip6_lookup_table, "ip6 lookup table",
-                        fib->lookup_table_nbuckets, fib->lookup_table_size);
-
+  return (src_fib_index);
 }
 
 /**
- * @brief Add/del route to IP6 SD FIB.
- *
- * Adds/remove routes to both destination and source FIBs. Entries added
- * to destination FIB are associated to adjacencies that point to the source
- * FIB and store the index of the particular source FIB associated to the
- * destination. Source FIBs are locally managed (see @ref lgm->ip4_src_fibs
- * and @ref lgm->ip6_src_fibs), but the adjacencies are allocated out of the
- * global adjacency pool.
+ * @brief Del route to IP4 or IP6 SD FIB.
  *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  dst_prefix      Destination IP6 prefix.
- * @param[in]   src_prefix      Source IP6 prefix.
- * @param[in]   table_id        Table id.
- * @param[in]   add_adj         Pointer to the adjacency to be added.
- * @param[in]   is_add          Add/del flag.
+ * Remove routes from both destination and source FIBs.
  *
- * @return 0 on success.
+ * @param[in]   src_fib_index   The index/ID of the SRC FIB
+ * @param[in]   src_prefix      Source IP prefix.
+ * @param[in]   dst_fib_index   The index/ID of the DST FIB
+ * @param[in]   dst_prefix      Destination IP prefix.
  */
-static int
-ip6_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
-                         ip_prefix_t * src_prefix, u32 table_id,
-                         ip_adjacency_t * add_adj, u8 is_add)
+void
+ip_src_dst_fib_del_route (u32 src_fib_index,
+                         const ip_prefix_t * src_prefix,
+                         u32 dst_fib_index, const ip_prefix_t * dst_prefix)
 {
-  u32 adj_index;
-  ip6_add_del_route_args_t a;
-  ip_adjacency_t *dst_adjp, dst_adj;
-  ip6_address_t dst = ip_prefix_v6 (dst_prefix), src;
-  u32 dst_address_length = ip_prefix_len (dst_prefix), src_address_length = 0;
-  ip6_src_fib_t *src_fib;
-
-  if (src_prefix)
-    {
-      src = ip_prefix_v6 (src_prefix);
-      src_address_length = ip_prefix_len (src_prefix);
-    }
-  else
-    memset (&src, 0, sizeof (src));
-
-  /* lookup dst adj and create it if it doesn't exist */
-  adj_index = ip6_get_route (lgm->im6, table_id, 0, &dst, dst_address_length);
+  fib_prefix_t dst_fib_prefix, src_fib_prefix;
 
-  if (is_add)
-    {
-      /* insert dst prefix to ip6 fib, if it's not in yet */
-      if (adj_index == 0)
-       {
-         /* allocate and init src ip6 fib */
-         pool_get (lgm->ip6_src_fibs, src_fib);
-         memset (src_fib, 0, sizeof (src_fib[0]));
-         ip6_src_fib_init (src_fib);
-
-         memset (&dst_adj, 0, sizeof (dst_adj));
-
-         /* reuse rewrite header to store pointer to src fib */
-         dst_adj.rewrite_header.sw_if_index = src_fib - lgm->ip6_src_fibs;
-
-         /* dst adj should point to lisp gpe ip lookup */
-         dst_adj.lookup_next_index = lgm->ip6_lookup_next_lgpe_ip6_lookup;
-
-         /* explicit_fib_index is used in IP6 FIB lookup, don't reuse it */
-         dst_adj.explicit_fib_index = ~0;
-         dst_adj.n_adj = 1;
-
-         /* make sure we have different signatures for adj in different tables
-          * but with the same lookup_next_index and for adj in the same table
-          * but associated to different destinations */
-         dst_adj.if_address_index = table_id;
-         dst_adj.indirect.next_hop.ip6 = dst;
-
-         memset (&a, 0, sizeof (a));
-         a.flags = IP6_ROUTE_FLAG_TABLE_ID;
-         a.table_index_or_table_id = table_id; /* vrf */
-         a.adj_index = ~0;
-         a.dst_address_length = dst_address_length;
-         a.dst_address = dst;
-         a.flags |= IP6_ROUTE_FLAG_ADD;
-         a.add_adj = &dst_adj;
-         a.n_add_adj = 1;
-
-         ip6_add_del_route (lgm->im6, &a);
-
-         /* lookup dst adj to obtain the adj index */
-         adj_index = ip6_get_route (lgm->im6, table_id, 0, &dst,
-                                    dst_address_length);
-
-         /* make sure insertion succeeded */
-         if (CLIB_DEBUG)
-           {
-             ASSERT (adj_index != 0);
-             dst_adjp = ip_get_adjacency (lgm->lm6, adj_index);
-             ASSERT (dst_adjp->rewrite_header.sw_if_index
-                     == dst_adj.rewrite_header.sw_if_index);
-           }
-       }
-    }
-  else
-    {
-      if (adj_index == 0)
-       {
-         clib_warning
-           ("Trying to delete inexistent dst route for %U. Aborting",
-            format_ip_prefix, dst_prefix);
-         return -1;
-       }
-    }
-
-  dst_adjp = ip_get_adjacency (lgm->lm6, adj_index);
-
-  /* add/del src prefix to src fib */
-  memset (&a, 0, sizeof (a));
-  a.flags = IP6_ROUTE_FLAG_TABLE_ID;
-  a.table_index_or_table_id = dst_adjp->rewrite_header.sw_if_index;
-  a.adj_index = ~0;
-  a.flags |= is_add ? IP6_ROUTE_FLAG_ADD : IP6_ROUTE_FLAG_DEL;
-  a.add_adj = add_adj;
-  a.n_add_adj = is_add ? 1 : 0;
-  /* if src prefix is null, add ::0 */
-  a.dst_address_length = src_address_length;
-  a.dst_address = src;
-  ip6_sd_fib_add_del_src_route (lgm, &a);
-
-  /* make sure insertion succeeded */
-  if (CLIB_DEBUG && is_add)
-    {
-      u32 sai;
-      ip_adjacency_t *src_adjp;
-      sai = ip6_sd_get_src_route (lgm, dst_adjp->rewrite_header.sw_if_index,
-                                 &src, src_address_length);
-      ASSERT (sai != 0);
-      src_adjp = ip_get_adjacency (lgm->lm6, sai);
-      ASSERT (src_adjp->if_address_index == add_adj->if_address_index);
-    }
-
-  /* if a delete, check if there are elements left in the src fib */
-  if (!is_add)
-    {
-      src_fib = pool_elt_at_index (lgm->ip6_src_fibs,
-                                  dst_adjp->rewrite_header.sw_if_index);
-      if (!src_fib)
-       return 0;
-
-      /* if there's nothing left */
-      if (clib_bitmap_count_set_bits
-         (src_fib->non_empty_dst_address_length_bitmap) == 0)
-       {
-         /* remove src fib .. */
-         pool_put (lgm->ip6_src_fibs, src_fib);
+  ASSERT (NULL != dst_prefix);
+  ASSERT (NULL != src_prefix);
 
-         /* .. and remove dst route */
-         memset (&a, 0, sizeof (a));
-         a.flags = IP6_ROUTE_FLAG_TABLE_ID;
-         a.table_index_or_table_id = table_id; /* vrf */
-         a.adj_index = ~0;
-         a.dst_address_length = dst_address_length;
-         a.dst_address = dst;
-         a.flags |= IP6_ROUTE_FLAG_DEL;
+  ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix);
+  ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix);
 
-         ip6_add_del_route (lgm->im6, &a);
-       }
-    }
-
-  return 0;
-}
+  fib_table_entry_delete (src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP);
 
-/**
- * @brief Retrieve IP6 SD FIB entry.
- *
- * Looks up SD IP6 route by first looking up the destination in VPP's main FIB
- * and subsequently the source in the src FIB. The index of the source FIB is
- * stored in the dst adjacency's @ref rewrite_header.sw_if_index. If source is
- * 0 do search with ::/0 src.
- *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  dst_prefix      Destination IP6 prefix.
- * @param[in]   src_prefix      Source IP6 prefix.
- * @param[in]   table_id        Table id.
- *
- * @return adjacency index if route found.
- */
-static u32
-ip6_sd_fib_get_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
-                     ip_prefix_t * src_prefix, u32 table_id)
-{
-  u32 adj_index;
-  ip6_address_t dst = ip_prefix_v6 (dst_prefix), src;
-  u32 dst_address_length = ip_prefix_len (dst_prefix), src_address_length = 0;
-  ip_adjacency_t *dst_adj;
-
-  if (src_prefix)
+  if (0 == fib_table_get_num_entries (src_fib_index,
+                                     src_fib_prefix.fp_proto,
+                                     FIB_SOURCE_LISP))
     {
-      src = ip_prefix_v6 (src_prefix);
-      src_address_length = ip_prefix_len (src_prefix);
+      /*
+       * there's nothing left, unlock the source FIB and the
+       * destination route
+       */
+      fib_table_entry_special_remove (dst_fib_index,
+                                     &dst_fib_prefix, FIB_SOURCE_LISP);
+      fib_table_unlock (src_fib_index, src_fib_prefix.fp_proto);
     }
-  else
-    memset (&src, 0, sizeof (src));
-
-  /* lookup dst adj */
-  adj_index = ip6_get_route (lgm->im6, table_id, 0, &dst, dst_address_length);
-  if (adj_index == 0)
-    return adj_index;
-
-  dst_adj = ip_get_adjacency (lgm->lm6, adj_index);
-  return ip6_sd_get_src_route (lgm, dst_adj->rewrite_header.sw_if_index, &src,
-                              src_address_length);
-}
-
-/**
- * @brief Add/del route to IP4 or IP6 SD FIB.
- *
- * Adds/remove routes to both destination and source FIBs. Entries added
- * to destination FIB are associated to adjacencies that point to the source
- * FIB and store the index of the particular source FIB associated to the
- * destination. Source FIBs are locally managed (see @ref lgm->ip4_src_fibs
- * and @ref lgm->ip6_src_fibs), but the adjacencies are allocated out of the
- * global adjacency pool.
- *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  dst_prefix      Destination IP prefix.
- * @param[in]   src_prefix      Source IP prefix.
- * @param[in]   table_id        Table id.
- * @param[in]   add_adj         Pointer to the adjacency to be added.
- * @param[in]   is_add          Add/del flag.
- *
- * @return 0 on success.
- */
-int
-ip_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
-                        ip_prefix_t * src_prefix, u32 table_id,
-                        ip_adjacency_t * add_adj, u8 is_add)
-{
-  return (ip_prefix_version (dst_prefix) == IP4 ?
-         ip4_sd_fib_add_del_route : ip6_sd_fib_add_del_route) (lgm,
-                                                               dst_prefix,
-                                                               src_prefix,
-                                                               table_id,
-                                                               add_adj,
-                                                               is_add);
 }
 
 /**
- * @brief Retrieve IP4 or IP6 SD FIB entry.
+ * @brief Add route to IP4 or IP6 SRC FIB.
  *
- * Looks up SD IP route by first looking up the destination in VPP's main FIB
- * and subsequently the source in the src FIB. The index of the source FIB is
- * stored in the dst adjacency's @ref rewrite_header.sw_if_index. If source is
- * 0 do search with ::/0 src.
+ * Adds a route to in the LISP SRC FIB with the result of the route
+ * being the DPO passed.
  *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  dst_prefix      Destination IP prefix.
+ * @param[in]   src_fib_index   The index/ID of the SRC FIB
  * @param[in]   src_prefix      Source IP prefix.
- * @param[in]   table_id        Table id.
- *
- * @return adjacency index if route found.
+ * @param[in]   src_dpo         The DPO the route will link to.
  */
-u32
-ip_sd_fib_get_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix,
-                    ip_prefix_t * src_prefix, u32 table_id)
-{
-  if (ip_prefix_version (dst_prefix) == IP4)
-    {
-      u32 *adj_index = ip4_sd_fib_get_route (lgm, dst_prefix, src_prefix,
-                                            table_id);
-      return (adj_index == 0) ? 0 : adj_index[0];
-    }
-  else
-    return ip6_sd_fib_get_route (lgm, dst_prefix, src_prefix, table_id);
-}
-
-always_inline void
-ip4_src_fib_lookup_one (lisp_gpe_main_t * lgm, u32 src_fib_index0,
-                       ip4_address_t * addr0, u32 * src_adj_index0)
-{
-  ip4_fib_mtrie_leaf_t leaf0, leaf1;
-  ip4_fib_mtrie_t *mtrie0;
-
-  /* if default route not hit in ip4 lookup */
-  if (PREDICT_TRUE (src_fib_index0 != (u32) ~ 0))
-    {
-      mtrie0 = &vec_elt_at_index (lgm->ip4_src_fibs, src_fib_index0)->mtrie;
-
-      leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 0);
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 1);
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 2);
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 3);
-
-      /* Handle default route. */
-      leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY) ?
-       mtrie0->default_leaf : leaf0;
-      src_adj_index0[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
-    }
-  else
-    src_adj_index0[0] = ~0;
-}
-
-always_inline void
-ip4_src_fib_lookup_two (lisp_gpe_main_t * lgm, u32 src_fib_index0,
-                       u32 src_fib_index1, ip4_address_t * addr0,
-                       ip4_address_t * addr1, u32 * src_adj_index0,
-                       u32 * src_adj_index1)
+void
+ip_src_fib_add_route_w_dpo (u32 src_fib_index,
+                           const ip_prefix_t * src_prefix,
+                           const dpo_id_t * src_dpo)
 {
-  ip4_fib_mtrie_leaf_t leaf0, leaf1;
-  ip4_fib_mtrie_t *mtrie0, *mtrie1;
-
-  /* if default route not hit in ip4 lookup */
-  if (PREDICT_TRUE
-      (src_fib_index0 != (u32) ~ 0 && src_fib_index1 != (u32) ~ 0))
-    {
-      mtrie0 = &vec_elt_at_index (lgm->ip4_src_fibs, src_fib_index0)->mtrie;
-      mtrie1 = &vec_elt_at_index (lgm->ip4_src_fibs, src_fib_index1)->mtrie;
-
-      leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
-
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 0);
-      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 0);
+  fib_prefix_t src_fib_prefix;
 
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 1);
-      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 1);
+  ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix);
 
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 2);
-      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 2);
+  /*
+   * add the entry into the source fib.
+   */
+  fib_node_index_t src_fei;
 
-      leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, addr0, 3);
-      leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, addr1, 3);
+  src_fei = fib_table_lookup_exact_match (src_fib_index, &src_fib_prefix);
 
-      /* Handle default route. */
-      leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY) ?
-       mtrie0->default_leaf : leaf0;
-      leaf1 = (leaf1 == IP4_FIB_MTRIE_LEAF_EMPTY) ?
-       mtrie1->default_leaf : leaf1;
-      src_adj_index0[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
-      src_adj_index1[0] = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
-    }
-  else
+  if (FIB_NODE_INDEX_INVALID == src_fei ||
+      !fib_entry_is_sourced (src_fei, FIB_SOURCE_LISP))
     {
-      ip4_src_fib_lookup_one (lgm, src_fib_index0, addr0, src_adj_index0);
-      ip4_src_fib_lookup_one (lgm, src_fib_index1, addr1, src_adj_index1);
+      fib_table_entry_special_dpo_add (src_fib_index,
+                                      &src_fib_prefix,
+                                      FIB_SOURCE_LISP,
+                                      FIB_ENTRY_FLAG_EXCLUSIVE, src_dpo);
     }
 }
 
-/**
- * @brief IPv4 src lookup node.
- * @node lgpe-ip4-lookup
- *
- * The LISP IPv4 source lookup dispatch node.
- *
- * This is the IPv4 source lookup dispatch node. It first looks up the
- * adjacency hit in the main (destination) FIB and then uses its
- * <code>rewrite_header.sw_if_index</code>to find the source FIB wherein
- * the source IP is subsequently looked up. Data in the resulting adjacency
- * is used to decide the next node (the lisp_gpe interface) and if a flow
- * hash must be computed, when traffic can be load balanced over multiple
- * tunnels.
- *
- *
- * @param[in]   vm      vlib_main_t corresponding to current thread.
- * @param[in]   node    vlib_node_runtime_t data for this node.
- * @param[in]   frame   vlib_frame_t whose contents should be dispatched.
- *
- * @return number of vectors in frame.
- */
-always_inline uword
-lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
-                vlib_frame_t * from_frame)
+static void
+ip_address_to_46 (const ip_address_t * addr,
+                 ip46_address_t * a, fib_protocol_t * proto)
 {
-  u32 n_left_from, next_index, *from, *to_next;
-  lisp_gpe_main_t *lgm = &lisp_gpe_main;
-
-  from = vlib_frame_vector_args (from_frame);
-  n_left_from = from_frame->n_vectors;
-
-  next_index = node->cached_next_index;
-
-  while (n_left_from > 0)
+  *proto = (IP4 == ip_addr_version (addr) ?
+           FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
+  switch (*proto)
     {
-      u32 n_left_to_next;
-
-      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-
-      while (n_left_from >= 4 && n_left_to_next >= 2)
-       {
-         u32 bi0, bi1;
-         vlib_buffer_t *b0, *b1;
-         ip4_header_t *ip0, *ip1;
-         u32 dst_adj_index0, src_adj_index0, src_fib_index0;
-         u32 dst_adj_index1, src_adj_index1, src_fib_index1;
-         ip_adjacency_t *dst_adj0, *src_adj0, *dst_adj1, *src_adj1;
-         u32 next0, next1;
-
-         next0 = next1 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-
-         /* Prefetch next iteration. */
-         {
-           vlib_buffer_t *p2, *p3;
-
-           p2 = vlib_get_buffer (vm, from[2]);
-           p3 = vlib_get_buffer (vm, from[3]);
-
-           vlib_prefetch_buffer_header (p2, LOAD);
-           vlib_prefetch_buffer_header (p3, LOAD);
-
-           CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
-           CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
-         }
-
-         bi0 = from[0];
-         bi1 = from[1];
-         to_next[0] = bi0;
-         to_next[1] = bi1;
-         from += 2;
-         to_next += 2;
-         n_left_to_next -= 2;
-         n_left_from -= 2;
-
-         b0 = vlib_get_buffer (vm, bi0);
-         b1 = vlib_get_buffer (vm, bi1);
-
-         ip0 = vlib_buffer_get_current (b0);
-         ip1 = vlib_buffer_get_current (b1);
-
-         /* dst lookup was done by ip4 lookup */
-         dst_adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
-         dst_adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
-
-         dst_adj0 = ip_get_adjacency (lgm->lm4, dst_adj_index0);
-         dst_adj1 = ip_get_adjacency (lgm->lm4, dst_adj_index1);
-
-         src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
-         src_fib_index1 = dst_adj1->rewrite_header.sw_if_index;
-
-         ip4_src_fib_lookup_two (lgm, src_fib_index0, src_fib_index1,
-                                 &ip0->src_address, &ip1->src_address,
-                                 &src_adj_index0, &src_adj_index1);
-
-         /* if a source fib exists */
-         if (PREDICT_TRUE ((u32) ~ 0 != src_adj_index0
-                           && (u32) ~ 0 != src_adj_index1))
-           {
-             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
-             vnet_buffer (b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
-
-             src_adj0 = ip_get_adjacency (lgm->lm4, src_adj_index0);
-             src_adj1 = ip_get_adjacency (lgm->lm4, src_adj_index1);
-
-             next0 = src_adj0->explicit_fib_index;
-             next1 = src_adj1->explicit_fib_index;
-
-             /* prepare buffer for lisp-gpe output node */
-             vnet_buffer (b0)->sw_if_index[VLIB_TX] =
-               src_adj0->rewrite_header.sw_if_index;
-             vnet_buffer (b1)->sw_if_index[VLIB_TX] =
-               src_adj1->rewrite_header.sw_if_index;
-
-             /* if multipath: saved_lookup_next_index is reused to store
-              * nb of sub-tunnels. If greater than 1, multipath is on.
-              * Note that flow hash should be 0 after ipx lookup! */
-             if (PREDICT_TRUE (src_adj0->saved_lookup_next_index > 1))
-               vnet_buffer (b0)->ip.flow_hash =
-                 ip4_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT);
-
-             if (PREDICT_TRUE (src_adj1->saved_lookup_next_index > 1))
-               vnet_buffer (b1)->ip.flow_hash =
-                 ip4_compute_flow_hash (ip1, IP_FLOW_HASH_DEFAULT);
-           }
-         else
-           {
-             if ((u32) ~ 0 != src_adj_index0)
-               {
-                 vnet_buffer (b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
-                 src_adj0 = ip_get_adjacency (lgm->lm4, src_adj_index0);
-                 next0 = src_adj0->explicit_fib_index;
-                 vnet_buffer (b0)->sw_if_index[VLIB_TX] =
-                   src_adj0->rewrite_header.sw_if_index;
-
-                 if (PREDICT_TRUE (src_adj0->saved_lookup_next_index > 1))
-                   vnet_buffer (b0)->ip.flow_hash =
-                     ip4_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT);
-               }
-             else
-               {
-                 next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-               }
-
-             if ((u32) ~ 0 != src_adj_index1)
-               {
-                 vnet_buffer (b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
-                 src_adj1 = ip_get_adjacency (lgm->lm4, src_adj_index1);
-                 next1 = src_adj1->explicit_fib_index;
-                 vnet_buffer (b1)->sw_if_index[VLIB_TX] =
-                   src_adj1->rewrite_header.sw_if_index;
-                 if (PREDICT_TRUE (src_adj1->saved_lookup_next_index > 1))
-                   vnet_buffer (b1)->ip.flow_hash =
-                     ip4_compute_flow_hash (ip1, IP_FLOW_HASH_DEFAULT);
-               }
-             else
-               {
-                 next1 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-               }
-           }
-
-         /* mark the packets for CP lookup if needed */
-         if (PREDICT_FALSE (LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
-           vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
-         if (PREDICT_FALSE (LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next1))
-           vnet_buffer (b1)->lisp.overlay_afi = LISP_AFI_IP;
-
-         vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
-                                          n_left_to_next, bi0, bi1, next0,
-                                          next1);
-       }
-
-      while (n_left_from > 0 && n_left_to_next > 0)
-       {
-         vlib_buffer_t *b0;
-         ip4_header_t *ip0;
-         u32 bi0, dst_adj_index0, src_adj_index0, src_fib_index0;
-         u32 next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-         ip_adjacency_t *dst_adj0, *src_adj0;
-
-         bi0 = from[0];
-         to_next[0] = bi0;
-         from += 1;
-         to_next += 1;
-         n_left_from -= 1;
-         n_left_to_next -= 1;
-
-         b0 = vlib_get_buffer (vm, bi0);
-         ip0 = vlib_buffer_get_current (b0);
-
-         /* dst lookup was done by ip4 lookup */
-         dst_adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
-         dst_adj0 = ip_get_adjacency (lgm->lm4, dst_adj_index0);
-         src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
-
-         /* do src lookup */
-         ip4_src_fib_lookup_one (lgm, src_fib_index0, &ip0->src_address,
-                                 &src_adj_index0);
-
-         /* if a source fib exists */
-         if (PREDICT_TRUE ((u32) ~ 0 != src_adj_index0))
-           {
-             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
-             src_adj0 = ip_get_adjacency (lgm->lm4, src_adj_index0);
-             next0 = src_adj0->explicit_fib_index;
-
-             /* prepare packet for lisp-gpe output node */
-             vnet_buffer (b0)->sw_if_index[VLIB_TX] =
-               src_adj0->rewrite_header.sw_if_index;
-
-             /* if multipath: saved_lookup_next_index is reused to store
-              * nb of sub-tunnels. If greater than 1, multipath is on */
-             if (PREDICT_TRUE (src_adj0->saved_lookup_next_index > 1))
-               vnet_buffer (b0)->ip.flow_hash =
-                 ip4_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT);
-           }
-         else
-           {
-             next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-           }
-
-         if (PREDICT_FALSE (LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
-           vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
-
-         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
-                                          n_left_to_next, bi0, next0);
-       }
-      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    case FIB_PROTOCOL_IP4:
+      a->ip4 = addr->ip.v4;
+      break;
+    case FIB_PROTOCOL_IP6:
+      a->ip6 = addr->ip.v6;
+      break;
+    default:
+      ASSERT (0);
+      break;
     }
-  return from_frame->n_vectors;
 }
 
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (lgpe_ip4_lookup_node) = {
-  .function = lgpe_ip4_lookup,
-  .name = "lgpe-ip4-lookup",
-  .vector_size = sizeof (u32),
-
-  .type = VLIB_NODE_TYPE_INTERNAL,
-
-  .n_next_nodes = LGPE_IP4_LOOKUP_N_NEXT,
-  .next_nodes = {
-#define _(sym,str) [LGPE_IP4_LOOKUP_NEXT_##sym] = str,
-      foreach_lgpe_ip4_lookup_next
-#undef _
-  },
-};
-/* *INDENT-ON* */
-
-static u32
-ip6_src_fib_lookup (lisp_gpe_main_t * lgm, u32 src_fib_index,
-                   ip6_address_t * src)
+static fib_route_path_t *
+ip_src_fib_mk_paths (const lisp_fwd_path_t * paths)
 {
-  int i, len;
-  int rv;
-  BVT (clib_bihash_kv) kv, value;
-  ip6_src_fib_t *fib = pool_elt_at_index (lgm->ip6_src_fibs, src_fib_index);
+  const lisp_gpe_adjacency_t *ladj;
+  fib_route_path_t *rpaths = NULL;
+  u8 best_priority;
+  u32 ii;
 
-  len = vec_len (fib->prefix_lengths_in_search_order);
+  vec_validate (rpaths, vec_len (paths) - 1);
 
-  for (i = 0; i < len; i++)
-    {
-      int dst_address_length = fib->prefix_lengths_in_search_order[i];
-      ip6_address_t *mask;
+  best_priority = paths[0].priority;
 
-      ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
+  vec_foreach_index (ii, paths)
+  {
+    if (paths[0].priority != best_priority)
+      break;
 
-      mask = &fib->fib_masks[dst_address_length];
+    ladj = lisp_gpe_adjacency_get (paths[ii].lisp_adj);
 
-      kv.key[0] = src->as_u64[0] & mask->as_u64[0];
-      kv.key[1] = src->as_u64[1] & mask->as_u64[1];
-      kv.key[2] = dst_address_length;
+    ip_address_to_46 (&ladj->remote_rloc,
+                     &rpaths[ii].frp_addr, &rpaths[ii].frp_proto);
 
-      rv =
-       BV (clib_bihash_search_inline_2) (&fib->ip6_lookup_table, &kv,
-                                         &value);
-      if (rv == 0)
-       return value.value;
-    }
-
-  return 0;
-}
+    rpaths[ii].frp_sw_if_index = ladj->sw_if_index;
+    rpaths[ii].frp_weight = (paths[ii].weight ? paths[ii].weight : 1);
+    rpaths[ii].frp_label = MPLS_LABEL_INVALID;
+  }
 
-always_inline void
-ip6_src_fib_lookup_one (lisp_gpe_main_t * lgm, u32 src_fib_index0,
-                       ip6_address_t * addr0, u32 * src_adj_index0)
-{
-  /* if default route not hit in ip6 lookup */
-  if (PREDICT_TRUE (src_fib_index0 != (u32) ~ 0))
-    src_adj_index0[0] = ip6_src_fib_lookup (lgm, src_fib_index0, addr0);
-  else
-    src_adj_index0[0] = ~0;
-}
+  ASSERT (0 != vec_len (rpaths));
 
-always_inline void
-ip6_src_fib_lookup_two (lisp_gpe_main_t * lgm, u32 src_fib_index0,
-                       u32 src_fib_index1, ip6_address_t * addr0,
-                       ip6_address_t * addr1, u32 * src_adj_index0,
-                       u32 * src_adj_index1)
-{
-  /* if default route not hit in ip6 lookup */
-  if (PREDICT_TRUE
-      (src_fib_index0 != (u32) ~ 0 && src_fib_index1 != (u32) ~ 0))
-    {
-      src_adj_index0[0] = ip6_src_fib_lookup (lgm, src_fib_index0, addr0);
-      src_adj_index1[0] = ip6_src_fib_lookup (lgm, src_fib_index1, addr1);
-    }
-  else
-    {
-      ip6_src_fib_lookup_one (lgm, src_fib_index0, addr0, src_adj_index0);
-      ip6_src_fib_lookup_one (lgm, src_fib_index1, addr1, src_adj_index1);
-    }
+  return (rpaths);
 }
 
 /**
- * @brief IPv6 src lookup node.
- * @node lgpe-ip6-lookup
- *
- * The LISP IPv6 source lookup dispatch node.
+ * @brief Add route to IP4 or IP6 SRC FIB.
  *
- * This is the IPv6 source lookup dispatch node. It first looks up the
- * adjacency hit in the main (destination) FIB and then uses its
- * <code>rewrite_header.sw_if_index</code>to find the source FIB wherein
- * the source IP is subsequently looked up. Data in the resulting adjacency
- * is used to decide the next node (the lisp_gpe interface) and if a flow
- * hash must be computed, when traffic can be load balanced over multiple
- * tunnels.
+ * Adds a route to in the LISP SRC FIB for the tunnel.
  *
- * @param[in]   vm      vlib_main_t corresponding to current thread.
- * @param[in]   node    vlib_node_runtime_t data for this node.
- * @param[in]   frame   vlib_frame_t whose contents should be dispatched.
- *
- * @return number of vectors in frame.
+ * @param[in]   src_fib_index   The index/ID of the SRC FIB
+ * @param[in]   src_prefix      Source IP prefix.
+ * @param[in]   paths           The paths from which to construct the
+ *                              load balance
  */
-always_inline uword
-lgpe_ip6_lookup (vlib_main_t * vm, vlib_node_runtime_t * node,
-                vlib_frame_t * from_frame)
+void
+ip_src_fib_add_route (u32 src_fib_index,
+                     const ip_prefix_t * src_prefix,
+                     const lisp_fwd_path_t * paths)
 {
-  u32 n_left_from, next_index, *from, *to_next;
-  lisp_gpe_main_t *lgm = &lisp_gpe_main;
-
-  from = vlib_frame_vector_args (from_frame);
-  n_left_from = from_frame->n_vectors;
-
-  next_index = node->cached_next_index;
-
-  while (n_left_from > 0)
-    {
-      u32 n_left_to_next;
-
-      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-
-      while (n_left_from >= 4 && n_left_to_next >= 2)
-       {
-         u32 bi0, bi1;
-         vlib_buffer_t *b0, *b1;
-         ip6_header_t *ip0, *ip1;
-         u32 dst_adj_index0, src_adj_index0, src_fib_index0, dst_adj_index1,
-           src_adj_index1, src_fib_index1;
-         ip_adjacency_t *dst_adj0, *src_adj0, *dst_adj1, *src_adj1;
-         u32 next0, next1;
-
-         next0 = next1 = LGPE_IP6_LOOKUP_NEXT_LISP_CP_LOOKUP;
-
-         /* Prefetch next iteration. */
-         {
-           vlib_buffer_t *p2, *p3;
-
-           p2 = vlib_get_buffer (vm, from[2]);
-           p3 = vlib_get_buffer (vm, from[3]);
-
-           vlib_prefetch_buffer_header (p2, LOAD);
-           vlib_prefetch_buffer_header (p3, LOAD);
-
-           CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
-           CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
-         }
-
-         bi0 = from[0];
-         bi1 = from[1];
-         to_next[0] = bi0;
-         to_next[1] = bi1;
-         from += 2;
-         to_next += 2;
-         n_left_to_next -= 2;
-         n_left_from -= 2;
-
-         b0 = vlib_get_buffer (vm, bi0);
-         b1 = vlib_get_buffer (vm, bi1);
-
-         ip0 = vlib_buffer_get_current (b0);
-         ip1 = vlib_buffer_get_current (b1);
-
-         /* dst lookup was done by ip6 lookup */
-         dst_adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
-         dst_adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
-
-         dst_adj0 = ip_get_adjacency (lgm->lm6, dst_adj_index0);
-         dst_adj1 = ip_get_adjacency (lgm->lm6, dst_adj_index1);
-
-         src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
-         src_fib_index1 = dst_adj1->rewrite_header.sw_if_index;
-
-         ip6_src_fib_lookup_two (lgm, src_fib_index0, src_fib_index1,
-                                 &ip0->src_address, &ip1->src_address,
-                                 &src_adj_index0, &src_adj_index1);
-
-         /* if a source fib exists */
-         if (PREDICT_TRUE ((u32) ~ 0 != src_adj_index0
-                           && (u32) ~ 0 != src_adj_index1))
-           {
-             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
-             vnet_buffer (b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
+  fib_prefix_t src_fib_prefix;
+  fib_route_path_t *rpaths;
 
-             src_adj0 = ip_get_adjacency (lgm->lm6, src_adj_index0);
-             src_adj1 = ip_get_adjacency (lgm->lm6, src_adj_index1);
+  ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix);
 
-             next0 = src_adj0->explicit_fib_index;
-             next1 = src_adj1->explicit_fib_index;
+  rpaths = ip_src_fib_mk_paths (paths);
 
-             /* prepare buffer for lisp-gpe output node */
-             vnet_buffer (b0)->sw_if_index[VLIB_TX] =
-               src_adj0->rewrite_header.sw_if_index;
-             vnet_buffer (b1)->sw_if_index[VLIB_TX] =
-               src_adj1->rewrite_header.sw_if_index;
-
-             /* if multipath: saved_lookup_next_index is reused to store
-              * nb of sub-tunnels. If greater than 1, multipath is on.
-              * Note that flow hash should be 0 after ipx lookup! */
-             if (PREDICT_TRUE (src_adj0->saved_lookup_next_index > 1))
-               vnet_buffer (b0)->ip.flow_hash =
-                 ip6_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT);
-
-             if (PREDICT_TRUE (src_adj1->saved_lookup_next_index > 1))
-               vnet_buffer (b1)->ip.flow_hash =
-                 ip6_compute_flow_hash (ip1, IP_FLOW_HASH_DEFAULT);
-           }
-         else
-           {
-             if (src_adj_index0 != (u32) ~ 0)
-               {
-                 vnet_buffer (b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
-                 src_adj0 = ip_get_adjacency (lgm->lm6, src_adj_index0);
-                 next0 = src_adj0->explicit_fib_index;
-                 vnet_buffer (b0)->sw_if_index[VLIB_TX] =
-                   src_adj0->rewrite_header.sw_if_index;
-
-                 if (PREDICT_TRUE (src_adj0->saved_lookup_next_index > 1))
-                   vnet_buffer (b0)->ip.flow_hash =
-                     ip6_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT);
-               }
-             else
-               {
-                 next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-               }
-
-             if (src_adj_index1 != (u32) ~ 0)
-               {
-                 vnet_buffer (b1)->ip.adj_index[VLIB_TX] = src_adj_index1;
-                 src_adj1 = ip_get_adjacency (lgm->lm6, src_adj_index1);
-                 next1 = src_adj1->explicit_fib_index;
-                 vnet_buffer (b1)->sw_if_index[VLIB_TX] =
-                   src_adj1->rewrite_header.sw_if_index;
-
-                 if (PREDICT_TRUE (src_adj1->saved_lookup_next_index > 1))
-                   vnet_buffer (b1)->ip.flow_hash =
-                     ip6_compute_flow_hash (ip1, IP_FLOW_HASH_DEFAULT);
-               }
-             else
-               {
-                 next1 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-               }
-           }
-
-         /* mark the packets for CP lookup if needed */
-         if (PREDICT_FALSE (LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
-           vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
-         if (PREDICT_FALSE (LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next1))
-           vnet_buffer (b1)->lisp.overlay_afi = LISP_AFI_IP;
-
-         vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
-                                          n_left_to_next, bi0, bi1, next0,
-                                          next1);
-       }
-
-      while (n_left_from > 0 && n_left_to_next > 0)
-       {
-         vlib_buffer_t *b0;
-         ip6_header_t *ip0;
-         u32 bi0, dst_adj_index0, src_adj_index0, src_fib_index0;
-         u32 next0 = LGPE_IP6_LOOKUP_NEXT_LISP_CP_LOOKUP;
-         ip_adjacency_t *dst_adj0, *src_adj0;
-
-         bi0 = from[0];
-         to_next[0] = bi0;
-         from += 1;
-         to_next += 1;
-         n_left_from -= 1;
-         n_left_to_next -= 1;
-
-         b0 = vlib_get_buffer (vm, bi0);
-         ip0 = vlib_buffer_get_current (b0);
-
-         /* dst lookup was done by ip6 lookup */
-         dst_adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
-         dst_adj0 = ip_get_adjacency (lgm->lm6, dst_adj_index0);
-         src_fib_index0 = dst_adj0->rewrite_header.sw_if_index;
-
-         /* do src lookup */
-         ip6_src_fib_lookup_one (lgm, src_fib_index0, &ip0->src_address,
-                                 &src_adj_index0);
-
-         /* if a source fib exists */
-         if (PREDICT_TRUE (src_adj_index0 != (u32) ~ 0))
-           {
-             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = src_adj_index0;
-             src_adj0 = ip_get_adjacency (lgm->lm6, src_adj_index0);
-             next0 = src_adj0->explicit_fib_index;
-
-             /* prepare packet for lisp-gpe output node */
-             vnet_buffer (b0)->sw_if_index[VLIB_TX] =
-               src_adj0->rewrite_header.sw_if_index;
-
-             /* if multipath: saved_lookup_next_index is reused to store
-              * nb of sub-tunnels. If greater than 1, multipath is on */
-             if (PREDICT_TRUE (src_adj0->saved_lookup_next_index > 1))
-               vnet_buffer (b0)->ip.flow_hash =
-                 ip6_compute_flow_hash (ip0, IP_FLOW_HASH_DEFAULT);
-           }
-         else
-           {
-             next0 = LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP;
-           }
-
-         /* mark the packets for CP lookup if needed */
-         if (PREDICT_FALSE (LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP == next0))
-           vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_IP;
-
-         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
-                                          n_left_to_next, bi0, next0);
-       }
-      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
-    }
-  return from_frame->n_vectors;
+  fib_table_entry_update (src_fib_index,
+                         &src_fib_prefix,
+                         FIB_SOURCE_LISP, FIB_ENTRY_FLAG_NONE, rpaths);
+  vec_free (rpaths);
 }
 
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (lgpe_ip6_lookup_node) = {
-  .function = lgpe_ip6_lookup,
-  .name = "lgpe-ip6-lookup",
-  .vector_size = sizeof (u32),
-
-  .type = VLIB_NODE_TYPE_INTERNAL,
-
-  .n_next_nodes = LGPE_IP6_LOOKUP_N_NEXT,
-  .next_nodes = {
-#define _(sym,str) [LGPE_IP6_LOOKUP_NEXT_##sym] = str,
-      foreach_lgpe_ip6_lookup_next
-#undef _
-  },
-};
-/* *INDENT-ON* */
-
 /*
  * fd.io coding-style-patch-verification: ON
  *