LISP: re-fetch mapping before it expires
[vpp.git] / src / vnet / lisp-cp / control.c
index d8a1372..c811e78 100644 (file)
 
 #define MAX_VALUE_U24 0xffffff
 
+/* mapping timer control constants (in seconds) */
+#define TIME_UNTIL_REFETCH_OR_DELETE  20
+#define MAPPING_TIMEOUT (((m->ttl) * 60) - TIME_UNTIL_REFETCH_OR_DELETE)
+
 lisp_cp_main_t lisp_control_main;
 
 u8 *format_lisp_cp_input_trace (u8 * s, va_list * args);
+static void *send_map_request_thread_fn (void *arg);
 
 typedef enum
 {
@@ -670,6 +675,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
     {
@@ -678,6 +686,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;
            }
@@ -1096,7 +1107,7 @@ remove_overlapping_sub_prefixes (lisp_cp_main_t * lcm, gid_address_t * eid,
     if (vnet_lisp_add_del_adjacency (adj_args))
       clib_warning ("failed to del adjacency!");
 
-    vnet_lisp_add_del_mapping (e, 0, 0, 0, 0, 0 /* is add */ , 0, 0);
+    vnet_lisp_del_mapping (e, NULL);
   }
 
   vec_free (a.eids_to_be_deleted);
@@ -1123,24 +1134,19 @@ is_local_ip (lisp_cp_main_t * lcm, ip_address_t * addr)
 }
 
 /**
- * Adds/removes/updates mapping. Does not program forwarding.
+ * Adds/updates mapping. Does not program forwarding.
  *
- * @param eid end-host identifier
+ * @param a parameters of the new mapping
  * @param rlocs vector of remote locators
- * @param action action for negative map-reply
- * @param is_add add mapping if non-zero, delete otherwise
- * @param res_map_index the map-index that was created/updated/removed. It is
- *                      set to ~0 if no action is taken.
- * @param is_static used for distinguishing between statically learned
-                    remote mappings and mappings obtained from MR
+ * @param res_map_index index of the newly created mapping
+ * @param locators_changed indicator if locators were updated in the mapping
  * @return return code
  */
 int
