LISP statistics

Change-Id: I399cac46d279e020ba33459ef759d9d29d3ac716
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
diff --git a/src/vnet/lisp-cp/control.c b/src/vnet/lisp-cp/control.c
index 5c90c03..47badeb 100644
--- a/src/vnet/lisp-cp/control.c
+++ b/src/vnet/lisp-cp/control.c
@@ -19,6 +19,7 @@
 #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>
 
@@ -284,6 +285,7 @@
   if (fe->is_src_dst)
     gid_address_copy (&a->lcl_eid, &fe->leid);
 
+  vnet_lisp_del_fwd_stats (a, feip[0]);
   vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index);
 
   /* delete entry in fwd table */
@@ -1228,9 +1230,6 @@
 
   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 :
@@ -2654,16 +2653,15 @@
   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)
 {
   u32 vni = 0;
-  u16 type;
 
   memset (src, 0, sizeof (*src));
   memset (dst, 0, sizeof (*dst));
-  type = vnet_buffer (b)->lisp.overlay_afi;
 
   if (LISP_AFI_IP == type || LISP_AFI_IP6 == type)
     {
@@ -2742,10 +2740,9 @@
 
 	  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 we have remote mapping for destination already in map-chache
 	     add forwarding tunnel directly. If not send a map-request */
@@ -3605,6 +3602,55 @@
   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)
+{
+  lisp_stats_t *s;
+  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;
+
+  s = pool_elt_at_index (lgm->lisp_stats_pool, stats_index);
+  stat->stats = *s;
+  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)
 {
@@ -3810,7 +3856,11 @@
   if (vnet_lisp_enable_disable_status () == 0)
     return VNET_API_ERROR_LISP_DISABLED;
 
-  lcm->stats_enabled = enable;
+  if (enable)
+    lcm->flags |= LISP_FLAG_STATS_ENABLED;
+  else
+    lcm->flags &= ~LISP_FLAG_STATS_ENABLED;
+
   return 0;
 }
 
@@ -3822,7 +3872,7 @@
   if (vnet_lisp_enable_disable_status () == 0)
     return VNET_API_ERROR_LISP_DISABLED;
 
-  return lcm->stats_enabled;
+  return lcm->flags & LISP_FLAG_STATS_ENABLED;
 }
 
 /* *INDENT-OFF* */
diff --git a/src/vnet/lisp-cp/control.h b/src/vnet/lisp-cp/control.h
index 933b34b..eae8a18 100644
--- a/src/vnet/lisp-cp/control.h
+++ b/src/vnet/lisp-cp/control.h
@@ -95,7 +95,8 @@
 } map_request_mode_t;
 
 #define foreach_lisp_flag_bit       \
-  _(USE_PETR, "Use Proxy-ETR")
+  _(USE_PETR, "Use Proxy-ETR")                  \
+  _(STATS_ENABLED, "Statistics enabled")
 
 typedef enum lisp_flag_bits
 {
@@ -210,9 +211,6 @@
   /* timing wheel for mappping timeouts */
   timing_wheel_t wheel;
 
-  /* statistics */
-  u8 stats_enabled;
-
   /* commodity */
   ip4_main_t *im4;
   ip6_main_t *im6;
@@ -235,6 +233,11 @@
   return &lisp_control_main;
 }
 
+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,
+				  u16 type);
+
 typedef struct
 {
   u8 is_add;
@@ -335,9 +338,6 @@
   return pool_elt_at_index (lcm->mapping_pool, lcm->petr_map_index);
 }
 
