Add API calls for packet generator

Added new API to:
- create packet generator interface
- enable packet generator per stream or all
- capture into file

Change-Id: I0e6c1f28069853e4b26f0dc9d282353b0b7f6512
Signed-off-by: Pavel Kotucek <pkotucek@cisco.com>
diff --git a/vnet/vnet/api_errno.h b/vnet/vnet/api_errno.h
index f16d978..2c247d4 100644
--- a/vnet/vnet/api_errno.h
+++ b/vnet/vnet/api_errno.h
@@ -82,7 +82,8 @@
 _(INVALID_WORKER, -89, "Invalid worker thread")                         \
 _(LISP_DISABLED, -90, "LISP is disabled")                               \
 _(CLASSIFY_TABLE_NOT_FOUND, -91, "Classify table not found")            \
-_(INVALID_EID_TYPE, -92, "Unsupported LSIP EID type")
+_(INVALID_EID_TYPE, -92, "Unsupported LSIP EID type")                   \
+_(CANNOT_CREATE_PCAP_FILE, -93, "Cannot create pcap file")
 
 typedef enum {
 #define _(a,b,c) VNET_API_ERROR_##a = (b),
diff --git a/vnet/vnet/pg/cli.c b/vnet/vnet/pg/cli.c
index d73add1..96c20b7 100644
--- a/vnet/vnet/pg/cli.c
+++ b/vnet/vnet/pg/cli.c
@@ -52,13 +52,58 @@
   .short_help = "Packet generator commands",
 };
 
+void pg_enable_disable (u32 stream_index, int is_enable)
+{
+    pg_main_t * pg = &pg_main;
+    pg_stream_t * s;
+
+    if (stream_index == ~0) {
+        /* No stream specified: enable/disable all streams. */
+        pool_foreach (s, pg->streams, ({
+            pg_stream_enable_disable (pg, s, is_enable);
+        }));
+    }
+    else
+    {
+        /* enable/disable specified stream. */
+        s = pool_elt_at_index (pg->streams, stream_index);
+        pg_stream_enable_disable (pg, s, is_enable);
+    }
+}
+
+clib_error_t * pg_capture (pg_capture_args_t *a)
+{
+    pg_main_t * pg = &pg_main;
+    pg_interface_t * pi;
+
+    if (a->is_enabled == 1)
+    {
+        struct stat sb;
+        if (stat ((char *) a->pcap_file_name, &sb) != -1)
+            return clib_error_return (0, "Cannot create pcap file");
+    }
+
+    pi = pool_elt_at_index (pg->interfaces, a->dev_instance);
+    vec_free (pi->pcap_file_name);
+    memset (&pi->pcap_main, 0, sizeof (pi->pcap_main));
+
+    if (a->is_enabled == 0)
+        return 0;
+
+    pi->pcap_file_name = a->pcap_file_name;
+    pi->pcap_main.file_name = (char *) pi->pcap_file_name;
+    pi->pcap_main.n_packets_to_capture = a->count;
+    pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
+
+    return 0;
+}
+
 static clib_error_t *
 enable_disable_stream (vlib_main_t * vm,
 		       unformat_input_t * input,
 		       vlib_cli_command_t * cmd)
 {
   pg_main_t * pg = &pg_main;
-  pg_stream_t * s;
   int is_enable = cmd->function_arg != 0;
   u32 stream_index = ~0;
 
@@ -71,18 +116,8 @@
     return clib_error_create ("unknown input `%U'",
 			      format_unformat_error, input);
 
-  /* No stream specified: enable/disable all streams. */
-  if (stream_index == ~0)
-    pool_foreach (s, pg->streams, ({
-      pg_stream_enable_disable (pg, s, is_enable);
-    }));
-  else
-    {
-      /* enable/disable specified stream. */
-      s = pool_elt_at_index (pg->streams, stream_index);
-      pg_stream_enable_disable (pg, s, is_enable);
-    }
-		      
+  pg_enable_disable (stream_index, is_enable);
+
   return 0;
 }
 
