LISP: Map-server fallback feature
[vpp.git] / src / vnet / lisp-cp / control.c
index cea9255..72af525 100644 (file)
@@ -28,6 +28,8 @@
 #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);
@@ -668,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
     {
@@ -676,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;
            }
@@ -697,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)
@@ -812,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");
@@ -1329,8 +1351,23 @@ vnet_lisp_add_del_adjacency (vnet_lisp_add_del_adjacency_args_t * a)
     {
       /* 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)
        {
@@ -1370,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)
 {
@@ -1414,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
  *
@@ -1992,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)
 {
@@ -2175,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;
 
@@ -2201,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);
@@ -2229,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;
 
@@ -2244,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;
@@ -2384,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
@@ -2490,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;
 }
 
@@ -2544,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, *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;
@@ -2594,15 +2713,15 @@ send_map_register (lisp_cp_main_t * lcm, u8 want_map_notif)
          }
       }
 
-    b = build_map_register (lcm, &sloc, ms, &nonce, want_map_notif, group,
-                           key_id, key, &bi);
+    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);
@@ -2614,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);
 
@@ -2667,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)
@@ -2690,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 */
@@ -2843,6 +2981,7 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b,
                                  gid_address_t * src, gid_address_t * dst,
                                  u16 type)
 {
+  ethernet_header_t *eh;
   u32 vni = 0;
 
   memset (src, 0, sizeof (*src));
@@ -2873,7 +3012,6 @@ 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);
@@ -2906,8 +3044,19 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b,
     }
   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;
+       }
     }
 }
 
@@ -3009,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);
@@ -3238,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 *
@@ -3306,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;
 }
 
@@ -3357,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 *
@@ -3424,6 +3583,7 @@ parse_map_records (vlib_buffer_t * b, map_records_arg_t * a, u8 count)
   /* parse record eid */
   for (i = 0; i < count; i++)
     {
+      locators = 0;
       len = lisp_msg_parse_mapping_record (b, &deid, &locators, NULL);
       if (len == ~0)
        {
@@ -3441,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)
 {
@@ -3452,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));
@@ -3485,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;
     }
@@ -3516,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;
@@ -3572,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;
@@ -3676,8 +3855,11 @@ 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);
@@ -3833,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;
@@ -3845,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);
@@ -3861,6 +4050,10 @@ 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;
 }
 
@@ -4020,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);
 }
@@ -4041,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)
     {