A Protocol Independent Hierarchical FIB (VPP-352)
[vpp.git] / vnet / vnet / lisp-gpe / lisp_gpe.c
index 579422b..f05c6a2 100644 (file)
  */
 
 #include <vnet/lisp-gpe/lisp_gpe.h>
-#include <vppinfra/math.h>
+#include <vnet/lisp-gpe/lisp_gpe_adjacency.h>
+#include <vnet/adj/adj_midchain.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_path_list.h>
+#include <vnet/dpo/drop_dpo.h>
+#include <vnet/dpo/load_balance.h>
 
 /** LISP-GPE global state */
 lisp_gpe_main_t lisp_gpe_main;
 
 /**
- * @brief Compute IP-UDP-GPE sub-tunnel encap/rewrite header.
- *
- * @param[in]   t       Parent of the sub-tunnel.
- * @param[in]   st      Sub-tunnel.
- * @param[in]   lp      Local and remote locators used in the encap header.
- *
- * @return 0 on success.
+ * @brief A Pool of all LISP forwarding entries
  */
-static int
-lisp_gpe_rewrite (lisp_gpe_tunnel_t * t, lisp_gpe_sub_tunnel_t * st,
-                 locator_pair_t * lp)
-{
-  u8 *rw = 0;
-  lisp_gpe_header_t *lisp0;
-  int len;
-
-  if (ip_addr_version (&lp->lcl_loc) == IP4)
-    {
-      ip4_header_t *ip0;
-      ip4_udp_lisp_gpe_header_t *h0;
-      len = sizeof (*h0);
+static lisp_fwd_entry_t *lisp_fwd_entry_pool;
 
-      vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
+/**
+ * DB of all forwarding entries. The Key is:{l-EID,r-EID,vni}
+ * where the EID encodes L2 or L3
+ */
+static uword *lisp_gpe_fwd_entries;
 
-      h0 = (ip4_udp_lisp_gpe_header_t *) rw;
+static void
+create_fib_entries (lisp_fwd_entry_t * lfe)
+{
+  dpo_proto_t dproto;
 
-      /* Fixed portion of the (outer) ip4 header */
-      ip0 = &h0->ip4;
-      ip0->ip_version_and_header_length = 0x45;
-      ip0->ttl = 254;
-      ip0->protocol = IP_PROTOCOL_UDP;
+  dproto = (ip_prefix_version (&lfe->key->rmt.ippref) == IP4 ?
+           FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
 
-      /* we fix up the ip4 header length and checksum after-the-fact */
-      ip_address_copy_addr (&ip0->src_address, &lp->lcl_loc);
-      ip_address_copy_addr (&ip0->dst_address, &lp->rmt_loc);
-      ip0->checksum = ip4_header_checksum (ip0);
+  lfe->src_fib_index = ip_dst_fib_add_route (lfe->eid_fib_index,
+                                            &lfe->key->rmt.ippref);
 
-      /* UDP header, randomize src port on something, maybe? */
-      h0->udp.src_port = clib_host_to_net_u16 (4341);
-      h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe);
+  if (LISP_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
+    {
+      dpo_id_t dpo = DPO_NULL;
 
-      /* LISP-gpe header */
-      lisp0 = &h0->lisp;
+      switch (lfe->action)
+       {
+       case LISP_NO_ACTION:
+         /* TODO update timers? */
+       case LISP_FORWARD_NATIVE:
+         /* TODO check if route/next-hop for eid exists in fib and add
+          * more specific for the eid with the next-hop found */
+       case LISP_SEND_MAP_REQUEST:
+         /* insert tunnel that always sends map-request */
+         dpo_set (&dpo, DPO_LISP_CP, 0, dproto);
+         break;
+       case LISP_DROP:
+         /* for drop fwd entries, just add route, no need to add encap tunnel */
+         dpo_copy (&dpo, drop_dpo_get (dproto));
+         break;
+       }
+      ip_src_fib_add_route_w_dpo (lfe->src_fib_index,
+                                 &lfe->key->lcl.ippref, &dpo);
+      dpo_reset (&dpo);
     }
   else
     {
-      ip6_header_t *ip0;
-      ip6_udp_lisp_gpe_header_t *h0;
-      len = sizeof (*h0);
-
-      vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
-
-      h0 = (ip6_udp_lisp_gpe_header_t *) rw;
-
-      /* Fixed portion of the (outer) ip6 header */
-      ip0 = &h0->ip6;
-      ip0->ip_version_traffic_class_and_flow_label =
-       clib_host_to_net_u32 (0x6 << 28);
-      ip0->hop_limit = 254;
-      ip0->protocol = IP_PROTOCOL_UDP;
-
-      /* we fix up the ip6 header length after-the-fact */
-      ip_address_copy_addr (&ip0->src_address, &lp->lcl_loc);
-      ip_address_copy_addr (&ip0->dst_address, &lp->rmt_loc);
-
-      /* UDP header, randomize src port on something, maybe? */
-      h0->udp.src_port = clib_host_to_net_u16 (4341);
-      h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe);
-
-      /* LISP-gpe header */
-      lisp0 = &h0->lisp;
+      ip_src_fib_add_route (lfe->src_fib_index,
+                           &lfe->key->lcl.ippref, lfe->paths);
     }
-
-  lisp0->flags = t->flags;
-  lisp0->ver_res = t->ver_res;
-  lisp0->res = t->res;
-  lisp0->next_protocol = t->next_protocol;
-  lisp0->iid = clib_host_to_net_u32 (t->vni);
-
-  st->is_ip4 = ip_addr_version (&lp->lcl_loc) == IP4;
-  st->rewrite = rw;
-  return 0;
 }
 
-static int
-weight_cmp (normalized_sub_tunnel_weights_t * a,
-           normalized_sub_tunnel_weights_t * b)
+static void
+delete_fib_entries (lisp_fwd_entry_t * lfe)
 {
-  int cmp = a->weight - b->weight;
-  return (cmp == 0
-         ? a->sub_tunnel_index - b->sub_tunnel_index : (cmp > 0 ? -1 : 1));
+  ip_src_dst_fib_del_route (lfe->src_fib_index,
+                           &lfe->key->lcl.ippref,
+                           lfe->eid_fib_index, &lfe->key->rmt.ippref);
 }
 
-/**
- * @brief Computes sub-tunnel load balancing vector.
- *
- * Algorithm is identical to that used for building unequal-cost multipath
- * adjacencies. Saves normalized sub-tunnel weights and builds load-balancing
- * vector consisting of list of sub-tunnel indexes replicated according to
- * weight.
- *
- * @param[in]   t       Tunnel for which load balancing vector is computed.
- */
 static void
-compute_sub_tunnels_balancing_vector (lisp_gpe_tunnel_t * t)
+gid_to_dp_address (gid_address_t * g, dp_address_t * d)
 {
-  uword n_sts, i, n_nsts, n_nsts_left;
-  f64 sum_weight, norm, error, tolerance;
-  normalized_sub_tunnel_weights_t *nsts = 0, *stp;
-  lisp_gpe_sub_tunnel_t *sts = t->sub_tunnels;
-  u32 *st_lbv = 0;
-
-  /* Accept 1% error */
-  tolerance = .01;
-
-  n_sts = vec_len (sts);
-  vec_validate (nsts, 2 * n_sts - 1);
-
-  sum_weight = 0;
-  for (i = 0; i < n_sts; i++)
+  switch (gid_address_type (g))
     {
-      /* Find total weight to normalize weights. */
-      sum_weight += sts[i].weight;
-
-      /* build normalized sub tunnels vector */
-      nsts[i].weight = sts[i].weight;
-      nsts[i].sub_tunnel_index = i;
-    }
-
-  n_nsts = n_sts;
-  if (n_sts == 1)
-    {
-      nsts[0].weight = 1;
-      _vec_len (nsts) = 1;
-      goto build_lbv;
+    case GID_ADDR_IP_PREFIX:
+    case GID_ADDR_SRC_DST:
+      ip_prefix_copy (&d->ippref, &gid_address_ippref (g));
+      d->type = FID_ADDR_IP_PREF;
+      break;
+    case GID_ADDR_MAC:
+    default:
+      mac_copy (&d->mac, &gid_address_mac (g));
+      d->type = FID_ADDR_MAC;
+      break;
     }
+}
 
-  /* Sort sub-tunnels by weight */
-  qsort (nsts, n_nsts, sizeof (u32), (void *) weight_cmp);
+static lisp_fwd_entry_t *
+find_fwd_entry (lisp_gpe_main_t * lgm,
+               vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
+               lisp_gpe_fwd_entry_key_t * key)
+{
+  uword *p;
 
-  /* Save copies of all next hop weights to avoid being overwritten in loop below. */
-  for (i = 0; i < n_nsts; i++)
-    nsts[n_nsts + i].weight = nsts[i].weight;
+  memset (key, 0, sizeof (*key));
 
-  /* Try larger and larger power of 2 sized blocks until we
-     find one where traffic flows to within 1% of specified weights. */
-  for (n_nsts = max_pow2 (n_sts);; n_nsts *= 2)
+  if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid))
     {
-      error = 0;
-
-      norm = n_nsts / sum_weight;
-      n_nsts_left = n_nsts;
-      for (i = 0; i < n_sts; i++)
-       {
-         f64 nf = nsts[n_sts + i].weight * norm;
-         word n = flt_round_nearest (nf);
-
-         n = n > n_nsts_left ? n_nsts_left : n;
-         n_nsts_left -= n;
-         error += fabs (nf - n);
-         nsts[i].weight = n;
-       }
-
-      nsts[0].weight += n_nsts_left;
-
-      /* Less than 5% average error per adjacency with this size adjacency block? */
-      if (error <= tolerance * n_nsts)
-       {
-         /* Truncate any next hops with zero weight. */
-         _vec_len (nsts) = i;
-         break;
-       }
+      /*
+       * the ip version of the source is not set to ip6 when the
+       * source is all zeros. force it.
+       */
+      ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) =
+       ip_prefix_version (&gid_address_ippref (&a->rmt_eid));
     }
 