@@ -455,12 +490,10 @@
 		  unformat_input_t * input,
 		  vlib_cli_command_t * cmd)
 {
-  pg_main_t * pg = &pg_main;
   clib_error_t * error = 0;
   vnet_main_t * vnm = vnet_get_main();
   unformat_input_t _line_input, * line_input = &_line_input;
   vnet_hw_interface_t * hi = 0;
-  pg_interface_t * pi;
   u8 * pcap_file_name = 0;
   u32 hw_if_index;
   u32 is_disable = 0;
@@ -501,28 +534,18 @@
   if (!pcap_file_name && is_disable == 0)
     return clib_error_return (0, "Please specify pcap file name");
 
-  if (is_disable == 0)
-    {
-      struct stat sb;
-      if (stat ((char *) pcap_file_name, &sb) != -1)
-	return clib_error_return (0, "Cannot create pcap file");
-    }
-
   unformat_free (line_input);
 
-  pi = pool_elt_at_index (pg->interfaces, hi->dev_instance);
-  vec_free (pi->pcap_file_name);
-  memset (&pi->pcap_main, 0, sizeof (pi->pcap_main));
+  pg_capture_args_t _a, *a=&_a;
 
-  if (is_disable)
-    return 0;
+  a->hw_if_index = hw_if_index;
+  a->dev_instance = hi->dev_instance;
+  a->is_enabled = !is_disable;
+  a->pcap_file_name = pcap_file_name;
+  a->count = count;
 
-  pi->pcap_file_name = pcap_file_name;
-  pi->pcap_main.file_name = (char *) pi->pcap_file_name;
-  pi->pcap_main.n_packets_to_capture = count;
-  pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
-
-  return 0;
+  error = pg_capture (a);
+  return error;
 }
 
 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
diff --git a/vnet/vnet/pg/pg.h b/vnet/vnet/pg/pg.h
index 750e7f7..54c270f 100644
--- a/vnet/vnet/pg/pg.h
+++ b/vnet/vnet/pg/pg.h
@@ -352,4 +352,16 @@
 					  void * fixed_packet_data,
 					  void * fixed_packet_data_mask);
 
+void pg_enable_disable (u32 stream_index, int is_enable);
+
+typedef struct {
+  u32  hw_if_index;
+  u32  dev_instance;
+  u8   is_enabled;
+  u8 * pcap_file_name;
+  u32  count;
+} pg_capture_args_t;
+
+clib_error_t * pg_capture (pg_capture_args_t *a);
+
 #endif /* included_vlib_pg_h */
diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c
index 70d8e96..0ab9b07 100644
--- a/vpp-api-test/vat/api_format.c
+++ b/vpp-api-test/vat/api_format.c
@@ -49,6 +49,8 @@
 
 #include "vat/json_format.h"
 
+#include <sys/stat.h>
+
 #define vl_typedefs             /* define message structures */
 #include <vpp-api/vpe_all_api_h.h> 
 #undef vl_typedefs
@@ -2781,13 +2783,41 @@
     vat_json_object_add_string_copy(node, "match", s);
 }
 
+static void vl_api_pg_create_interface_reply_t_handler
+(vl_api_pg_create_interface_reply_t * mp)
+{
+    vat_main_t * vam = &vat_main;
+
+    vam->retval = ntohl(mp->retval);
+    vam->result_ready = 1;
+}
+
+static void vl_api_pg_create_interface_reply_t_handler_json
+(vl_api_pg_create_interface_reply_t * mp)
+{
+    vat_main_t * vam = &vat_main;
+    vat_json_node_t node;
+
+    i32 retval = ntohl(mp->retval);
+    if (retval == 0) {
+        vat_json_init_object(&node);
+
+        vat_json_object_add_int(&node, "sw_if_index", ntohl(mp->sw_if_index));
+
+        vat_json_print(vam->ofp, &node);
+        vat_json_free(&node);
+    }
+    vam->retval = ntohl(mp->retval);
+    vam->result_ready = 1;
+}
+
 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
 #define vl_api_vnet_ip6_fib_counters_t_print vl_noop_handler
 
 /* 
- * Generate boilerplate reply handlers, which 
+ * Generate boilerplate reply handlers, which
  * dig the return value out of the xxx_reply_t API message,
  * stick it into vam->retval, and set vam->result_ready
  *
@@ -2882,7 +2912,9 @@
 _(policer_add_del_reply)                                \
 _(netmap_create_reply)                                  \
 _(netmap_delete_reply)                                  \
-_(ipfix_enable_reply)
+_(ipfix_enable_reply)                                   \
+_(pg_capture_reply)                                     \
+_(pg_enable_disable_reply)
 
 #define _(n)                                    \
     static void vl_api_##n##_t_handler          \
@@ -3088,7 +3120,10 @@
 _(CLASSIFY_SESSION_DETAILS, classify_session_details)                   \
 _(IPFIX_ENABLE_REPLY, ipfix_enable_reply)                               \
 _(IPFIX_DETAILS, ipfix_details)                                         \
-_(GET_NEXT_INDEX_REPLY, get_next_index_reply)
+_(GET_NEXT_INDEX_REPLY, get_next_index_reply)                           \
+_(PG_CREATE_INTERFACE_REPLY, pg_create_interface_reply)                 \
+_(PG_CAPTURE_REPLY, pg_capture_reply)                                   \
+_(PG_ENABLE_DISABLE_REPLY, pg_enable_disable_reply)
 
 /* M: construct, but don't yet send a message */
 