-vnet_lisp_add_del_mapping (gid_address_t * eid, locator_t * rlocs, u8 action,
-                          u8 authoritative, u32 ttl, u8 is_add, u8 is_static,
-                          u32 * res_map_index)
+vnet_lisp_add_mapping (vnet_lisp_add_del_mapping_args_t * a,
+                      locator_t * rlocs,
+                      u32 * res_map_index, u8 * is_updated)
 {
-  vnet_lisp_add_del_mapping_args_t _m_args, *m_args = &_m_args;
   vnet_lisp_add_del_locator_set_args_t _ls_args, *ls_args = &_ls_args;
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
   u32 mi, ls_index = 0, dst_map_index;
@@ -1155,115 +1161,138 @@ vnet_lisp_add_del_mapping (gid_address_t * eid, locator_t * rlocs, u8 action,
 
   if (res_map_index)
     res_map_index[0] = ~0;
+  if (is_updated)
+    is_updated[0] = 0;
 
-  memset (m_args, 0, sizeof (m_args[0]));
   memset (ls_args, 0, sizeof (ls_args[0]));
 
   ls_args->locators = rlocs;
-
-  mi = gid_dictionary_lookup (&lcm->mapping_index_by_gid, eid);
+  mi = gid_dictionary_lookup (&lcm->mapping_index_by_gid, &a->eid);
   old_map = ((u32) ~ 0 != mi) ? pool_elt_at_index (lcm->mapping_pool, mi) : 0;
 
-  if (is_add)
-    {
-      /* check if none of the locators match localy configured address */
-      vec_foreach (loc, rlocs)
+  /* 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)))
       {
-       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;
-         }
+       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)
+  /* overwrite: if mapping already exists, decide if locators should be
+   * updated and be done */
+  if (old_map && gid_address_cmp (&old_map->eid, &a->eid) == 0)
+    {
+      if (!a->is_static && (old_map->is_static || old_map->local))
        {
-         if (!is_static && (old_map->is_static || old_map->local))
-           {
-             /* do not overwrite local or static remote mappings */
-             clib_warning ("mapping %U rejected due to collision with local "
-                           "or static remote mapping!", format_gid_address,
-                           eid);
-             return 0;
-           }
-
-         locator_set_t *old_ls;
-
-         /* update mapping attributes */
-         old_map->action = action;
-         old_map->authoritative = authoritative;
-         old_map->ttl = ttl;
-
-         old_ls = pool_elt_at_index (lcm->locator_set_pool,
-                                     old_map->locator_set_index);
-         if (compare_locators (lcm, old_ls->locator_indices,
-                               ls_args->locators))
-           {
-             /* set locator-set index to overwrite */
-             ls_args->is_add = 1;
-             ls_args->index = old_map->locator_set_index;
-             vnet_lisp_add_del_locator_set (ls_args, 0);
-             if (res_map_index)
-               res_map_index[0] = mi;
-           }
+         /* do not overwrite local or static remote mappings */
+         clib_warning ("mapping %U rejected due to collision with local "
+                       "or static remote mapping!", format_gid_address,
+                       &a->eid);
+         return 0;
        }
-      /* new mapping */
-      else
-       {
-         remove_overlapping_sub_prefixes (lcm, eid, 0 == ls_args->locators);
 
-         ls_args->is_add = 1;
-         ls_args->index = ~0;
+      locator_set_t *old_ls;
 
-         vnet_lisp_add_del_locator_set (ls_args, &ls_index);
+      /* update mapping attributes */
+      old_map->action = a->action;
+      if (old_map->action != a->action && NULL != is_updated)
+       is_updated[0] = 1;
 
-         /* add mapping */
-         gid_address_copy (&m_args->eid, eid);
-         m_args->is_add = 1;
-         m_args->action = action;
-         m_args->locator_set_index = ls_index;
-         m_args->is_static = is_static;
-         m_args->ttl = ttl;
-         vnet_lisp_map_cache_add_del (m_args, &dst_map_index);
+      old_map->authoritative = a->authoritative;
+      old_map->ttl = a->ttl;
 
-         if (res_map_index)
-           res_map_index[0] = dst_map_index;
+      old_ls = pool_elt_at_index (lcm->locator_set_pool,
+                                 old_map->locator_set_index);
+      if (compare_locators (lcm, old_ls->locator_indices, ls_args->locators))
+       {
+         /* set locator-set index to overwrite */
+         ls_args->is_add = 1;
+         ls_args->index = old_map->locator_set_index;
+         vnet_lisp_add_del_locator_set (ls_args, 0);
+         if (is_updated)
+           is_updated[0] = 1;
        }
+      if (res_map_index)
+       res_map_index[0] = mi;
     }
+  /* new mapping */
   else
     {
-      if (old_map == 0 || gid_address_cmp (&old_map->eid, eid) != 0)
-       {
-         clib_warning ("cannot delete mapping for eid %U",
-                       format_gid_address, eid);
-         return -1;
-       }
-
-      m_args->is_add = 0;
-      gid_address_copy (&m_args->eid, eid);
-      m_args->locator_set_index = old_map->locator_set_index;
+      remove_overlapping_sub_prefixes (lcm, &a->eid, 0 == ls_args->locators);
 
-      /* delete mapping associated from map-cache */
-      vnet_lisp_map_cache_add_del (m_args, 0);
+      ls_args->is_add = 1;
+      ls_args->index = ~0;
 
-      ls_args->is_add = 0;
-      ls_args->index = old_map->locator_set_index;
-      /* delete locator set */
-      vnet_lisp_add_del_locator_set (ls_args, 0);
+      vnet_lisp_add_del_locator_set (ls_args, &ls_index);
 
-      /* delete timer associated to the mapping if any */
-      if (old_map->timer_set)
-       mapping_delete_timer (lcm, mi);
+      /* add mapping */
+      a->is_add = 1;
+      a->locator_set_index = ls_index;
+      vnet_lisp_map_cache_add_del (a, &dst_map_index);
 
-      /* return old mapping index */
       if (res_map_index)
-       res_map_index[0] = mi;
+       res_map_index[0] = dst_map_index;
+    }
+
+  /* success */
+  return 0;
+}
+
+/**
+ * Removes a mapping. Does not program forwarding.
+ *
+ * @param eid end-host indetifier
+ * @param res_map_index index of the removed mapping
+ * @return return code
+ */
+int
+vnet_lisp_del_mapping (gid_address_t * eid, u32 * res_map_index)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  vnet_lisp_add_del_mapping_args_t _m_args, *m_args = &_m_args;
+  vnet_lisp_add_del_locator_set_args_t _ls_args, *ls_args = &_ls_args;
+  mapping_t *old_map;
+  u32 mi;
+
+  memset (m_args, 0, sizeof (m_args[0]));
+  if (res_map_index)
+    res_map_index[0] = ~0;
+
+  mi = gid_dictionary_lookup (&lcm->mapping_index_by_gid, eid);
+  old_map = ((u32) ~ 0 != mi) ? pool_elt_at_index (lcm->mapping_pool, mi) : 0;
+
+  if (old_map == 0 || gid_address_cmp (&old_map->eid, eid) != 0)
+    {
+      clib_warning ("cannot delete mapping for eid %U",
+                   format_gid_address, eid);
+      return -1;
     }
 
+  m_args->is_add = 0;
+  gid_address_copy (&m_args->eid, eid);
+  m_args->locator_set_index = old_map->locator_set_index;
+
+  /* delete mapping associated from map-cache */
+  vnet_lisp_map_cache_add_del (m_args, 0);
+
+  ls_args->is_add = 0;
+  ls_args->index = old_map->locator_set_index;
+
+  /* delete locator set */
+  vnet_lisp_add_del_locator_set (ls_args, 0);
+
+  /* delete timer associated to the mapping if any */
+  if (old_map->timer_set)
+    mapping_delete_timer (lcm, mi);
+
+  /* return old mapping index */
+  if (res_map_index)
+    res_map_index[0] = mi;
+
   /* success */
   return 0;
 }
@@ -1496,6 +1525,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
  *
@@ -2074,6 +2123,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)
 {
@@ -2257,7 +2321,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;
 
@@ -2283,6 +2347,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);
@@ -2311,7 +2376,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;
 
@@ -2326,24 +2391,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;
@@ -2466,37 +2536,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
@@ -2572,7 +2643,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;
 }
 