-build_lbv:
+  gid_to_dp_address (&a->rmt_eid, &key->rmt);
+  gid_to_dp_address (&a->lcl_eid, &key->lcl);
+  key->vni = a->vni;
 
-  /* build load balancing vector */
-  vec_foreach (stp, nsts)
-  {
-    for (i = 0; i < stp[0].weight; i++)
-      vec_add1 (st_lbv, stp[0].sub_tunnel_index);
-  }
+  p = hash_get_mem (lisp_gpe_fwd_entries, key);
 
-  t->sub_tunnels_lbv = st_lbv;
-  t->sub_tunnels_lbv_count = n_nsts;
-  t->norm_sub_tunnel_weights = nsts;
+  if (NULL != p)
+    {
+      return (pool_elt_at_index (lisp_fwd_entry_pool, p[0]));
+    }
+  return (NULL);
 }
 
-/** Create sub-tunnels and load-balancing vector for all locator pairs
- * associated to a tunnel.*/
-static void
-create_sub_tunnels (lisp_gpe_main_t * lgm, lisp_gpe_tunnel_t * t)
+static int
+lisp_gpe_fwd_entry_path_sort (void *a1, void *a2)
 {
-  lisp_gpe_sub_tunnel_t st;
-  locator_pair_t *lp = 0;
-  int i;
-
-  /* create sub-tunnels for all locator pairs */
-  for (i = 0; i < vec_len (t->locator_pairs); i++)
-    {
-      lp = &t->locator_pairs[i];
-      st.locator_pair_index = i;
-      st.parent_index = t - lgm->tunnels;
-      st.weight = lp->weight;
-
-      /* compute rewrite for sub-tunnel */
-      lisp_gpe_rewrite (t, &st, lp);
-      vec_add1 (t->sub_tunnels, st);
-    }
+  lisp_fwd_path_t *p1 = a1, *p2 = a2;
 
-  /* normalize weights and compute sub-tunnel load balancing vector */
-  compute_sub_tunnels_balancing_vector (t);
+  return (p1->priority - p2->priority);
 }
 
