Fix LISP src/dst based policy
[vpp.git] / vnet / vnet / lisp-cp / control.c
index 82fcb4f..3b843a0 100644 (file)
@@ -230,6 +230,8 @@ dp_del_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
   a->locator_pairs = fe->locator_pairs;
   a->vni = gid_address_vni (&fe->reid);
   gid_address_copy (&a->rmt_eid, &fe->reid);
+  if (fe->is_src_dst)
+    gid_address_copy (&a->lcl_eid, &fe->leid);
 
   vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index);
 
@@ -368,7 +370,7 @@ dp_add_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
   u32 sw_if_index;
   uword *feip = 0, *dpid;
   fwd_entry_t *fe;
-  u8 type;
+  u8 type, is_src_dst = 0;
 
   memset (a, 0, sizeof (*a));
 
@@ -386,12 +388,21 @@ dp_add_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
   /* insert data plane forwarding entry */
   a->is_add = 1;
 
-  if (GID_ADDR_SRC_DST == gid_address_type (&dst_map->eid))
+  if (MR_MODE_SRC_DST == lcm->map_request_mode)
     {
-      gid_address_sd_to_flat (&a->rmt_eid, &dst_map->eid,
-                             &gid_address_sd_dst (&dst_map->eid));
-      gid_address_sd_to_flat (&a->lcl_eid, &dst_map->eid,
-                             &gid_address_sd_src (&dst_map->eid));
+      if (GID_ADDR_SRC_DST == gid_address_type (&dst_map->eid))
+       {
+         gid_address_sd_to_flat (&a->rmt_eid, &dst_map->eid,
+                                 &gid_address_sd_dst (&dst_map->eid));
+         gid_address_sd_to_flat (&a->lcl_eid, &dst_map->eid,
+                                 &gid_address_sd_src (&dst_map->eid));
+       }
+      else
+       {
+         gid_address_copy (&a->rmt_eid, &dst_map->eid);
+         gid_address_copy (&a->lcl_eid, &src_map->eid);
+       }
+      is_src_dst = 1;
     }
   else
     gid_address_copy (&a->rmt_eid, &dst_map->eid);
@@ -440,10 +451,94 @@ dp_add_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, u32 dst_map_index)
   pool_get (lcm->fwd_entry_pool, fe);
   fe->locator_pairs = a->locator_pairs;
   gid_address_copy (&fe->reid, &a->rmt_eid);
+  gid_address_copy (&fe->leid, &src_map->eid);
+  fe->is_src_dst = is_src_dst;
   hash_set (lcm->fwd_entry_by_mapping_index, dst_map_index,
            fe - lcm->fwd_entry_pool);
 }
 
+/**
+ * Returns vector of adjacencies.
+ *
+ * The caller must free the vector returned by this function.
+ *
+ * @param vni virtual network identifier
+ * @return vector of adjacencies
+ */
+lisp_adjacency_t *
+vnet_lisp_adjacencies_get_by_vni (u32 vni)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  fwd_entry_t *fwd;
+  lisp_adjacency_t *adjs = 0, adj;
+
+  /* *INDENT-OFF* */
+  pool_foreach(fwd, lcm->fwd_entry_pool,
+  ({
+    if (gid_address_vni (&fwd->reid) != vni)
+      continue;
+
+    gid_address_copy (&adj.reid, &fwd->reid);
+    gid_address_copy (&adj.leid, &fwd->leid);
+    vec_add1 (adjs, adj);
+  }));
+  /* *INDENT-ON* */
+
+  return adjs;
+}
+
+static clib_error_t *
+lisp_show_adjacencies_command_fn (vlib_main_t * vm,
+                                 unformat_input_t * input,
+                                 vlib_cli_command_t * cmd)
+{
+  lisp_adjacency_t *adjs, *adj;
+  vlib_cli_output (vm, "%s %40s\n", "leid", "reid");
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u32 vni = ~0;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "vni %d", &vni))
+       ;
+      else
+       {
+         vlib_cli_output (vm, "parse error: '%U'",
+                          format_unformat_error, line_input);
+         return 0;
+       }
+    }
+
+  if (~0 == vni)
+    {
+      vlib_cli_output (vm, "error: no vni specified!");
+      return 0;
+    }
+
+  adjs = vnet_lisp_adjacencies_get_by_vni (vni);
+
+  vec_foreach (adj, adjs)
+  {
+    vlib_cli_output (vm, "%U %40U\n", format_gid_address, &adj->leid,
+                    format_gid_address, &adj->reid);
+  }
+  vec_free (adjs);
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (lisp_show_adjacencies_command) = {
+    .path = "show lisp adjacencies",
+    .short_help = "show lisp adjacencies",
+    .function = lisp_show_adjacencies_command_fn,
+};
+/* *INDENT-ON* */
+
 /**
  * Add/remove mapping to/from map-cache. Overwriting not allowed.
  */
@@ -788,6 +883,85 @@ compare_locators (lisp_cp_main_t * lcm, u32 * old_ls_indexes,
   return 0;
 }
 