@@ -2626,28 +2696,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;
@@ -2676,15 +2736,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);
@@ -2696,7 +2756,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);
 
@@ -3095,8 +3159,9 @@ lisp_cp_lookup_inline (vlib_main_t * vm,
                                                   to_next,
                                                   n_left_to_next, pi0,
                                                   next0);
+                 continue;
                }
-             continue;
+             goto done;
            }
 
          /* if we have remote mapping for destination already in map-chache
@@ -3139,6 +3204,7 @@ lisp_cp_lookup_inline (vlib_main_t * vm,
              pkts_mapped++;
            }
 
+       done:
          b0->error = node->errors[LISP_CP_LOOKUP_ERROR_DROP];
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
@@ -3329,8 +3395,7 @@ remove_expired_mapping (lisp_cp_main_t * lcm, u32 mi)
   if (vnet_lisp_add_del_adjacency (adj_args))
     clib_warning ("failed to del adjacency!");
 
-  vnet_lisp_add_del_mapping (&m->eid, 0, 0, 0, ~0, 0 /* is_add */ ,
-                            0 /* is_static */ , 0);
+  vnet_lisp_del_mapping (&m->eid, NULL);
   mapping_delete_timer (lcm, mi);
 }
 
@@ -3349,6 +3414,73 @@ mapping_start_expiration_timer (lisp_cp_main_t * lcm, u32 mi,
   timing_wheel_insert (&lcm->wheel, exp_clock_time, mi);
 }
 