-#define foreach_copy_field                      \
-_(encap_fib_index)                              \
-_(decap_fib_index)                              \
-_(decap_next_index)                             \
-_(vni)                                          \
-_(action)
-
 /**
- * @brief Create/delete IP encapsulated tunnel.
+ * @brief Add/Delete LISP IP forwarding entry.
  *
- * Builds GPE tunnel for L2 or L3 packets and populates tunnel pool
- * @ref lisp_gpe_tunnel_by_key in @ref lisp_gpe_main_t.
+ * creation of forwarding entries for IP LISP overlay:
  *
- * @param[in]   a               Tunnel parameters.
- * @param[in]   is_l2           Flag indicating if encapsulated content is l2.
- * @param[out]  tun_index_res   Tunnel index.
+ * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
+ * @param[in]   a       Parameters for building the forwarding entry.
  *
  * @return 0 on success.
  */
 static int
-add_del_ip_tunnel (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, u8 is_l2,
-                  u32 * tun_index_res)
+add_ip_fwd_entry (lisp_gpe_main_t * lgm,
+                 vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
 {
-  lisp_gpe_main_t *lgm = &lisp_gpe_main;
-  lisp_gpe_tunnel_t *t = 0;
-  lisp_gpe_tunnel_key_t key;
-  lisp_gpe_sub_tunnel_t *stp = 0;
-  uword *p;
-
-  /* prepare tunnel key */
-  memset (&key, 0, sizeof (key));
-
-  /* fill in the key's remote eid */
-  if (!is_l2)
-    ip_prefix_copy (&key.rmt.ippref, &gid_address_ippref (&a->rmt_eid));
-  else
-    mac_copy (&key.rmt.mac, &gid_address_mac (&a->rmt_eid));
-
-  key.vni = clib_host_to_net_u32 (a->vni);
-
-  p = mhash_get (&lgm->lisp_gpe_tunnel_by_key, &key);
-
-  if (a->is_add)
-    {
-      /* adding a tunnel: tunnel must not already exist */
-      if (p)
-       return VNET_API_ERROR_INVALID_VALUE;
-
-      if (a->decap_next_index >= LISP_GPE_INPUT_N_NEXT)
-       return VNET_API_ERROR_INVALID_DECAP_NEXT;
+  lisp_gpe_fwd_entry_key_t key;
+  lisp_fwd_entry_t *lfe;
+  fib_protocol_t fproto;
 
-      pool_get_aligned (lgm->tunnels, t, CLIB_CACHE_LINE_BYTES);
-      memset (t, 0, sizeof (*t));
+  lfe = find_fwd_entry (lgm, a, &key);
 
-      /* copy from arg structure */
-#define _(x) t->x = a->x;
-      foreach_copy_field;
-#undef _
+  if (NULL != lfe)
+    /* don't support updates */
+    return VNET_API_ERROR_INVALID_VALUE;
 
-      t->locator_pairs = vec_dup (a->locator_pairs);
+  pool_get (lisp_fwd_entry_pool, lfe);
+  memset (lfe, 0, sizeof (*lfe));
+  lfe->key = clib_mem_alloc (sizeof (key));
+  memcpy (lfe->key, &key, sizeof (key));
 
-      /* if vni is non-default */
-      if (a->vni)
-       t->flags = LISP_GPE_FLAGS_I;
+  hash_set_mem (lisp_gpe_fwd_entries, lfe->key, lfe - lisp_fwd_entry_pool);
 
-      /* work in lisp-gpe not legacy mode */
-      t->flags |= LISP_GPE_FLAGS_P;
+  fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
+           FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
 
-      /* next proto */
-      if (!is_l2)
-       t->next_protocol = ip_prefix_version (&key.rmt.ippref) == IP4 ?
-         LISP_GPE_NEXT_PROTO_IP4 : LISP_GPE_NEXT_PROTO_IP6;
-      else
-       t->next_protocol = LISP_GPE_NEXT_PROTO_ETHERNET;
-
-      /* build sub-tunnels for lowest priority locator-pairs */
-      if (!a->is_negative)
-       create_sub_tunnels (lgm, t);
-
-      mhash_set (&lgm->lisp_gpe_tunnel_by_key, &key, t - lgm->tunnels, 0);
+  lfe->type = (a->is_negative ?
+              LISP_FWD_ENTRY_TYPE_NEGATIVE : LISP_FWD_ENTRY_TYPE_NORMAL);
+  lfe->eid_table_id = a->table_id;
+  lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto,
+                                                         lfe->eid_table_id);
 
-      /* return tunnel index */
-      if (tun_index_res)
-       tun_index_res[0] = t - lgm->tunnels;
-    }
-  else
+  if (LISP_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
     {
-      /* deleting a tunnel: tunnel must exist */
-      if (!p)
-       {
-         clib_warning ("Tunnel for eid %U doesn't exist!",
-                       format_gid_address, &a->rmt_eid);
-         return VNET_API_ERROR_NO_SUCH_ENTRY;
-       }
-
-      t = pool_elt_at_index (lgm->tunnels, p[0]);
+      lisp_fwd_path_t *path;
+      u32 index;
 
-      mhash_unset (&lgm->lisp_gpe_tunnel_by_key, &key, 0);
+      vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1);
 
-      vec_foreach (stp, t->sub_tunnels)
+      vec_foreach_index (index, a->locator_pairs)
       {
-       vec_free (stp->rewrite);
+       path = &lfe->paths[index];
+
+       path->priority = a->locator_pairs[index].priority;
+       path->weight = a->locator_pairs[index].weight;
+
+       path->lisp_adj =
+         lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs
+                                                     [index],
+                                                     lfe->eid_table_id,
+                                                     lfe->key->vni);
       }
-      vec_free (t->sub_tunnels);
-      vec_free (t->sub_tunnels_lbv);
-      vec_free (t->locator_pairs);
-      pool_put (lgm->tunnels, t);
+      vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort);
     }
 
