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;
+};