+static void
+process_expired_mapping (lisp_cp_main_t * lcm, u32 mi)
+{
+  int rv;
+  vnet_lisp_gpe_add_del_fwd_entry_args_t _a, *a = &_a;
+  mapping_t *m = pool_elt_at_index (lcm->mapping_pool, mi);
+  uword *fei;
+  fwd_entry_t *fe;
+  vlib_counter_t c;
+  u8 have_stats = 0;
+
+  if (m->delete_after_expiration)
+    {
+      remove_expired_mapping (lcm, mi);
+      return;
+    }
+
+  fei = hash_get (lcm->fwd_entry_by_mapping_index, mi);
+  if (!fei)
+    return;
+
+  fe = pool_elt_at_index (lcm->fwd_entry_pool, fei[0]);
+
+  memset (a, 0, sizeof (*a));
+  a->rmt_eid = fe->reid;
+  if (fe->is_src_dst)
+    a->lcl_eid = fe->leid;
+  a->vni = gid_address_vni (&fe->reid);
+
+  rv = vnet_lisp_gpe_get_fwd_stats (a, &c);
+  if (0 == rv)
+    have_stats = 1;
+
+  if (m->almost_expired)
+    {
+      m->almost_expired = 0;   /* reset flag */
+      if (have_stats)
+       {
+         if (m->packets != c.packets)
+           {
+             /* mapping is in use, re-fetch */
+             map_request_args_t mr_args;
+             memset (&mr_args, 0, sizeof (mr_args));
+             mr_args.seid = fe->leid;
+             mr_args.deid = fe->reid;
+
+             send_map_request_thread_fn (&mr_args);
+           }
+         else
+           remove_expired_mapping (lcm, mi);
+       }
+      else
+       remove_expired_mapping (lcm, mi);
+    }
+  else
+    {
+      m->almost_expired = 1;
+      mapping_start_expiration_timer (lcm, mi, TIME_UNTIL_REFETCH_OR_DELETE);
+
+      if (have_stats)
+       /* save counter */
+       m->packets = c.packets;
+      else
+       m->delete_after_expiration = 1;
+    }
+}
+
 static void
 map_records_arg_free (map_records_arg_t * a)
 {
@@ -3371,6 +3503,7 @@ process_map_reply (map_records_arg_t * a)
   pending_map_request_t *pmr;
   u64 *noncep;
   uword *pmr_index;
+  u8 is_changed = 0;
 
   if (a->is_rloc_probe)
     goto done;
@@ -3386,26 +3519,36 @@ process_map_reply (map_records_arg_t * a)
 
   vec_foreach (m, a->mappings)
   {
+    vnet_lisp_add_del_mapping_args_t _m_args, *m_args = &_m_args;
+    memset (m_args, 0, sizeof (m_args[0]));
+    gid_address_copy (&m_args->eid, &m->eid);
+    m_args->action = m->action;
+    m_args->authoritative = m->authoritative;
+    m_args->ttl = m->ttl;
+    m_args->is_static = 0;
+
     /* insert/update mappings cache */
-    vnet_lisp_add_del_mapping (&m->eid, m->locators, m->action,
-                              m->authoritative, m->ttl,
-                              1, 0 /* is_static */ , &dst_map_index);
+    vnet_lisp_add_mapping (m_args, m->locators, &dst_map_index, &is_changed);
 
     if (dst_map_index == (u32) ~ 0)
       continue;
 
-    /* try to program forwarding only if mapping saved or updated */
-    vnet_lisp_add_del_adjacency_args_t _adj_args, *adj_args = &_adj_args;
-    memset (adj_args, 0, sizeof (adj_args[0]));
+    if (is_changed)
+      {
+       /* try to program forwarding only if mapping saved or updated */
+       vnet_lisp_add_del_adjacency_args_t _adj_args, *adj_args = &_adj_args;
+       memset (adj_args, 0, sizeof (adj_args[0]));
 
-    gid_address_copy (&adj_args->leid, &pmr->src);
-    gid_address_copy (&adj_args->reid, &m->eid);
-    adj_args->is_add = 1;
-    if (vnet_lisp_add_del_adjacency (adj_args))
-      clib_warning ("failed to add adjacency!");
+       gid_address_copy (&adj_args->leid, &pmr->src);
+       gid_address_copy (&adj_args->reid, &m->eid);
+       adj_args->is_add = 1;
+
+       if (vnet_lisp_add_del_adjacency (adj_args))
+         clib_warning ("failed to add adjacency!");
+      }
 
     if ((u32) ~ 0 != m->ttl)
-      mapping_start_expiration_timer (lcm, dst_map_index, m->ttl * 60);
+      mapping_start_expiration_timer (lcm, dst_map_index, MAPPING_TIMEOUT);
   }
 
   /* remove pending map request entry */
@@ -3472,7 +3615,11 @@ process_map_notify (map_records_arg_t * 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 *
@@ -3619,8 +3766,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;
     }
@@ -3650,7 +3797,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;
@@ -3984,9 +4131,11 @@ 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;
@@ -4004,6 +4153,9 @@ 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;
 }
 
@@ -4163,7 +4315,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);
 }
@@ -4184,15 +4336,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)
     {
@@ -4244,7 +4479,7 @@ send_map_resolver_service (vlib_main_t * vm,
          u32 *mi = 0;
          vec_foreach (mi, expired)
          {
-           remove_expired_mapping (lcm, mi[0]);
+           process_expired_mapping (lcm, mi[0]);
          }
          _vec_len (expired) = 0;
        }