@@ -12329,6 +12364,127 @@
     return 0;
 }
 
+int api_pg_create_interface (vat_main_t *vam)
+{
+    unformat_input_t * input = vam->input;
+    vl_api_pg_create_interface_t *mp;
+    f64 timeout;
+
+    u32 if_id = ~0;
+    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
+        if (unformat (input, "if_id %d", &if_id))
+            ;
+        else
+            break;
+    }
+    if (if_id == ~0) {
+        errmsg ("missing pg interface index\n");
+        return -99;
+    }
+
+    /* Construct the API message */
+    M(PG_CREATE_INTERFACE, pg_create_interface);
+    mp->context = 0;
+    mp->interface_id = ntohl(if_id);
+
+    S; W;
+    /* NOTREACHED */
+    return 0;
+}
+
+int api_pg_capture (vat_main_t *vam)
+{
+    unformat_input_t * input = vam->input;
+    vl_api_pg_capture_t *mp;
+    f64 timeout;
+
+    u32 if_id = ~0;
+    u8 enable = 1;
+    u32 count = 1;
+    u8 pcap_file_set = 0;
+    u8 * pcap_file = 0;
+    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
+        if (unformat (input, "if_id %d", &if_id))
+            ;
+        else if (unformat (input, "pcap %s", &pcap_file))
+            pcap_file_set = 1;
+        else if (unformat (input, "count %d", &count))
+            ;
+        else if (unformat (input, "disable"))
+            enable = 0;
+        else
+            break;
+    }
+    if (if_id == ~0) {
+        errmsg ("missing pg interface index\n");
+        return -99;
+    }
+    if (pcap_file_set>0) {
+    	if (vec_len (pcap_file) > 255) {
+    	    errmsg ("pcap file name is too long\n");
+    	    return -99;
+    	}
+    }
+
+    u32 name_len = vec_len(pcap_file);
+    /* Construct the API message */
+    M(PG_CAPTURE, pg_capture);
+    mp->context = 0;
+    mp->interface_id = ntohl(if_id);
+    mp->is_enabled = enable;
+    mp->count = ntohl(count);
+    mp->pcap_name_length = ntohl(name_len);
+    if (pcap_file_set != 0) {
+        clib_memcpy(mp->pcap_file_name, pcap_file, name_len);
+    }
+    vec_free(pcap_file);
+
+    S; W;
+    /* NOTREACHED */
+    return 0;
+}
+
+int api_pg_enable_disable (vat_main_t *vam)
+{
+    unformat_input_t * input = vam->input;
+    vl_api_pg_enable_disable_t *mp;
+    f64 timeout;
+
+    u8 enable = 1;
+    u8 stream_name_set = 0;
+    u8 * stream_name = 0;
+    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
+        if (unformat (input, "stream %s", &stream_name))
+            stream_name_set = 1;
+    	else if (unformat (input, "disable"))
+            enable = 0;
+        else
+            break;
+    }
+
+    if (stream_name_set>0) {
+    	if (vec_len (stream_name) > 255) {
+            errmsg ("stream name too long\n");
+            return -99;
+    	}
+    }
+
+    u32 name_len = vec_len(stream_name);
+    /* Construct the API message */
+    M(PG_ENABLE_DISABLE, pg_enable_disable);
+    mp->context = 0;
+    mp->is_enabled = enable;
+    if (stream_name_set != 0) {
+        mp->stream_name_length = ntohl(name_len);
+        clib_memcpy(mp->stream_name, stream_name, name_len);
+    }
+    vec_free(stream_name);
+
+    S; W;
+    /* NOTREACHED */
+    return 0;
+}
+
 static int q_or_quit (vat_main_t * vam)
 {
     longjmp (vam->jump_buf, 1);
@@ -12844,7 +13000,10 @@
                 "src_address <ip4> [fib_id <nn>] [path_mtu <nn>] "      \
                 "[template_interval <nn>]")                             \
 _(ipfix_dump, "")                                                       \