-  return 0;
+  create_fib_entries (lfe);
+
+  return (0);
 }
 
-/**
- * @brief Build IP adjacency for LISP Source/Dest FIB.
- *
- * Because LISP forwarding does not follow typical IP forwarding path, the
- * adjacency's fields are overloaded (i.e., hijacked) to carry LISP specific
- * data concerning the lisp-gpe interface the packets hitting the adjacency
- * should be sent to and the tunnel that should be used.
- *
- * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
- * @param[out]  adj             Adjacency to be populated.
- * @param[in]   table_id        VRF for adjacency.
- * @param[in]   vni             Virtual Network identifier (tenant id).
- * @param[in]   tun_index       Tunnel index.
- * @param[in]   n_sub_tun       Number of sub-tunnels.
- * @param[in]   is_negative     Flag to indicate if the adjacency is for a
- *                              negative mapping.
- * @param[in]   action          Action to be taken for negative mapping.
- * @param[in]   ip_ver          IP version for the adjacency.
- *
- * @return 0 on success.
- */
-static int
-build_ip_adjacency (lisp_gpe_main_t * lgm, ip_adjacency_t * adj, u32 table_id,
-                   u32 vni, u32 tun_index, u32 n_sub_tun, u8 is_negative,
-                   u8 action, u8 ip_ver)
+static void
+del_ip_fwd_entry_i (lisp_fwd_entry_t * lfe)
 {
-  uword *lookup_next_index, *lgpe_sw_if_index, *lnip;
+  lisp_fwd_path_t *path;
+  fib_protocol_t fproto;
 
-  memset (adj, 0, sizeof (adj[0]));
-  adj->n_adj = 1;
-  /* fill in lookup_next_index with a 'legal' value to avoid problems */
-  adj->lookup_next_index = (ip_ver == IP4) ?
-    lgm->ip4_lookup_next_lgpe_ip4_lookup :
-    lgm->ip6_lookup_next_lgpe_ip6_lookup;
+  vec_foreach (path, lfe->paths)
+  {
+    lisp_gpe_adjacency_unlock (path->lisp_adj);
+  }
 
-  /* positive mapping */
-  if (!is_negative)
-    {
-      /* send packets that hit this adj to lisp-gpe interface output node in
-       * requested vrf. */
-      lnip = (ip_ver == IP4) ?
-       lgm->lgpe_ip4_lookup_next_index_by_table_id :
-       lgm->lgpe_ip6_lookup_next_index_by_table_id;
-      lookup_next_index = hash_get (lnip, table_id);
-      lgpe_sw_if_index = hash_get (lgm->l3_ifaces.sw_if_index_by_vni, vni);
-
-      /* the assumption is that the interface must've been created before
-       * programming the dp */
-      ASSERT (lookup_next_index != 0 && lgpe_sw_if_index != 0);
-
-      /* hijack explicit fib index to store lisp interface node index,
-       * if_address_index for the tunnel index and saved lookup next index
-       * for the number of sub tunnels */
-      adj->explicit_fib_index = lookup_next_index[0];
-      adj->if_address_index = tun_index;
-      adj->rewrite_header.sw_if_index = lgpe_sw_if_index[0];
-      adj->saved_lookup_next_index = n_sub_tun;
-    }
-  /* negative mapping */
-  else
-    {
-      adj->rewrite_header.sw_if_index = ~0;
-      adj->rewrite_header.next_index = ~0;
-      adj->if_address_index = tun_index;
+  delete_fib_entries (lfe);
 
-      switch (action)
-       {
-       case LISP_NO_ACTION:
-         /* TODO update timers? */
-       case LISP_FORWARD_NATIVE:
-         /* TODO check if route/next-hop for eid exists in fib and add
-          * more specific for the eid with the next-hop found */
-       case LISP_SEND_MAP_REQUEST:
-         /* insert tunnel that always sends map-request */
-         adj->explicit_fib_index = (ip_ver == IP4) ?
-           LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP :
-           LGPE_IP6_LOOKUP_NEXT_LISP_CP_LOOKUP;
-         break;
-       case LISP_DROP:
-         /* for drop fwd entries, just add route, no need to add encap tunnel */
-         adj->explicit_fib_index = (ip_ver == IP4 ?
-                                    LGPE_IP4_LOOKUP_NEXT_DROP :
-                                    LGPE_IP6_LOOKUP_NEXT_DROP);
-         break;
-       default:
-         return -1;
-       }
-    }
-  return 0;
+  fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
+           FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
+  fib_table_unlock (lfe->eid_fib_index, fproto);
+
+  hash_unset_mem (lisp_gpe_fwd_entries, lfe->key);
+  clib_mem_free (lfe->key);
+  pool_put (lisp_fwd_entry_pool, lfe);
 }
 
 /**
  * @brief Add/Delete LISP IP forwarding entry.
  *
- * Coordinates the creation/removal of forwarding entries for IP LISP overlay:
- * creates lisp-gpe tunnel, builds tunnel customized forwarding entry and
- * injects new route in Source/Dest FIB.
+ * removal of forwarding entries for IP LISP overlay:
  *
  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
  * @param[in]   a       Parameters for building the forwarding entry.
@@ -455,63 +250,21 @@ build_ip_adjacency (lisp_gpe_main_t * lgm, ip_adjacency_t * adj, u32 table_id,
  * @return 0 on success.
  */
 static int
