LISP: Map-server fallback feature
[vpp.git] / src / vnet / lisp-cp / control.c
index f0383e1..72af525 100644 (file)
 #include <vnet/lisp-cp/lisp_msg_serdes.h>
 #include <vnet/lisp-gpe/lisp_gpe_fwd_entry.h>
 #include <vnet/lisp-gpe/lisp_gpe_tenant.h>
+#include <vnet/lisp-gpe/lisp_gpe_tunnel.h>
 #include <vnet/fib/fib_entry.h>
 #include <vnet/fib/fib_table.h>
+#include <vnet/ethernet/arp_packet.h>
+#include <vnet/ethernet/packet.h>
 
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 
+#define MAX_VALUE_U24 0xffffff
+
 lisp_cp_main_t lisp_control_main;
 
 u8 *format_lisp_cp_input_trace (u8 * s, va_list * args);
@@ -43,13 +48,6 @@ typedef struct
   u8 smr_invoked;
 } map_request_args_t;
 
-typedef struct
-{
-  u64 nonce;
-  u8 is_rloc_probe;
-  mapping_t *mappings;
-} map_records_arg_t;
-
 u8
 vnet_lisp_get_map_request_mode (void)
 {
@@ -284,6 +282,7 @@ dp_del_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
   if (fe->is_src_dst)
     gid_address_copy (&a->lcl_eid, &fe->leid);
 
+  vnet_lisp_gpe_del_fwd_counters (a, feip[0]);
   vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index);
 
   /* delete entry in fwd table */
@@ -487,6 +486,7 @@ dp_add_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
     gid_address_copy (&a->rmt_eid, rmt_eid);
 
   a->vni = gid_address_vni (&a->rmt_eid);
+  a->is_src_dst = is_src_dst;
 
   /* get vrf or bd_index associated to vni */
   type = gid_address_type (&a->rmt_eid);
@@ -529,10 +529,18 @@ dp_add_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
       a->action = rmt_map->action;
     }
 
-  vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index);
+  rv = vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index);
+  if (rv)
+    {
+      if (a->locator_pairs)
+       vec_free (a->locator_pairs);
+      return;
+    }
 
-  /* add tunnel to fwd entry table XXX check return value from DP insertion */
+  /* add tunnel to fwd entry table */
   pool_get (lcm->fwd_entry_pool, fe);
+  vnet_lisp_gpe_add_fwd_counters (a, fe - lcm->fwd_entry_pool);
+
   fe->locator_pairs = a->locator_pairs;
   gid_address_copy (&fe->reid, &a->rmt_eid);
 
@@ -662,6 +670,9 @@ vnet_lisp_add_del_map_server (ip_address_t * addr, u8 is_add)
       memset (ms, 0, sizeof (*ms));
       ip_address_copy (&ms->address, addr);
       vec_add1 (lcm->map_servers, ms[0]);