-u8 vnet_lisp_stats_enable_disable_state (void);
-vnet_api_error_t vnet_lisp_stats_enable_disable (u8 enable);
-
 #endif /* VNET_CONTROL_H_ */
 
 /*
diff --git a/src/vnet/lisp-cp/lisp_types.c b/src/vnet/lisp-cp/lisp_types.c
index b646668..ad3a4bd 100644
--- a/src/vnet/lisp-cp/lisp_types.c
+++ b/src/vnet/lisp-cp/lisp_types.c
@@ -573,6 +573,44 @@
   return (sizeof (u16) + size);
 }
 
+void
+gid_to_dp_address (gid_address_t * g, dp_address_t * d)
+{
+  switch (gid_address_type (g))
+    {
+    case GID_ADDR_SRC_DST:
+      switch (gid_address_sd_dst_type (g))
+	{
+	case FID_ADDR_IP_PREF:
+	  ip_prefix_copy (&d->ippref, &gid_address_sd_dst_ippref (g));
+	  d->type = FID_ADDR_IP_PREF;
+	  break;
+	case FID_ADDR_MAC:
+	  mac_copy (&d->mac, &gid_address_sd_dst_mac (g));
+	  d->type = FID_ADDR_MAC;
+	  break;
+	default:
+	  clib_warning ("Source/Dest address type %d not supported!",
+			gid_address_sd_dst_type (g));
+	  break;
+	}
+      break;
+    case GID_ADDR_IP_PREFIX:
+      ip_prefix_copy (&d->ippref, &gid_address_ippref (g));
+      d->type = FID_ADDR_IP_PREF;
+      break;
+    case GID_ADDR_MAC:
+      mac_copy (&d->mac, &gid_address_mac (g));
+      d->type = FID_ADDR_MAC;
+      break;
+    case GID_ADDR_NSH:
+    default:
+      d->nsh = gid_address_nsh (g).spi << 8 | gid_address_nsh (g).si;
+      d->type = FID_ADDR_NSH;
+      break;
+    }
+}
+
 u32
 lcaf_hdr_parse (void *offset, lcaf_t * lcaf)
 {
diff --git a/src/vnet/lisp-cp/lisp_types.h b/src/vnet/lisp-cp/lisp_types.h
index 672835b..a65a479 100644
--- a/src/vnet/lisp-cp/lisp_types.h
+++ b/src/vnet/lisp-cp/lisp_types.h
@@ -126,6 +126,8 @@
 typedef fid_address_t dp_address_t;
 
 #define fid_addr_ippref(_a) (_a)->ippref
+#define fid_addr_prefix_length(_a) ip_prefix_len(&fid_addr_ippref(_a))
+#define fid_addr_ip_version(_a) ip_prefix_version(&fid_addr_ippref(_a))
 #define fid_addr_mac(_a) (_a)->mac
 #define fid_addr_nsh(_a) (_a)->nsh
 #define fid_addr_type(_a) (_a)->type
@@ -359,6 +361,7 @@
 build_src_dst (gid_address_t * sd, gid_address_t * src, gid_address_t * dst);
 
 void gid_address_from_ip (gid_address_t * g, ip_address_t * ip);
+void gid_to_dp_address (gid_address_t * g, dp_address_t * d);
 
 #endif /* VNET_LISP_GPE_LISP_TYPES_H_ */
 
diff --git a/src/vnet/lisp-cp/one_api.c b/src/vnet/lisp-cp/one_api.c
index 4faf624..ab9e7a6 100644
--- a/src/vnet/lisp-cp/one_api.c
+++ b/src/vnet/lisp-cp/one_api.c
@@ -58,6 +58,21 @@
 
 #include <vlibapi/api_helper_macros.h>
 
+#define REPLY_DETAILS(t, body)                                  \
+do {                                                            \
+    unix_shared_memory_queue_t * q;                             \
+    rv = vl_msg_api_pd_handler (mp, rv);                        \
+    q = vl_api_client_index_to_input_queue (mp->client_index);  \
+    if (!q)                                                     \
+        return;                                                 \
+                                                                \
+    rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
+    rmp->_vl_msg_id = ntohs((t));                               \
+    rmp->context = mp->context;                                 \
+    do {body;} while (0);                                       \
+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
+} while(0);
+
 #define foreach_vpe_api_msg                             \
 _(ONE_ADD_DEL_LOCATOR_SET, one_add_del_locator_set)                     \
 _(ONE_ADD_DEL_LOCATOR, one_add_del_locator)                             \
@@ -1301,9 +1316,61 @@
 }
 
 static void