-add_del_ip_fwd_entry (lisp_gpe_main_t * lgm,
-                     vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
+del_ip_fwd_entry (lisp_gpe_main_t * lgm,
+                 vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
 {
-  ip_adjacency_t adj, *adjp;
-  lisp_gpe_tunnel_t *t;
-  u32 rv, tun_index = ~0, n_sub_tuns = 0;
-  ip_prefix_t *rmt_pref, *lcl_pref;
-  u8 ip_ver;
-
-  rmt_pref = &gid_address_ippref (&a->rmt_eid);
-  lcl_pref = &gid_address_ippref (&a->lcl_eid);
-  ip_ver = ip_prefix_version (rmt_pref);
-
-  /* add/del tunnel to tunnels pool and prepares rewrite */
-  if (0 != a->locator_pairs)
-    {
-      rv = add_del_ip_tunnel (a, 0 /* is_l2 */ , &tun_index);
-      if (rv)
-       {
-         clib_warning ("failed to build tunnel!");
-         return rv;
-       }
-      if (a->is_add)
-       {
-         t = pool_elt_at_index (lgm->tunnels, tun_index);
-         n_sub_tuns = t->sub_tunnels_lbv_count;
-       }
-    }
-
-  /* setup adjacency for eid */
-  rv = build_ip_adjacency (lgm, &adj, a->table_id, a->vni, tun_index,
-                          n_sub_tuns, a->is_negative, a->action, ip_ver);
-
-  /* add/delete route for eid */
-  rv |= ip_sd_fib_add_del_route (lgm, rmt_pref, lcl_pref, a->table_id, &adj,
-                                a->is_add);
-
-  if (rv)
-    {
-      clib_warning ("failed to insert route for tunnel!");
-      return rv;
-    }
+  lisp_gpe_fwd_entry_key_t key;
+  lisp_fwd_entry_t *lfe;
 
-  /* check that everything worked */
-  if (CLIB_DEBUG && a->is_add)
-    {
-      u32 adj_index;
-      adj_index = ip_sd_fib_get_route (lgm, rmt_pref, lcl_pref, a->table_id);
-      ASSERT (adj_index != 0);
+  lfe = find_fwd_entry (lgm, a, &key);
 
-      adjp = ip_get_adjacency ((ip_ver == IP4) ? lgm->lm4 : lgm->lm6,
-                              adj_index);
+  if (NULL == lfe)
+    /* no such entry */
+    return VNET_API_ERROR_INVALID_VALUE;
 
-      ASSERT (adjp != 0 && adjp->if_address_index == tun_index);
-    }
+  del_ip_fwd_entry_i (lfe);
 
-  return rv;
+  return (0);
 }
 
 static void
@@ -536,7 +289,7 @@ make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6],
  *
  * @return index of mapping matching the lookup key.
  */
-u32
+index_t
 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
                    u8 dst_mac[6])
 {
@@ -555,7 +308,7 @@ lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
        return value.value;
     }
 