-_(get_next_index, "node-name <node-name> next-node-name <node-name>")
+_(get_next_index, "node-name <node-name> next-node-name <node-name>")   \
+_(pg_create_interface, "if_id <nn>")                                    \
+_(pg_capture, "if_id <nnn> pcap <file_name> count <nnn> [disable]")     \
+_(pg_enable_disable, "[stream <id>] disable")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c
index 4fe183b..91ec833 100644
--- a/vpp/vpp-api/api.c
+++ b/vpp/vpp-api/api.c
@@ -370,7 +370,10 @@
 _(CLASSIFY_SESSION_DETAILS,classify_session_details)                    \
 _(IPFIX_ENABLE,ipfix_enable)                                            \
 _(IPFIX_DUMP,ipfix_dump)                                                \
-_(GET_NEXT_INDEX, get_next_index)
+_(GET_NEXT_INDEX, get_next_index)                                       \
+_(PG_CREATE_INTERFACE, pg_create_interface)                             \
+_(PG_CAPTURE, pg_capture)                                               \
+_(PG_ENABLE_DISABLE, pg_enable_disable)
 
 #define QUOTE_(x) #x
 #define QUOTE(x) QUOTE_(x)
@@ -7212,6 +7215,86 @@
     vl_msg_api_send_shmem (q, (u8 *)&rmp);
 }
 
+static void vl_api_pg_create_interface_t_handler (vl_api_pg_create_interface_t *mp)
+{
+    vl_api_pg_create_interface_reply_t *rmp;
+    int rv = 0;
+
+    pg_main_t * pg = &pg_main;
+    u32 sw_if_index = pg_interface_add_or_get (pg, ntohl(mp->interface_id));
+
+    REPLY_MACRO2(VL_API_PG_CREATE_INTERFACE_REPLY,
+    ({
+       rmp->sw_if_index = ntohl(sw_if_index);
+    }));
+}
+
+static void vl_api_pg_capture_t_handler (vl_api_pg_capture_t *mp)
+{
+    vl_api_pg_capture_reply_t *rmp;
+    int rv = 0;
+
+    vnet_main_t * vnm = vnet_get_main();
+    vnet_interface_main_t * im = &vnm->interface_main;
+    vnet_hw_interface_t * hi = 0;
+
+    u8 * intf_name = format (0, "pg%d", ntohl(mp->interface_id), 0);
+    u32 hw_if_index = ~0;
+    uword * p = hash_get_mem (im->hw_interface_by_name, intf_name);
+    if (p)
+    	hw_if_index = *p;
+    vec_free (intf_name);
+
+    if (hw_if_index != ~0) {
+        pg_capture_args_t _a, *a=&_a;
+
+        u32 len = ntohl(mp->pcap_name_length);
+        u8 * pcap_file_name = vec_new(u8, len);
+        clib_memcpy(pcap_file_name, mp->pcap_file_name, len);
+
+        hi = vnet_get_sup_hw_interface (vnm, hw_if_index);
+        a->hw_if_index = hw_if_index;
+        a->dev_instance = hi->dev_instance;
+        a->is_enabled = mp->is_enabled;
+        a->pcap_file_name = pcap_file_name;
+        a->count = ntohl(mp->count);
+
+        clib_error_t * e = pg_capture (a);
+        if (e) {
+            clib_error_report(e);
+            rv = VNET_API_ERROR_CANNOT_CREATE_PCAP_FILE;
+        }
+
+        vec_free (pcap_file_name);
+    }
+	REPLY_MACRO(VL_API_PG_CAPTURE_REPLY);
+}
+
+static void vl_api_pg_enable_disable_t_handler (vl_api_pg_enable_disable_t *mp)
+{
+    vl_api_pg_enable_disable_reply_t *rmp;
+    int rv = 0;
+
+    pg_main_t * pg = &pg_main;
+    u32 stream_index = ~0;
+
+    int is_enable = mp->is_enabled != 0;
+    u32 len = ntohl(mp->stream_name_length)-1;
+
+    if (len>0) {
+        u8 * stream_name = vec_new(u8, len);
+        clib_memcpy(stream_name, mp->stream_name, len);
+        uword * p = hash_get_mem (pg->stream_index_by_name, stream_name);
+        if (p)
+            stream_index = *p;
+        vec_free(stream_name);
+    }
+
+    pg_enable_disable (stream_index, is_enable);
+
+    REPLY_MACRO(VL_API_PG_ENABLE_DISABLE_REPLY);
+}
+
 #define BOUNCE_HANDLER(nn)                                              \
 static void vl_api_##nn##_t_handler (                                   \
     vl_api_##nn##_t *mp)                                                \