+
+      if (vec_len (lcm->map_servers) == 1)
+       lcm->do_map_server_election = 1;
     }
   else
     {
@@ -670,6 +681,9 @@ vnet_lisp_add_del_map_server (ip_address_t * addr, u8 is_add)
          ms = vec_elt_at_index (lcm->map_servers, i);
          if (!ip_address_cmp (&ms->address, addr))
            {
+             if (!ip_address_cmp (&ms->address, &lcm->active_map_server))
+               lcm->do_map_server_election = 1;
+
              vec_del1 (lcm->map_servers, i);
              break;
            }
@@ -691,6 +705,20 @@ vnet_lisp_map_cache_add_del (vnet_lisp_add_del_mapping_args_t * a,
   mapping_t *m, *old_map;
   u32 **eid_indexes;
 
+  if (gid_address_type (&a->eid) == GID_ADDR_NSH)
+    {
+      if (gid_address_vni (&a->eid) != 0)
+       {
+         clib_warning ("Supported only default VNI for NSH!");
+         return VNET_API_ERROR_INVALID_ARGUMENT;
+       }
+      if (gid_address_nsh_spi (&a->eid) > MAX_VALUE_U24)
+       {
+         clib_warning ("SPI is greater than 24bit!");
+         return VNET_API_ERROR_INVALID_ARGUMENT;
+       }
+    }
+
   mi = gid_dictionary_lookup (&lcm->mapping_index_by_gid, &a->eid);
   old_map = mi != ~0 ? pool_elt_at_index (lcm->mapping_pool, mi) : 0;
   if (a->is_add)
@@ -806,7 +834,7 @@ vnet_lisp_add_del_local_mapping (vnet_lisp_add_del_mapping_args_t * a,
   else if (GID_ADDR_MAC == type)
     dp_table = hash_get (lcm->bd_id_by_vni, vni);
 
-  if (!dp_table)
+  if (!dp_table && GID_ADDR_NSH != type)
     {
       clib_warning ("vni %d not associated to a %s!", vni,
                    GID_ADDR_IP_PREFIX == type ? "vrf" : "bd");
@@ -817,6 +845,99 @@ vnet_lisp_add_del_local_mapping (vnet_lisp_add_del_mapping_args_t * a,
   return vnet_lisp_map_cache_add_del (a, map_index_result);
 }
 
+static void
+add_l2_arp_bd (BVT (clib_bihash_kv) * kvp, void *arg)
+{
+  u32 **ht = arg;
+  u32 bd = (u32) kvp->key[0];
+  hash_set (ht[0], bd, 0);
+}
+
+u32 *
+vnet_lisp_l2_arp_bds_get (void)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  u32 *bds = 0;
+
+  gid_dict_foreach_l2_arp_entry (&lcm->mapping_index_by_gid,
+                                add_l2_arp_bd, &bds);
+  return bds;
+}
+
+typedef struct
+{
+  void *vector;
+  u32 bd;
+} lisp_add_l2_arp_args_t;
+
+static void
+add_l2_arp_entry (BVT (clib_bihash_kv) * kvp, void *arg)
+{
+  lisp_add_l2_arp_args_t *a = arg;
+  lisp_api_l2_arp_entry_t **vector = a->vector, e;
+
+  if ((u32) kvp->key[0] == a->bd)
+    {
+      mac_copy (e.mac, (void *) &kvp->value);
+      e.ip4 = (u32) kvp->key[1];
+      vec_add1 (vector[0], e);
+    }
+}
+
+lisp_api_l2_arp_entry_t *
+vnet_lisp_l2_arp_entries_get_by_bd (u32 bd)
+{
+  lisp_api_l2_arp_entry_t *entries = 0;
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  lisp_add_l2_arp_args_t a;
+
+  a.vector = &entries;
+  a.bd = bd;
+
+  gid_dict_foreach_l2_arp_entry (&lcm->mapping_index_by_gid,
+                                add_l2_arp_entry, &a);
+  return entries;
+}
+
+int
+vnet_lisp_add_del_l2_arp_entry (gid_address_t * key, u8 * mac, u8 is_add)
+{
+  if (vnet_lisp_enable_disable_status () == 0)
+    {
+      clib_warning ("LISP is disabled!");
+      return VNET_API_ERROR_LISP_DISABLED;
+    }
+
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  int rc = 0;
+
+  u64 res = gid_dictionary_lookup (&lcm->mapping_index_by_gid, key);
+  if (is_add)
+    {
+      if (res != GID_LOOKUP_MISS_L2)
+       {
+         clib_warning ("Entry %U exists in DB!", format_gid_address, key);
+         return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
+       }
+      u64 val = mac_to_u64 (mac);
+      gid_dictionary_add_del (&lcm->mapping_index_by_gid, key, val,
+                             1 /* is_add */ );
+    }
+  else
+    {
+      if (res == GID_LOOKUP_MISS_L2)
+       {
+         clib_warning ("ONE ARP entry %U not found - cannot delete!",
+                       format_gid_address, key);
+         return -1;
+       }
+      gid_dictionary_add_del (&lcm->mapping_index_by_gid, key, 0,
+                             0 /* is_add */ );
+    }
+
+  return rc;
+}
+
 int
 vnet_lisp_eid_table_map (u32 vni, u32 dp_id, u8 is_l2, u8 is_add)
 {
@@ -826,7 +947,7 @@ vnet_lisp_eid_table_map (u32 vni, u32 dp_id, u8 is_l2, u8 is_add)
   if (vnet_lisp_enable_disable_status () == 0)
     {
       clib_warning ("LISP is disabled!");
-      return -1;
+      return VNET_API_ERROR_LISP_DISABLED;
     }
 
   dp_table_by_vni = is_l2 ? &lcm->bd_id_by_vni : &lcm->table_id_by_vni;
@@ -863,11 +984,11 @@ vnet_lisp_eid_table_map (u32 vni, u32 dp_id, u8 is_l2, u8 is_add)
                        "mapping!", vni, dp_id);
          return -1;
        }
-      hash_unset (dp_table_by_vni[0], vni);
-      hash_unset (vni_by_dp_table[0], dp_id);
-
       /* remove dp iface */
       dp_add_del_iface (lcm, vni, is_l2, 0);
+
+      hash_unset (dp_table_by_vni[0], vni);
+      hash_unset (vni_by_dp_table[0], dp_id);
     }
   return 0;
 
@@ -1038,18 +1159,6 @@ vnet_lisp_add_del_mapping (gid_address_t * eid, locator_t * rlocs, u8 action,
       return VNET_API_ERROR_LISP_DISABLED;
     }
 
-  /* check if none of the locators match localy configured address */
-  vec_foreach (loc, rlocs)
-  {
-    ip_prefix_t *p = &gid_address_ippref (&loc->address);
-    if (is_local_ip (lcm, &ip_prefix_addr (p)))
-      {
-       clib_warning ("RLOC %U matches a local address!",
-                     format_gid_address, &loc->address);
-       return VNET_API_ERROR_LISP_RLOC_LOCAL;
-      }
-  }
-
   if (res_map_index)
     res_map_index[0] = ~0;
 
@@ -1063,6 +1172,18 @@ vnet_lisp_add_del_mapping (gid_address_t * eid, locator_t * rlocs, u8 action,
 
   if (is_add)
     {
+      /* check if none of the locators match localy configured address */
+      vec_foreach (loc, rlocs)
+      {
+       ip_prefix_t *p = &gid_address_ippref (&loc->address);
+       if (is_local_ip (lcm, &ip_prefix_addr (p)))
+         {
+           clib_warning ("RLOC %U matches a local address!",
+                         format_gid_address, &loc->address);
+           return VNET_API_ERROR_LISP_RLOC_LOCAL;
+         }
+      }
+
       /* overwrite: if mapping already exists, decide if locators should be
        * updated and be done */
       if (old_map && gid_address_cmp (&old_map->eid, eid) == 0)
@@ -1228,13 +1349,25 @@ vnet_lisp_add_del_adjacency (vnet_lisp_add_del_adjacency_args_t * a)
 
   if (a->is_add)
     {
-      /* TODO 1) check if src/dst 2) once we have src/dst working, use it in
-       * delete*/
-
       /* check if source eid has an associated mapping. If pitr mode is on,
        * just use the pitr's mapping */
-      local_mi = lcm->lisp_pitr ? lcm->pitr_map_index :
-       gid_dictionary_lookup (&lcm->mapping_index_by_gid, &a->leid);
+      if (lcm->lisp_pitr)
+       local_mi = lcm->pitr_map_index;
+      else
+       {
+         if (gid_address_type (&a->reid) == GID_ADDR_NSH)
+           {
+             if (lcm->nsh_map_index == ~0)
+               local_mi = GID_LOOKUP_MISS;
+             else
+               local_mi = lcm->nsh_map_index;
+           }
+         else
+           {
+             local_mi = gid_dictionary_lookup (&lcm->mapping_index_by_gid,
+                                               &a->leid);
+           }
+       }
 
       if (GID_LOOKUP_MISS == local_mi)
        {
@@ -1274,6 +1407,57 @@ vnet_lisp_set_map_request_mode (u8 mode)
   return 0;
 }
 
+int
+vnet_lisp_nsh_set_locator_set (u8 * locator_set_name, u8 is_add)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
+  u32 locator_set_index = ~0;
+  mapping_t *m;
+  uword *p;
+
+  if (vnet_lisp_enable_disable_status () == 0)
+    {
+      clib_warning ("LISP is disabled!");
+      return VNET_API_ERROR_LISP_DISABLED;
+    }
+
+  if (is_add)
+    {
+      if (lcm->nsh_map_index == (u32) ~ 0)
+       {
+         p = hash_get_mem (lcm->locator_set_index_by_name, locator_set_name);
+         if (!p)
+           {
+             clib_warning ("locator-set %v doesn't exist", locator_set_name);
+             return -1;
+           }
+         locator_set_index = p[0];
+
+         pool_get (lcm->mapping_pool, m);
+         memset (m, 0, sizeof *m);
+         m->locator_set_index = locator_set_index;
+         m->local = 1;
+         m->nsh_set = 1;
+         lcm->nsh_map_index = m - lcm->mapping_pool;
+
+         if (~0 == vnet_lisp_gpe_add_nsh_iface (lgm))
+           return -1;
+       }
+    }
+  else
+    {
+      if (lcm->nsh_map_index != (u32) ~ 0)
+       {
+         /* remove NSH mapping */
+         pool_put_index (lcm->mapping_pool, lcm->nsh_map_index);
+         lcm->nsh_map_index = ~0;
+         vnet_lisp_gpe_del_nsh_iface (lgm);
+       }
+    }
+  return 0;
+}
+
 int
 vnet_lisp_pitr_set_locator_set (u8 * locator_set_name, u8 is_add)
 {
@@ -1301,6 +1485,7 @@ vnet_lisp_pitr_set_locator_set (u8 * locator_set_name, u8 is_add)
       pool_get (lcm->mapping_pool, m);
       m->locator_set_index = locator_set_index;
       m->local = 1;
+      m->pitr_set = 1;
       lcm->pitr_map_index = m - lcm->mapping_pool;
 
       /* enable pitr mode */
@@ -1317,6 +1502,26 @@ vnet_lisp_pitr_set_locator_set (u8 * locator_set_name, u8 is_add)
   return 0;
 }
 
+int
+vnet_lisp_map_register_fallback_threshold_set (u32 value)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  if (0 == value)
+    {
+      return VNET_API_ERROR_INVALID_ARGUMENT;
+    }
+
+  lcm->max_expired_map_registers = value;
+  return 0;
+}
+
+u32
+vnet_lisp_map_register_fallback_threshold_get (void)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  return lcm->max_expired_map_registers;
+}
+
 /**
  * Configure Proxy-ETR
  *
@@ -1345,6 +1550,7 @@ vnet_lisp_use_petr (ip_address_t * ip, u8 is_add)
   if (is_add)
     {
       /* Create dummy petr locator-set */
+      memset (&loc, 0, sizeof (loc));
       gid_address_from_ip (&loc.address, ip);
       loc.priority = 1;
       loc.state = loc.weight = 1;
@@ -1467,6 +1673,58 @@ is_locator_in_locator_set (lisp_cp_main_t * lcm, locator_set_t * ls,
   return 0;
 }
 
+static void
+update_adjacencies_by_map_index (lisp_cp_main_t * lcm, u8 is_local,
+                                u32 mapping_index, u8 remove_only)
+{
+  fwd_entry_t *fwd;
+  mapping_t *map;
+  vnet_lisp_add_del_adjacency_args_t _a, *a = &_a;
+
+  map = pool_elt_at_index (lcm->mapping_pool, mapping_index);
+
+  /* *INDENT-OFF* */
+  pool_foreach(fwd, lcm->fwd_entry_pool,
+  ({
+    if ((is_local && 0 == gid_address_cmp (&map->eid, &fwd->leid)) ||
+        (!is_local && 0 == gid_address_cmp (&map->eid, &fwd->reid)))
+      {
+        a->is_add = 0;
+        gid_address_copy (&a->leid, &fwd->leid);
+        gid_address_copy (&a->reid, &fwd->reid);
+
+        vnet_lisp_add_del_adjacency (a);
+
+        if (!remove_only)
+          {
+            a->is_add = 1;
+            vnet_lisp_add_del_adjacency (a);
+          }
+      }
+    }));
+  /* *INDENT-ON* */
+}
+
+static void
+update_fwd_entries_by_locator_set (lisp_cp_main_t * lcm, u8 is_local,
+                                  u32 ls_index, u8 remove_only)
+{
+  u32 i, *map_indexp;
+  u32 **eid_indexes;
+
+  if (vec_len (lcm->locator_set_to_eids) <= ls_index)
+    return;
+
+  eid_indexes = vec_elt_at_index (lcm->locator_set_to_eids, ls_index);
+
+  for (i = 0; i < vec_len (eid_indexes[0]); i++)
+    {
+      map_indexp = vec_elt_at_index (eid_indexes[0], i);
+      update_adjacencies_by_map_index (lcm, is_local, map_indexp[0],
+                                      remove_only);
+    }
+}
+
 static inline void
 remove_locator_from_locator_set (locator_set_t * ls, u32 * locit,
                                 u32 ls_index, u32 loc_id)
@@ -1549,24 +1807,38 @@ vnet_lisp_add_del_locator (vnet_lisp_add_del_locator_set_args_t * a,
   else
     {
       ls_index = p[0];
+      u8 removed;
 
-      itloc = a->locators;
-      loc_id = 0;
-      vec_foreach (locit, ls->locator_indices)
+      vec_foreach (itloc, a->locators)
       {
-       loc = pool_elt_at_index (lcm->locator_pool, locit[0]);
+       removed = 0;
+       loc_id = 0;
+       vec_foreach (locit, ls->locator_indices)
+       {
+         loc = pool_elt_at_index (lcm->locator_pool, locit[0]);
 
-       if (loc->local && loc->sw_if_index == itloc->sw_if_index)
-         {
-           remove_locator_from_locator_set (ls, locit, ls_index, loc_id);
-         }
-       if (0 == loc->local &&
-           !gid_address_cmp (&loc->address, &itloc->address))
-         {
-           remove_locator_from_locator_set (ls, locit, ls_index, loc_id);
-         }
+         if (loc->local && loc->sw_if_index == itloc->sw_if_index)
+           {
+             removed = 1;
+             remove_locator_from_locator_set (ls, locit, ls_index, loc_id);
+           }
+         if (0 == loc->local &&
+             !gid_address_cmp (&loc->address, &itloc->address))
+           {
+             removed = 1;
+             remove_locator_from_locator_set (ls, locit, ls_index, loc_id);
+           }
+
+         if (removed)
+           {
+             /* update fwd entries using this locator in DP */
+             update_fwd_entries_by_locator_set (lcm, loc->local, ls_index,
+                                                vec_len (ls->locator_indices)
+                                                == 0);
+           }
 
-       loc_id++;
+         loc_id++;
+       }
       }
     }
 
@@ -1828,6 +2100,21 @@ vnet_lisp_add_del_map_resolver (vnet_lisp_add_del_map_resolver_args_t * a)
   return 0;
 }
 
+int
+vnet_lisp_map_register_set_ttl (u32 ttl)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  lcm->map_register_ttl = ttl;
+  return 0;
+}
+
+u32
+vnet_lisp_map_register_get_ttl (void)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  return lcm->map_register_ttl;
+}
+
 int
 vnet_lisp_add_del_mreq_itr_rlocs (vnet_lisp_add_del_mreq_itr_rloc_args_t * a)
 {
@@ -1862,7 +2149,8 @@ vnet_lisp_add_del_mreq_itr_rlocs (vnet_lisp_add_del_mreq_itr_rloc_args_t * a)
 /* Statistics (not really errors) */
 #define foreach_lisp_cp_lookup_error           \
 _(DROP, "drop")                                \
-_(MAP_REQUESTS_SENT, "map-request sent")
+_(MAP_REQUESTS_SENT, "map-request sent")       \
+_(ARP_REPLY_TX, "ARP replies sent")
 
 static char *lisp_cp_lookup_error_strings[] = {
 #define _(sym,string) string,
@@ -1881,6 +2169,7 @@ typedef enum
 typedef enum
 {
   LISP_CP_LOOKUP_NEXT_DROP,
+  LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX,
   LISP_CP_LOOKUP_N_NEXT,
 } lisp_cp_lookup_next_t;
 
@@ -2009,7 +2298,7 @@ build_map_request (lisp_cp_main_t * lcm, gid_address_t * deid,
 
   /* push outer ip header */
   pkt_push_udp_and_ip (vm, b, LISP_CONTROL_PORT, LISP_CONTROL_PORT, sloc,
-                      rloc);
+                      rloc, 1);
 
   bi_res[0] = bi;
 
@@ -2035,6 +2324,7 @@ build_encapsulated_map_request (lisp_cp_main_t * lcm,
     }
 
   b = vlib_get_buffer (vm, bi);
+  b->flags = 0;
 
   /* leave some space for the encap headers */
   vlib_buffer_make_headroom (b, MAX_LISP_MSG_ENCAP_LEN);
@@ -2063,7 +2353,7 @@ build_encapsulated_map_request (lisp_cp_main_t * lcm,
 
   /* push outer ip header */
   pkt_push_udp_and_ip (vm, b, LISP_CONTROL_PORT, LISP_CONTROL_PORT, sloc,
-                      mr_ip);
+                      mr_ip, 1);
 
   bi_res[0] = bi;
 
@@ -2078,24 +2368,29 @@ reset_pending_mr_counters (pending_map_request_t * r)
   r->retries_num = 0;
 }
 
-static int
-elect_map_resolver (lisp_cp_main_t * lcm)
-{
-  lisp_msmr_t *mr;
-
-  vec_foreach (mr, lcm->map_resolvers)
-  {
-    if (!mr->is_down)
-      {
-       ip_address_copy (&lcm->active_map_resolver, &mr->address);
-       lcm->do_map_resolver_election = 0;
-       return 1;
-      }
-  }
-  return 0;
+#define foreach_msmr \
+  _(server) \
+  _(resolver)
+
+#define _(name) \
+static int                                                              \
+elect_map_ ## name (lisp_cp_main_t * lcm)                               \
+{                                                                       \
+  lisp_msmr_t *mr;                                                      \
+  vec_foreach (mr, lcm->map_ ## name ## s)                              \
+  {                                                                     \
+    if (!mr->is_down)                                                   \
+      {                                                                 \
+       ip_address_copy (&lcm->active_map_ ##name, &mr->address);       \
+       lcm->do_map_ ## name ## _election = 0;                          \
+       return 1;                                                       \
+      }                                                                 \
+  }                                                                     \
+  return 0;                                                             \
 }
-
-static void
+foreach_msmr
+#undef _
+  static void
 free_map_register_records (mapping_t * maps)
 {
   mapping_t *map;
@@ -2218,37 +2513,38 @@ build_map_register (lisp_cp_main_t * lcm, ip_address_t * sloc,
 
   /* push outer ip header */
   pkt_push_udp_and_ip (vm, b, LISP_CONTROL_PORT, LISP_CONTROL_PORT, sloc,
-                      ms_ip);
+                      ms_ip, 1);
 
   bi_res[0] = bi;
   return b;
 }
 
-static int
-get_egress_map_resolver_ip (lisp_cp_main_t * lcm, ip_address_t * ip)
-{
-  lisp_msmr_t *mr;
-  while (lcm->do_map_resolver_election
-        | (0 == ip_fib_get_first_egress_ip_for_dst (lcm,
-                                                    &lcm->active_map_resolver,
-                                                    ip)))
-    {
-      if (0 == elect_map_resolver (lcm))
-       /* all map resolvers are down */
-       {
-         /* restart MR checking by marking all of them up */
-         vec_foreach (mr, lcm->map_resolvers) mr->is_down = 0;
-         return -1;
-       }
-    }
-  return 0;
+#define _(name) \
+static int                                                              \
+get_egress_map_ ##name## _ip (lisp_cp_main_t * lcm, ip_address_t * ip)  \
+{                                                                       \
+  lisp_msmr_t *mr;                                                      \
+  while (lcm->do_map_ ## name ## _election                              \
+        | (0 == ip_fib_get_first_egress_ip_for_dst                     \
+            (lcm, &lcm->active_map_ ##name, ip)))                       \
+    {                                                                   \
+      if (0 == elect_map_ ## name (lcm))                                \
+       /* all map resolvers/servers are down */                        \
+       {                                                               \
+         /* restart MR/MS checking by marking all of them up */        \
+         vec_foreach (mr, lcm->map_ ## name ## s) mr->is_down = 0;     \
+         return -1;                                                    \
+       }                                                               \
+    }                                                                   \
+  return 0;                                                             \
 }
 
+foreach_msmr
+#undef _
 /* CP output statistics */
 #define foreach_lisp_cp_output_error                  \
 _(MAP_REGISTERS_SENT, "map-registers sent")           \
 _(RLOC_PROBES_SENT, "rloc-probes sent")
-
 static char *lisp_cp_output_error_strings[] = {
 #define _(sym,string) string,
   foreach_lisp_cp_output_error
@@ -2313,7 +2609,7 @@ send_rloc_probe (lisp_cp_main_t * lcm, gid_address_t * deid,
 
   vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
 
-  next_index = (ip_addr_version (&lcm->active_map_resolver) == IP4) ?
+  next_index = (ip_addr_version (rloc) == IP4) ?
     ip4_lookup_node.index : ip6_lookup_node.index;
 
   f = vlib_get_frame_to_node (lcm->vlib_main, next_index);
@@ -2324,7 +2620,6 @@ send_rloc_probe (lisp_cp_main_t * lcm, gid_address_t * deid,
   f->n_vectors = 1;
   vlib_put_frame_to_node (lcm->vlib_main, next_index, f);
 
-  hash_set (lcm->map_register_messages_by_nonce, nonce, 0);
   return 0;
 }
 
@@ -2378,28 +2673,18 @@ send_rloc_probes (lisp_cp_main_t * lcm)
 static int
 send_map_register (lisp_cp_main_t * lcm, u8 want_map_notif)
 {
+  pending_map_register_t *pmr;
   u32 bi, map_registers_sent = 0;
   vlib_buffer_t *b;
   ip_address_t sloc;
   vlib_frame_t *f;
   u64 nonce = 0;
   u32 next_index, *to_next;
-  ip_address_t *ms = 0;
-  mapping_t *records, *r, *g;
+  mapping_t *records, *r, *group, *k;
 
-  // TODO: support multiple map servers and do election
-  if (0 == vec_len (lcm->map_servers))
+  if (get_egress_map_server_ip (lcm, &sloc) < 0)
     return -1;
 
-  ms = &lcm->map_servers[0].address;
-
-  if (0 == ip_fib_get_first_egress_ip_for_dst (lcm, ms, &sloc))
-    {
-      clib_warning ("no eligible interface address found for %U!",
-                   format_ip_address, &lcm->map_servers[0]);
-      return -1;
-    }
-
   records = build_map_register_record_list (lcm);
   if (!records)
     return -1;
@@ -2412,18 +2697,31 @@ send_map_register (lisp_cp_main_t * lcm, u8 want_map_notif)
     if (!key)
       continue;                        /* no secret key -> map-register cannot be sent */
 
-    g = 0;
-    // TODO: group mappings that share common key
-    vec_add1 (g, r[0]);
-    b = build_map_register (lcm, &sloc, ms, &nonce, want_map_notif, g,
-                           key_id, key, &bi);
-    vec_free (g);
+    group = 0;
+    vec_add1 (group, r[0]);
+
+    /* group mappings that share common key */
+    for (k = r + 1; k < vec_end (records); k++)
+      {
+       if (k->key_id != r->key_id)
+         continue;
+
+       if (vec_is_equal (k->key, r->key))
+         {
+           vec_add1 (group, k[0]);
+           k->key = 0;         /* don't process this mapping again */
+         }
+      }
+
+    b = build_map_register (lcm, &sloc, &lcm->active_map_server, &nonce,
+                           want_map_notif, group, key_id, key, &bi);
+    vec_free (group);
     if (!b)
       continue;
 
     vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
 
-    next_index = (ip_addr_version (&lcm->active_map_resolver) == IP4) ?
+    next_index = (ip_addr_version (&lcm->active_map_server) == IP4) ?
       ip4_lookup_node.index : ip6_lookup_node.index;
 
     f = vlib_get_frame_to_node (lcm->vlib_main, next_index);
@@ -2435,7 +2733,11 @@ send_map_register (lisp_cp_main_t * lcm, u8 want_map_notif)
     vlib_put_frame_to_node (lcm->vlib_main, next_index, f);
     map_registers_sent++;
 
-    hash_set (lcm->map_register_messages_by_nonce, nonce, 0);
+    pool_get (lcm->pending_map_registers_pool, pmr);
+    memset (pmr, 0, sizeof (*pmr));
+    pmr->time_to_expire = PENDING_MREG_EXPIRATION_TIME;
+    hash_set (lcm->map_register_messages_by_nonce, nonce,
+             pmr - lcm->pending_map_registers_pool);
   }
   free_map_register_records (records);
 
@@ -2488,7 +2790,7 @@ _send_encapsulated_map_request (lisp_cp_main_t * lcm,
     }
 
   /* get locator-set for seid */
-  if (!lcm->lisp_pitr)
+  if (!lcm->lisp_pitr && gid_address_type (deid) != GID_ADDR_NSH)
     {
       map_index = gid_dictionary_lookup (&lcm->mapping_index_by_gid, seid);
       if (map_index == ~0)
@@ -2511,9 +2813,24 @@ _send_encapsulated_map_request (lisp_cp_main_t * lcm,
     }
   else
     {
-      map_index = lcm->pitr_map_index;
-      map = pool_elt_at_index (lcm->mapping_pool, lcm->pitr_map_index);
-      ls_index = map->locator_set_index;
+      if (lcm->lisp_pitr)
+       {
+         map = pool_elt_at_index (lcm->mapping_pool, lcm->pitr_map_index);
+         ls_index = map->locator_set_index;
+       }
+      else
+       {
+         if (lcm->nsh_map_index == (u32) ~ 0)
+           {
+             clib_warning ("No locator-set defined for NSH!");
+             return -1;
+           }
+         else
+           {
+             map = pool_elt_at_index (lcm->mapping_pool, lcm->nsh_map_index);
+             ls_index = map->locator_set_index;
+           }
+       }
     }
 
   /* overwrite locator set if map-request itr-rlocs configured */
@@ -2628,10 +2945,8 @@ lisp_get_vni_from_buffer_ip (lisp_cp_main_t * lcm, vlib_buffer_t * b,
 }
 
 always_inline u32
-lisp_get_vni_from_buffer_eth (lisp_cp_main_t * lcm, vlib_buffer_t * b)
+lisp_get_bd_from_buffer_eth (vlib_buffer_t * b)
 {
-  uword *vnip;
-  u32 vni = ~0;
   u32 sw_if_index0;
 
   l2input_main_t *l2im = &l2input_main;
@@ -2642,26 +2957,38 @@ lisp_get_vni_from_buffer_eth (lisp_cp_main_t * lcm, vlib_buffer_t * b)
   config = vec_elt_at_index (l2im->configs, sw_if_index0);
   bd_config = vec_elt_at_index (l2im->bd_configs, config->bd_index);
 
-  vnip = hash_get (lcm->vni_by_bd_id, bd_config->bd_id);
+  return bd_config->bd_id;
+}
+
+always_inline u32
+lisp_get_vni_from_buffer_eth (lisp_cp_main_t * lcm, vlib_buffer_t * b)
+{
+  uword *vnip;
+  u32 vni = ~0;
+  u32 bd = lisp_get_bd_from_buffer_eth (b);
+
+  vnip = hash_get (lcm->vni_by_bd_id, bd);
   if (vnip)
     vni = vnip[0];
   else
-    clib_warning ("bridge domain %d is not mapped to any vni!",
-                 config->bd_index);
+    clib_warning ("bridge domain %d is not mapped to any vni!", bd);
 
   return vni;
 }
 
-always_inline void
+void
 get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b,
-                                 gid_address_t * src, gid_address_t * dst)
+                                 gid_address_t * src, gid_address_t * dst,
+                                 u16 type)
 {
+  ethernet_header_t *eh;
   u32 vni = 0;
-  u16 type;
 
   memset (src, 0, sizeof (*src));
   memset (dst, 0, sizeof (*dst));
-  type = vnet_buffer (b)->lisp.overlay_afi;
+
+  gid_address_type (dst) = GID_ADDR_NO_ADDRESS;
+  gid_address_type (src) = GID_ADDR_NO_ADDRESS;
 
   if (LISP_AFI_IP == type || LISP_AFI_IP6 == type)
     {
@@ -2685,25 +3012,51 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b,
     }
   else if (LISP_AFI_MAC == type)
     {
-      ethernet_header_t *eh;
+      ethernet_arp_header_t *ah;
 
       eh = vlib_buffer_get_current (b);
 
-      gid_address_type (src) = GID_ADDR_MAC;
-      gid_address_type (dst) = GID_ADDR_MAC;
-      mac_copy (&gid_address_mac (src), eh->src_address);
-      mac_copy (&gid_address_mac (dst), eh->dst_address);
+      if (clib_net_to_host_u16 (eh->type) == ETHERNET_TYPE_ARP)
+       {
+         ah = (ethernet_arp_header_t *) (((u8 *) eh) + sizeof (*eh));
+         if (clib_net_to_host_u16 (ah->opcode)
+             != ETHERNET_ARP_OPCODE_request)
+           return;
+
+         gid_address_type (dst) = GID_ADDR_ARP;
+         gid_address_arp_bd (dst) = lisp_get_bd_from_buffer_eth (b);
+         clib_memcpy (&gid_address_arp_ip4 (dst),
+                      &ah->ip4_over_ethernet[1].ip4, 4);
+       }
+      else
+       {
+         gid_address_type (src) = GID_ADDR_MAC;
+         gid_address_type (dst) = GID_ADDR_MAC;
+         mac_copy (&gid_address_mac (src), eh->src_address);
+         mac_copy (&gid_address_mac (dst), eh->dst_address);
 
-      /* get vni */
-      vni = lisp_get_vni_from_buffer_eth (lcm, b);
+         /* get vni */
+         vni = lisp_get_vni_from_buffer_eth (lcm, b);
 
-      gid_address_vni (dst) = vni;
-      gid_address_vni (src) = vni;
+         gid_address_vni (dst) = vni;
+         gid_address_vni (src) = vni;
+       }
     }
   else if (LISP_AFI_LCAF == type)
     {
-      /* Eventually extend this to support NSH and other */
-      ASSERT (0);
+      lisp_nsh_hdr_t *nh;
+      eh = vlib_buffer_get_current (b);
+
+      if (clib_net_to_host_u16 (eh->type) == ETHERNET_TYPE_NSH)
+       {
+         nh = (lisp_nsh_hdr_t *) (((u8 *) eh) + sizeof (*eh));
+         u32 spi = clib_net_to_host_u32 (nh->spi_si << 8);
+         u8 si = (u8) clib_net_to_host_u32 (nh->spi_si);
+         gid_address_nsh_spi (dst) = spi;
+         gid_address_nsh_si (dst) = si;
+
+         gid_address_type (dst) = GID_ADDR_NSH;
+       }
     }
 }
 
@@ -2712,38 +3065,80 @@ lisp_cp_lookup_inline (vlib_main_t * vm,
                       vlib_node_runtime_t * node,
                       vlib_frame_t * from_frame, int overlay)
 {
-  u32 *from, *to_next_drop, di, si;
+  u32 *from, *to_next, di, si;
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
-  u32 pkts_mapped = 0;
-  uword n_left_from, n_left_to_next_drop;
+  u32 pkts_mapped = 0, next_index;
+  uword n_left_from, n_left_to_next;
+  vnet_main_t *vnm = vnet_get_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)
     {
-      vlib_get_next_frame (vm, node, LISP_CP_LOOKUP_NEXT_DROP,
-                          to_next_drop, n_left_to_next_drop);
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
 
-      while (n_left_from > 0 && n_left_to_next_drop > 0)
+      while (n_left_from > 0 && n_left_to_next > 0)
        {
-         u32 pi0;
+         u32 pi0, sw_if_index0, next0;
+         u64 mac0;
          vlib_buffer_t *b0;
          gid_address_t src, dst;
+         ethernet_arp_header_t *arp0;
+         ethernet_header_t *eth0;
+         vnet_hw_interface_t *hw_if0;
 
          pi0 = from[0];
          from += 1;
          n_left_from -= 1;
-         to_next_drop[0] = pi0;
-         to_next_drop += 1;
-         n_left_to_next_drop -= 1;
+         to_next[0] = pi0;
+         to_next += 1;
+         n_left_to_next -= 1;
 
          b0 = vlib_get_buffer (vm, pi0);
-         b0->error = node->errors[LISP_CP_LOOKUP_ERROR_DROP];
-         vnet_buffer (b0)->lisp.overlay_afi = overlay;
 
          /* src/dst eid pair */
-         get_src_and_dst_eids_from_buffer (lcm, b0, &src, &dst);
+         get_src_and_dst_eids_from_buffer (lcm, b0, &src, &dst, overlay);
+
+         if (gid_address_type (&dst) == GID_ADDR_ARP)
+           {
+             mac0 = gid_dictionary_lookup (&lcm->mapping_index_by_gid, &dst);
+             if (GID_LOOKUP_MISS_L2 != mac0)
+               {
+                 /* send ARP reply */
+
+                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+                 vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
+
+                 hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
+
+                 eth0 = vlib_buffer_get_current (b0);
+                 arp0 = (ethernet_arp_header_t *) (((u8 *) eth0)
+                                                   + sizeof (*eth0));
+                 arp0->opcode =
+                   clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
+                 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
+                 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
+                              (u8 *) & mac0, 6);
+                 clib_memcpy (&arp0->ip4_over_ethernet[0].ip4,
+                              &gid_address_arp_ip4 (&dst), 4);
+
+                 /* Hardware must be ethernet-like. */
+                 ASSERT (vec_len (hw_if0->hw_address) == 6);
+
+                 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
+                 clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
+
+                 b0->error = node->errors[LISP_CP_LOOKUP_ERROR_ARP_REPLY_TX];
+                 next0 = LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX;
+                 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                                  to_next,
+                                                  n_left_to_next, pi0,
+                                                  next0);
+               }
+             continue;
+           }
 
          /* if we have remote mapping for destination already in map-chache
             add forwarding tunnel directly. If not send a map-request */
@@ -2763,8 +3158,14 @@ lisp_cp_lookup_inline (vlib_main_t * vm,
                }
              else
                {
-                 si = gid_dictionary_lookup (&lcm->mapping_index_by_gid,
-                                             &src);
+                 if (GID_ADDR_NSH != gid_address_type (&dst))
+                   {
+                     si = gid_dictionary_lookup (&lcm->mapping_index_by_gid,
+                                                 &src);
+                   }
+                 else
+                   si = lcm->nsh_map_index;
+
                  if (~0 != si)
                    {
                      dp_add_fwd_entry_from_mt (si, di);
@@ -2779,6 +3180,7 @@ lisp_cp_lookup_inline (vlib_main_t * vm,
              pkts_mapped++;
            }
 
+         b0->error = node->errors[LISP_CP_LOOKUP_ERROR_DROP];
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              lisp_cp_lookup_trace_t *tr = vlib_add_trace (vm, node, b0,
@@ -2791,10 +3193,13 @@ lisp_cp_lookup_inline (vlib_main_t * vm,
            }
          gid_address_free (&dst);
          gid_address_free (&src);
+         next0 = LISP_CP_LOOKUP_NEXT_DROP;
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next,
+                                          n_left_to_next, pi0, next0);
        }
 
-      vlib_put_next_frame (vm, node, LISP_CP_LOOKUP_NEXT_DROP,
-                          n_left_to_next_drop);
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
   vlib_node_increment_counter (vm, node->node_index,
                               LISP_CP_LOOKUP_ERROR_MAP_REQUESTS_SENT,
@@ -2846,6 +3251,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_ip4_node) = {
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
+      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -2865,6 +3271,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_ip6_node) = {
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
+      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -2884,6 +3291,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_l2_node) = {
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
+      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -2903,6 +3311,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_nsh_node) = {
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
+      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -2984,14 +3393,14 @@ mapping_start_expiration_timer (lisp_cp_main_t * lcm, u32 mi,
 static void
 map_records_arg_free (map_records_arg_t * a)
 {
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
   mapping_t *m;
   vec_foreach (m, a->mappings)
   {
     vec_free (m->locators);
     gid_address_free (&m->eid);
   }
-
-  clib_mem_free (a);
+  pool_put (lcm->map_records_args_pool[vlib_get_thread_index ()], a);
 }
 
 void *
@@ -3052,7 +3461,7 @@ process_map_reply (map_records_arg_t * a)
   pool_put (lcm->pending_map_requests_pool, pmr);
 
 done:
-  map_records_arg_free (a);
+  a->is_free = 1;
   return 0;
 }
 
@@ -3103,8 +3512,12 @@ process_map_notify (map_records_arg_t * a)
       return;
     }
 
-  map_records_arg_free (a);
+  a->is_free = 1;
+  pool_put_index (lcm->pending_map_registers_pool, pmr_index[0]);
   hash_unset (lcm->map_register_messages_by_nonce, a->nonce);
+
+  /* reset map-notify counter */
+  lcm->expired_map_registers = 0;
 }
 
 static mapping_t *
@@ -3165,9 +3578,12 @@ parse_map_records (vlib_buffer_t * b, map_records_arg_t * a, u8 count)
   mapping_t m;
   locator_t *loc;
 
+  memset (&m, 0, sizeof (m));
+
   /* parse record eid */
   for (i = 0; i < count; i++)
     {
+      locators = 0;
       len = lisp_msg_parse_mapping_record (b, &deid, &locators, NULL);
       if (len == ~0)
        {
@@ -3185,6 +3601,24 @@ parse_map_records (vlib_buffer_t * b, map_records_arg_t * a, u8 count)
   return 0;
 }
 
+static map_records_arg_t *
+map_record_args_get ()
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  map_records_arg_t *rec;
+
+  /* Cleanup first */
+  /* *INDENT-OFF* */
+  pool_foreach (rec, lcm->map_records_args_pool[vlib_get_thread_index()], ({
+    if (rec->is_free)
+      map_records_arg_free (rec);
+  }));
+  /* *INDENT-ON* */
+
+  pool_get (lcm->map_records_args_pool[vlib_get_thread_index ()], rec);
+  return rec;
+}
+
 static map_records_arg_t *
 parse_map_notify (vlib_buffer_t * b)
 {
@@ -3196,8 +3630,9 @@ parse_map_notify (vlib_buffer_t * b)
   gid_address_t deid;
   u16 auth_data_len = 0;
   u8 record_count;
-  map_records_arg_t *a = clib_mem_alloc (sizeof (*a));
+  map_records_arg_t *a;
 
+  a = map_record_args_get ();
   memset (a, 0, sizeof (*a));
   mnotif_hdr = vlib_buffer_get_current (b);
   vlib_buffer_pull (b, sizeof (*mnotif_hdr));
@@ -3229,8 +3664,8 @@ parse_map_notify (vlib_buffer_t * b)
   if (!is_auth_data_valid (mnotif_hdr, vlib_buffer_get_tail (b)
                           - (u8 *) mnotif_hdr, key_id, key))
     {
-      clib_warning ("Map-notify auth data verification failed for nonce %lu!",
-                   a->nonce);
+      clib_warning ("Map-notify auth data verification failed for nonce "
+                   "0x%lx!", a->nonce);
       map_records_arg_free (a);
       return 0;
     }
@@ -3260,7 +3695,7 @@ build_map_reply (lisp_cp_main_t * lcm, ip_address_t * sloc,
   lisp_msg_put_map_reply (b, records, nonce, probe_bit);
 
   /* push outer ip header */
-  pkt_push_udp_and_ip (vm, b, LISP_CONTROL_PORT, dst_port, sloc, dst);
+  pkt_push_udp_and_ip (vm, b, LISP_CONTROL_PORT, dst_port, sloc, dst, 1);
 
   bi_res[0] = bi;
   return b;
@@ -3316,7 +3751,7 @@ send_map_reply (lisp_cp_main_t * lcm, u32 mi, ip_address_t * dst,
 static void
 find_ip_header (vlib_buffer_t * b, u8 ** ip_hdr)
 {
-  const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
+  const i32 start = vnet_buffer (b)->l3_hdr_offset;
   if (start < 0 && start < -sizeof (b->pre_data))
     {
       *ip_hdr = 0;
@@ -3411,7 +3846,7 @@ done:
   vec_free (itr_rlocs);
 }
 
-static map_records_arg_t *
+map_records_arg_t *
 parse_map_reply (vlib_buffer_t * b)
 {
   locator_t probed;
@@ -3420,13 +3855,21 @@ parse_map_reply (vlib_buffer_t * b)
   u32 i, len = 0;
   mapping_t m;
   map_reply_hdr_t *mrep_hdr;
-  map_records_arg_t *a = clib_mem_alloc (sizeof (*a));
+  map_records_arg_t *a;
+
+  a = map_record_args_get ();
   memset (a, 0, sizeof (*a));
+
   locator_t *locators;
 
   mrep_hdr = vlib_buffer_get_current (b);
   a->nonce = MREP_NONCE (mrep_hdr);
   a->is_rloc_probe = MREP_RLOC_PROBE (mrep_hdr);
+  if (!vlib_buffer_has_space (b, sizeof (*mrep_hdr)))
+    {
+      clib_mem_free (a);
+      return 0;
+    }
   vlib_buffer_pull (b, sizeof (*mrep_hdr));
 
   for (i = 0; i < MREP_REC_COUNT (mrep_hdr); i++)
@@ -3572,6 +4015,8 @@ lisp_cp_init (vlib_main_t * vm)
 {
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
   clib_error_t *error = 0;
+  vlib_thread_main_t *vtm = vlib_get_thread_main ();
+  u32 num_threads;
 
   if ((error = vlib_call_init_function (vm, lisp_gpe_init)))
     return error;
@@ -3584,11 +4029,16 @@ lisp_cp_init (vlib_main_t * vm)
   lcm->lisp_pitr = 0;
   lcm->flags = 0;
   memset (&lcm->active_map_resolver, 0, sizeof (lcm->active_map_resolver));
+  memset (&lcm->active_map_server, 0, sizeof (lcm->active_map_server));
 
   gid_dictionary_init (&lcm->mapping_index_by_gid);
   lcm->do_map_resolver_election = 1;
+  lcm->do_map_server_election = 1;
   lcm->map_request_mode = MR_MODE_DST_ONLY;
 
+  num_threads = 1 /* main thread */  + vtm->n_threads;
+  vec_validate (lcm->map_records_args_pool, num_threads - 1);
+
   /* default vrf mapped to vni 0 */
   hash_set (lcm->table_id_by_vni, 0, 0);
   hash_set (lcm->vni_by_table_id, 0, 0);
@@ -3600,9 +4050,63 @@ lisp_cp_init (vlib_main_t * vm)
 
   u64 now = clib_cpu_time_now ();
   timing_wheel_init (&lcm->wheel, now, vm->clib_time.clocks_per_second);
+  lcm->nsh_map_index = ~0;
+  lcm->map_register_ttl = MAP_REGISTER_DEFAULT_TTL;
+  lcm->max_expired_map_registers = MAX_EXPIRED_MAP_REGISTERS_DEFAULT;
+  lcm->expired_map_registers = 0;
   return 0;
 }
 
+static int
+lisp_stats_api_fill (lisp_cp_main_t * lcm, lisp_gpe_main_t * lgm,
+                    lisp_api_stats_t * stat, lisp_stats_key_t * key,
+                    u32 stats_index)
+{
+  vlib_counter_t v;
+  vlib_combined_counter_main_t *cm = &lgm->counters;
+  lisp_gpe_fwd_entry_key_t fwd_key;
+  const lisp_gpe_tunnel_t *lgt;
+  fwd_entry_t *fe;
+
+  memset (stat, 0, sizeof (*stat));
+  memset (&fwd_key, 0, sizeof (fwd_key));
+
+  fe = pool_elt_at_index (lcm->fwd_entry_pool, key->fwd_entry_index);
+  ASSERT (fe != 0);
+
+  gid_to_dp_address (&fe->reid, &stat->deid);
+  gid_to_dp_address (&fe->leid, &stat->seid);
+  stat->vni = gid_address_vni (&fe->reid);
+
+  lgt = lisp_gpe_tunnel_get (key->tunnel_index);
+  stat->loc_rloc = lgt->key->lcl;
+  stat->rmt_rloc = lgt->key->rmt;
+
+  vlib_get_combined_counter (cm, stats_index, &v);
+  stat->counters = v;
+  return 1;
+}
+
+lisp_api_stats_t *
+vnet_lisp_get_stats (void)
+{
+  lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  lisp_api_stats_t *stats = 0, stat;
+  lisp_stats_key_t *key;
+  u32 index;
+
+  /* *INDENT-OFF* */
+  hash_foreach_mem (key, index, lgm->lisp_stats_index_by_key,
+  {
+    if (lisp_stats_api_fill (lcm, lgm, &stat, key, index))
+      vec_add1 (stats, stat);
+  });
+  /* *INDENT-ON* */
+
+  return stats;
+}
+
 static void *
 send_map_request_thread_fn (void *arg)
 {
@@ -3709,7 +4213,7 @@ remove_dead_pending_map_requests (lisp_cp_main_t * lcm)
   /* *INDENT-ON* */
 
   vec_foreach (pmr_index, to_be_removed)
-    pool_put_index (lcm->pending_map_requests_by_nonce, pmr_index[0]);
+    pool_put_index (lcm->pending_map_requests_pool, pmr_index[0]);
 
   vec_free (to_be_removed);
 }
@@ -3730,15 +4234,98 @@ update_rloc_probing (lisp_cp_main_t * lcm, f64 dt)
     }
 }
 
+static int
+update_pending_map_register (pending_map_register_t * r, f64 dt, u8 * del_all)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  lisp_msmr_t *ms;
+  del_all[0] = 0;
+
+  r->time_to_expire -= dt;
+
+  if (r->time_to_expire < 0)
+    {
+      lcm->expired_map_registers++;
+
+      if (lcm->expired_map_registers >= lcm->max_expired_map_registers)
+       {
+         ms = get_map_server (&lcm->active_map_server);
+         if (!ms)
+           {
+             clib_warning ("Map server %U not found - probably deleted "
+                           "by the user recently.", format_ip_address,
+                           &lcm->active_map_server);
+           }
+         else
+           {
+             clib_warning ("map server %U is unreachable, ignoring",
+                           format_ip_address, &lcm->active_map_server);
+
+             /* mark current map server unavailable so it won't be
+              * elected next time */
+             ms->is_down = 1;
+             ms->last_update = vlib_time_now (lcm->vlib_main);
+           }
+
+         elect_map_server (lcm);
+
+         /* indication for deleting all pending map registers */
+         del_all[0] = 1;
+         lcm->expired_map_registers = 0;
+         return 0;
+       }
+      else
+       {
+         /* delete pending map register */
+         return 0;
+       }
+    }
+  return 1;
+}
+
 static void
 update_map_register (lisp_cp_main_t * lcm, f64 dt)
 {
+  u32 *to_be_removed = 0, *pmr_index;
   static f64 time_left = QUICK_MAP_REGISTER_INTERVAL;
   static u64 mreg_sent_counter = 0;
 
+  pending_map_register_t *pmr;
+  u8 del_all = 0;
+
   if (!lcm->is_enabled || !lcm->map_registering)
     return;
 
+  /* *INDENT-OFF* */
+  pool_foreach (pmr, lcm->pending_map_registers_pool,
+  ({
+    if (!update_pending_map_register (pmr, dt, &del_all))
+    {
+      if (del_all)
+        break;
+      vec_add1 (to_be_removed, pmr - lcm->pending_map_registers_pool);
+    }
+  }));
+  /* *INDENT-ON* */
+
+  if (del_all)
+    {
+      /* delete all pending map register messages so they won't
+       * trigger another map server election.. */
+      pool_free (lcm->pending_map_registers_pool);
+      hash_free (lcm->map_register_messages_by_nonce);
+
+      /* ..and trigger registration against next map server (if any) */
+      time_left = 0;
+    }
+  else
+    {
+      vec_foreach (pmr_index, to_be_removed)
+       pool_put_index (lcm->pending_map_registers_pool, pmr_index[0]);
+    }
+
+  vec_free (to_be_removed);
+
   time_left -= dt;
   if (time_left <= 0)
     {
@@ -3800,6 +4387,33 @@ send_map_resolver_service (vlib_main_t * vm,
   return 0;
 }
 
+vnet_api_error_t
+vnet_lisp_stats_enable_disable (u8 enable)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+
+  if (vnet_lisp_enable_disable_status () == 0)
+    return VNET_API_ERROR_LISP_DISABLED;
+
+  if (enable)
+    lcm->flags |= LISP_FLAG_STATS_ENABLED;
+  else
+    lcm->flags &= ~LISP_FLAG_STATS_ENABLED;
+
+  return 0;
+}
+
+u8
+vnet_lisp_stats_enable_disable_state (void)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+
+  if (vnet_lisp_enable_disable_status () == 0)
+    return VNET_API_ERROR_LISP_DISABLED;
+
+  return lcm->flags & LISP_FLAG_STATS_ENABLED;
+}
+
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (lisp_retry_service_node,static) = {
     .function = send_map_resolver_service,