-  return ~0;
+  return lisp_gpe_main.l2_lb_miss;
 }
 
 /**
@@ -601,6 +354,12 @@ l2_fib_init (lisp_gpe_main_t * lgm)
   BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib",
                         1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS),
                         L2_FIB_DEFAULT_HASH_MEMORY_SIZE);
+
+  /*
+   * the result from a 'miss' in a L2 Table
+   */
+  lgm->l2_lb_miss = load_balance_create (1, DPO_PROTO_IP4, 0);
+  load_balance_set_bucket (lgm->l2_lb_miss, 0, drop_dpo_get (DPO_PROTO_IP4));
 }
 
 /**
@@ -618,27 +377,75 @@ static int
 add_del_l2_fwd_entry (lisp_gpe_main_t * lgm,
                      vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
 {
-  int rv;
-  u32 tun_index;
-  bd_main_t *bdm = &bd_main;
-  uword *bd_indexp;
-
-  /* create tunnel */
-  rv = add_del_ip_tunnel (a, 1 /* is_l2 */ , &tun_index);
-  if (rv)
-    return rv;
-
-  bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id);
-  if (!bd_indexp)
-    {
-      clib_warning ("bridge domain %d doesn't exist", a->bd_id);
-      return -1;
-    }
-
-  /* add entry to l2 lisp fib */
-  lisp_l2_fib_add_del_entry (lgm, bd_indexp[0], gid_address_mac (&a->lcl_eid),
-                            gid_address_mac (&a->rmt_eid), tun_index,
-                            a->is_add);
+  /* lisp_gpe_fwd_entry_key_t key; */
+  /* lisp_fwd_entry_t *lfe; */
+  /* fib_protocol_t fproto; */
+  /* uword *bd_indexp; */
+
+  /* bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); */
+  /* if (!bd_indexp) */
+  /*   { */
+  /*     clib_warning ("bridge domain %d doesn't exist", a->bd_id); */
+  /*     return -1; */
+  /*   } */
+
+  /* lfe = find_fwd_entry(lgm, a, &key); */
+
+  /* if (NULL != lfe) */
+  /*   /\* don't support updates *\/ */
+  /*   return VNET_API_ERROR_INVALID_VALUE; */
+
+  /* int rv; */
+  /* u32 tun_index; */
+  /* fib_node_index_t old_path_list; */
+  /* bd_main_t *bdm = &bd_main; */
+  /* fib_route_path_t *rpaths; */
+  /* lisp_gpe_tunnel_t *t; */
+  /* const dpo_id_t *dpo; */
+  /* index_t lbi; */
+
+  /* /\* create tunnel *\/ */
+  /* rv = add_del_ip_tunnel (a, 1 /\* is_l2 *\/ , &tun_index, NULL); */
+  /* if (rv) */
+  /*   return rv; */
+
+  /* bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); */
+  /* if (!bd_indexp) */
+  /*   { */
+  /*     clib_warning ("bridge domain %d doesn't exist", a->bd_id); */
+  /*     return -1; */
+  /*   } */
+
+  /* t = pool_elt_at_index (lgm->tunnels, tun_index); */
+  /* old_path_list = t->l2_path_list; */
+
+  /* if (LISP_NO_ACTION == t->action) */
+  /*   { */
+  /*     rpaths = lisp_gpe_mk_paths_for_sub_tunnels (t); */
+
+  /*     t->l2_path_list = fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, */
+  /*                                          rpaths); */
+
+  /*     vec_free (rpaths); */
+  /*     fib_path_list_lock (t->l2_path_list); */
+
+  /*     dpo = fib_path_list_contribute_forwarding (t->l2_path_list, */
+  /*                                             FIB_FORW_CHAIN_TYPE_UNICAST_IP); */
+  /*     lbi = dpo->dpoi_index; */
+  /*   } */
+  /* else if (LISP_SEND_MAP_REQUEST == t->action) */
+  /*   { */
+  /*     lbi = lgm->l2_lb_cp_lkup; */
+  /*   } */
+  /* else */
+  /*   { */
+  /*     lbi = lgm->l2_lb_miss; */
+  /*   } */
+  /* fib_path_list_unlock (old_path_list); */
+
+  /* /\* add entry to l2 lisp fib *\/ */
+  /* lisp_l2_fib_add_del_entry (lgm, bd_indexp[0], gid_address_mac (&a->lcl_eid), */
+  /*                         gid_address_mac (&a->rmt_eid), lbi, a->is_add); */
   return 0;
 }
 