diff --git a/vpp/vpp-api/custom_dump.c b/vpp/vpp-api/custom_dump.c
index 4e8b064..cb91d73 100644
--- a/vpp/vpp-api/custom_dump.c
+++ b/vpp/vpp-api/custom_dump.c
@@ -1887,6 +1887,47 @@
     FINISH;
 }
 
+static void *vl_api_pg_create_interface_t_print
+(vl_api_pg_create_interface_t * mp, void *handle)
+{
+    u8 * s;
+
+    s = format (0, "SCRIPT: pg_create_interface ");
+    s = format (0, "if_id %d", ntohl(mp->interface_id));
+
+    FINISH;
+}
+
+static void *vl_api_pg_capture_t_print
+(vl_api_pg_capture_t * mp, void *handle)
+{
+    u8 * s;
+
+    s = format (0, "SCRIPT: pg_capture ");
+    s = format (0, "if_id %d ", ntohl(mp->interface_id));
+    s = format (0, "pcap %s", mp->pcap_file_name);
+    if (mp->count != ~0)
+      s = format (s, "count %d ", ntohl(mp->count));
+    if (!mp->is_enabled)
+    	s = format (s, "disable");
+
+    FINISH;
+}
+
+static void *vl_api_pg_enable_disable_t_print
+(vl_api_pg_enable_disable_t * mp, void *handle)
+{
+	u8 * s;
+
+    s = format (0, "SCRIPT: pg_enable_disable ");
+    if (ntohl(mp->stream_name_length) > 0)
+      s = format (s, "stream %s", mp->stream_name);
+    if (!mp->is_enabled)
+      s = format (s, "disable");
+
+    FINISH;
+}
+
 #define foreach_custom_print_function                                   \
 _(CREATE_LOOPBACK, create_loopback)                                     \
 _(SW_INTERFACE_SET_FLAGS, sw_interface_set_flags)                       \
@@ -1985,7 +2026,10 @@
 _(CLASSIFY_SESSION_DUMP,classify_session_dump)                          \
 _(IPFIX_ENABLE,ipfix_enable)                                            \
 _(IPFIX_DUMP,ipfix_dump)                                                \
-_(GET_NEXT_INDEX, get_next_index)
+_(GET_NEXT_INDEX, get_next_index)                                       \
+_(PG_CREATE_INTERFACE,pg_create_interface)                              \
+_(PG_CAPTURE, pg_capture)                                               \
+_(PG_ENABLE_DISABLE, pg_enable_disable)
 
 void vl_msg_api_custom_dump_configure (api_main_t *am)
 {
diff --git a/vpp/vpp-api/vpe.api b/vpp/vpp-api/vpe.api
index 69ef708..2ec4d77 100644
--- a/vpp/vpp-api/vpe.api
+++ b/vpp/vpp-api/vpe.api
@@ -4134,3 +4134,74 @@
     i32 retval;
     u32 next_index;
 };
+
+/** \brief PacketGenerator create interface request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param interface_id - interface index
+*/
+define pg_create_interface {
+    u32 client_index;
+    u32 context;
+    u32 interface_id;
+};
+
+/** \brief PacketGenerator create interface response
+    @param context - sender context, to match reply w/ request
+    @param retval - return value for request
+*/
+define pg_create_interface_reply {
+    u32 context;
+    i32 retval;
+    u32 sw_if_index;
+};
+
+/** \brief PacketGenerator capture packets on given interface request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param interface_id - pg interface index
+    @param is_enabled - 1 if enabling streams, 0 if disabling
+    @param count - number of packets to be captured
+    @param pcap_file - pacp file name to store captured packets
+*/
+define pg_capture {
+    u32 client_index;
+    u32 context;
+    u32 interface_id;
+    u8  is_enabled;
+    u32 count;
+    u32 pcap_name_length;
+    u8  pcap_file_name[pcap_name_length];
+};
+
+/** \brief PacketGenerator capture packets response
+    @param context - sender context, to match reply w/ request
+    @param retval - return value for request
+*/
+define pg_capture_reply {
+    u32 context;
+    i32 retval;
+};
+
+/** \brief Enable / disable packet generator request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_enabled - 1 if enabling streams, 0 if disabling
+    @param stream - stream name to be enable/disabled, if not specified handle all streams 
+*/
+define pg_enable_disable {
+    u32 client_index;
+    u32 context;
+    u8  is_enabled;
+    u32 stream_name_length;
+    u8  stream_name[stream_name_length];
+};
+
+/** \brief Reply for enable / disable packet generator
+    @param context - returned sender context, to match reply w/ request
+    @param retval - return code
+*/
+define pg_enable_disable_reply {
+    u32 context;
+    i32 retval;
+};