+lisp_fid_addr_to_api (fid_address_t * fid, u8 * dst, u8 * api_eid_type,
+		      u8 * prefix_length)
+{
+  switch (fid_addr_type (fid))
+    {
+    case FID_ADDR_IP_PREF:
+      *prefix_length = fid_addr_prefix_length (fid);
+      if (fid_addr_ip_version (fid) == IP4)
+	{
+	  *api_eid_type = 0;	/* ipv4 type */
+	  clib_memcpy (dst, &fid_addr_ippref (fid), 4);
+	}
+      else
+	{
+	  *api_eid_type = 1;	/* ipv6 type */
+	  clib_memcpy (dst, &fid_addr_ippref (fid), 16);
+	}
+      break;
+    case FID_ADDR_MAC:
+      *api_eid_type = 2;	/* l2 mac type */
+      mac_copy (dst, fid_addr_mac (fid));
+      break;
+    default:
+      ASSERT (0);
+    }
+}
+
+static void
 vl_api_one_stats_dump_t_handler (vl_api_one_stats_dump_t * mp)
 {
+  vl_api_one_stats_details_t *rmp;
+  lisp_api_stats_t *stats, *stat;
+  u8 rv = 0;
 
+  stats = vnet_lisp_get_stats ();
+  vec_foreach (stat, stats)
+  {
+      /* *INDENT-OFF* */
+      REPLY_DETAILS (VL_API_ONE_STATS_DETAILS,
+      ({
+        lisp_fid_addr_to_api (&stat->deid, rmp->deid, &rmp->eid_type,
+                              &rmp->deid_pref_len);
+        lisp_fid_addr_to_api (&stat->seid, rmp->seid, &rmp->eid_type,
+                              &rmp->seid_pref_len);
+        rmp->vni = clib_host_to_net_u32 (stat->vni);
+
+        rmp->is_ip4 = ip_addr_version (&stat->rmt_rloc) == IP4 ? 1 : 0;
+        ip_address_copy_addr (rmp->rloc, &stat->rmt_rloc);
+        ip_address_copy_addr (rmp->lloc, &stat->loc_rloc);
+
+        rmp->pkt_count = clib_host_to_net_u32 (stat->stats.pkt_count);
+        rmp->bytes = clib_host_to_net_u32 (stat->stats.bytes);
+      }));
+      /* *INDENT-ON* */
+  }
 }
 
 /*
diff --git a/src/vnet/lisp-cp/one_cli.c b/src/vnet/lisp-cp/one_cli.c
index 2ceeaf4..b5bc529 100644
--- a/src/vnet/lisp-cp/one_cli.c
+++ b/src/vnet/lisp-cp/one_cli.c
@@ -352,7 +352,7 @@
 	}
     }
 
-  if (!eid_set)
+  if (!del_all && !eid_set)
     {
       clib_warning ("missing eid!");
       goto done;
@@ -372,8 +372,6 @@
       goto done;
     }
 
-  /* TODO build src/dst with seid */
-
   /* if it's a delete, clean forwarding */
   if (!is_add)
     {
@@ -1654,13 +1652,47 @@
 
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (one_show_stats_command) = {
-    .path = "show one stats",
-    .short_help = "show ONE statistics",
+    .path = "show one statistics status",
+    .short_help = "show ONE statistics enable/disable status",
     .function = lisp_show_stats_command_fn,
 };
 /* *INDENT-ON* */
 
 static clib_error_t *
+lisp_show_stats_details_command_fn (vlib_main_t * vm,
+				    unformat_input_t * input,
+				    vlib_cli_command_t * cmd)
+{
+  lisp_api_stats_t *stat, *stats = vnet_lisp_get_stats ();
+
+  if (vec_len (stats) > 0)
+    vlib_cli_output (vm,
+		     "[src-EID, dst-EID] [loc-rloc, rmt-rloc] count bytes\n");
+  else
+    vlib_cli_output (vm, "No statistics found.\n");
+
+  vec_foreach (stat, stats)
+  {
+    vlib_cli_output (vm, "[%U, %U] [%U, %U] %7u %7u\n",
+		     format_fid_address, &stat->seid,
+		     format_fid_address, &stat->deid,
+		     format_ip_address, &stat->loc_rloc,
+		     format_ip_address, &stat->rmt_rloc,
+		     stat->stats.pkt_count, stat->stats.bytes);
+  }
+  vec_free (stats);
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (one_show_stats_details_command) = {
+    .path = "show one statistics details",
+    .short_help = "show ONE statistics",
+    .function = lisp_show_stats_details_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
 lisp_stats_enable_disable_command_fn (vlib_main_t * vm,
 				      unformat_input_t * input,
 				      vlib_cli_command_t * cmd)
@@ -1692,12 +1724,29 @@
 
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (one_stats_enable_disable_command) = {
-    .path = "one stats",
+    .path = "one statistics",
     .short_help = "enable/disable ONE statistics collecting",
     .function = lisp_stats_enable_disable_command_fn,
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+lisp_stats_flush_command_fn (vlib_main_t * vm,
+			     unformat_input_t * input,
+			     vlib_cli_command_t * cmd)
+{
+  vnet_lisp_flush_stats ();
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (one_stats_flush_command) = {
+    .path = "one statistics flush",
+    .short_help = "Flush ONE statistics",
+    .function = lisp_stats_flush_command_fn,
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *