From 9e6ed6e2e1b8af46a317f9448da2bca8d8c5395d Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Tue, 12 Jul 2016 10:18:26 +0200 Subject: [PATCH] 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 --- vnet/vnet/api_errno.h | 3 +- vnet/vnet/pg/cli.c | 89 +++++++++++++--------- vnet/vnet/pg/pg.h | 12 +++ vpp-api-test/vat/api_format.c | 167 +++++++++++++++++++++++++++++++++++++++++- vpp/vpp-api/api.c | 85 ++++++++++++++++++++- vpp/vpp-api/custom_dump.c | 46 +++++++++++- vpp/vpp-api/vpe.api | 71 ++++++++++++++++++ 7 files changed, 433 insertions(+), 40 deletions(-) diff --git a/vnet/vnet/api_errno.h b/vnet/vnet/api_errno.h index f16d9781bdc..2c247d451ff 100644 --- a/vnet/vnet/api_errno.h +++ b/vnet/vnet/api_errno.h @@ -82,7 +82,8 @@ _(FAILED_TO_ATTACH_TO_JAVA_THREAD, -88, "Failed to attach to Java thread") \ _(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 d73add1220d..96c20b70144 100644 --- a/vnet/vnet/pg/cli.c +++ b/vnet/vnet/pg/cli.c @@ -52,13 +52,58 @@ VLIB_CLI_COMMAND (vlib_cli_pg_command, static) = { .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 @@ enable_disable_stream (vlib_main_t * vm, 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 @@ pg_capture_cmd_fn (vlib_main_t * vm, 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 @@ pg_capture_cmd_fn (vlib_main_t * vm, 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; - - 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; + 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; - 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 750e7f740b2..54c270ff0a8 100644 --- a/vnet/vnet/pg/pg.h +++ b/vnet/vnet/pg/pg.h @@ -352,4 +352,16 @@ void pg_edit_group_get_fixed_packet_data (pg_stream_t * s, 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 70d8e96a603..0ab9b078c97 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 + #define vl_typedefs /* define message structures */ #include #undef vl_typedefs @@ -2781,13 +2783,41 @@ static void vl_api_classify_session_details_t_handler_json (vl_api_classify_sess 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 @@ _(af_packet_delete_reply) \ _(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_TABLE_INFO_REPLY, classify_table_info_reply) \ _(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 @@ int api_ipfix_dump (vat_main_t *vam) 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 @@ _(ipfix_enable, "collector_address [collector_port ] " \ "src_address [fib_id ] [path_mtu ] " \ "[template_interval ]") \ _(ipfix_dump, "") \ -_(get_next_index, "node-name next-node-name ") +_(get_next_index, "node-name next-node-name ") \ +_(pg_create_interface, "if_id ") \ +_(pg_capture, "if_id pcap count [disable]") \ +_(pg_enable_disable, "[stream ] 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 4fe183bc579..91ec833e7b0 100644 --- a/vpp/vpp-api/api.c +++ b/vpp/vpp-api/api.c @@ -370,7 +370,10 @@ _(CLASSIFY_SESSION_DUMP,classify_session_dump) \ _(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 @@ static void vl_api_ipfix_dump_t_handler (vl_api_ipfix_dump_t *mp) 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 4e8b0642763..cb91d73cbfb 100644 --- a/vpp/vpp-api/custom_dump.c +++ b/vpp/vpp-api/custom_dump.c @@ -1887,6 +1887,47 @@ static void *vl_api_get_next_index_t_print 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_TABLE_INFO,classify_table_info) \ _(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 69ef70887d6..2ec4d770e2c 100644 --- a/vpp/vpp-api/vpe.api +++ b/vpp/vpp-api/vpe.api @@ -4134,3 +4134,74 @@ define get_next_index_reply { 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; +}; -- 2.16.6