+typedef struct
+{
+  u8 is_negative;
+  void *lcm;
+  gid_address_t *eids_to_be_deleted;
+} remove_mapping_args_t;
+
+/**
+ * Callback invoked when a sub-prefix is found
+ */
+static void
+remove_mapping_if_needed (u32 mi, void *arg)
+{
+  u8 delete = 0;
+  remove_mapping_args_t *a = arg;
+  lisp_cp_main_t *lcm = a->lcm;
+  mapping_t *m;
+  locator_set_t *ls;
+
+  m = pool_elt_at_index (lcm->mapping_pool, mi);
+  if (!m)
+    return;
+
+  ls = pool_elt_at_index (lcm->locator_set_pool, m->locator_set_index);
+
+  if (a->is_negative)
+    {
+      if (0 != vec_len (ls->locator_indices))
+       delete = 1;
+    }
+  else
+    {
+      if (0 == vec_len (ls->locator_indices))
+       delete = 1;
+    }
+
+  if (delete)
+    vec_add1 (a->eids_to_be_deleted, m->eid);
+}
+
+/**
+ * This function searches map cache and looks for IP prefixes that are subset
+ * of the provided one. If such prefix is found depending on 'is_negative'
+ * it does follows:
+ *
+ * 1) if is_negative is true and found prefix points to positive mapping,
+ *    then the mapping is removed
+ * 2) if is_negative is false and found prefix points to negative mapping,
+ *    then the mapping is removed
+ */
+static void
+remove_overlapping_sub_prefixes (lisp_cp_main_t * lcm, gid_address_t * eid,
+                                u8 is_negative)
+{
+  gid_address_t *e;
+  remove_mapping_args_t a;
+  memset (&a, 0, sizeof (a));
+
+  /* do this only in src/dst mode ... */
+  if (MR_MODE_SRC_DST != lcm->map_request_mode)
+    return;
+
+  /* ... and  only for IP prefix */
+  if (GID_ADDR_SRC_DST != gid_address_type (eid)
+      || (FID_ADDR_IP_PREF != gid_address_sd_dst_type (eid)))
+    return;
+
+  a.is_negative = is_negative;
+  a.lcm = lcm;
+
+  gid_dict_foreach_subprefix (&lcm->mapping_index_by_gid, eid,
+                             remove_mapping_if_needed, &a);
+
+  vec_foreach (e, a.eids_to_be_deleted)
+    vnet_lisp_add_del_mapping (e, 0, 0, 0, 0, 0 /* is add */ , 0, 0);
+
+  vec_free (a.eids_to_be_deleted);
+}
+
 /**
  * Adds/removes/updates mapping. Does not program forwarding.
  *
@@ -840,7 +1014,7 @@ vnet_lisp_add_del_mapping (gid_address_t * eid, locator_t * rlocs, u8 action,
              /* 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);
+                           eid);
              return 0;
            }
 
@@ -867,6 +1041,8 @@ vnet_lisp_add_del_mapping (gid_address_t * eid, locator_t * rlocs, u8 action,
       /* new mapping */
       else
        {
+         remove_overlapping_sub_prefixes (lcm, eid, 0 == ls_args->locators);
+
          ls_args->is_add = 1;
          ls_args->index = ~0;
 
@@ -977,7 +1153,8 @@ lisp_add_del_adjacency (lisp_cp_main_t * lcm, gid_address_t * local_eid,
       return VNET_API_ERROR_LISP_DISABLED;
     }
 
-  remote_mi = gid_dictionary_lookup (&lcm->mapping_index_by_gid, remote_eid);
+  remote_mi = gid_dictionary_sd_lookup (&lcm->mapping_index_by_gid,
+                                       remote_eid, local_eid);
   if (GID_LOOKUP_MISS == remote_mi)
     {
       clib_warning ("Remote eid %U not found. Cannot add adjacency!",
@@ -2544,7 +2721,7 @@ lisp_add_del_mreq_itr_rlocs_command_fn (vlib_main_t * vm,
     {
       if (unformat (line_input, "del"))
        is_add = 0;
-      else if (unformat (line_input, "add %s", &locator_set_name))
+      else if (unformat (line_input, "add %_%v%_", &locator_set_name))
        is_add = 1;
       else
        {
@@ -2757,7 +2934,8 @@ build_encapsulated_map_request (lisp_cp_main_t * lcm,
   /* get rlocs */
   rlocs = build_itr_rloc_list (lcm, loc_set);
 
-  if (MR_MODE_SRC_DST == lcm->map_request_mode)
+  if (MR_MODE_SRC_DST == lcm->map_request_mode
+      && GID_ADDR_SRC_DST != gid_address_type (deid))
     {
       gid_address_t sd;
       memset (&sd, 0, sizeof (sd));
@@ -3562,14 +3740,17 @@ send_map_request_thread_fn (void *arg)
   map_request_args_t *a = arg;
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
 
-  lisp_pending_map_request_lock (lcm);
-
   if (a->is_resend)
+    /* if resending, we already have the lock */
     resend_encapsulated_map_request (lcm, &a->seid, &a->deid, a->smr_invoked);
   else
-    send_encapsulated_map_request (lcm, &a->seid, &a->deid, a->smr_invoked);
+    {
+      /* get lock before sending map-request */
+      lisp_pending_map_request_lock (lcm);
+      send_encapsulated_map_request (lcm, &a->seid, &a->deid, a->smr_invoked);
+      lisp_pending_map_request_unlock (lcm);
+    }
 
-  lisp_pending_map_request_unlock (lcm);
 
   return 0;
 }