@@ -669,7 +476,11 @@ vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
   switch (type)
     {
     case GID_ADDR_IP_PREFIX:
-      return add_del_ip_fwd_entry (lgm, a);
+      if (a->is_add)
+       return add_ip_fwd_entry (lgm, a);
+      else
+       return del_ip_fwd_entry (lgm, a);
+      break;
     case GID_ADDR_MAC:
       return add_del_l2_fwd_entry (lgm, a);
     default:
@@ -807,103 +618,77 @@ done:
 
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (lisp_gpe_add_del_fwd_entry_command, static) = {
-  .path = "lisp gpe tunnel",
-  .short_help = "lisp gpe tunnel add/del vni <vni> vrf <vrf> [leid <leid>]"
+  .path = "lisp gpe entry",
+  .short_help = "lisp gpe entry add/del vni <vni> vrf <vrf> [leid <leid>]"
       "reid <reid> [loc-pair <lloc> <rloc> p <priority> w <weight>] "
       "[negative action <action>]",
   .function = lisp_gpe_add_del_fwd_entry_command_fn,
 };
 /* *INDENT-ON* */
 
-/** Format LISP-GPE next indexes. */
 static u8 *
-format_decap_next (u8 * s, va_list * args)
+format_lisp_fwd_path (u8 * s, va_list ap)
 {
-  u32 next_index = va_arg (*args, u32);
+  lisp_fwd_path_t *lfp = va_arg (ap, lisp_fwd_path_t *);
 
-  switch (next_index)
-    {
-    case LISP_GPE_INPUT_NEXT_DROP:
-      return format (s, "drop");
-    case LISP_GPE_INPUT_NEXT_IP4_INPUT:
-      return format (s, "ip4");
-    case LISP_GPE_INPUT_NEXT_IP6_INPUT:
-      return format (s, "ip6");
-    default:
-      return format (s, "unknown %d", next_index);
-    }
-  return s;
+  s = format (s, "pirority:%d weight:%d ", lfp->priority, lfp->weight);
+  s = format (s, "adj:[%U]\n",
+             format_lisp_gpe_adjacency,
+             lisp_gpe_adjacency_get (lfp->lisp_adj),
+             LISP_GPE_ADJ_FORMAT_FLAG_NONE);
+
+  return (s);
 }
 
-/** Format LISP-GPE tunnel. */
-u8 *
-format_lisp_gpe_tunnel (u8 * s, va_list * args)
+static u8 *
+format_lisp_gpe_fwd_entry (u8 * s, va_list ap)
 {
-  lisp_gpe_tunnel_t *t = va_arg (*args, lisp_gpe_tunnel_t *);
-  lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
-  locator_pair_t *lp = 0;
-  normalized_sub_tunnel_weights_t *nstw;
-
-  s =
-    format (s, "tunnel %d vni %d (0x%x)\n", t - lgm->tunnels, t->vni, t->vni);
-  s =
-    format (s, " fibs: encap %d, decap %d decap next %U\n",
-           t->encap_fib_index, t->decap_fib_index, format_decap_next,
-           t->decap_next_index);
-  s = format (s, " lisp ver %d ", (t->ver_res >> 6));
-
-#define _(n,v) if (t->flags & v) s = format (s, "%s-bit ", #n);
-  foreach_lisp_gpe_flag_bit;
-#undef _
-
-  s = format (s, "next_protocol %d ver_res %x res %x\n",
-             t->next_protocol, t->ver_res, t->res);
-
-  s = format (s, " locator-pairs:\n");
-  vec_foreach (lp, t->locator_pairs)
-  {
-    s = format (s, "  local: %U remote: %U weight %d\n",
-               format_ip_address, &lp->lcl_loc, format_ip_address,
-               &lp->rmt_loc, lp->weight);
-  }
+  lisp_fwd_entry_t *lfe = va_arg (ap, lisp_fwd_entry_t *);
 
-  s = format (s, " active sub-tunnels:\n");
-  vec_foreach (nstw, t->norm_sub_tunnel_weights)
-  {
-    lp = vec_elt_at_index (t->locator_pairs, nstw->sub_tunnel_index);
-    s = format (s, "  local: %U remote: %U weight %d\n", format_ip_address,
-               &lp->lcl_loc, format_ip_address, &lp->rmt_loc, nstw->weight);
-  }
-  return s;
+  s = format (s, "VNI:%d VRF:%d EID: %U -> %U",
+             lfe->key->vni, lfe->eid_table_id,
+             format_fid_address, &lfe->key->lcl,
+             format_fid_address, &lfe->key->rmt);
+  if (LISP_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
+    {
+      s = format (s, "\n Negative - action:%U",
+                 format_negative_mapping_action, lfe->action);
+    }
+  else
+    {
+      lisp_fwd_path_t *path;
+
+      s = format (s, "\n via:");
+      vec_foreach (path, lfe->paths)
+      {
+       s = format (s, "\n  %U", format_lisp_fwd_path, path);
+      }
+    }
+
+  return (s);
 }
 
-/** CLI command to show LISP-GPE tunnels. */
 static clib_error_t *
-show_lisp_gpe_tunnel_command_fn (vlib_main_t * vm,
-                                unformat_input_t * input,
-                                vlib_cli_command_t * cmd)
+lisp_gpe_fwd_entry_show (vlib_main_t * vm,
+                        unformat_input_t * input, vlib_cli_command_t * cmd)
 {
-  lisp_gpe_main_t *lgm = &lisp_gpe_main;
-  lisp_gpe_tunnel_t *t;
-
-  if (pool_elts (lgm->tunnels) == 0)
-    vlib_cli_output (vm, "No lisp-gpe tunnels configured...");
+  lisp_fwd_entry_t *lfe;
 
-  /* *INDENT-OFF* */
-  pool_foreach (t, lgm->tunnels,
+/* *INDENT-OFF* */
+  pool_foreach (lfe, lisp_fwd_entry_pool,
   ({
-    vlib_cli_output (vm, "%U", format_lisp_gpe_tunnel, t);
+    vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe);
   }));
-  /* *INDENT-ON* */
+/* *INDENT-ON* */
 
-  return 0;
+  return (NULL);
 }
 
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (show_lisp_gpe_tunnel_command, static) =
-{
-  .path = "show lisp gpe tunnel",
-  .function = show_lisp_gpe_tunnel_command_fn,
+VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = {
+  .path = "show lisp gpe entry",
+  .short_help = "show lisp gpe entry vni <vni> vrf <vrf> [leid <leid>] reid <reid>",
+  .function = lisp_gpe_fwd_entry_show,
 };
 /* *INDENT-ON* */
 
@@ -921,29 +706,9 @@ clib_error_t *
 vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a)
 {
   lisp_gpe_main_t *lgm = &lisp_gpe_main;
-  vnet_main_t *vnm = lgm->vnet_main;
 
   if (a->is_en)
     {
-      /* add lgpe_ip4_lookup as possible next_node for ip4 lookup */
-      if (lgm->ip4_lookup_next_lgpe_ip4_lookup == ~0)
-       {
-         lgm->ip4_lookup_next_lgpe_ip4_lookup =
-           vlib_node_add_next (vnm->vlib_main, ip4_lookup_node.index,
-                               lgpe_ip4_lookup_node.index);
-       }
-      /* add lgpe_ip6_lookup as possible next_node for ip6 lookup */
-      if (lgm->ip6_lookup_next_lgpe_ip6_lookup == ~0)
-       {
-         lgm->ip6_lookup_next_lgpe_ip6_lookup =
-           vlib_node_add_next (vnm->vlib_main, ip6_lookup_node.index,
-                               lgpe_ip6_lookup_node.index);
-       }
-      else
-       {
-         /* ask cp to re-add ifaces and defaults */
-       }
-
       lgm->is_en = 1;
     }
   else
@@ -951,37 +716,17 @@ vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a)
       CLIB_UNUSED (uword * val);
       hash_pair_t *p;
       u32 *dp_tables = 0, *dp_table;
-      lisp_gpe_tunnel_key_t *tunnels = 0, *tunnel;
-      vnet_lisp_gpe_add_del_fwd_entry_args_t _at, *at = &_at;
       vnet_lisp_gpe_add_del_iface_args_t _ai, *ai = &_ai;
+      lisp_fwd_entry_t *lfe;
 
-      /* remove all tunnels */
-
+      /* remove all entries */
       /* *INDENT-OFF* */
-      mhash_foreach(tunnel, val, &lgm->lisp_gpe_tunnel_by_key, ({
-        vec_add1(tunnels, tunnel[0]);
+      pool_foreach (lfe, lisp_fwd_entry_pool,
+      ({
+       del_ip_fwd_entry_i (lfe);
       }));
       /* *INDENT-ON* */
 
-      vec_foreach (tunnel, tunnels)
-      {
-       memset (at, 0, sizeof (at[0]));
-       at->is_add = 0;
-       if (tunnel->rmt.type == GID_ADDR_IP_PREFIX)
-         {
-           gid_address_type (&at->rmt_eid) = GID_ADDR_IP_PREFIX;
-           ip_prefix_copy (&gid_address_ippref (&at->rmt_eid),
-                           &tunnel->rmt.ippref);
-         }
-       else
-         {
-           gid_address_type (&at->rmt_eid) = GID_ADDR_MAC;
-           mac_copy (&gid_address_mac (&at->rmt_eid), &tunnel->rmt.mac);
-         }
-       vnet_lisp_gpe_add_del_fwd_entry (at, 0);
-      }
-      vec_free (tunnels);
-
       /* disable all l3 ifaces */
 
       /* *INDENT-OFF* */
@@ -1109,6 +854,7 @@ format_vnet_lisp_gpe_status (u8 * s, va_list * args)
   return format (s, "%s", lgm->is_en ? "enabled" : "disabled");
 }
 
+
 /** LISP-GPE init function. */
 clib_error_t *
 lisp_gpe_init (vlib_main_t * vm)
@@ -1128,11 +874,10 @@ lisp_gpe_init (vlib_main_t * vm)
   lgm->im6 = &ip6_main;
   lgm->lm4 = &ip4_main.lookup_main;
   lgm->lm6 = &ip6_main.lookup_main;
-  lgm->ip4_lookup_next_lgpe_ip4_lookup = ~0;
-  lgm->ip6_lookup_next_lgpe_ip6_lookup = ~0;
 
-  mhash_init (&lgm->lisp_gpe_tunnel_by_key, sizeof (uword),
-             sizeof (lisp_gpe_tunnel_key_t));
+  lisp_gpe_fwd_entries = hash_create_mem (0,
+                                         sizeof (lisp_gpe_fwd_entry_key_t),
+                                         sizeof (uword));
 
   l2_fib_init (lgm);